Spring不是无端开发出来的,开发Spring的时候用到了一大堆设计模式.或者说Spring是一个通用的组件而非业务组件,要适应各种业务场景,所以必然是抽象的也是处处体现其设计的.
代理模式:
代理模式的使用场景: 需要有一个执行者与一个被代理对象,而且是必须要执行的事情,但是真实对象并不能够参与或者满足条件,此时需要用代理模式。执行者需要拿到真实对象的代理也就是代理对象的引用,才能执行。Spring十分核心的一个设计模式就是代理模式,举个栗子,项目中我的一些方法需要被日志管理,比如在中台项目的支付中心当中,对于钱包流水的记录需要日志管理.显式地去写log.info会很臭很长,于是用Spring的AOP来做日志管理就太正常不过了.而Spring的AOP是要面向切面的,业务类上并没有显式书写切面代码,所以需要代理模式来进行增强,而JVM运行的则是被代理之后的对象而不是业务类的本类.
代理模式有动态代理和静态代理两种实现,上一个静态代理的Demo
/**
* 订单生成接口
*/
public interface OrderCreatorFacade {
/**
* 正向订单
* @param outSourceNum 外部订单号
*/
void createOrder(String outSourceNum);
/**
* 逆向订单
* @param outSourceNum 外部订单号
*/
void aftersaleOrder(String outSourceNum);
}
/**
* 订单创建Facade实现
*/
public class OrderCreateFacadeImpl implements OrderCreatorFacade {
@Override
public void createOrder(String outSourceNum) {
System.out.println("向下游系统推送正向订单:" + outSourceNum);
}
@Override
public void aftersaleOrder(String outSourceNum) {
System.out.println("向下游系统推送逆向订单:" + outSourceNum);
}
/**
* 订单创建代理类
*/
public class OrderCreateProxy implements OrderCreatorFacade {
/**
* 被代理类
*/
private OrderCreatorFacade creator;
/**
* 上位系统
*/
private String from;
/**
* 下游系统
*/
private String to;
public OrderCreateProxy(OrderCreatorFacade creator, String from, String to) {
this.creator = creator;
this.from = from;
this.to = to;
}
@Override
public void createOrder(String outSourceNum) {
//DO SOMETHING
System.out.println("上游系统:" + from + " ");
creator.createOrder(outSourceNum);
System.out.println("到下游系统: " + to);
//DO SOMETHING
}
@Override
public void aftersaleOrder(String outSourceNum) {
//DO SOMETHING
System.out.println("上游系统:" + from + " ");
creator.aftersaleOrder(outSourceNum);
System.out.println("到下游系统: " + to);
//DO SOMETHING
}
}
/**
* 试验田
*/
public class TestField {
public static void main(String[] args) {
OrderCreateProxy proxy = new OrderCreateProxy(new OrderCreateFacadeImpl()
, "店管家", "TTX-OMS");
proxy.aftersaleOrder("PO-10086");
proxy.createOrder("PO-11000");
}
}
这里有一个问题,如果被代理对象实现了多个接口,就需要为每一个接口都实现一个代理类,如果说有些业务逻辑是共用的,例如方法运行后记录日志,或者方法运行前对某个参数加一个常量值等,这个时候就会产生大量冗余代码,违反程序设计的复用性原则,所以需要用到动态代理.
Spring当中运用的动态代理有两种,分别为JDK原生的动态代理和cglib动态代理,JDK动态代理是在代理类被程序调用时才会被虚拟机创建实例,根据被代理类实例对象,方法名等动态创建一个代理类的字节码文件,这样可以被虚拟机所运行,之后就可以调用代理类的对应方法了.而cglib(code generate lib)见名知意,代码生成库,则是通过继承被代理类而或得到一个子类,并且将父类中的方法@Override一番实现代理功能,当然因为实现方式是继承所以不能实现final的类..
JDK动态代理Demo:
/**
* 绑定信用卡Facade
*/
public interface BindingCardFacade {
void bind(String sn);
}
/**
* 绑定信用卡Facade实现
*/
public class BindingCardServiceImpl implements BindingCardFacade {
public static final String BANK_NAME = "华夏银行";
@Override
public void bind(String sn) {
System.out.println("绑定信用卡:" + sn + "银行名称为" + BANK_NAME);
}
}
/**
* 绑卡Proxy
*/
public class BingCardProxy implements InvocationHandler {
/**
* target 为具体的entity
*/
private Object target;
public Object bind(Object target) {
this.target = target;
/**
* 一共三个参数 1.被增强对象的类加载器 2.被增强对象实现的接口 3.被增强对象本身
*/
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
return proxy;
}
/**
* invoke方法 方法的增强
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke do sth...");
Object invoke = method.invoke(target, args);
System.out.println("After invoke do sth...");
return invoke;
}
}
测试类:
/**
* 试验田
*/
public class TestField {
public static void main(String[] args) {
BindingCardFacade bindingCardService = new BindingCardServiceImpl();
BingCardProxy bcp = new BingCardProxy ();
BindingCardFacade proxy = (BindingCardFacade) bcp.bind(bindingCardService);
proxy.bind("6228480388867664512");
}
}
Cglib动态代理Demo:
/**
* 消息通知Sender
*/
public class NotifySender {
public void sendNotifyMsg(String message) {
System.out.println("发送消息通知" + message);
}
}
/**
* 消息通知Proxy
*/
public class NotifySenderProxy implements MethodInterceptor {
/**
* 传入具体的被代理对象
*/
private Object target;
public Object getInstance(Object target) {
this.target = target;
//Enhancer用于生成代理类
Enhancer enhancer = new Enhancer();
//制定父类来生成代理类
enhancer.setSuperclass(this.target.getClass());
//代理类上所有的方法调用都会调用callback 需要实现intercept方法进行拦截 所以传入this
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//before do sth
System.out.println("连接RabbitMQ, 设定Exchanger和RoutingKey");
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("接收ACK....");
//after do sth
return object;
}
}
/**
* 试验田
*/
public class CglibField {
public static void main(String[] args) {
NotifySenderProxy proxy = new NotifySenderProxy();
NotifySender instance = (NotifySender) proxy.getInstance(new NotifySender());
instance.sendNotifyMsg("Java把PHP的脸按在地上一顿摩擦.......");
}
}
工厂模式:
Spring中十分重要的一个设计模式就是工厂模式,例如BeanFactory XMLBeanFactory等都是工厂模式的具体实现.工厂模式也是在Java多年的发展中由很多前辈们积累的宝贵经验.当一个Bean需要被实例化的时候,一般的编码方式肯定是new出来或者getInstance();但这有一个问题,如果说有几个类需要创建实例对象,这几个类都有一些共同点,姑且可以看成某个类的一些变种,例如:需要实例化若干步枪,步枪的大体功能都是瞄准射击,步枪种类千奇百怪,口径不尽相同,有半自动全自动栓动前装药后装药预装药,有壳无壳,能上刺刀不能上刺刀…此时如果说要一个个new出来的话违背了程序开发的复用性原则,此时就需要工厂模式.
先来一个没有工厂模式的情况:
需要三支步枪要new出对应的实例来…硬编码不利于解耦,但凡是稍微有点追求的码畜也不应该这么做才是.
有工厂模式:
/**
* 步枪工厂
* @author DELL
* @date 2018/9/4
*/
public class RilfeFactory {
public static Rilfe getMA5D() {
return new MA5D();
}
public static Rilfe getMauser() {
return new Mauser();
}
public static Rilfe getQBZ03() {
return new QBZ03();
}
}
/**
* 试验田
* @author DELL
* @date 2018/9/4
*/
public class TestFeild {
public static void main(String[] args) {
Rilfe ma5D = RilfeFactory.getMA5D();
Rilfe mauser = RilfeFactory.getMauser();
Rilfe qbz03 = RilfeFactory.getQBZ03();
ma5D.shoot("MA");
}
}
看起来稍微好一些了,但是还是有一个问题存在,此时工厂只能生产这三种型号的步枪,如果需要再生产别的型号,则需要修改工厂的代码,这又显得十分的2B了.因为MA5D是25世纪产物,毛瑟是19世纪产物,QBZ是20世纪产物,所以我们可以根据纪元来让工厂判断生产什么型号,但这依旧十分麻烦.
改进使用反射生成武器:
/**
* 步枪工厂
* @author DELL
* @date 2018/9/4
*/
public class RilfeFactory {
public static Rilfe getInstance(String path) throws Exception {
return (Rilfe) Class.forName(path).newInstance();
}
}
/**
* 试验田
* @author DELL
* @date 2018/9/4
*/
public class TestFeild {
public static void main(String[] args) throws Exception {
Rilfe ma5d = RilfeFactory.getInstance("com.unsc.springsource.factory.entity.MA5D");
ma5d.reload(36, "7.62*51穿甲弹");
Mauser mauser = (Mauser) RilfeFactory.getInstance("com.unsc.springsource.factory.entity.Mauser");
mauser.bayonet("98K");
}
}
从此天下太平.而Spring的IOC容器则更进一步, 开发人员配置的配置就是被工厂加载和管理,再被程序所调用,原本需要写全的路径,也被配置文件所替代.IOC容器就是一个大工厂.