Java代理详解
1. 静态代理
1.1 实现方式
- 为每个需要被代理的类都写一个对应的代理类,并实现目标类中想要代理的方法接口
- 创建代理对象时,通过构造器放入一个目标对象作为成员变量
- 实现被代理的方法,根据需要处理添加业务逻辑
1.2 代码示例
public class StaticProxyDemo {
public static void main(String[] args) {
// 一般在bean注入时用代理对象替换掉被代理的对象, 所以代理时一般以接口维度处理
DemoInterface demoInterface = new DemoProxy(new DemoImpl());
demoInterface.say();
}
/**
* 接口
*/
public interface DemoInterface {
void say();
}
/**
* 被代理类
*/
public static class DemoImpl implements DemoInterface {
@Override
public void say() {
System.out.println("impl");
}
}
/**
* 静态代理类
*/
public static class DemoProxy implements DemoInterface {
private final DemoInterface demoInterface;
public DemoProxy(DemoInterface demoInterface) {
this.demoInterface = demoInterface;
}
@Override
public void say() {
System.out.println("proxy before");
demoInterface.say();
System.out.println("proxy after");
}
}
}
1.3 静态代理的缺点
- 手动编写目标类:如果是针对单个对象的特殊逻辑还好,但如果是希望针对大范围的代理,工作量就会很大,代码也很臃肿
- 修改不便:当代理的对应接口出现调整时,代理类也需要跟随调整,即使调整的逻辑跟代理类的逻辑完全没关系
使用代理的场景基本是为了实现一些通用逻辑,个性化逻辑大部分情况下没必要使用代理来处理。
2. JDK动态代理
2.1 实现方式
Java内置的一种代理方式,通过反射来在运行时动态地创建代理类。
JDK动态代理要求目标对象必须实现接口。
实现流程:
- 实现
InvocationHandler
接口 或 使用Proxy
类生成代理类对象 - 利用反射执行代理类中的方法
2.2 代码示例
import java.lang.reflect.*;
public class JdkProxyDemo {
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
DemoInterface demoInterface = (DemoInterface) JdkProxyDemo.getProxy(new DemoImpl());
System.out.println(demoInterface.say());
DemoInterface demoInterface2 = (DemoInterface) JdkProxyDemo.getInvocationHandlerProxy(new DemoImpl());
System.out.println(demoInterface2.say());
}
/**
* 接口
*/
public interface DemoInterface {
String say();
}
/**
* 被代理类
*/
public static class DemoImpl implements DemoInterface {
@Override
public String say() {
return "impl";
}
}
/**
* Proxy对象代理
*
* @param proxyTarget 被代理对象
* @return 代理对象
*/
public static Object getProxy(Object proxyTarget) {
return Proxy.newProxyInstance(
// 类加载器, 无特殊情况使用与目标对象相同的类加载器
proxyTarget.getClass().getClassLoader(),
// 代理对象实现的接口, 一般与代理对象保持一致, 保证所有接口方法被代理
proxyTarget.getClass().getInterfaces(),
(proxy1, method, args) -> {
System.out.println(method.getName() + "JDK Proxy Before");
Object result = method.invoke(proxyTarget, args);
System.out.println(method.getName() + "JDK Proxy After");
return result;
}
);
}
/**
* Proxy对象代理
*
* @param proxyTarget 被代理对象
* @return 代理对象
*/
public static Object getInvocationHandlerProxy(Object proxyTarget) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> proxyClazz = Proxy.getProxyClass(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces());
Constructor<?> constructor = proxyClazz.getConstructor(InvocationHandler.class);
return constructor.newInstance((InvocationHandler) (proxy1, method, args) -> {
System.out.println(method.getName() + "JDK InvocationHandler Proxy Before");
Object result = method.invoke(proxyTarget, args);
System.out.println(method.getName() + "JDK InvocationHandler Proxy After");
return result;
});
}
}
3. CGLIB动态代理
CGLIB代理(Code Generation Library)是一个高性能代码生成库。可以创建各种类型代码,其中包含了代理。
在代理方面,CGLIB代理不要求目标对象实现接口,因此它的使用范围更广,不过性能相比JDK动态代理要差一点(但不多)
CGLIB 动态代理的实现机制是生成目标类的子类,通过调用父类(目标类)的方法实现,在调用父类方法时在代理中进行增强。
PS:由于是继承方式实现的,所以final类和final方法无法被代理
实现流程:
- 导入CGLIB依赖包;
- 代理类实现
MethodInterceptor
; - 使用
Enhancer
类得到动态代理的实例,并执行指定方法
4. Javassist动态代理
Javassist是一个用于操作Java字节码的指令库,可以分析、编辑和创建Java字节码,通过对.class文件的修改,实现对类的修改和创建。
一般在开发中不会用到,主要是用于封装框架,比如struts2和hibernate。
Javassist动态代理主要有以下两种方式:
- 代理工厂创建:实现MethodHandler,处理代理逻辑,与CGLIB类似
- 动态代码创建:通过Java代码,分析class文件结构,动态的去创建代理代码,可以在运行时生成业务逻辑