1 概念
1.1 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
1.2 组成
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
1.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代理方式