1. AOP概念
1.1 JoinPoint连接点:程序执行中的特定点,如方法执行,调用构造函数或字段复制等,面向切面编程,JoinPoint就是要被切入的对象。
1.2 Advice通知:在一个连接点中,切面采取的行动,针对切入点,要做的事情。
1.3 Pointcut切点:一个匹配连接点的正则表达式。每个任何连接点匹配一个切入点时,就执行与该切入点相关联的指定通知。关注哪些被切入点可以切入。
1.4 Aspect切面(Advisor):一个分布在应用程序中多个位置的标准代码/功能,通常与实际的业务逻辑(例事务管理)不同。每个切面都侧重于一个特定的横切功能。
1.5 Weaving织入:链接切面和目标对象来创建一个通知对象的过程。
1.6 Advice通知 + Pointcut切点形成了切面Aspect/Advisor
1.7 JoinPoint与Pointcut区别:在Spring AOP中,所有方法执行都是JoinPoint。Pointcut是描述信息,修饰的是JoinPoint,通过Pointcut就可以确定哪些JoinPoint可以织入Advice,JointPoint与Pointcut本质上两个不同的纬度。Advice是在JointPoint执行,Pointcut确定了哪些JoinPoint执行Advice。
2. AOP实现
2.1 织入节点
编译期间:切面在目标类编译时被织入,需要引入独立的编译器。
编译后:增强已经编译出来的类,如我们要增强依赖的 jar 包中的某个类的某个方法。
类加载期:在 JVM 进行类加载的时候进行织入。
运行期:切面在应用运行的某个时期被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象。
2.2 实现
AspectJ:AspectJ 是一个采用Java 实现的AOP框架,它能够对代码进行编译(一般在编译期进行),让代码具有AspectJ 的 AOP 功能,AspectJ 是目前实现 AOP 框架中最成熟,功能最丰富的语言。ApectJ 主要采用的是编译期静态织入的方式。
AspectWerkz:基于Java的简单、动态、轻量级、强大的AOP框架。可以在运行时或编译时轻松的改造任何(旧)应用程序或除了rt.jar以外的外部类库。
JBoss AOP:JBoss 4.0带了一个AOP框架。这个框架和JBoss应用服务器紧密地结合,但是也能够在应用中单独运行它。
Spring AOP:Spring AOP 是通过动态代理技术实现的,而动态代理是基于反射设计的。Spring AOP 采用了两种混合的实现方式:JDK 动态代理和 CGLib 动态代理
2.3 Spring AOP与AspectJ 对比
Spring AOP 与AspectJ都是完整的AOP方案,不存在依赖。Spring AOP作为一个整合框架,兼容了AspectJ切面定义,底层使用动态代理。
3. AspectJ实现
3.1 定义切面
针对test()方法进行拦截,方法执行前打印"before interceptor"
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectjInterceptor {
@Pointcut("execution( * com.hobbit.aspectj.Hello.test())")
public void execute(){ }
@Before("execute()")
public void beforeLog(){
System.out.println("before interceptor");
}
}
3.2 定义PointCut
public class Hello {
public void test(){
System.out.println("hello aspectj");
}
}
3.3 设置编译器
Java Compiler -> User compiler 选择Ajc,同时设置aspectjtools.jar
3.4 运行结果
3.5 反编译文件
4. Spring AOP实现
Spring aop既可以使用AspectJ注解实现拦截,也可以不依赖AspectJ,使用Spring advice + pointcut,实现拦截注入。
4.1 使用AspectJ注解
4.1.1 定义元注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
String value();
}
4.1.2 定义切面
import java.util.Objects;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogInterceptor {
@Pointcut("@annotation(com.hobbit.interceptor.LogAnnotation)")
public void annotationPointCut(){ }
@Before("annotationPointCut()")
public void beforeLog(JoinPoint joinPoint){
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
LogAnnotation logAnnotation = methodSignature.getMethod().getAnnotation(LogAnnotation.class);
if(Objects.nonNull(logAnnotation)){
System.out.println("before interceptor : " + logAnnotation.value());
}
}
}
4.1.3 织入
import com.hobbit.entity.UserEntity;
import com.hobbit.interceptor.LogAnnotation;
import com.hobbit.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@LogAnnotation("queryUser")
@Transactional
public UserEntity queryUser(int id){
return userMapper.getById(id);
}
}
4.2 Spring AOP实现
Spring AOP实现了完整的方案,通过定义advice + pointcut 实现对象动态织入。
4.2.1 定义Advice
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class LogBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable{
System.out.println("before advice for class:" + target.getClass() +" and method:" + method.getName());
}
}
4.2.2 配置PointCut及切面
<bean id="logBeforeAdvice" class="com.hobbit.interceptor.LogBeforeAdvice"></bean>
<bean id="logPointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value="com.hobbit.service.ShowService.show"/>
</bean>
<bean id="logAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logBeforeAdvice"></property>
<property name="pointcut" ref="logPointCut"></property>
</bean>
4.2.3 通过ProxyFactoryBean创建代理
<bean id="showProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="showService"></property>
<property name="interceptorNames" value="logAdvisor"></property>
<property name="proxyInterfaces" value="com.hobbit.service.ShowInterface"></property>
</bean>
4.2.5 Spring AOP 类图
4.2.4 Spring AOP时序图
tips: 代理对象什么节点创建的?
5. Spring AOP生效
5.1 Spring 创建代理的节点
Spring 通过org.springframework.beans.factory.config.BeanPostProcessor #postProcessAfterInitialization实现AspectJ注解代理对象的创建,具体在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization实现代理对象的创建,方法如下
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();
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());
}
核心点是根据beanName找到对应的切面列表,完成代理对象的创建
容器启动时,注册BeanPostProcessor对象,org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
代理对象创建前
创建后
5.2 Spring AOP拦截
Spring AOP通过把Advice转化成MethodIntercetor,通过递归调用的方式实现了Advice的织入,完成advice的调用后,调用目标方法完成切入。核心点:让Advice和Joinpoint会面。
目标对象:ReflectiveMethodInvocation,在JdkDynamicAopProxy invoke方法中创建,是JoinPiont的一个实现。
拦截对象:MethodInterceptor.invoke(MethodInvocation invocation)包括了要拦截的对象。实现不同形式的拦截
5.3 总结
Spring的AOP实现并不依赖于AspectJ任何类,它自己实现了一套AOP的。比如它Spring自己提供的BeforeAdvice和AfterAdvice都是对AOP联盟规范的标准实现。以及Spring自己抽象出来的对Advice的包装:org.springframework.aop.Advisor贯穿Spring AOP的始终。