代理模式

1 概念

1.1 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

1.2 组成

  抽象角色:通过接口或抽象类声明真实角色实现的业务方法。

  代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

  真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

1.3 优点
  1. 职责清晰:真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
  2. 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
  3. 高扩展性
1.4 UML类图

1.5 模式结构

  一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理

  对象实现同一个接口,先访问代理类再访问真正要访问的对象。

  代理模式分为:静态代理、动态代理。

  静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

  动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

2 静态代理和动态代理
2.1 静态代理

静态代理是基于接口的实现:

 1)创建一个抽象角色,指定真实角色具有哪些业务行为

1 /**
2  * 明星唱歌接口类
3  */
4 public interface SingService {
5     void sing();
6 }

 2)创建真实角色,实现其基本单一业务职责功能,如明星角色的主要任务就是唱歌

1 /**
2  * 明星类
3  */
4 public class Star implements SingService {
5     @Override
6     public void sing() {
7         System.out.println("明星唱歌......");
8     }
9 }

 3)创建代理角色,其职责是负责真实角色的打理工作

 1 /**
 2  * 经纪人代理类
 3  */
 4 public class Agent implements SingService {
 5 
 6     private Star star;
 7 
 8     public Agent(Star star) {
 9         this.star = star;
10     }
11 
12 
13     @Override
14     public void sing() {
15         System.out.println("同意明星唱歌。。。。");
16     }
17 
18     /**
19      * 经纪人代理明星处理谈判事宜,决定明星是否唱歌
20      *
21      * @param cash
22      */
23     public void contract(Long cash) {
24         if (cash <= 10000L) {
25             System.out.println("费用少了,谈判失败。。。");
26         } else {
27             sing();
28             star.sing();
29         }
30     }
31 }

 4)客户端测试

/**
 * 客户端测试类
 */
public class SingTest {

    public static void main(String[] args) {
        Star star = new Star();
        Agent service = new Agent(star);
        service.contract(200000L);
    }
}

 5)测试结果

同意明星唱歌。。。。
明星唱歌......
2.2 动态代理

动态代理分为JDK动态代理和cglib动态代理,JDK动态代理实现方式又分两种。

2.2.1 基于JDK的代理模式:
 1 jdk中为实现代理提供了支持,主要用到2个类:
 2 1、java.lang.reflect.Proxy
 3 2、java.lang.reflect.InvocationHandler
 4 java.lang.reflect.Proxy常用到的静态方法:
 5 (1)getProxyClass方法:为指定的接口创建代理类,返回代理类的Class对象
 6 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
 7 参数说明:
 8 loader:定义代理类的类加载器
 9 interfaces:指定需要实现的接口列表,创建的代理默认会按顺序实现interfaces指定的接口
10 (2)newProxyInstance方法:创建代理类的实例对象
11 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
12 这个方法先为指定的接口创建代理类,然后会生成代理类的一个实例,最后一个参数比较特殊,是InvocationHandler类型的
13 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
14 这个方法会返回一个代理对象,当调用代理对象的任何方法的时候,会就被InvocationHandler接口的invoke方法处理
15 (3)isProxy方法:判断指定的类是否是一个代理类
16 public static boolean isProxyClass(Class<?> cl)
17 (4)getInvocationHandler方法:获取代理对象的InvocationHandler对象
18 public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException

1) 创建抽象角色,即声明接口方法

public interface IService {

    void m1();
    void m2();
    void m3();
}

2)创建代理并实现

 1 public class JdkProxy {
 2 
 3     /**
 4      * 方式1:
 5      * 1.调用Proxy.getProxyClass方法获取代理类的Class对象
 6      * 2.使用InvocationHandler接口创建代理类的处理器
 7      * 3.通过代理类和InvocationHandler创建代理对象
 8      * 4.上面已经创建好代理对象了,接着我们就可以使用代理对象了
 9      */
10     @Test
11     public void proxyTest1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
12         //1、获取接口对应代理类的Class对象
13         Class<IService> proxyClass = (Class<IService>) Proxy.getProxyClass(IService.class.getClassLoader(), IService.class);
14         //2、创建代理类的处理器
15         InvocationHandler invocationHandler = (proxy, method, args) -> {
16             System.out.println("我是InvocationHandler,被调用的处理方法是: " + method.getName());
17             return null;
18         };
19         //3、创建代理实例
20         IService proxyService = proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
21         //4、调用代理的方法
22         proxyService.m1();
23         proxyService.m2();
24         proxyService.m3();
25     }
26 
27     /**
28      * 方式2:
29      * 1.使用InvocationHandler接口创建代理类的处理器
30      * 2.使用Proxy类的静态方法newProxyInstance直接创建代理对象
31      * 3.使用代理对象
32      */
33     @Test
34     public void proxyTest2() {
35         //1、创建代理类的处理器
36         InvocationHandler invocationHandler = (proxy, method, args) -> {
37             System.out.println("我是InvocationHandler,被调用的处理方法是: " + method.getName());
38             return null;
39         };
40         //2、创建代理实例
41         IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(), new Class[]{IService.class}, invocationHandler);
42         //3、调用代理的方法
43         proxyService.m1();
44         proxyService.m2();
45         proxyService.m3();
46     }
47 }

