Java 动态代理
一、什么是 代理 、静态代理 与 动态代理
代理模式(Proxy Pattern)
一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式在客户端和目标对象之间起到一个中介的作用
(1)功能增强: 基于某个功能,再增加一些功能。
(目标类只负责核心功能,其他附属功能通过代理类完成。代理类的方法名与目标类的方法相同,内容不同,在核心功能外加了一些额外逻辑)
(2)控制访问: 防止直接访问目标
这种模式广泛应用于软件设计中,尤其是在需要延迟加载、访问控制、缓存实现、日志记录、事务管理、远程调用等场景中
静态代理:代理类和目标类在编译期间就确定下来,它们之间存在静态的代理关系。静态代理通常是通过编写代理类来实现的,代理类与目标类实现相同的接口,并在代理类中调用目标类的方法。
动态代理:动态代理在程序运行时动态生成代理类及其对象,而无需手动编写代理类。
二、如何进行 动态代理
使用JDK进行动态代理(Java.lang.reflect 包中的Proxy类和InvocationHandler接口):
基于接口实现代理
使用java反射包中的接口和类实现动态代理,要求代理类和工具类实现同一个接口。
其中反射包是java.lang,reflect,里面有三个类:InvocationHandler、Method、Proxy
简单案例的代码实现:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义一个接口
interface Subject {
void request();
}
// 创建对象实现接口方法
class Element implements Subject {
@Override
public void request() {
System.out.println("执行element的方法");
}
}
// 代理处理器
class MyInvocationHandler implements InvocationHandler {
private Object target; // 对象
// 构造方法
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用前逻辑语句");
// 调用对象的方法
Object result = method.invoke(target, args);
System.out.println("调用后逻辑语句");
return result;
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
Subject subject = new Element();
// 创建InvocationHandler实例
MyInvocationHandler handler = new MyInvocationHandler(subject);
// 创建代理对象
Subject proxySubject = (Subject) Proxy.newProxyInstance(
Element.class.getClassLoader(), // 类加载器
new Class<?>[]{Subject.class}, // 代理类需要实现的接口
handler // 代理实例的调用处理器
);
// 通过代理对象调用方法
proxySubject.request();
}
}
JDK的动态代理底层是通过Java反射机制实现的,并且需要目标对象继承自一个接口才能生成它的代理类。
Cglib/Javassist
高级的字节码生成库,总体性能比JDK动态代理好,且功能强大
Cglib (Code Generation Library ) 是一个第三方代码生成类库,运行时在内存中动态
引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
代码实现
public class CglibProxy implements MethodInterceptor{
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz){
//设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
//实现MethodInterceptor接口方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("前置代理");
//通过代理类调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置代理");
return result;
}
}
public class DoCGLib {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//通过生成子类的方式创建代理类
[TESTCLASS] proxyImp = ([TESTCLASS] )proxy.getProxy([TESTCLASS].class);
proxyImp.add();
}
}
CgLib的特点:
使用CGLib实现动态代理,完全不受代理类必须实现接口的限制
CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高
CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类
Javassist被用于struts2和hibernate中,都用来做动态字节码修改使用。一般开发中不会用到,但在封装框架时比较有用,故暂不介绍