AOP是什么 AOP(Aspect Oriented Programming),可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
它利用一种称为”横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面。所谓”切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用”横切”技术,AOP把软件系统分为两个部分:核心关注点 和横切关注点 。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来 。
AOP核心概念 横切关注点(对哪些方法进行切入) 对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
切面 (aspect,把原来糅杂在业务逻辑代码中的非业务代码抽取出来,把功能相同的放在一个类中形成一个切面)
类是对物体特征的抽象,切面就是对横切关注点的抽象
连接点(joinpoint)(需要切入的点) 被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
切入点(pointcut) 对连接点进行拦截的定义
通知(advice) 所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
目标对象 代理的目标对象
织入(weave) 将切面应用到目标对象并导致代理对象创建的过程
引入(introduction) 在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
基本案例 1 2 3 4 5 6 7 8 9 10 11 12 13 @Slf4j @Component public class Calculator { public int div (int x,int y) { log.info("Mycaculator...div" ); return x / y; } public int add (int x,int y) { log.info("Mycaculator...add" ); return x + y; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Aspect @Slf4j @Component public class CalculateAspect { @Pointcut("execution(public int com.study.aop.Calculator.*(..))") private void pointcut () {}; @Before("pointcut()") public void before () { log.info("前置通知。。。" ); } @After("pointcut()") public void after () { log.info("后置通知。。。" ); } @AfterReturning("pointcut()") public void afterReturn () { log.info("返回通知。。。" ); } @AfterThrowing("pointcut()") public void afterThrowing () { log.info("异常通知。。。" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Configuration @EnableAspectJAutoProxy @ComponentScan(basePackages = {"com.study.aop"}) public class AopConfig { }@Slf4j public class AopMain { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class); Calculator calculator = context.getBean(Calculator.class); int add = calculator.add(1 , 2 ); log.info(add+"" ); int div = calculator.div(10 , 0 ); log.info(div+"" ); } } -- 结果2022 -09-0822:23 :55 INFO CalculateAspect:17 - 前置通知。。。2022 -09-0822:23 :55 INFO Calculator:35 - Mycaculator...add2022 -09-0822:23 :55 INFO Calculator:39 - add result:3 2022 -09-0822:23 :55 INFO CalculateAspect:22 - 后置通知。。。2022 -09-0822:23 :55 INFO CalculateAspect:27 - 返回通知。。。2022 -09-0822:23 :55 INFO AopMain:13 - 3 2022 -09-0822:23 :55 INFO CalculateAspect:17 - 前置通知。。。2022 -09-0822:23 :55 INFO Calculator:28 - Mycaculator...div2022 -09-0822:23 :55 INFO CalculateAspect:22 - 后置通知。。。2022 -09-0822:23 :55 INFO CalculateAspect:32 - 异常通知。。。 Exception in thread "main" java.lang.ArithmeticException: / by zero
从@EnableAspectJAutoProxy说起 不难看出该注解为我们注入了AnnotationAwareAspectJAutoProxyCreator这个类,分析这个类的继承结构
不难发现这个类觉有BeanFactoryAware、BeanPostProcessor、InstantiationAwareBeanPostProcessor的特性,因此可以得出下图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 @Override public void setBeanFactory (BeanFactory beanFactory) { super .setBeanFactory(beanFactory); if (!(beanFactory instanceof ConfigurableListableBeanFactory)) { throw new IllegalArgumentException( "AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory); } initBeanFactory((ConfigurableListableBeanFactory) beanFactory); }protected void initBeanFactory (ConfigurableListableBeanFactory beanFactory) { this .advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory); }@Override public Object postProcessBeforeInstantiation (Class<?> beanClass, String beanName) throws BeansException { Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || !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 ; } } TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null ) { if (StringUtils.hasLength(beanName)) { 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 ; }@Override protected boolean shouldSkip (Class<?> beanClass, String beanName) { List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { return true ; } } return super .shouldSkip(beanClass, beanName); }@Override public Object postProcessAfterInitialization (@Nullable 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; }
InstantiationAwareBeanPostProcessor的作用 简述:postProcessBeforeInstantiation()加载事务和AOP的切面并放到缓存中 ,加载事务Advisors和AOP的切面转化为Advisors并放到缓存中
BeanPostProcessor的作用 简述:postProcessAfterInitialization() 判断可以之前缓存的切面是否可以应用到类中,代理目标类
注解参数 1 @EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = true)
proxyTargetClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 protected Object createProxy ( Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { if (this .beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this .beanFactory, beanName, beanClass); } ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this ); if (!proxyFactory.isProxyTargetClass()) { 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 ); } return proxyFactory.getProxy(getProxyClassLoader()); } public Object getProxy (ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); } 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() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { jdk代理 return new JdkDynamicAopProxy(config); } } 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, true ); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this ); }
exposeProxy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Slf4j @Component public class Calculator { public int div (int x,int y) { log.info("Mycaculator...div" ); log.info("div result:{}" ,x / y); return x / y; } public int add (int x,int y) { log.info("Mycaculator...add" ); Calculator proxy =(Calculator) AopContext.currentProxy(); proxy.div(x,y); log.info("add result:{}" ,x+y); return x + y; } }
@EnableAspectJAutoProxy(exposeProxy = true) 这个东东是用来干什么的?没有配置exposeProxy 暴露代理对象的时候我们方法调用,我们在add方法中 通过this来调用本类的方法div()方法的时候,发现div()的方法不会被拦截,而我们配置了后exposeProxy的属性,我们发现可以通过
int retVal = ((Calculate) AopContext.currentProxy()).div(numA,numB);
调用的时候,发现了div()方法可以被拦截
1 2 3 4 5 6 7 8 9 10 11 final class JdkDynamicAopProxy implements AopProxy , InvocationHandler , Serializable { @Override @Nullable public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if (this .advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true ; } } }
原理:把这个exposeProxy设置为true,会把代理对象存放在线程变量中,
AopContext.currentProxy())是从线程变量中获取代理对象(源码中分析)
AOP代理对象的执行流程 调用过程简述:实际调用 Calculator.add 时,实际上调用的是代理对象的对应方法,以JDK代理为例。运用了责任链模式、代理模式、模板方法
JdkDynamicAopProxy#invoke
进行一些列判断
判断是否暴露代理对象
是则把本代理对象设置进入上下文
获取调用方法上的拦截器链chain = getInterceptorsAndDynamicInterceptionAdvice
缓存获取
获取代理类Advised 保存的所有advisors,遍历根据pointcut匹配该方法的advisor
chain为空,则反射调用目标方法
chain不为空 new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain).proceed();
ExposeInvocationInterceptor.invoke(this)
AspectJAfterThrowingAdvice.invoke(this)
AfterReturnAdvice.invoke(this)
AspectJAfterAdvice.invoke(this)
AspectJmethodBeforeAdvice.invoke(this)
执行before方法
mi.proceed() 目标方法
执行finally,执行后置通知(after)
返回通知/异常通知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 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 { Object retVal; if (this .advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true ; } target = targetSource.getTarget(); if (target != null ) { targetClass = target.getClass(); } List<Object> chain = this .advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); } Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 =====================org.springframework.aop.framework.AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice=========== 把增强器中转为方法拦截器链 public List<Object> getInterceptorsAndDynamicInterceptionAdvice (Method method, Class<?> targetClass) { MethodCacheKey cacheKey = new MethodCacheKey(method); List<Object> cached = this .methodCache.get(cacheKey); if (cached == null ) { cached = this .advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this , method, targetClass); this .methodCache.put(cacheKey, cached); } return cached; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 =====================org.springframework.aop.framework.AdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice==== public List<Object> getInterceptorsAndDynamicInterceptionAdvice ( Advised config, Method method, Class<?> targetClass) { List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length); Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); for (Advisor advisor : config.getAdvisors()) { if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { MethodInterceptor[] interceptors = registry.getInterceptors(advisor); if (mm.isRuntime()) { for (MethodInterceptor interceptor : interceptors) { interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); } } else { interceptorList.addAll(Arrays.asList(interceptors)); } } } } else if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } else { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } return interceptorList; }
AOP切面如何工作 第一步:(AOP的概念和如何启用) 第一点:先跟他说 aop 是什么东西 vs oop 第二点:跟他把aop 的基本概念说清楚 ? 第三点:@EnableAspectJAutoProxy=====> 导入了AnnotationAwareAspectJAutoProxyCreator组件 我们分析出AnnotationAwareAspectJAutoProxyCreator 的继承关系图发现了他具有 BeanPostProcessor接口特性和InstantiationAwareBeanPostProcessor的特性. 我们发现InstantiationAwareBeanPostProcessor 在实例化之前(调用构造方法之前)执行的. 创建第一个bean,根据Bean的生命周期中的createBean的环节 resolveBeforeInstantiation连触发我的InstantiationAwareBeanPostProcessor的的before方法. 此事在这个环节就会去把我们的切面信息(@Aspectj)的信息找出来 然后进行缓存.
第二步:(创建代理对象的过程) BeanPostProcessor .afterinItialize方法中 创建要切的对象的时候,根据方法进行匹配去找自己的切面(增强器),然后把增强器和被切的对象创成一个代理对象.
第三步: 代理对象调用过程(jdk代理 ,cglib代理) proxyTragetClass来指定是否固定cglib 通过责任链模式+递归的来进行调用 先执行我们的 异常通知…….(catch里面执行异常通知的方法) 返回通知:(由于正是在这里返回通知中没有进行任何的try catch处理,,,,那么程序抛出异常 就不会执行返回通知的方法而是直接执行到异常通知了) 后置通知:正是因为在后置通知中,代码在finally里中 所以他才是总是被执行的…… 前置通知:执行我们的前置通知. 递归终止条件满足:执行我们的目标方法: 1、前置方法 2、后置通知 3、返回通知/异常通知