https://blog.csdn.net/ywlmsm1224811/article/details/92583559
https://www.bilibili.com/video/BV1SJ411v7fq?p=2
静态代理
示例
public class StaticProxy {
private Subject subject;
public StaticProxy() {}
public StaticProxy(Subject subject) {
this.subject = subject;
}
public void request() {
System.out.println("静态代理");
subject.request();
}
}
public class StaticProxyTest {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
StaticProxy staticProxy = new StaticProxy(realSubject);
staticProxy.request();
}
}
静态代理的缺点
- 当目标类增加了,代理类可能也需要成倍的增加,代理类数量过多
- 当接口更新(功能增加或修改),会影响众多实现类,各代理类也需要修改
jdk动态代理
jdk动态代理写法:
- 被代理接口及其实现类
- 实现InvocationHandler invoke方法,传入参数为代理对象和参数,在此方法中进行功能增强
- 测试类通过调用Proxy.newProxyInstance()生成proxy对象,传入对象分别为被代理类加载器、被代理类接口、和InvocationHandler的实现类实例化对象
被代理接口及其实现类
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("execute real subject!");
}
}
InvocationHandler实现
public 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("-----before-----");
method.invoke(target, args);
System.out.println("-----after------");
return null;
}
}
测试
public class DynamicProxyTest {
public static void main(String[] args) {
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(new RealSubject());
Subject proxy = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), myInvocationHandler);
proxy.request();
}
}
源码
生成Proxy代理类
添加以下属性,会自动在默认com.sun.proxy包下生成$Proxy0.class文件。主要通过Proxy中内部类ProxyClassFactory调用apply方法,从而调用ProxyGenerator中generateProxyClass生成。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
此属性可在ProxyGenerator中查看,
private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
if (saveGeneratedFiles) {......}
生成的文件为:
package com.sun.proxy;
import com.java.study.spring.aop.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
......
public final void request() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.java.study.spring.aop.Subject").getMethod("request");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
所以proxy.request()是等同先执行$Proxy0.request(),super.h是之前传入InvocationHandler的实现类,调用invoke方法进行功能增加后,再通过发射,执行实际被代理类方法
是Proxy——>InvocationHandler-->Subject顺序
CGLIB
对类的代理
public class TargetProxy implements MethodInterceptor {
public <T> T getProxy(Class clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return (T)enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib proxy");
return methodProxy.invokeSuper(o, objects);
}
}
public class CglibTest {
public static void main(String[] args) {
TargetProxy targetProxy = new TargetProxy();
RealSubject proxy = targetProxy.getProxy(RealSubject.class);
proxy.request();
}
}
对接口代理
如果是接口,需要在interceptor中进行实现
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib 接口实现");
return "接口实现";
}
CGLIB源码
加以下可将增强类输出
System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/.../com/sun/proxy/");
public class RealSubject$$EnhancerByCGLIB$$e5ed5f82 extends RealSubject implements Factory {
final void CGLIB$request$0() {
super.request();
}
public final void request() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$request$0$Method, CGLIB$emptyArgs, CGLIB$request$0$Proxy);
} else {
super.request();
}
}
final boolean CGLIB$equals$1(Object var1) {
return super.equals(var1);
}
......
注意
由于是继承方式,所以final修饰的类和方法不可被增强
总结
JDK动态代理和CGLIB的区别:
- JDK动态代理:是通过反射机制生成一个实现代理接口的的类(在jvm内存中),核心是实现InvocationHandler接口,重写invoke方法进行面向切面的处理,调用相应通知
CGLIB动态代理:动态生成一个要代理类的子类,子类重写要代理类(非final修饰)的不是final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑;比JDK动态代理要快
- JDK不能对接口进行代理,CGLIB不能对final修饰类和方法进行代理
在spring中:
- 如果目标对象实现了接口,默认使用JDK动态代理;也可强制使用CGLIB
- 如果目标对象没有实现接口,必须采用CGLIB动态代理
TODO
JDK源码解读
CGLIB源码解读