JDK动态代理总结:相对于静态代理,JDK 动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。JDK 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。但是 JDK 动态代理有个缺憾:JDK 实现动态代理需要实现类通过接口定义业务方法。也就是说它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计就注定了这个遗憾。

2.2.2 基于cglib的代理模式

1 cglib是一个强大、高性能的字节码生成库,它用于在运行时扩展Java类和实现接口,被许多AOP框架使用,如我们常用的Spring AOP; 
2 本质上它是通过动态的生成一个子类去覆盖所要代理的类(非final修饰的类和方法)。
3 Enhancer可能是CGLIB中最常用的一个类,和jdk中的Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。
4 Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。 5 基于同样的道理,Enhancer也不能对final类进行代理操作。

1) 创建抽象角色,声明一个普通的class

public class MService {
    public void m1(){
        System.out.println("我是方法m1()");
    }

    public String m2(){
        System.out.println("我是方法m2()");
        return "test-m2";
    }
}

2)

 1 public class CglibProxy {
 2 
 3     /**
 4      * 基于Enhancer实现:
 5      * (1).创建Enhancer对象
 6      * (2).创建代理类
 7      * (3).设置回调
 8      * (4).获取代理对象
 9      * (5).调用代理对象的方法
10      *
11      */
12     @Test
13     public void cglibTest1(){
14         //1 创建Enhancer对象
15         Enhancer enhancer = new Enhancer();
16         //2 通过setSuperclass创建代理对象
17         enhancer.setSuperclass(MService.class);
18         //3.设置拦截器回调,需实现org.springframework.cglib.proxy.Callback接口,
19         // 此处我们使用的是org.springframework.cglib.proxy.MethodInterceptor,也是一个接口,实现了Callback接口,
20         // 当调用代理对象的任何方法的时候,都会被MethodInterceptor接口的invoke方法处理
21         enhancer.setCallback(new MethodInterceptor() {
22             /**
23              * 代理对象拦截器
24              * @param o 代理对象
25              * @param method 被代理类的方法
26              * @param objects 调用方法传递的参数
27              * @param methodProxy 方法代理对象
28              * @return
29              */
30             @Override
31             public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
32                 System.out.println("代理对象的方法:"+ method);
33                 //通过调用methodProxy的方法invokeSuper获取
34                 Object result = methodProxy.invokeSuper(o, objects);
35                 System.out.println(result);
36                 return result;
37             }
38         });
39 
40         //设置过滤器回调
41         enhancer.setCallbackFilter(method -> {
42             System.out.println("过滤方法:" + method);
43             return 0;
44         });
45         MService mService = (MService) enhancer.create();
46         mService.m1();
47         mService.m2();
48     }
49 
50 }

cglib代理总结:CGLIB 创建的动态代理对象比 JDK 创建的动态代理对象的性能更高,但是 CGLIB 创建代理对象时所花费的时间却比 JDK 多得多。所以对于单例的对象,因为无需频繁创建对象,用 CGLIB 合适,反之使用JDK方式要更为合适一些。同时由于 CGLIB 由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。同时,CGLIB也摆脱了interface代理的桎梏,也能支持普通的class。

3 spring采用的代理

spring5源码:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

    /**
     * Determine whether the supplied {@link AdvisedSupport} has only the
     * {@link org.springframework.aop.SpringProxy} interface specified
     * (or no proxy interfaces specified at all).
     */
    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
    }

}
从if中的判断条件可以看到3个方面影响着Spring的判断:
config.isOptimize()
/**
     * Return whether proxies should perform aggressive optimizations.
     */
    public boolean isOptimize() {
        return this.optimize;
    }
config.isProxyTargetClass()
/**
     * Return whether to proxy the target class directly as well as any interfaces.
     */
    public boolean isProxyTargetClass() {
        return this.proxyTargetClass;
    }
hasNoUserSuppliedProxyInterfaces(config))
/**
     * Determine whether the supplied {@link AdvisedSupport} has only the
     * {@link org.springframework.aop.SpringProxy} interface specified
     * (or no proxy interfaces specified at all).
     */
    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
    }

(1)optimize:默认是false,用来控制通过CGLIB创建的代理是否使用激进的优化策略。除非完全了解AOP代理是如何优化,否则不推荐用户使用这个设置,目前这个属性仅用于CGLIB代理,对于JDK动态代理(缺省代理)无效。
(2)proxyTargetClass:默认是false,这个属性为treu时,目标类本身被代理而不是目标类的接口。如果这个属性值被设为true,CGLIB代理将被创建,。
(3)hasNoUserSuppliedProxyInterfaces:是否存在代理接口。
使用总结:如果目标对象实现的是接口,那么spring默认采用JDK代理方式;如果目标对象实现的是类,spring默认采用CGLIB代理方式;如果目标对象实现的是接口,也可以通过设置optimize属性,进行强制使用CGLIB代理方式

 

 

 

posted @ 2020-06-08 13:04  寻尘向阳  阅读(243)  评论(0编辑  收藏  举报