代理
动态代理
JDK动态代理
动态代理是在程序运行过程中,动态地生成类字节码,参与运行,有很高的能动性。
实现步骤:
- 需提供一个接口和具体实现接口方法的类
- 创建一个调用处理器类,实现InvocationHandler接口,并实现invoke方法,这个是代理方法
- 构造代理对象,传入目标类加载器,类实现的接口和调用处理器
- 强转返回类型,调用接口方法
缺点:只能代理实现接口的类
@FunctionalInterface
interface InnMessage<T> {
void printMessage(T t);
}
@Data
public class Message implements InvocationHandler {
public static void main(String[] args) {
Message message = new Message();
message.setTarget(new InnerMessage());
/*
加载器 - 用于定义代理类接口的类加载器
接口 - 代理类需要实现的接口列表(运行时定义的类)
h - 用于分派方法调用的调用处理器(代理类调用处理器的invoke方法相当于)
*/
InnMessage proxy = (InnMessage) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{InnMessage.class}, message);
proxy.printMessage("你好");
}
//代理类中的真实对象
private Object target;
//代理方法
/*
代理实例 - 调用方法的代理实例
方法 - 对应于在代理实例上调用的接口方法的方法实例。方法对象的声明类将是声明该方法的接口,这可能是代理类继承该方法的代理接口的超接口。
参数 - 包含在代理实例上方法调用中传递的参数值的对象数组,如果接口方法不接受参数,则为null。原始类型的参数被包装在相应的原始包装类的实例中,例如java.lang.Integer或java.lang.Boolean。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
class InnerMessage<T> implements InnMessage<T> {
@Override
public void printMessage(T t) {
System.out.println(t);
}
}
CGLIB动态代理
字节码生成库用于生成和转换Java字节码的高级API。它被AOP、测试、数据访问框架用于生成动态代理对象和拦截字段访问。
步骤:
- 定义一个类
- 自定义
MethodInterceptor
并重写intercept
方法,intercept
用于拦截增强被代理类的方法,和 JDK 动态代理中的invoke
方法类似 - 通过
Enhancer
类的create()
创建代理类
public class Cglib {
public static void main(String[] arg) {
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(ClassLoader.getSystemClassLoader());
// 设置被代理类
enhancer.setSuperclass(CglibService.class);
// 设置方法拦截器
enhancer.setCallback(new MethodInterceptorImpl());
// 创建代理类
CglibService o = (CglibService) enhancer.create();
o.accept("I love you");
}
private static class MethodInterceptorImpl implements MethodInterceptor {
/**
* @param obj 被代理的对象(需要增强的对象)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param proxy 用于调用原始方法
* @return Object
* @throws Throwable e
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object o = proxy.invokeSuper(obj, args);
return o;
}
}
private static class CglibService {
public CglibService() {}
public void accept(String o) {
System.out.println(o);
}
}
}
JDK 动态代理和 CGLIB 动态代理对比
- JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。
- CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
评论区