Spring|Spring AOP之原理解析(三)
这里只是列出来一些必要high level的点,可以帮助理解AOP底层是怎么工作的。
1. Spring AOP的原理-动态代理
Spring AOP 的原理很简单,就是动态代理,它和 AspectJ 不一样,AspectJ 是直接修改掉你的字节码。
代理模式很简单,接口 + 真实实现类 + 代理类,其中 真实实现类 和 代理类 都要实现接口,实例化的时候要使用代理类。所以,Spring AOP 需要做的是生成这么一个代理类,然后替换掉真实实现类来对外提供服务。
替换的过程怎么理解呢?在 Spring IOC 容器中非常容易实现,就是在 getBean(…) 的时候返回的实际上是代理类的实例,而这个代理类我们自己没写代码,它是 Spring 采用 JDK Proxy 或 CGLIB 动态生成的。
getBean(…) 方法用于查找或实例化容器中的 bean,这也是为什么 Spring AOP 只能作用于 Spring 容器中的 bean 的原因,对于不是使用 IOC 容器管理的对象,Spring AOP 是无能为力的。XML配置中,总是会声明如下bean:
或者是其他的*AutoProxyCreator,如AbstractAutoProxyCreator等,其实他们都实现了SmartInstantiationAwareBeanPostProcessor,最终继承自BeanPostProcessor。postProcessAfterInitialization方法正式生成自动代理类来替换具体实现Bean的地方。
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
IoC容器实例化Bean的时候,getBean -> initializeBean -> beanPostProcessor.postProcessAfterInitialization。
2. JDK/CGLib 动态代理 相关源码:接上面createProxy的调用:
protected Object createProxy(
Class> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {ProxyFactory proxyFactory = new ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}return proxyFactory.getProxy(this.proxyClassLoader);
}
到ProxyFactory中getProxy方法:
/**
* Create a new proxy according to the settings in this factory.
* Can be called repeatedly. Effect will vary if we've added
* or removed interfaces. Can add and remove interceptors.
* 【Spring|Spring AOP之原理解析(三)】Uses the given class loader (if necessary for proxy creation).
* @param classLoader the class loader to create the proxy with
* (or {@code null} for the low-level proxy facility's default)
* @return the proxy object
*/
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}//ProxyCreatorSupport.java
/**
* Subclasses should call this to get a new AOP proxy. They should not
* create an AOP proxy with {@code this} as an argument.
*/
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}//DefaultAopProxyFactory.java
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
得结论:
createAopProxy 方法有可能返回 JdkDynamicAopProxy 实例,也有可能返回 ObjenesisCglibAopProxy 实例。
- 如果被代理的目标类实现了一个或多个自定义的接口,那么就会使用 JDK 动态代理;
- 如果没有实现任何接口,会使用 CGLIB 实现代理;
- 如果设置了 proxy-target-class="true",那么都会使用 CGLIB。
CGLIB 基于类继承,可以增强类中非final和非private的方法。
3. 动态代理-方法的调用 AopProxy接口的两个实现类:JdkDynamicAopProxy和CglibAopProxy,分别对应JDK和CGLIB动态代理的实现。
public interface AopProxy {/**
* Create a new proxy object.
* Uses the AopProxy's default class loader (if necessary for proxy creation):
* usually, the thread context class loader.
* @return the new proxy object (never {@code null})
* @see Thread#getContextClassLoader()
*/
Object getProxy();
/**
* Create a new proxy object.
* Uses the given class loader (if necessary for proxy creation).
* {@code null} will simply be passed down and thus lead to the low-level
* proxy facility's default, which is usually different from the default chosen
* by the AopProxy implementation's {@link #getProxy()} method.
* @param classLoader the class loader to create the proxy with
* (or {@code null} for the low-level proxy facility's default)
* @return the new proxy object (never {@code null})
*/
Object getProxy(ClassLoader classLoader);
}//JdkDynamicAopProxy
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}//CglibAopProxy
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + 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);
// 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 UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
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 ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Exception ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
结合Spring AOP之原理解析(一)看它们的实现本质。简单来说,JDK的实现基于InvocationHandler,JdkDynamicAopProxy本身就是先了InvocationHandler;CGLIB则基于Enhancer。
这里重点看下织入的切面方法是怎么被调用的。
/**
* Implementation of {@code InvocationHandler.invoke}.
* Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class> targetClass = null;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}// Get the interception chain for this method.
List
重点在这里:
// Get the interception chain for this method.
List
这里就是各个Advice或MethodInterceptor执行的地方。仔细阅读,其实这里是个递归调用,如果有多个增强的话,会分别执行。
CGLIB代理的调用是通过callback实现的,最终也是落到上面ReflectiveMethodInvocation的proceed方法。重点的源码片段如下:
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
enhancer.setInterceptDuringConstruction(false);
enhancer.setCallbacks(callbacks);
return (this.constructorArgs != null ?
enhancer.create(this.constructorArgTypes, this.constructorArgs) :
enhancer.create());
}
每个Callback是MethodInterceptor的实现,以DynamicUnadvisedExposedInterceptor为例:
/**
* Interceptor for unadvised dynamic targets when the proxy needs exposing.
*/
private static class DynamicUnadvisedExposedInterceptor implements MethodInterceptor, Serializable {private final TargetSource targetSource;
public DynamicUnadvisedExposedInterceptor(TargetSource targetSource) {
this.targetSource = targetSource;
}@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
Object target = this.targetSource.getTarget();
try {
oldProxy = AopContext.setCurrentProxy(proxy);
Object retVal = methodProxy.invoke(target, args);
return processReturnType(proxy, target, method, retVal);
}
finally {
AopContext.setCurrentProxy(oldProxy);
this.targetSource.releaseTarget(target);
}
}
}
推荐阅读
- 读书笔记之《查令十字街84号》
- 30岁之前,请逼自己成为这种人
- Kubelet之TLS|Kubelet之TLS BootStrap启动
- 简笔画日记之第四天(袋鼠的简笔画)
- 听,小牛妈妈分享《如何找到你的核心用户,助力你的事业风生水起》之所感
- 《赘婿》之商道
- 三宝妈百日分享之五|三宝妈百日分享之五 思路决定出路
- 记一个伪文青的自我救赎之旅
- 20190107感受力之内听觉训练
- 深闺之孤