• 设计模式06-代理模式


    1.5.深入分析代理模式

    1.5.1.深度分析代理模式【上】

    时长:46min

    学习目标

      》代理模式的应用场景及实现原理

      》区分静态代理与动态代理

      》cglib和jdk Proxy实现动态代理的区别

      》手写实现定义的动态代理

      》spring Aop基于动态代理,打下基础

    5.1.代理模式定义

    5.1.1.什么是代理模式?

      Proxy Pattern,是指为其他对象提供一种代理,以控制对这个对象的访问。

      代理对象,在客户端与目标对象之间起到中介作用,起到功能增强的作用。

      属于结构型设计模式。

    代理之生活理解:

      可以参照中介来理解。如:租房中介,用户需要租房子,但是找不到或没时间找房子【用户功能不够强大】,那么,

    就找到一个代理【中介】,中介是专门找到房子的【他的功能比用户强大】,最后中介帮用户找到了房子。

      所以,代理是对目标对象【用户】功能的增强。

    生活中代理场景:

      房产中介,快递小哥,黄牛党。

      

    5.1.2.代理模式的适用场景

    》保护目标对象

    》增强目标对象

    三层架构:典型的静态代理模式

    5.1.3.代理模式的优点与缺点

    优点

      代理模式,能够将代理对象与真实的目标对象分离

      一定程度上,降低了系统的耦合程度,易于扩展

      代理可以起到保护目标对象的作用。

      可以增强目标对象的职责,功能。

    缺点

      会造成系统设计中类数量的增加。

      在客户端与目标对象之间增加了一个代理对象,请求速度变慢。

      增加系统复杂性

    5.1.4.spring中的代理选择原则

    1.当Bean有实现接口时,默认使用Jdk的动态代理

    2.当Bean没有实现接口时,Spring会选择cglib实现。

    3.spring可以通过配置强制使用cglib,只需在spring配置文件中,使用如下配置:

    <aop:aspectj-autoproxy proxy-target-class="true"/> //强制设置cglib实现

    5.2.代理模式的实现示例代码

    5.2.1.通用写法

    5.2.1.1.目标对象定义

      由于面向接口编程,定义顶层接口:

    package com.wf.proxy.general;
    
    /**
     * @ClassName ISubject
     * @Description 目标对象的抽象接口
     * @Author wf
     * @Date 2020/5/15 17:00
     * @Version 1.0
     */
    public interface ISubject {
        void request();
    }

    目标对象实现:

    package com.wf.proxy.general;
    
    /**
     * @ClassName RealSubject
     * @Description 目标对象
     * @Author wf
     * @Date 2020/5/15 17:02
     * @Version 1.0
     */
    public class RealSubject implements ISubject {
        @Override
        public void request() {
            System.out.println("目标对象真实的服务实现");
        }
    }
    5.2.1.2.代理类定义
    package com.wf.proxy.general;
    
    /**
     * @ClassName Proxy
     * @Description 代理对象
     * @Author wf
     * @Date 2020/5/15 17:05
     * @Version 1.0
     */
    public class Proxy implements ISubject {
        //代理对象 需要对目标对象的功能进行增强
        //即增强目标对象的方法,所以实现同一接口
        //增强目标对象,所以个课程目标对象【基于多态,可以引入顶层接口bean】
        private ISubject subject;
        
        //客户端要传参目标对象,这里定义构建器传参
        public Proxy(ISubject subject) {
            this.subject = subject;
        }
    
        //程序中,什么叫功能增强,就是多加一段代码,提供更多功能
        @Override
        public void request() {
            doBefore();
            //方法前增强
            subject.request();
            //方法后增强
            doAfter();
        }
    
        private void doAfter() {
            System.out.println("方法后面进行增强");
        }
    
        private void doBefore() {
            System.out.println("方法前面进行增强");
        }
    }
    5.2.1.3.客户端调用
    package com.wf.proxy.general;
    
    
    /**
     * @ClassName Client
     * @Description 客户端调用
     * @Author wf
     * @Date 2020/5/15 17:15
     * @Version 1.0
     */
    public class Client {
        public static void main(String[] args) {
            RealSubject subject = new RealSubject();
            Proxy proxy = new Proxy(subject);
            proxy.request();
        }
    }

    测试结果如下:

     说明:

      显然,request方法的功能得到了增强。

    5.2.1.4.系统类图

    5.3.静态代理与动态代理

    5.3.1.什么是静态代理?

      先以一个生活场景,通过代码来说明。

      程序员平时加班较多,没时间找对象-----------父母着急,帮忙物色对象【充当代理】,安排相亲-------最后找到女朋友。

      抽象出业务模型

      Person----Parent-----------findLove

    5.3.1.1.代码示例
    1.定义顶层接口
    package com.wf.proxy.staticproxy;
    
    /**
     * @ClassName IPerson
     * @Description 程序员顶层抽象接口
     * @Author wf
     * @Date 2020/5/15 17:31
     * @Version 1.0
     */
    public interface IPerson {
        void findLove();
    }
    2.目标对象
    package com.wf.proxy.staticproxy;
    
    /**
     * @ClassName Person
     * @Description 程序员 目标对象
     * @Author wf
     * @Date 2020/5/15 17:33
     * @Version 1.0
     */
    public class Person implements IPerson {
        @Override
        public void findLove() {
            System.out.println("程序员,自己没有找到女朋友");
        }
    }
    3.代理类---父母
    package com.wf.proxy.staticproxy;
    
    /**
     * @ClassName ParentProxy
     * @Description 父母 代理类
     * @Author wf
     * @Date 2020/5/15 17:36
     * @Version 1.0
     */
    public class ParentProxy implements IPerson {
        private IPerson person;
    
        public ParentProxy(IPerson person) {
            this.person = person;
        }
    
        @Override
        public void findLove() {
            //
            System.out.println("开始帮儿子,物色对象");
            person.findLove();
            System.out.println("儿子说,ok,开始交往");
        }
    }
    4.客户端代码
    package com.wf.proxy.staticproxy;
    
    /**
     * @ClassName Client
     * @Description 客户端调用类
     * @Author wf
     * @Date 2020/5/15 17:41
     * @Version 1.0
     */
    public class Client {
        public static void main(String[] args) {
            Person person = new Person();
            ParentProxy proxy = new ParentProxy(person);
            proxy.findLove();
        }
    }

    测试结果如下:

     说明:

      因为有大量人可能也没有找到对象,针对这种需求,社会上形成一个产业链,叫婚介所,婚恋网。

      因此,系统需要扩展

      但是,张三的父母,力量有限,只能给儿子一个人物色对象。

      如果李四也想找到张三的父母物色对象,就没办法完成。也就是父母这个代理只能代理儿子的事情。

      这就是静态代理。

      如果想要为更多人做代理,代理需要更为强大,于是,产生动态代理

    静态代理:

      只能代理某特定的目标对象。如:房产中介,只能代理租客。

    动态代理:

      代理对象可以代理任意的目标对象。

    5.3.2.动态代理的实现

      在java中,动态代理有两种实现方案:

     》jdk Proxy代理类实现

     》cglib库实现

    5.3.2.1.jdk实现动态代理
    1.婚介所代理类创建
    package com.wf.proxy.dynamicproxy.jdkproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @ClassName MarriageAgency
     * @Description 婚介所 代理类
     * @Author wf
     * @Date 2020/5/15 18:23
     * @Version 1.0
     */
    public class MarriageAgency implements InvocationHandler {
        //jdk动态代理 实现一个抽象的接口【接口可以定义任意功能】
    
        //加强目标对象功能,需要引入目标对象
        private IPerson person;
    
        public IPerson getProxy(IPerson person) {
            this.person = person;
            //根据目标对象,得到代理对象
            //jdk底层是基于字节码,需要传参代理类的类加载器,并且代理对象需要实现接口功能【传参接口,实例this】
            Class<? extends IPerson> personClass = person.getClass();
            return (IPerson)Proxy.newProxyInstance(personClass.getClassLoader(), personClass.getInterfaces(),this);
        }
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //加强目标对象的方法
            doBefore();
            Object result = method.invoke(this.person, args);
            doAfter();
            return result;
        }
    
        private void doAfter() {
            System.out.println("是不是确认关系,开始交往");
        }
    
        private void doBefore() {
            System.out.println("开始物色对象");
        }
    }
    2.顶层接口类
    package com.wf.proxy.dynamicproxy.jdkproxy;
    
    /**
     * @ClassName IPerson
     * @Description 程序员顶层抽象接口
     * @Author wf
     * @Date 2020/5/15 17:31
     * @Version 1.0
     */
    public interface IPerson {
        void findLove();
    }

     然后,就可以定义不同的人实现IPerson接口。测试类【略】

    5.3.2.2.基于cglib实现动态代理
    1.添加cglib pom依赖
      <!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
    <dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>2.2</version>
    <!-- <scope>test</scope>-->
    </dependency>
    2.定义代理类
    package com.wf.proxy.dynamicproxy.cglibproxy;
    
    import com.wf.proxy.dynamicproxy.jdkproxy.IPerson;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @ClassName CglibMarriageAgency
     * @Description 婚介所 代理类
     * @Author wf
     * @Date 2020/5/15 18:23
     * @Version 1.0
     */
    public class CglibMarriageAgency implements MethodInterceptor {
        //jdk动态代理 实现一个抽象的接口【接口可以定义任意功能】
    
        //加强目标对象功能,需要引入目标对象
        private IPerson person;
    
        public Object getProxy(Class<?> clazz) {
            //根据目标对象,得到代理对象
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        private void doAfter() {
            System.out.println("是不是确认关系,开始交往");
        }
    
        private void doBefore() {
            System.out.println("开始物色对象");
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            doBefore();
            Object result = methodProxy.invokeSuper(obj, objects);
            doAfter();
            return result;
        }
    }

    测试类:

    package com.wf.proxy.dynamicproxy.cglibproxy;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/5/18 14:15
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            PersonZhang person = (PersonZhang) new CglibMarriageAgency().getProxy(PersonZhang.class);
            person.findLove();
        }
    }

    测试结果如下:

     1.5.2.深度分析代理模式【下】

    时长:1h22min

     5.2.动态代理实现原理分析

    5.2.1.Jdk实现原理

    5.2.1.1.示例代码
    1.测试代码
    package com.wf.proxy.dynamicproxy.jdkproxy;
    
    /**
     * @ClassName Test
     * @Description
     * @Author wf
     * @Date 2020/5/18 16:06
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            MarriageAgency proxy = new MarriageAgency();
            IPerson personLi = proxy.getProxy(new PersonLi());
            personLi.findLove();
        }
    }
    2.断点调试程序

     

     可以发现,personLi是一个特殊的类型声明。$Proxy0@521【以$开头的实例,都是动态代理生成的,只在内存可见】

    3.工具类查看代理实现

    package com.wf.proxy.dynamicproxy.jdkproxy;
    
    import sun.misc.ProxyGenerator;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    
    /**
     * @ClassName Test
     * @Description
     * @Author wf
     * @Date 2020/5/18 16:06
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            MarriageAgency proxy = new MarriageAgency();
            IPerson personLi = proxy.getProxy(new PersonLi());
            personLi.findLove();
            //使用工具,获取代理类的代理对象内部实现
            byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IPerson.class});
            try {
                FileOutputStream fos = new FileOutputStream("proxy0.class");
    
                fos.write(bytes);
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    在文件目录下生成类:

     打开内容如下:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import com.wf.proxy.dynamicproxy.jdkproxy.IPerson;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements IPerson {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final void findLove() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m3 = Class.forName("com.wf.proxy.dynamicproxy.jdkproxy.IPerson").getMethod("findLove");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    5.2.1.2.模仿jdk自定义动态代理实现
    1.定义MyInvocationHandler
    package com.wf.proxy.dynamicproxy.defproxy;
    
    import java.lang.reflect.Method;
    
    /**
     * @ClassName MyInvocationHandler
     * @Description 自定义handler
     * @Author wf
     * @Date 2020/5/18 16:35
     * @Version 1.0
     */
    public interface MyInvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable;
    }
    2.自定义Proxy工具类
    package com.wf.proxy.dynamicproxy.defproxy;
    
    import javax.tools.JavaCompiler;
    import javax.tools.JavaFileObject;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import java.io.File;
    import java.io.FileWriter;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    /**
     * @ClassName MyProxy
     * @Description 自定义Proxy工具类
     * @Author wf
     * @Date 2020/5/18 16:37
     * @Version 1.0
     */
    public class MyProxy {
    
        public static final String ln = "
    ";
    
        public static Object newProxyInstance(MyClassLoader loader, Class<?>[] interfaces, MyInvocationHandler h){
            //1.动态生成源码.java文件
            String srcFile = generateSrc(interfaces);
            System.out.println(srcFile);
            //2.输出.java文件到磁盘
            String filePath = MyProxy.class.getResource("").getPath();
            File file = new File(filePath+"$Proxy0.java");
            try {
                FileWriter fw = new FileWriter(file);
                fw.write(srcFile);
                fw.flush();
                fw.close();
    
                //3.编译.java文件,成$Proxy0.class文件
                JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
                Iterable<? extends JavaFileObject> it = manager.getJavaFileObjects(file);
                JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, it);
                task.call();
                manager.close();
    
    
                //4.把生成的.class文件加载到jvm中
                Class<?> proxyClass = loader.findClass("$Proxy0");
                Constructor<?> constructor = proxyClass.getConstructor(MyInvocationHandler.class);
    
    
                //5.返回新的代理对象
                return constructor.newInstance(h);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private static String generateSrc(Class<?>[] interfaces) {
            StringBuilder sb = new StringBuilder();
            sb.append(MyProxy.class.getPackage()).append(";").append(ln);
            sb.append("import ").append(interfaces[0].getName()).append(";").append(ln);
            sb.append("import java.lang.reflect.*;").append(ln);
            sb.append("public final class $Proxy0 implements ").append(interfaces[0].getSimpleName()).append("{").append(ln);
            sb.append("MyInvocationHandler h;").append(ln);
            sb.append("public $Proxy0(MyInvocationHandler h){").append(ln).
                    append("    this.h = h;").append(ln).
                    append("    }").append(ln);
            for(Method m: interfaces[0].getMethods()){
                Class<?>[] params = m.getParameterTypes();
                StringBuilder paramNames = new StringBuilder();
                StringBuilder paramValues = new StringBuilder();
                StringBuilder paramClasses = new StringBuilder();
    
                for(int i=0; i < params.length; i++){
                    Class clazz = params[i];
                    String type = clazz.getName();
                    String paramName = toLowerFirstCase(clazz.getSimpleName());
                    paramNames.append(type).append(" ").append(paramName);
                    paramValues.append(paramName);
                    paramClasses.append(clazz.getName()).append(".class");
                    if(i < params.length -1){
                        paramNames.append(",");
                        paramValues.append(",");
                        paramClasses.append(",");
                    }
                }
                sb.append("public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append("(")
                        .append(paramNames).append("){").append(ln);
                sb.append("     try {" ).append(ln).
                        append("        Method m = ").append(interfaces[0].getName()).append(".class.getMethod("").append(m.getName()).
                        append("",new Class[]{").append(paramClasses).append("} );").append(ln);
                sb.append("     ").append(hasReturnValue(m.getReturnType()) ? "return ("+m.getReturnType().getTypeName()+")": "").
                        append("this.h.invoke(this, m, ").append("new Object[]{").append(paramValues).append("});").append(ln).
                append("    } catch (RuntimeException | Error var2) {
    " ).
                append("            throw var2;
    " ).
                append("    } catch (Throwable var3) {
    " ).
                append("            throw new UndeclaredThrowableException(var3);
    " ).
                append("        }
    " );//.append(getReturnEmptyCode(m.getReturnType())).append(ln);
                sb.append("    }");
            }
            return sb.append(ln).append("}").toString();
        }
    
        private static String getReturnEmptyCode(Class<?> returnType) {
            if(hasReturnValue(returnType)){
                return "return null;";
            }
            return "return;";
        }
    
    
        private static boolean hasReturnValue(Class<?> returnType) {
            if(returnType == Void.class || returnType == void.class){
                return false;
            }
            return true;
        }
    
        private static String toLowerFirstCase(String paramName) {
            char first = paramName.charAt(0);
    
            //首字母大写判断
            if(first >= 'A' && first<= 'Z'){
                first += 32;
                paramName = first + paramName.substring(1);
            }
    
            return paramName;
        }
    
    }

    会在项目根目录cglib_proxy_classes生成cglib相关实例信息,如下所示:

    5.3.cglib与jdk实现动态代理的总结

    cglib采用继承的方式,覆盖父类的方法

    jdk采用实现接口的方式,要求代理的目标对象,必须实现一个接口。、

    两者的思想:

      都是生成字节码,再重组成一个新的类。

    缺点及优点:

      Jdk Proxy,对于用户而言,依赖性更强,调用也更复杂。生成逻辑较为简单,执行效率较低,每次都要用到反射。

      cglib 对目标类没有任何要求。效率更高,性能更好,底层没有用到反射。

      cglib有个坑,目标代理对象不能有final修饰的方法,忽略final修饰的方法。

    5.4.代理的运用案例

    5.4.1.示例代码

    5.4.1.1.Service服务
    1.接口服务定义
    package com.wf.proxy.dbroute;
    
    import com.wf.proxy.dbroute.dbsource.DynamicDataSourceEntity;
    
    import java.text.ChoiceFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @ClassName OrderServiceImpl
     * @Description 订单服务实现,OrderServiceImpl类就相当于代理类,orderDao就是目标对象,这是静态代理
     * @Author wf
     * @Date 2020/5/19 14:24
     * @Version 1.0
     */
    public class OrderServiceImpl implements IOrderService {
        private OrderDao orderDao;
        //时间年份格式化
        private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    
        public OrderServiceImpl() {
            //spring中使用自动注入
            //普通maven工程中,使用构造器注入
            this.orderDao = new OrderDao();
        }
    
        /**
         * 在调用orderDao.insert前后会做一些处理,比如,设置订单创建时间
         * 这里的前后处理,需要架构师来统一操作,如果每个都写的话,很乱
         * 在当前代理的上层,还需要一个父类代理
         * @param order
         * @return
         */
        @Override
        public int createOrder(Order order) {
            System.out.println("OrderService服务调用orderDao创建订单");
            Long time = order.getCreateTime();
            //根据订单创建年份,进行路由,选择数据源
            Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("自动切换数据源到【DB_"+dbRouter+"】");
    
            DynamicDataSourceEntity.set(dbRouter);
            int rows = this.orderDao.insert(order);
            //用完后,释放数据源
            DynamicDataSourceEntity.restore();
            return rows;
        }
    }
    2.创建订单业务bean
    package com.wf.proxy.dbroute;
    
    /**
     * @ClassName Order
     * @Description 订单业务bean
     * @Author wf
     * @Date 2020/5/19 14:23
     * @Version 1.0
     */
    public class Order {
        private Long createTime;
    
        public void setCreateTime(Long createTime) {
            this.createTime = createTime;
        }
    
        public Long getCreateTime() {
            return createTime;
        }
    }
    3.创建dao层服务

      这里为示例简洁性,直接定义class服务,而没有面向接口编程。

    package com.wf.proxy.dbroute;
    
    /**
     * @ClassName OrderDao
     * @Description 订单dao实现
     * @Author wf
     * @Date 2020/5/19 15:01
     * @Version 1.0
     */
    public class OrderDao {
    
        public int insert(Order order) {
            System.out.println("创建开始");
            return 0;
        }
    }
    4.数据源处理工具类

      这里需要切换数据源,定义数据源处理工具类,内部使用ThreadLocal存储。

    package com.wf.proxy.dbroute.dbsource;
    
    /**
     * @ClassName DynamicDataSourceEntity
     * @Description 动态切换数据源工具类
     * @Author wf
     * @Date 2020/5/19 14:38
     * @Version 1.0
     */
    public class DynamicDataSourceEntity {
        private static final ThreadLocal<String> local = new ThreadLocal<>();
        private static String DEFAULT_SOURCE = null;
    
        //单例控制
        private DynamicDataSourceEntity(){}
        public static String get(){
            return local.get();
        }
        public static void restore(){
            local.set(DEFAULT_SOURCE);
        }
    
        public static void set(String source){
            local.set(source);
        }
        //DB_2018
        //DB_2019
        public static void set(int year){
            local.set("DB_"+year);
        }
    
    }
    5.测试类
    package com.wf.proxy.dbroute;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @ClassName DBRouterProxyTest
     * @Description 数据源路由测试
     * @Author wf
     * @Date 2020/5/19 14:55
     * @Version 1.0
     */
    public class DBRouterProxyTest {
        public static void main(String[] args) {
            try{
                Order order = new Order();
    //            order.setCreateTime(System.currentTimeMillis());
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
                Date date = sdf.parse("2021/03/01");
                order.setCreateTime(date.getTime());
    
                IOrderService orderService = new OrderServiceImpl();
                orderService.createOrder(order);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    测试结果如下:

    说明:
      这里主要输出OrderServiceImpl服务实现的逻辑,由于希望把insert逻辑调用与数据源处理的逻辑分离,对该类创建代理类。

    5.4.1.2.定义代理类处理数据源切换
    1.增加上层代理类
    package com.wf.proxy.dbroute.proxy;
    
    import com.wf.proxy.dbroute.IOrderService;
    import com.wf.proxy.dbroute.Order;
    import com.wf.proxy.dbroute.dbsource.DynamicDataSourceEntity;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @ClassName OrderServiceStaticProxy
     * @Description 定义代理类,处理数据源切换
     * @Author wf
     * @Date 2020/5/19 14:49
     * @Version 1.0
     */
    public class OrderServiceStaticProxy implements IOrderService {
        //时间年份格式化
        private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
        private IOrderService orderService;
    
        public OrderServiceStaticProxy(IOrderService orderService) {
            this.orderService = orderService;
        }
    
        @Override
        public int createOrder(Order order) {
            Long time = order.getCreateTime();
            //根据订单创建年份,进行路由,选择数据源
            Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据");
    
            DynamicDataSourceEntity.set(dbRouter);
            this.orderService.createOrder(order);
            //用完后,释放数据源
            DynamicDataSourceEntity.restore();
            return 0;
        }
    }
    2.修改OrderServiceImpl服务
    package com.wf.proxy.dbroute;
    
    import com.wf.proxy.dbroute.dbsource.DynamicDataSourceEntity;
    
    import java.text.ChoiceFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @ClassName OrderServiceImpl
     * @Description 订单服务实现,OrderServiceImpl类就相当于代理类,orderDao就是目标对象,这是静态代理
     * @Author wf
     * @Date 2020/5/19 14:24
     * @Version 1.0
     */
    public class OrderServiceImpl implements IOrderService {
        private OrderDao orderDao;
        //时间年份格式化
        //private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    
        public OrderServiceImpl() {
            //spring中使用自动注入
            //普通maven工程中,使用构造器注入
            this.orderDao = new OrderDao();
        }
    
        /**
         * 在调用orderDao.insert前后会做一些处理,比如,设置订单创建时间
         * 这里的前后处理,需要架构师来统一操作,如果每个都写的话,很乱
         * 在当前代理的上层,还需要一个父类代理
         * @param order
         * @return
         */
        @Override
        public int createOrder(Order order) {
            System.out.println("OrderService服务调用orderDao创建订单");
            /*Long time = order.getCreateTime();
            //根据订单创建年份,进行路由,选择数据源
            Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("自动切换数据源到【DB_"+dbRouter+"】");
    
            DynamicDataSourceEntity.set(dbRouter);*/
            int rows = this.orderDao.insert(order);
            //用完后,释放数据源
    //        DynamicDataSourceEntity.restore();
            return rows;
        }
    }
    3.修改测试类
    package com.wf.proxy.dbroute;
    
    import com.sun.org.apache.xpath.internal.operations.Or;
    import com.wf.proxy.dbroute.proxy.OrderServiceStaticProxy;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @ClassName DBRouterProxyTest
     * @Description 数据源路由测试
     * @Author wf
     * @Date 2020/5/19 14:55
     * @Version 1.0
     */
    public class DBRouterProxyTest {
        /*public static void main(String[] args) {
            try{
                Order order = new Order();
    //            order.setCreateTime(System.currentTimeMillis());
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
                Date date = sdf.parse("2021/03/01");
                order.setCreateTime(date.getTime());
    
                IOrderService orderService = new OrderServiceImpl();
                orderService.createOrder(order);
            }catch (Exception e){
                e.printStackTrace();
            }
        }*/
    
        public static void main(String[] args) {
            try{
                Order order = new Order();
    //            order.setCreateTime(System.currentTimeMillis());
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
                Date date = sdf.parse("2021/03/01");
                order.setCreateTime(date.getTime());
                //调用代理类,创建代理对象
                IOrderService orderService = new OrderServiceStaticProxy(new OrderServiceImpl());
                orderService.createOrder(order);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    测试结果如下图:

     说明:

      现在的静态代理,只能代理OrderService不能代理其他Service服务,为了系统扩展性,和复用性。需要使用动态代理

    5.4.1.3.动态代理处理数据源切换
    1.创建动态代理类
    package com.wf.proxy.dbroute.proxy;
    
    import com.wf.proxy.dbroute.dbsource.DynamicDataSourceEntity;
    import com.wf.proxy.dynamicproxy.defproxy.MyClassLoader;
    import com.wf.proxy.dynamicproxy.defproxy.MyInvocationHandler;
    import com.wf.proxy.dynamicproxy.defproxy.MyProxy;
    import java.lang.reflect.Method;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @ClassName ServiceDynamicProxy
     * @Description 动态代理,创建代理类
     * @Author wf
     * @Date 2020/5/19 16:15
     * @Version 1.0
     */
    public class ServiceDynamicProxy implements MyInvocationHandler {
        //时间年份格式化
        private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
        private Object targetObj;
    
        public Object getInstance(Object targetObj){
            this.targetObj = targetObj;
            Class<?> clazz = targetObj.getClass();
            return MyProxy.newProxyInstance(new MyClassLoader(), clazz.getInterfaces(),this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            doBefore(args[0]);
            Object result = method.invoke(targetObj, args);
            doAfter();
            return result;
        }
    
        private void doAfter() {
            System.out.println("Proxy after method");
            //用完后,释放数据源
            DynamicDataSourceEntity.restore();
        }
    
        private void doBefore(Object arg) {
            System.out.println("Proxy before method");
            
            //约定优于配置
            try {
                Long time = (Long) arg.getClass().getMethod("getCreateTime").invoke(arg);
                //根据订单创建年份,进行路由,选择数据源
                Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
                System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据");
    
                DynamicDataSourceEntity.set(dbRouter);
            } catch (Exception e) {
                e.printStackTrace();
            }
            
        }
        
    }
    2.修改测试类

    5.5.代理模式相关问题思考

    5.5.1.总结静态代理与动态代理的根本区别

    静态代理:硬编码【只适用于某特定类型的bean的代理】,手动注入。

      依赖目标对象的引用,手动调用目标对象的方法。

    动态代理:

      具有更强的扩展性,自动注入,自动生成一个新的类【和目标类同一继承体系】

    特征:

        拿到目标对象的引用

        拿到目标对象的方法,进行增强

    5.5.2.使用java代码实现Proxy类的带参方法实现

      

  • 相关阅读:
    107. Binary Tree Level Order Traversal II
    108. Convert Sorted Array to Binary Search Tree
    111. Minimum Depth of Binary Tree
    49. Group Anagrams
    使用MALTAB标定实践记录
    442. Find All Duplicates in an Array
    522. Longest Uncommon Subsequence II
    354. Russian Doll Envelopes
    opencv 小任务3 灰度直方图
    opencv 小任务2 灰度
  • 原文地址:https://www.cnblogs.com/wfdespace/p/12896546.html
Copyright © 2020-2023  润新知