静态代理和动态代理

186 阅读4分钟

代理模式

一.静态代理

痛点

假如我们想对方法进行一个增强, 需要修改源代码. 假如有100个方法, 我们需要修改100个方法的源代码来实现方法增强.

/**
 * 增删改查方法增强例子
 * @author : Shen Hanbo
 * @date : 2020/11/9 16:01
 */
public class CrudServiceImpl implements CrudService {

    public void insert() {
        System.out.println("增强新增前置处理");
        System.out.println("新增");
        System.out.println("增强新增后置处理");
    }

    public void delete() {
        System.out.println("增强删除前置处理");
        System.out.println("删除");
        System.out.println("增强删除后置处理");
    }

    public void update() {
        System.out.println("增强更新前置处理");
        System.out.println("更新");
        System.out.println("增强更新后置处理");
    }

    public void query() {
        System.out.println("增强查询前置处理");
        System.out.println("查询");
        System.out.println("增强查询后置处理");
    }

    //...假如下面还有一堆方法的话, 每个方法都需要去手动改源代码
}

解决思路

生成一个代理类, 代理类要和被代理类拥有一样方法(实现同一个接口), 然后调用方直接调用代理类的方法, 而非被代理类的方法. 在代理类的实现方法中, 增强被代理类的方法, 然后客户端请求代理类.

代码实现

这里用房东/中介/租客来举例子

  • 首先房东和中介都需要有"租房"这个方法, 中介来增强房东的"租房"方法,因此需要定义一个租房接口.

    /**
     * 租房接口
     * @author : Shen Hanbo
     * @date : 2020/11/9 15:52
     */
    public interface ZuFang {
    
        /**
         * 房东和中介都需要实现租房接口,因为他们都有租房的功能
         */
        void zuFang();
    
    }
    
  • 房东的"租房"方法, 即被代理的方法

    /**
     * 房东类
     * @author : Shen Hanbo
     * @date : 2020/11/9 15:54
     */
    public class FangDong implements ZuFang {
    
        /**
         * 房东出租房子(被代理方法)
         */
        public void zuFang() {
            System.out.println("房东出租房子");
        }
    }
    
  • 中介的"租房"方法, 即代理方法, 在代理方法中对原方法进行增强

    /**
     * 中介(代理类)
     * @author : Shen Hanbo
     * @date : 2020/11/9 16:10
     */
    public class ZhongJie implements ZuFang {
    
        /**
         * 需要一个房东
         */
        private FangDong fangDong;
    
        public ZhongJie(FangDong fangDong) {
            this.fangDong = fangDong;
        }
    
        /**
         * 中介代理房东出租房子(代理方法)
         */
        public void zuFang() {
            //前置增强
            System.out.println("前置增强");
            //原方法
            fangDong.zuFang();
            //后置增强
            System.out.println("后置增强");
        }
    }
    
  • 调用

    public static void main(String[] args) {
        //房东
        FangDong fangDong = new FangDong();
        //中介代理房东
        ZhongJie zhongJie = new ZhongJie(fangDong);
        //调用中介的租房方法
        zhongJie.zuFang();
    }
     /*
        打印结果:
            前置增强
            房东出租房子
            后置增强
    */
    

二.动态代理

痛点

  • 静态代理需要手动编写代理类, 比如上个例子中的ZhongJie类.

  • 静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类, 每个接口都需要自己的代理类.

解决思路

在上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象

代码实现

InvocationHandler接口

    //Object proxy:被代理的对象  
    //Method method:要调用的方法  
    //Object[] args:方法调用时所需要参数  
    public interface InvocationHandler {  
      	//实际调用的方法会走这个方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
    }  

Proxy类

    //用于获取代理类
	//CLassLoader loader:类的加载器  
    //Class<?> interfaces:得到全部的接口  
    //InvocationHandler h:得到InvocationHandler接口的子类的实例  
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException  

实现

    public class LogHandler implements InvocationHandler {  
      
        // 目标对象(被代理类)  
        private Object targetObject;  
        //生成代理类:绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。              
        public Object newProxyInstance(Object targetObject){  
            this.targetObject=targetObject;  
            //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例    
            //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器  
            //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口  
            //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法  
            //根据传入的目标返回一个代理对象  
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),  
                    targetObject.getClass().getInterfaces(),this);  
        }  
        @Override  
        //关联的这个实现类的方法被调用时将被执行  
        /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/  
        public Object invoke(Object proxy, Method method, Object[] args)  
                throws Throwable {  
            System.out.println("动态代理前置增强");
            Object ret=method.invoke(targetObject, args);  
             System.out.println("动态代理后置增强");
            return ret;  
        }  
      
    }  

  public static void main(String[] args) {
      LogHandler logHandler = new LogHandler(new FangDong());
      ZuFang o = (ZuFang)logHandler.newProxyInstance();
      o.zuFang();
  }