设计模式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类的带参方法实现

  

原文地址:https://www.cnblogs.com/wfdespace/p/12896546.html