代理模式
静态代理:某个事件发生在编译的时候;静态代理是在编译的时候生产代理类字节码文件;
动态代理:某个事件发生在运行的时候;运行的时候生成代理类字节码文件;
静态代理
静态代理其实就是需要有个接口,然后有个实现类来实现这个接口,还有个代理类也同样实现这个接口,并且内部有个实现类的属性,可以通过实例化的方式来给这个类初始化值。
然后 new 代理类,从而让代理类调用的方式和原来的实现类是一样的。
其实还有一种代理模式其实是基于继承的,其实也是可以实现代理效果的。
静态代理类有两种:
- 代理类通过继承的方式来实现;
- 代理类内部有个实现类的属性,通过实例化这个属性,从而调用对应的方法;
动态代理
- 基于 JDK 的动态代理实现;需要实现 InovationHandler
- 基于 CGLIB 来进行实现;需要用到一个核心接口 MethodInterceptor和一个核心类 Enhancer;
静态代理有两种实现方式,一个基于接口,一个基于继承;JDK 的是选择使用实现一个接口,而 CGLIB 是使用继承的方式来实现;
JDK的动态代理
耗时的地方主要是在生产动态代理字节码和利用反射执行这两个环节上;
接口定义
1
2
3
public interface UserService {
void addUser(String name);
}
实现类
1
2
3
4
5
6
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("正在添加用户:" + name);
}
}
定义代理处理器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogHandler implements InvocationHandler {
// 被代理对象
private final Object target;
public LogHandler(Object target) {
this.target = target;
}
// 所有被代理的方法调用,都会进入这里
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法开始:" + method.getName());
Object result = method.invoke(target, args); // 调用原方法
System.out.println("方法结束:" + method.getName());
return result;
}
}
生产代理对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.lang.reflect.Proxy;
public class ProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserServiceImpl();
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 目标对象实现的接口
new LogHandler(target) // 调用处理器
);
// 调用方法(会被拦截)
proxy.addUser("凌峰");
}
}
CGLIB的动态代理
不是使用 java 反射来执行的,而是使用 CGlib 自己的 FastClass 来执行。
目标类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UserService {
private final String tag;
public UserService() { this("default"); }
public UserService(String tag) { this.tag = tag; }
public String addUser(String name) {
System.out.println("业务:添加用户 -> " + name + " [" + tag + "]");
return "OK";
}
public final void finalMethod() {
System.out.println("final 方法不会被拦截");
}
}
MethodInterceptor核心
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LogInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object proxyObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 可选择跳过对 Object 的方法增强
if (method.getDeclaringClass() == Object.class) {
return methodProxy.invokeSuper(proxyObj, args);
}
long start = System.nanoTime();
System.out.println("前置增强:method=" + method.getName());
try {
// 调用父类方法(注意:CGLIB用 invokeSuper)
Object result = methodProxy.invokeSuper(proxyObj, args);
System.out.println("返回增强:result=" + result);
return result;
} catch (Throwable t) {
System.out.println("异常增强:" + t.getMessage());
throw t;
} finally {
System.out.println("耗时(ms):" + (System.nanoTime() - start));
}
}
}
创建代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import net.sf.cglib.proxy.Enhancer;
public class CglibDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new LogInterceptor()); // 单一回调
UserService proxy = (UserService) enhancer.create();
proxy.addUser("Lingfeng");
proxy.finalMethod(); // 不会被拦截(final)
}
}
JDK 动态代理和 CGLIB 动态代理的区别
维度 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
代理对象的产生方式 | 实现接口,在运行期生成一个实现同样接口的 $Proxy 类 |
生成子类,在运行期创建目标类的子类(基于 ASM/字节码增强) |
对目标的要求 | 必须有接口(仅能代理接口方法) | 无需接口;但类/方法不能是 final ,private 方法也无法拦截 |
调用转发入口 | InvocationHandler#invoke (反射调用 Method.invoke ,新 JDK 已做优化) |
MethodInterceptor#intercept (通常走 FastClass,减少反射开销) |
可代理的方法范围 | 仅接口中声明的方法(Object 的 equals/hashCode/toString 也会转发,需自行处理) | 目标类的非 final、非 private、非 static实例方法 |
性能(一般认知) | 早期略慢;JDK 8+ 已优化,常见业务中两者差距很小 | 早期常更快(FastClass);现代 JDK 下和 JDK 代理相近 |
生成类的形态 | com.sun.proxy.$Proxy0 ,实现接口 |
xxx$$EnhancerByCGLIB$$... ,继承目标类 |
构造器要求 | 不依赖构造器 | 需要能调用到某个构造器(可 enhancer.create(argTypes, args) ) |
注解可见性 | 代理类在接口层,直接对代理类取类级注解易丢失实现类注解(框架通常用工具拿到 targetClass) | 代理类继承目标类,类级注解更容易取到(仍建议通过目标类取) |
与 Spring 的关系 | 优先选择(如果有接口) | 当没有接口或强制 proxyTargetClass=true 时使用 |
典型限制 | 无法拦截类上非接口方法(自调用问题更显著) | 不能代理 final 类/方法;自调用问题依然存在 |
常见栈信息/调试 | 栈里能看到 $Proxy0 |
栈里能看到 $$EnhancerByCGLIB$$ |