SpringAOP概念和注解使用
Spring 的 AOP可以在方法的之前和之后实现增强应用场景:日志、事务、安全控制、计算方法耗时时间,因为AOP可以解决我们程序上的代码冗余问题。
Aop编程底层的原理
动态代理技术
基于Jdk实现InvocationHandler 底层使用反射技术
基于CGLIB实现 字节码技术
注解使用
1、引入 jar 包
maven引入(SpringBoot项目)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、编写切面类喝配置类
切面类(包含五种通知的使用)
@Aspect
@Component
public class LoginAop {
//Aspect 定义切点类
/**
* @Pointcut 定义切入点
*/
// @Pointcut(value = "execution (* com.mayikt.service..*.*(..))")
@Pointcut("execution (* com.kaico.spring.aop.service..*.*(..))")
public void loginAop() {
}
/**
* 前置通知
*
* @param joinPoint
*/
@Before("loginAop()")
public void doBefore(JoinPoint joinPoint) {
System.out.println(">>>>>>>前置通知<<<<<<<<<<< ");
}
/**
* 后置通知,发生异常也会执行
*/
@After("loginAop()")
public void doAfter(JoinPoint joinPoint) {
System.out.println(">>>>>>>>后置通知<<<<<<<<<");
}
/**
* 环绕通知
*
* @param joinPoint
*/
@Around("loginAop()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(">>>>环绕通知之前执行...>>>>>>");
joinPoint.proceed();// 执行目标方案,也就是目标方法
System.out.println(">>>>环绕通知之后执行...>>>>>>");
}
/**
* 运行通知
*/
@AfterReturning("loginAop()")
public void afterReturning(JoinPoint joinPoint) {
System.out.println("运行通知执行.....");
}
/**
* 异常通知
*
* @param joinPoint
*/
@AfterThrowing("loginAop()")
public void afterThrowing(JoinPoint joinPoint) {
System.out.println(">>>>>异常通知");
}
配置类
@Configuration
@ComponentScan(basePackages = {"com.kaico.spring.aop.service","com.kaico.spring.aop.aop"})
@EnableAspectJAutoProxy//开启spring aop功能
public class MyConfig {
}
3、编写测试类
public class AppLogin {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
memberService.login("zhangsan", "123456");
}
}
事务的实现方式
spring事务的基本使用:事务注解@Transactional
和手动事务
事务原理:当方法执行的时候如果抛出异常的情况 就会回滚当前的事务 如果没有抛出异常 就提交事务
手动事务的实现方式
编写事务工具类,手动开启事务,手动提交、回滚事务。
@Component
public class TransactionalUtils {
//TransactionAspectSupport currentTransactionStatus().setRollbackOnly();
/**
* 获取当前事务管理器
*/
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
public TransactionStatus begin() {
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
System.out.println("获取当前的事务>>>>>");
return transaction;
}
/**
* 提交事务
*/
public void commit(TransactionStatus transactionStatus) {
System.out.println("提交当前的事务>>>>>");
dataSourceTransactionManager.commit(transactionStatus);
}
public void rollback(TransactionStatus transactionStatus) {
System.out.println("回滚当前的事务>>>>>");
dataSourceTransactionManager.rollback(transactionStatus);
}
}
测试工具类的方法
public int addOrderInfo(int j) {
TransactionStatus begin = transactionalUtils.begin();
try {
int i = orderInfoMapper.addOrderInfo();
int result = 1 / j;
transactionalUtils.commit(begin);
} catch (Exception e) {
e.printStackTrace();
transactionalUtils.rollback(begin);
}
return 1;
}
使用SpringAOP重构实现声明事务
1、首先自定义事务注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtTransactional {
}
2、编写AOP切面类使用事务注解
@Aspect
@Component
@Scope("prototype")
public class TransactionalAop {
// TransactionalAop Aop是原型的。
//Aspect 定义切点类
@Autowired
private TransactionalUtils transactionalUtils;
/**
* TransactionalUtils 存在那些问题
*/
/**
* @Pointcut 定义切入点
*/
// @Pointcut(value = "execution (* com.mayikt.service..*.*(..))")
@Pointcut("execution (* com.kaico.spring.aop..*.*(..))")
public void transactionalAop() {
}
/**
* 环绕通知
*
* @param joinPoint
*/
@Around("transactionalAop()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取方法名称
String methodName = joinPoint.getSignature().getName();
// 获取目标对象
Class<?> classTarget = joinPoint.getTarget().getClass();
// 获取目标对象类型
Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
// 获取目标对象方法
Method objMethod = classTarget.getMethod(methodName, par);
// 判断该目标方法上是否有加上自定义事务注解
ExtTransactional extTransactional = objMethod.getDeclaredAnnotation(ExtTransactional.class);
if (extTransactional == null) {
return joinPoint.proceed();// 执行目标方法
}
TransactionStatus begin = transactionalUtils.begin();
try {
System.out.println(">>>>自定义事物环绕通知之前执行...>>>>>>");
Object proceed = joinPoint.proceed();// 执行目标方案
System.out.println(">>>>自定义事物环绕通知之后执行...>>>>>>");
transactionalUtils.commit(begin);
return proceed;
} catch (Exception e) {
// 目标方法抛出异常的情况下 回滚当前事务
transactionalUtils.rollback(begin);
return 0;
}
// 思考如何回滚呢? 抛出异常
}
}
由源码得知,当目标方法被 try catch之后,导致方法正常执行下去没有在切面类中抛出异常,所以事务无法回滚。
Spring事务使用注意事项
声明式事务再try之后,回滚不了,因为再环绕通知中已经将异常捕获了,让目标方法将代码正常执行下去了。
解决方法: 在catch中手动回滚事务,或者手动抛异常出去。
@Transactional
public int addOrderInfo02(int j) {
try {
int i = orderInfoMapper.addOrderInfo();
int result = 1 / j;
return 1;
} catch (Exception e) {
e.printStackTrace();
// 当try之后,目标方法正常走完,不会执行aop里面的catch里面的回滚方法,手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// 或者自定义抛出异常
return 0;
}
}
Aop底层原理分析
1.首先启动SpringAop时,会使用该@EnableAspectJAutoProxy注解【开启Aop权限】
2.将@Import(AspectJAutoProxyRegistrar.class)注入SpringIOC容器中
3.AspectJAutoProxyRegistrar【手动注册 切面类】中会注册对象
BeanId:org.springframework.aop.config.internalAutoProxyCreator
BeanClass :AnnotationAwareAspectJAutoProxyCreator【】
4.AnnotationAwareAspectJAutoProxyCreator最为核心:使用后置通知在bean的对象初始化的时候,实现对代理对象的增强。
AnnotationAwareAspectJAutoProxyCreator祖宗:
AbstractAutoProxyCreator 祖宗有是BeanPostProcessor
5.被代理对象在初始化的时候,AbstractAutoProxyCreator 经过这样的一个类拦截。
判断该被代理对象是否有被有实现过接口,如果有实现过接口就使用jdk动态代理,如果没有实现接口则使用cglib动态代理。
6.当我们调用目标方法的时候,会执行JdkDynamicAopProxy的invoke方法,在使用递归算法循环遍历执行MethodInterceptor(每个通知)的invoke方法。
@EnableAspectJAutoProxy源码分析
1、第一步:使用该注解开启SpringAOP
如下图源码,该注解是将 AspectJAutoProxyRegistrar
类注入到 IOC容器中。
2、 注入AnnotationAwareAspectJAutoProxyCreator
AspectJAutoProxyRegistrar
类会将AnnotationAwareAspectJAutoProxyCreator
类注入到IOC容器中。
由源码类图得知AnnotationAwareAspectJAutoProxyCreator 的祖宗是 BeanPostProcessor,BeanPostProcessors是对我们bean的初始化方法实现增强.lnit方法之前和之后处理。
AnnotationAwarespectJAutoProxyCrcator前置和后置到底在微什么事情?。是在这个类AbstractutoProxyCreator执行。
前置:不会处理任何事情
后置:postProcessAfterInitialization() 使用后置处理实现代理对象一个创建
创建代理对象,如果被代理的这个类如果实现了接口的话,也就使用JDK动态代理,如果没有实现接口的话则使用cglib。
注意:只要是一个接口都可以。
疑问:aop 中存在很多通知的,每个通知最终如何形成链执行。For循环?
总结:@EnableAspectJAutoProxy 作用:就是注册代理对象创建工厂到IOC容器中,然后再注入其他bean时会判断该对象是否在 aop 切入点扫包范围中,最后该bean在IOC容器中初始化时会根据是否实现接口觉得采用哪种方式来实现动态代理。