AOP:Aspect Oriented Programming,面向切面编程,是横向的对不同事物的处理方式。
Spring AOP依赖包:
1 | <dependency> |
Spring AOP配置方式:
xml配置(需要aop命名空间)
aspect:通知类不需要实现相应通知接口,配置时指定通知类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!--配置目标类,内部的方法是连接点-->
<bean id="userService" class="com.zzr.service.impl.UserServiceImpl"/>
<!--配置通知类,内部的方法是增强方法-->
<bean id=“myAdvice" class="com.zzr.advice.MyAdvice"/>
<aop:config>
<!--配置切点表达式,对哪些方法进行增强 (筛选)-->
<aop:pointcut id="myPointcut" expression="execution(void com.zzr.service.impl.UserServiceImpl.show())"/>
<!--切面=切点+通知-->
<aop:aspect ref="myAdvice">
<!--指定前置通知方法是beforeAdvice-->
<aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
<!--指定后置通知方法是afterAdvice-->
<aop:after-returning method="afterAdvice" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>advisor:通知类实现对应通知接口(如MethodBeforeAdvice(前置通知), AfterReturningAdvice(后置通知),MethodInterceptor(环绕通知)),配置时不用指定通知类型
1
2
3
4
5
6
7
8
9
10<!--配置目标类,内部的方法是连接点-->
<bean id="userService" class="com.zzr.service.impl.UserServiceImpl"/>
<!--配置通知类,内部的方法是增强方法-->
<bean id=advices" class="com.zzr.advice.advices"/>
<!-- 使用advisor配置 -->
<aop:config>
<!-- advice-ref:通知Bean的id -->
<aop:advisor advice-ref="advices" pointcut="execution(void com.zzr.service.impl.UserServiceImpl.show())"/>
</aop:config>两种配置方式对比:
- 一个advisor只能配置一个固定通知和一个切点表达式;
- 一个aspect可以配置多个通知和多个切点表达式任意组合,粒度更细。
- 如果通知类型多、允许随意搭配情况下可以使用aspect进行配置;
- 如果通知类型单一、且通知类中通知方法一次性都会使用到的情况下可以使用advisor进行配置;
- 在通知类型已经固定,不用人为指定通知类型时,可以使用advisor进行配置,例如Spring事务控制的配置;
切点表达式:execution([访问修饰符]返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略不写;
- 返回值类型、某一级包名、类名、方法名 可以使用 * 表示任意;
- 包名与类名之间使用单点 . 表示该包下的类,使用双点 .. 表示该包及其子包下的类;
- 参数列表可以使用两个点 .. 表示任意参数。
- 例如:
1
2
3
4
5
6
7
8
9
10//表示访问修饰符为public、无返回值、在com.zzr.aop包下的TargetImpl类的无参方法show
execution(public void com.zzr.aop.TargetImpl.show())
//表述com.zzr.aop包下的TargetImpl类的任意方法
execution(* com.zzr.aop.TargetImpl.*(..))
//表示com.zzr.aop包下的任意类的任意方法
execution(* com.zzr.aop.*.*(..))
//表示com.zzr.aop包及其子包下的任意类的任意方法
execution(* com.zzr.aop..*.*(..))
//表示任意包中的任意类的任意方法
execution(* *..*.*(..))
通知类型:
1
2
3
4
5前置通知 < aop:before > 目标方法执行之前执行
后置通知 < aop:after-returning > 目标方法执行之后执行,目标方法异常时,不在执行
环绕通知 < aop:around > 目标方法执行前后执行,目标方法异常时,环绕后方法不在执行
异常通知 < aop:after-throwing > 目标方法抛出异常时执行
最终通知 < aop:after > 不管目标方法是否有异常,最终都会执行
注解配置:
通知类加@Aspect
在通知类中的通知方法加对应通知注解和参数对应的切点表达式
Spring核心配置类加@EnableAspectJAutoProxy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//第一步
public class AnnoAdvice {
//第二步
//切点表达式抽取
public void pointcut(){}
//前置通知
public void before(JoinPoint joinPoint){}
//后置通知
public void AfterReturning(JoinPoint joinPoint){}
//环绕通知
public void around(ProceedingJoinPoint joinPoint) throws Throwable {}
//异常通知
public void AfterThrowing(JoinPoint joinPoint){}
//最终通知
public void After(JoinPoint joinPoint){}
}1
2
3
4
//第三步 配置文件用<aop:aspectj-autoproxy/>
public class ApplicationContextConfig {}
Spring AOP原理:利用动态代理技术,通过解析aop对应标签或注解,向Spring容器注入一个AspectJAwareAdvisorAutoProxyCreator的BeanPostProcessor,在该BeanPostProcessor中完成代理对象的生成
- 动态代理两种方式
- 基于接口的动态代理:JDK 动态代理技术(有接口默认)
- 基于子类的动态代理:Cglib 动态代理技术 (手动配置<aop:config proxy-target-class=“true”>强制使用Cglib方式)
Spring声明式事务:
xml配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<!--事务命名空间-->
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
<!--平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--Spring提供的事务通知-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes> <!--事务属性-->
<tx:method name="方法名称"
isolation="隔离级别"
propagation="传播行为" <!--主要解决是A方法调用B方法时,事务的传播方式问题(业务调业务,事务嵌套问题)-->
read-only="只读状态"
timeout="超时时间"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="myAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
</aop:config>
<tx:annotation-driven transaction-manager="transactionManager"/>注解配置:
- 对应业务方法加@Transactional注解和参数对应的事务属性
- 核心配置类加@EnableTransactionManagement注解开启事务
- 配置PlatformTransactionManager平台事务管理器(需要数据源)
1
2
3
4
5
6
7
8
9
10
11
12
public class AccoutServiceImpl implements AccountService {
private AccountMapper accountMapper;
//<tx:method name="*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
public void transferMoney(String decrAccountName, String incrAccountName, int money) {
accountMapper.decrMoney(decrAccountName,money); //转出钱
int i = 1/0; //模拟某些逻辑产生的异常
accountMapper.incrMoney(incrAccountName,money); //转入钱
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//xml标签为<tx:annotation-driven transaction-manager="transactionManager"/>
public class ApplicationContextConfig {
public PlatformTransactionManager tansactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
// ... 省略其他配置 ...
}
Spring事务原理:通过标签或注解解析,向Spring容器注入一个TransactionInterceptor,TransactionInterceptor实现了MethodInterceptor(环绕通知接口),TransactionInterceptor中的invoke方法会被执行,跟踪invoke方法,最终会看到事务的开启和提交
- 本文作者: zzr
- 本文链接: http://zzruei.github.io/2023/1297375e6e.html
- 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!