定义
Proxy Pattern 是一种结构模式,指为其他对象提供一种代理以控制对这个对象的访问。
类图
代理模式一般包含三种角色:
Subject:
定义的RealSubject和Proxy的共有方法,这个类可以是接口,也可以是抽象类。这样所有使用RealSubject的地方都可以用Proxy代替。
RealSubject:
被代理类,此类定义了真实的Subject实现。
Proxy:
代理类一般要持有一个被代理的对象的引用,并可能负责创建和删除被代理对象。
静态代理和动态代理
代理模式的实现大体上可以分为两种,一种是静态代理,一种是动态代理。两种代理从JVM加载类的角度来讲,本质上都是一样的。
静态代理
上面的类图就是传统的静态代理,代码不贴了。(然而相关代码接下来的demo里有用到。)
优点:
- 实现简单。
缺点:
- 静态代理只能通过手动完成被代理对象的注入。
- 如果被代理类增加了新的方法,代理类需要同步增加,违背了开闭原则。
- 一个代理类只能处理一个被代理类。
动态代理
JAVA的SDK中已经有了实现方法,Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
使用JDK动态代理需要注意,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。
优点:
- 动态代理扩展性好。
- 动态代理采用在运行时动态生成字节码的方式,取消了被代理类的扩展限制,遵循开闭原则。
- 被代理对象自动注入。
缺点:
- 执行效率相对要低,每次都要反射动态调用控。
代码示例
public static class SubjectInvocationHandler implements InvocationHandler {
private Object object;
public SubjectInvocationHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before 动态代理...");
System.out.println(proxy.getClass().getName());
System.out.println(this.object.getClass().getName());
return method.invoke(object, args);
}
}
public static void main(String[] args) {
Subject o = (Subject) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, new SubjectInvocationHandler(new RealSubject()));
o.request();
}
Cglib动态代理
添加Maven依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
代码示例
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
System.out.println("before method run...");
Object result = proxy.invokeSuper(obj, args1);
System.out.println("after method run...");
return result;
});
RealSubject subject = (RealSubject) enhancer.create();
subject.request();
}
Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。
Cglib采用ASM框架写Class字节码,实现比JDK复杂,所以生产代理类比JDK动态代理效率低。
JDK是采用反射机制调用的,而CGLib通过FastClass机制直接调用方法,所以只需效率更高。
FastClass
FastClass不使用反射类(Constructor或Method)来调用委托类方法,而是动态生成一个新的类(继承FastClass),向类中写入委托类实例直接调用方法的语句,用模板方式解决Java语法不支持问题,同时改善Java反射性能。
动态类为委托类方法调用语句建立索引,使用者根据方法签名(方法名+参数类型)得到索引值,再通过索引值进入相应的方法调用语句,得到调用结果。
Spring中的代理选择原则
- 当Bean有实现接口时,Spring就会调用JDK动态代理。
- 当Bean没有实现接口时,Spring会选择CGLib代理。
- Spring可以通过配置强制使用CGLib代理。
补充阅读
警惕动态代理导致的Metaspace内存泄漏问题
https://blog.csdn.net/xyghehehehe/article/details/78820135