Spring AOP(7)代理类的调用基于CGLIB
听说 Spring AOP 有坑?那就来踩一踩
今天看到这篇文章,我觉得有必要继续分析一下基于CGLIB的代理。
CGLIB的原理
同样使用RealSubject,但是将接口去掉,在CGLIB中,重要条件是生成一个子类,然后重写要代理类的方法。
public class RealSubject {
//public 方法,可以被子类继承重写
public void sayName() {
System.out.println("say my name");
}
//private方法,子类会继承,但是不能访问
private void myPrivate() {
System.out.println("this is private method");
}
}
RealSubject realSubject = new RealSubject();
Enhancer enhancer = new Enhancer();
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("before invoke...");
Object result = methodProxy.invoke(realSubject, objects);
System.out.println("after invoke...");
return result;
});
enhancer.setSuperclass(realSubject.getClass());
//通过Enhancer创建代理类
Subject subjectProxy = (Subject) enhancer.create();
subjectProxy.sayName();
========
before invoke...
say my name
after invoke...
关于继承,有两个tips:
(1): 子类覆写父类的方法,方法权限必须比父类的权限相同或者更大,如果小于父类的方法权限,则编译报错。
(2): 如果父类的访问权限是private,子类中同名方法不会覆盖父类的方法,而是相当于子类新定义的方法。
这里代理类subjectProxy是RealSubject的子类,但是类中的private, final方法不能被代理, static方法不生成代理方法。
从下面截图中也能看到,代理类的declaredMethods中只有sayName()。
CGLIB代理类方法调用
一般spring框架中都是通过method.invoke反射方式来调用方法,那么在存在代理proxy的情况下,method的调用会有以下不同的结果。
这个是真实对象的方法,通过invoke调用真实的对象
Method sayName = realSubject.getClass().getDeclaredMethod("sayName");
sayName.setAccessible(true);
sayName.invoke(realSubject);
=========
say my name
这个是真实对象的方法,通过invoke调用代理对象,代理生效
Method sayName = realSubject.getClass().getDeclaredMethod("sayName");
sayName.setAccessible(true);
sayName.invoke(subjectProxy);
=========
before invoke...
say my name
after invoke...
这个是代理对象的方法,通过invoke调用真实的对象
Method sayName = subjectProxy.getClass().getDeclaredMethod("sayName");
sayName.setAccessible(true);
sayName.invoke(realSubject);
=========
报错:Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.gary.spring.proxy.TestCglib.main(TestCglib.java:27)
CGLIBAopProxy
Spring AOP(3)基于XML理解代理类的创建描述了AbstractAutoProxyCreator创建代理类的主要步骤,最后一行proxyFactory.getProxy(getProxyClassLoader())就是决定是CGLIBAopProxy还是JdkDynamicAopProxy来创建代理对象。
JdkDynamicAopProxy的getProxy()比较简单,直接通过Proxy.newProxyInstance()创建代理对象。
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
CGLIBAopProxy的getProxy()稍微复杂一点,跟上面CGLIB原理的示例代码一样,需要通过Enhancer来创建代理类,这里主要就是主装callback(即MethodInteceptor)
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
//这里是核心方法,组装callback
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
callback就是代理类增强的地方,类似InvocationHandler的invoke()方法。
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimization choices...
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
// Choose an "aop" interceptor (used for AOP calls).
//这个类似InvocationHandler,提供Invoke()方法
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
// Choose a "straight to target" interceptor. (used for calls that are
// unadvised but can return this). May be required to expose the proxy.
Callback targetInterceptor;
if (exposeProxy) {
targetInterceptor = (isStatic ?
new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
}
else {
targetInterceptor = (isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
}
// Choose a "direct to target" dispatcher (used for
// unadvised calls to static targets that cannot return this).
Callback targetDispatcher = (isStatic ?
new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
//组装mainCallBacks
Callback[] mainCallbacks = new Callback[] {
//aopInterceptor和targetInterceptor是固定一直有的
aopInterceptor, // for normal advice
targetInterceptor, // invoke target without considering advice, if optimized
new SerializableNoOp(), // no override for methods mapped to this
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};
Callback[] callbacks;
// If the target is a static one and the advice chain is frozen,
// then we can make some optimizations by sending the AOP calls
// direct to the target using the fixed chain for that method.
if (isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap<>(methods.length);
// TODO: small memory optimization here (can skip creation for methods with no advice)
for (int x = 0; x < methods.length; x++) {
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
this.fixedInterceptorMap.put(methods[x].toString(), x);
}
// Now copy both the callbacks from mainCallbacks
// and fixedCallbacks into the callbacks array.
callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
this.fixedInterceptorOffset = mainCallbacks.length;
}
else {
callbacks = mainCallbacks;
}
return callbacks;
}
那么方法调用的时候,肯定是走DynamicAdvisedInterceptor的intercept()。跟JdkDynamicAopProxy的invoke()方法类似。
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
private final AdvisedSupport advised;
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
//直接调用真实对象
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
// 通过CglibMethodInvocation来调用proceed()
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}