AOP(Aspect Oriented Programming)是基於切面編程的,可無侵入的在原本功能的切面層添加自定義代碼,一般用於日志收集、權限認證等場景。
AOP基本概念
通知(Advice): AOP 框架中的增強處理。通知描述了切面何時執行以及如何執行增強處理;
Before :前置通知,在連接點方法前調用;對應Spring中@Before注解; After :后置通知,在連接點方法后調用;對應Spring中的@After注解; AfterReturning:返回通知,在連接點方法執行並正常返回后調用,要求連接點方法在執行過程中沒有發生異常;對應Spring中的@AfterReturning注解; AfterThrowing:異常通知,當連接點方法異常時調用;對應Spring中的@AfterThrowing注解; Around:環繞通知,它將覆蓋原有方法,但是允許你通過反射調用原有方法;對應Spring中的@Around注解;
連接點(Join Point): 連接點表示應用執行過程中能夠插入切面的一個點。在 Spring AOP 中,連接點總是方法的調用,可以說目標對象中的方法就是一個連接點;
切點(Pointcut): 就是連接點的集合;對應Spring中的@Pointcut注解;
目前Spring支持的切點匹配表達式主要有以下幾種: execution:可以定義到的最小粒度是方法,修飾符,包名,類名,方法名,Spring AOP主要也是使用這個匹配表達式; within:只能定義到類;例如@Pointcut(within(com.jnu.example.*)) this:當前生成的代理對象的類型匹配; target:目標對象類型匹配; annotation:針對注解; args:只針對參數; 例如: execution (* com.sample.service..*. *(..)) 整個表達式可以分為五個部分: 1、execution()::表達式主體; 2、第一個*號:表示返回類型, *號表示所有的類型; 3、包名:表示需要攔截的包名,包名后面的..,表明com.sample.service包、及其子包; 4、第二個*號:表示類名,*號表示所有的類; 5、*(..):最后這個星號表示方法名,*號表示所有的方法,后面括弧里面表示方法的參數,兩個點表示任何參數;
切面(Aspect): 切面是通知和切點的結合;對應Spring中的注解@Aspect修飾的一個類;
目標對象(Target object):即被代理的對象;
代理對象(AOP proxy):包含了目標對象的代碼和增強后的代碼的那個對象;
自定義注解實例可以參見:注解
@EnableAspectJAutoProxy分析
在spring-boot-autoconfigure-**.jar中,有個配置類,來控制AOP功能的開啟和關閉:

@Configuration(proxyBeanMethods = false) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Advice.class) static class AspectJAutoProxyingConfiguration { @Configuration(proxyBeanMethods = false) @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) static class JdkDynamicAutoProxyConfiguration { } @Configuration(proxyBeanMethods = false) @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) static class CglibAutoProxyConfiguration { } } @Configuration(proxyBeanMethods = false) @ConditionalOnMissingClass("org.aspectj.weaver.Advice") @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) static class ClassProxyingConfiguration { ClassProxyingConfiguration(BeanFactory beanFactory) { if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } } } }
- 使用@ConditionalOnProperty注解,默認開啟AOP功能;
- spring.aop.proxy-target-class沒有配置時,默認開啟Cglib動態代理;
- @EnableAspectJAutoProxy注解,聲明開啟AOP功能。
@EnableAspectJAutoProxy注解的核心是引入了AspectJAutoProxyRegistrar,然后向容器中注冊一個AnnotationAwareAspectJAutoProxyCreator:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { /** * Register, escalate, and configure the AspectJ auto proxy creator based on the value * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing * {@code @Configuration} class. */ @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } }
此時下面源碼中的第三個方法會進入if代碼塊內,然后返回null:

@Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) { return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null); } @Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } @Nullable private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
AnnotationAwareAspectJAutoProxyCreator分析
在AnnotationAwareAspectJAutoProxyCreator的繼承關系中,有一個AbstractAutoProxyCreator類。該類定義了下面兩個方法,它們分別攔截目標bean創建,和初始化流程:
@Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { Object cacheKey = getCacheKey(beanClass, beanName); if (beanName == null || !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { return null; } if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. if (beanName != null) { TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { this.targetSourcedBeans.add(beanName); Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } } return null; } //在創建其他bean時,調用該方法,創建代理的bean,從而將AOP方式注入的方法,注入到目標bean中 @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;
postProcessAfterInitialization方法中的wrapIfNecessary方法,會為目標對象動態地創建
JdkDynamicAopProxy
或者
ObjenesisCglibAopProxy
的代理對象。
AOP實戰
日期代理:

1 @Aspect 2 @Component("logAspect") 3 public class LogAspect { 4 5 private static final Logger log = LoggerFactory.getLogger(LogAspect.class); 6 7 // 配置織入點 8 @Pointcut("@annotation(com.ryj.annotation.Log)") 9 public void logPointCut() { 10 } 11 12 /** 13 * 是否存在注解,如果存在就獲取 14 */ 15 private static Log getAnnotationLog(JoinPoint joinPoint) throws Exception { 16 Signature signature = joinPoint.getSignature(); 17 MethodSignature methodSignature = (MethodSignature) signature; 18 Method method = methodSignature.getMethod(); 19 if (method != null) { 20 return method.getAnnotation(Log.class); 21 } 22 return null; 23 } 24 25 26 @Around("logPointCut()") 27 public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { 28 long startTimeMillis = System.currentTimeMillis(); 29 Log controllerLog = getAnnotationLog(joinPoint); 30 if (controllerLog == null) { 31 return null; 32 } 33 // 獲得方法名稱 34 String className = joinPoint.getTarget().getClass().getSimpleName(); 35 String methodName = joinPoint.getSignature().getName(); 36 String action = controllerLog.action(); 37 String title = controllerLog.title(); 38 Object[] args = joinPoint.getArgs(); 39 //序列化時過濾掉request和response 40 List<Object> logArgs = Arrays.stream(args) 41 .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse))) 42 .collect(Collectors.toList()); 43 String argStr = JSON.toJSONString(logArgs); 44 //打印日志,如有需要還可以存入數據庫 45 log.info(">>>>>模塊[{}]:{} 操作[{}]:{} 參數:{}",className,title,methodName,action,argStr); 46 //log.info("訪問的接口 ==> {} 請求參數 ==> {}" ,requestPath, argStr); 47 Object result = joinPoint.proceed() ; 48 log.info(">>>>>耗時 ==> {}ms" ,System.currentTimeMillis() - startTimeMillis); 49 return result ; 50 } 51 52 }