zoukankan      html  css  js  c++  java
  • 设计模式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类的带参方法实现

      

  • 相关阅读:
    phpspider爬虫框架的使用
    【php设计模式】责任链模式
    【php设计模式】策略模式
    【php设计模式】观察者模式
    【php设计模式】模板模式
    【温故知新】php 魔术方法
    【php设计模式】享元模式
    Java50道经典习题-程序18 乒乓球赛
    Java50道经典习题-程序19 输入行数打印菱形图案
    Java50道经典习题-程序20 求前20项之和
  • 原文地址:https://www.cnblogs.com/wfdespace/p/12896546.html
Copyright © 2011-2022 走看看