AOP的相关术语
先说明AOP的这些术语想要做什么事? 其实就是要确定在哪些方法中的哪些地方去插入横切逻辑。
连接点 JointPoint
连接点其实是时机点,也就是可以插入横切逻辑代码的时机,比如说方法开始执行时、方法结束时、方法异常结束时等,连接点也被称为候选点,是切入点的候选点。
切入点 PonitCut
真正被横切逻辑代码增强的方法就是切入点,也就是我们需要增强的方法。
Advice增强
说的就是横切逻辑以及时机点(方位点),分类有前置通知、后置通知、环绕通知、异常通知、最终通知。
织入 Weaving
切面 Aspect
说的就是advice增强织入到切入点的这整个一件事。
代码实现
xml方式
直接代码进行展示,流程就是先导入相关依赖,然后声明一个Aop的类,在xml中进行相关配置
pom依赖如下:<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version></dependency>
Aop的类,其实就是一个工具类public class AopUtils {public void before(JoinPoint joinPoint) {Object[] args = joinPoint.getArgs();for (Object arg : args) {System.out.println("前置通知的参数为:" + arg);}System.out.println("前置通知结束");}public void after(JoinPoint joinPoint) {System.out.println("后置通知结束");}public void exception(JoinPoint joinPoint) {System.out.println("异常通知结束");}public void finallyAdvice() {System.out.println("最终通知");}public void returnAdvice() {System.out.println("正常返回通知");}public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕通知前置");proceedingJoinPoint.proceed();System.out.println("环绕通知后置");}}
// 进行applicationContext.xml的配置<?xml version="1.0" encoding="UTF-8"?><beans default-lazy-init="false" xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><beans><bean id="aopUtils" class="com.wangzhi.util.AopUtils"/><bean id="user" class="com.wangzhi.domain.User"/><aop:config><aop:aspect id="aop" ref="aopUtils"><!--配置切入点--><aop:pointcut id="userSetName" expression="execution(public void com.wangzhi.domain.User.setName(String))"/><!--配置前置后置等相关通知--><aop:before method="before" pointcut-ref="userSetName"/><aop:after method="finallyAdvice" pointcut-ref="userSetName"/><aop:after-returning method="returnAdvice" pointcut-ref="userSetName"/><aop:after-throwing method="exception" pointcut-ref="userSetName"/></aop:aspect></aop:config></beans></beans>
aspectj表达式的写法中,可以用*代替方法名、类名、包名等
注解相关
xml+注解形式
package com.wangzhi.util;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.context.annotation.Bean;import org.springframework.stereotype.Component;@Component@Aspectpublic class AopUtils {@Pointcut("execution(public void com.wangzhi.domain.User.*(..))")public void aopMethod() {}@Before("aopMethod()")public void before(JoinPoint joinPoint) {Object[] args = joinPoint.getArgs();for (Object arg : args) {System.out.println("前置通知的参数为:" + arg);}System.out.println("前置通知结束");}@AfterReturning(value = "aopMethod()", returning = "value")public void after(String value) {System.out.println("后置通知结束");}@AfterThrowing("aopMethod()")public void exception(JoinPoint joinPoint) {System.out.println("异常通知结束");}@After("aopMethod()")public void finallyAdvice() {System.out.println("最终通知");}@Around("aopMethod()")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕通知前置");proceedingJoinPoint.proceed();System.out.println("环绕通知后置");}}
<?xml version="1.0" encoding="UTF-8"?><beans default-lazy-init="false" xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><beans><aop:aspectj-autoproxy proxy-target-class="true"/></beans></beans>
纯注解
其实就是将注解驱动和代理设置放在配置Bean上,使用的注解为: @EnableAspectJAutoProxy
源码追踪
AOP是通过代理实现的,所以我们要找到的是创建代理Bean的代码,看看是怎么的一个流程,那就需要看一下我们创建Bean的方法,也就是refresh中的finishBeanFactoryInitialization(beanFactory)
- DefaultListableBeanFactory#preInstantiateSingletons
- AbstractBeanFactory#getBean(java.lang.String)
- AbstractBeanFactory#doGetBean
- AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
- AbstractAutoProxyCreator#postProcessAfterInitialization
- AbstractAutoProxyCreator#wrapIfNecessary
- AbstractAutoProxyCreator#createProxy
ProxyFactory#getProxy(java.lang.ClassLoader)
public Object getProxy(@Nullable ClassLoader classLoader) {return createAopProxy().getProxy(classLoader);}
CglibAopProxy#getProxy(java.lang.ClassLoader)
当然也可能是JDK的动态代理,这个就看具体的配置了,知道两个代理的区别:jdk的动态代理必须要有接口的实现就可以了。
总结就是其实是依赖于Bean的后置处理器来完成反射对象的创建。
