概述
Spring 整合AspectJ框架实现AOP只是Spring框架中AOP的一种实现方式,此方式相对比较简单,实现方便。但此方式底层还是要转换为Spring原生AOP的实现,Spring AOP原生方式实现的核心有三大部分构成,分别是:
- JDK代理。
- CGLIB代理
- org.aopalliance包下的拦截体系
架构分析
其中DefaultAdvisorAutoProxyCreator这个类功能更为强大,这个类的奇妙之处是他实现BeanPostProcessor接口,当ApplicationContext读取所有的Bean配置信息后,这个类将扫描上下文,寻找所有的Advisor对象(一个Advisor由切入点和通知组成),将这些Advisor应用到所有符合切入点的Bean中。
实际案例
项目结构
PjApplication主启动类
主启动类 配置 DefaultAdvisorAutoProxyCreator对象 ,一般是配置在配置类上,这里为了简化代码,放在主启动类上.
@SpringBootApplication
public class PjApplication {
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
//此对象会在容器启动时扫描Advisor对象,然后基于切入点为目标对象创建代理对象
//然后再执行切入点方法时,自动执行Advice对象通知方法
return new DefaultAdvisorAutoProxyCreator();
}
public static void main(String[] args) {
SpringApplication.run(CgbSourceAop01Application.class, args);
}
}
@Bean注解应用于配置类中(使用了@Configuration修饰)
@Bean描述的方法其返回值会交给spring管理,spring管理这个bean默认bean名字为方法名
- DefaultAdvisorAutoProxyCreator对象会在容器启动时扫描Advisor对象
- 然后基于切入点为目标对象创建代理对象
- 然后再执行切入点方法时,自动执行Advice对象通知方法
RequiredLog 注解
/**借助此注解描述切入点方法*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
}
service 接口
public interface MailService {
boolean sendMsg(String msg);
}
MailServiceImpl 目标方法实现类
@Service
public class MailServiceImpl implements MailService {
@RequiredLog //希望这个注解描述的方法为一个切入点方法(目标方法)
@Override
public boolean sendMsg(String msg) {
//System.out.println("start:"+System.currentTimeMillis());
System.out.println("send "+msg);
//System.out.println("end:"+System.currentTimeMillis());
return true;
}
}
LogAdvice 通知方法
继承 MethodInterceptor接口
/**
* 日志通知对象
*/
public class LogAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("start:"+System.currentTimeMillis());
Object result=invocation.proceed();//执行目标方法
System.out.println("end:"+System.currentTimeMillis());
return result;
}
}
- 切入点对应的目标方法执行之前执行,可以在目标方法执行之前和之后做点拓展业务
- invocation连接点对象(封装了要执行的目标方法信息,可以通过反射调用目标方法)
LogAdvisor 顾问 (内部注册通知,判断切入点方法)
实现啦 Advisor接口
Spring容器在启动时会基于BeanPostProcessor找到所有的Advisor对象(顾问),并基于Advisor对象中切入点的描述为目标对象创建代理对象.当执行目标方法时会执行Advisor对象关联通知.
@Component
public class LogAdvisor extends StaticMethodMatcherPointcutAdvisor {
private static final long serialVersionUID = -3987392064269894257L;
public LogAdvisor() {
//在advisor内部注册一个日志通知
setAdvice(new LogAdvice());
}
/**
*
* matches方法用于判定方法参数中的method是否为一个切入点方法,当它的返回值
* 为true时,表示参数中的method对象为一个切入点方法.
* @param method 对应了目标方法对象(可能是目标方法所在类的父类方法对象)
* @param targetClass 代表目标对象类型
*/
@Override
public boolean matches(Method method, Class<?> targetClass) {
try {
Method targetMethod=
targetClass.getMethod(method.getName(), method.getParameterTypes());
//检测目标方法上是否有requiredLog注解
return targetMethod.isAnnotationPresent(RequiredLog.class);
}catch(Exception e) {
return false;
}
}
}
测试 Tests
@SpringBootTest
public class MailServiceTests {
@Autowired
private MailService mailService;
@Test
void testSendMsg() {
mailService.sendMsg("hello cgb2006");
}
}