AOP,也就是 Aspect-oriented Programming,译为面向切面编程,是计算机科学中的一个设计思想,旨在通过切面技术为业务主体增加额外的通知(Advice),从而对声明为“切点”(Pointcut)的代码块进行统一管理和装饰。
通常我们使用spring-aop时都会使用注解@EnableAspectJAutoProxy开启aop支持,springboot中采用自动装配我们就不需要手动加上注解了,但原理都一样。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*/
boolean proxyTargetClass() default false;
/**
* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {@code AopContext} access will work.
* @since 4.3.1
*/
boolean exposeProxy() default false;
}
注解@EnableAspectJAutoProxy上面还有一个注解@Import(AspectJAutoProxyRegistrar.class),这个注解的功能就是注册beanDefinition,注册的就是AnnotationAwareAspectJAutoProxyCreator,
注册beanDefinition实际上就是注册bean,看这个类的关系图
这个类实现了BeanPostProcessor,bean的后置处理器,一眼就知道这是一个埋点会对所有的bean就行处理,根据后置处理器的生命周期,其主要是在bean的初始化前后进行对bean进行修饰。主要就是在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization这个类中实现。
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
核心实现就是org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && 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.
//1.筛选切面
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//2.创建代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
前面三个if的逻辑也很重要,主要就是判断这个bean是否需要进行aop,我们直接从注释Create Proxy if we have advice(如果有增强器就创建代理),那么这个advice是什么意思呢?
首先代码org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
会将spring中我们需要的advisor筛选出来
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//查询所有的advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//筛选出只适配该bean的advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
//扩展一个advisor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
//对advisor进行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
查询所有advisor时主要分两类,一个时直接实现了advisor接口的类,另一种是通过注解@Aspect修饰的类,下面的@Before、@After或者@Around都是被解析成一个一个Advisor。那么我们就着重看一下Advisor的数据结构。其实我spring提供给用户使用的advisor是PointcutAdvisor,我个人理解就是基于切入点的advisor,
pointcut – the Pointcut targeting the Advice
advice – the Advice to run when Pointcut matches
public interface Advisor {
Advice EMPTY_ADVICE = new Advice() {};
Advice getAdvice();
boolean isPerInstance();
public interface PointcutAdvisor extends Advisor {
/**
* Get the Pointcut that drives this advisor.
*/
Pointcut getPointcut();
}
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}
public interface MethodInterceptor extends Interceptor {
Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;
}
介于上面两个接口的方法,可以看到一个advisor需要包含advice和pointcut。Pointcut又必须提供
CassFilter和MethodMatcher,又是类匹配又是方法匹配,相信大多数小伙伴都能猜测到多半就是用于筛选adviosr的,没错他确实也是这样的。那Advice是什么,spring给我们提供的的实现就是org.aopalliance.intercept.MethodInterceptor用于方法增强。基于上述描述,我们可以近似的理解
advisor就是切面,advice是增强器也就是通知,Pointcut就是切入点。一个advisor必须包含Advice和Pointcut。我们实现一个自己的advisor时,若不想基于注解@Aespect实现就可以通过这种方式实现,通过这种方式我个人觉得会更加灵活。@Transaction的实现就是自定义Advisor实现的感兴趣可以看一下BeanFactoryTransactionAttributeSourceAdvisor的实现。
塞选出切面后,第二步就是创建代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
这个方法中有一个非常重要的属性proxyFactory
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
//创建代理工程
ProxyFactory proxyFactory = new ProxyFactory();
//实际是将@EnableAspectJAutoProxy注解的属性拷贝过来
proxyFactory.copyFrom(this);
if (proxyFactory.isProxyTargetClass()) {
// Explicit handling of JDK proxy targets (for introduction advice scenarios)
if (Proxy.isProxyClass(beanClass)) {
// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
for (Class<?> ifc : beanClass.getInterfaces()) {
proxyFactory.addInterface(ifc);
}
}
}
else {
// No proxyTargetClass flag enforced, let's apply our default checks...
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
//切面
proxyFactory.addAdvisors(advisors);
//设置目标对象
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
//创建代理对象
return proxyFactory.getProxy(classLoader);
}
这个单词翻译过来就是代理工厂,那么代理对象肯定是通过这个对象生成的,我们只看jdk动态代理,那么代理对象就是通过这里产出org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
扩展一个问题,如果我们在业务层想获取到原目标对象我们该怎么操作呢?
因为代理对象实现了目标对象的同时也实现了 下面这些接口
org.springframework.aop.framework.AopProxyUtils#completeProxiedInterfaces(org.springframework.aop.framework.AdvisedSupport, boolean)
if (!advised.isInterfaceProxied(SpringProxy.class)) {
proxiedInterfaces.add(SpringProxy.class);
}
if (!advised.isOpaque() && !advised.isInterfaceProxied(Advised.class)) {
proxiedInterfaces.add(Advised.class);
}
if (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class)) {
proxiedInterfaces.add(DecoratingProxy.class);
}
所以我们只要要将代理对象强转为Advised类型,再通过getTargetSource().getTarget()就可以拿到原对象。
了解jdk动态代理的,最后一个参数为InvocationHandler,JdkDynamicAopProxy是实现了InvocationHandler的,当通过代理对象调用目标方法时,其实是会进入InvocationHandler的invoke方法,那么核心逻辑就是org.springframework.aop.framework.JdkDynamicAopProxy#invoke这个方法。接下来我们就拆成几块研究。
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
这行代码的exposeProxy属性就是注解@EnableAspectJAutoProxy的exposeProxy属性,当设置为true时,会将代理对象设置在线程上下文中,可以通过AopContext.currentProxy()获取当前线程上下文的代理对象。
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
这里又会获取一变切面的通知,因为代理对象是针对类级别,但增强是针对方法级别的,所以这里会再次判断目标方法是否需要增强。
if (chain.isEmpty()) {
// 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 = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
这里当增强器不为空时,就会依次调用增强器进行方法增强,最终调用目标方法。这里利用了一个责任链模式,感兴趣可以细看。
这里只是理清了aop的主线而已,还有很多细节未挖掘。