Spring中的若干设计模式

版权声明:@Author 犯罪嫌疑人卢某 洒家辛苦总结 希望尊重洒家的劳动哟 https://blog.csdn.net/unscdf117/article/details/82355650

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");
    }
}

JDK代理结果

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的脸按在地上一顿摩擦.......");
    }
}

Cglib实现

工厂模式:
Spring中十分重要的一个设计模式就是工厂模式,例如BeanFactory XMLBeanFactory等都是工厂模式的具体实现.工厂模式也是在Java多年的发展中由很多前辈们积累的宝贵经验.当一个Bean需要被实例化的时候,一般的编码方式肯定是new出来或者getInstance();但这有一个问题,如果说有几个类需要创建实例对象,这几个类都有一些共同点,姑且可以看成某个类的一些变种,例如:需要实例化若干步枪,步枪的大体功能都是瞄准射击,步枪种类千奇百怪,口径不尽相同,有半自动全自动栓动前装药后装药预装药,有壳无壳,能上刺刀不能上刺刀…此时如果说要一个个new出来的话违背了程序开发的复用性原则,此时就需要工厂模式.

先来一个没有工厂模式的情况:
直接new
Result
需要三支步枪要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容器就是一个大工厂.

猜你喜欢

转载自blog.csdn.net/unscdf117/article/details/82355650
今日推荐