spring aop 是面向切面编程,底层也是使用代理模式来进行实现的
在spring aop 如果是实现接口方式 使用 jdk 提供的代理模式,如果继承的方式,就使用cglib来实现代理模式。aop来实现代码的分离,以及动态来执行某些操作(方法)。
- 一些概念
被代理对象:只包含核心代码的对象。
代理对象:对被代理对象增强的对象。
连接点(joinpoint):指核心代码中的所有方法。
切入点(pointcut):指核心代码中需要增强的方法。
通知(advice):指事务、日志等代码,增强代码。
织入(weaving):是一个动作,指将增强代码加入到切入点中执行。
切面(aspect):通知+切入点 - 小案例
1.添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--用于来解析切入点表达式-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
- 编写代理对象类(核心代理业务类)、增强类
- xml文件中添加约束,实例化代理对象和增强对象,配置切面
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 代理对象-->
<bean id="ser" class="com.fyj.service.impl.UserService"></bean>
<!-- 增强对象-->
<bean id="aop" class="com.fyj.springaop.Aop"></bean>
<!-- <aop:config>-->
<!-- <aop:aspect ref="aop">-->
<!-- <aop:after method="show1" pointcut="execution(public void com.fyj.service.impl.UserService.core())"></aop:after>-->
<!-- </aop:aspect>-->
<!-- </aop:config>-->
<!-- 可以将pointcut提取出来-->
<aop:config>
<aop:pointcut id="pc" expression="execution(public void com.fyj.service.impl.UserService.core())"/>
<aop:aspect ref="aop">
<aop:before method="show1" pointcut-ref="pc"></aop:before>
<aop:after-returning method="show2" pointcut-ref="pc"></aop:after-returning>
<aop:after-throwing method="show3" pointcut-ref="pc"></aop:after-throwing>
<aop:after method="show4" pointcut-ref="pc"></aop:after>
</aop:aspect>
</aop:config>
execution(public void com.fyj.service.impl.UserService.core()中写的是代理对象中被增强的方法,public可以省略 void、路径名和方法名可以用*代替
4.获得实体bean执行被增强的方法
@org.junit.Test
public void test1(){
ApplicationContext app = new ClassPathXmlApplicationContext("aop.xml");
UserService userService = (UserService) app.getBean("ser");
userService.core();
}
- spring中四种增强
前置增强 ==>在核心业务代码前面执行操作aop:before
后置增强 ==>在核心代码的后面执行的操作 after-returning
异常增强 ==>核心操作发生异常执行的操作 aop:after-throwing
最终增强 ==>最后执行的代码 aop:after
环绕增强实际是自定义的增强
xml中配置
<aop:config>
<aop:aspect ref="aop">
<aop:around method="show" pointcut="execution(public void com.fyj.service.TestService.core())"></aop:around>
</aop:aspect>
</aop:config>
增强类
public void show(ProceedingJoinPoint point){
try {
System.out.println("before");
//执行代理方法
point.proceed(point.getArgs());
System.out.println("after");
} catch (Throwable throwable) {
System.out.println("throw");
}finally {
System.out.println("finally");
}
}
使用注解来实现面向切面开发
xml,中删除切面的配置信息,添加
<beans 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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.fyj"></context:component-scan>
<aop:aspectj-autoproxy />
</beans>
增强对象中
@Component(value = "tu2")
public class TestUtils2 {
@Pointcut(value = "execution(public void com.fyj.service.TestService2.core())")
public void show(){}
@Before("show()")
public void before(){
System.out.println("before");
}
}
@Pointcut指定了切点,show()方法是没有任何意义的。@Before(“show()”)设置了前置增强。同样的注解还有@AfterReturning@AfterThrowing@After@Around 环绕增强
spring关于事务的操作
编程式事务:直接在java代码中添加事务,没有动态代理之前最原始方式
声明式事务:采用spring aop的思想来进行事务管理,事务代码与核心代码进行分离
spring中的事物控制主要通过以下三个api来实现:
1.PlatformTransactionManager 负责事务的管理,是个接口,其子类负责具体工作
类或方法 | 描述 |
---|---|
DataSourceTransactionManager | PlatformTransactionManager的实现类,用于使用标准数据源时的事务管理 |
TransactionStatus getTransaction(TranstionDefinition def) | 获取事务状态信息 |
void commit(TransactionStatus status) | 提交事务 |
void rollback(TransactionStatus status) | 回滚事务 |
2.TransactionDefinition 定义事务的相关参数
规定事务隔离级别,事务传播行为
隔离级别 | 描述 |
---|---|
TRANSACTION_READ_UNCOMMITTED | 读未提交,存在问题:脏读 |
TRANSACTION_READ_COMMITTED | 读已经提交 ,存在问题:可重复读 |
TRANSACTION_REPEATABLE_READ | 可重复读 ,存在问题:幻读 |
RANSACTION_SERIALIZABLE | 可串化,解决以上所有的问题:使用数据库(同步锁)锁来进行操作 |
事务传播行为是指当一个业务方法被另一个事务方法调用时,如何进行事务控制
3.TransactionStatus 代表事务运行的实时状态
方法 | 描述 |
---|---|
boolean isNewTransaction() | 是否是新事务 |
boolean hasSavepoint() | 是否有回滚点 |
void setRollbackOnly() | 设置回滚事务 |
boolean isRollbackOnly() | 是否是回滚事务 |
void flush() | 刷新事务 |
boolean isCompleted() | 事务是否完成 |
即事务管理器读取事务参数进行事务管理,会产生事务的运行状态
- 基于xml文件进行事务管理
需要使用AOP来进行事务控制,spring提供事务代码(增强类),我们需要去编写业务代码和配置切面
transfer是需要配置事务的方法
@Component(value = "ser")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier(value = "dao")
private UserDao userDao;
// @Transactional(propagation = REQUIRED)
public void transfer(User user1, User user2) {
userDao.update(user1);
int num = 0/0;
userDao.update(user2);
}
}
<!-- 设置事务管理对象-->
<bean id="tvm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds"></property>
</bean>
<!-- 设置(环绕)通知-->
<tx:advice transaction-manager="tvm" id="tva">
<tx:attributes>
<!-- 规定待匹配的方法,以及事务设置-->
<tx:method name="transfer" propagation="REQUIRED"/>
<!-- 设置*则会匹配所有的方法-->
<!-- <tx:method name="*" propagation="REQUIRED"></tx:method>-->
</tx:attributes>
</tx:advice>
<!-- 设置切面-->
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.fyj.service.impl.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="tva" pointcut-ref="pc"></aop:advisor>
</aop:config>
<!-- <tx:annotation-driven transaction-manager="tvm"></tx:annotation-driven>-->
@org.junit.Test
public void Test1(){
ApplicationContext app = new ClassPathXmlApplicationContext("User.xml");
UserService service = (UserService) app.getBean("ser");
User user =new User(1,"a",50);
User user1 = new User(2,"b",100);
service.transfer(user,user1);
}
}
验证:当执行到 service中的 int num = 0/0; 发生运行时异常,userDao.update(user1);的操作并生效。将 int num = 0/0;注释,再次运行userDao.update(user1)和userDao.update(user2)都生效。
配置时也可以使用注解的方式
service中的注解
xml中不需要在配置切面,开启事务的注解