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/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://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
@Aspect
public 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/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://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的后置处理器来完成反射对象的创建。