Spring AOP 复习
-
Aspect Oriented Programming
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,利用aop可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低
作用:在程序运行期间,不修改源码对已有方法进行增强
优势:减少重复代码,提高开发效率,维护方便
SpringAOP即通过配置的方式实现aop
joinpoint连接点 指被拦截的点,在spring中指方法
pointcut切入点 被增强的方法
advice通知 拦截到joinpoint后要做的事
前置通知:切入点之前的逻辑
后置通知:切入点正常执行之后的逻辑
异常通知:catch中的逻辑
最终通知:finally中的逻辑
环绕通知:整个被加强后的方法(包括各通知和切入点)
introduction引介 一种特殊的通知,在不修改类代码的前提下在运行期为类动态地添加一些方法或field
target目标对象 被代理的对象
weaving织入 把增强应用到目标对象来创建新的代理对象的过程,spring采用动态代理织入
proxy代理 一个类被aop织入增强后,就产生一个结果代理类
aspect切面 是切入点和通知(引介)的结合
应用
a. 开发阶段
编写核心业务代码 --> 把公用代码抽取出来制成通知 --> 在配置文件中声明切入点和通知之间的关系
b. 运行阶段
Spring框架监控切入点方法的执行,一旦监控到切入点方法被运行,使用代理机制,动态创建目标对旬的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入
xml
- 建立maven工程,添加依赖
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency>
- 编写核心业务类及方法(如业务层接口)
public class AccountServiceImpl implements AccountService{ void saveAccount(){ System.out.println("saving...") } }
- 编写提供公共代码的类
public class advice{ public void preMethod(){...}; }
- 在resources包下编写配置文件bean.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"> </beans>
- 配置spring ioc,把service对象配置进容器
<bean id="accountService" class="包路径.AccountServiceImpl"></bean>
- 把通知类配置成bean
<bean id="advice" class="包路径.Advice"></bean>
- 配置切面
<aop:config> <aop:aspect id="advice" ref="advice"> <aop:before method="preMethod" pointcut="* com.domain.service.impl.*.*(..)"></aop:before> </aop:aspect> </aop:config>
aop:config标签表明开始aop的配置
aop:aspect标签表明配置切面
id属性提供切面的唯一标识
ref属性指定通知类beanId
aop:before标签表示前置通知
method属性用于指定方法
pointcut属性用于通过切入点表达式指定对哪些方法进行增强
(before前置, after-returning后置, after-throwing异常, after最终)
切入点表达式 (用导入的aspectJ依赖解析)
访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
访问修饰符可以省略,返回值、包名、类名、方法名、参数类型可以用通配符*表示任意(但必须有,一个通配符对应一个),包名可以使用..表示当前包及其子包,参数列表可以只写数据类型,
参数列表可以用..表示任意个数的参数
通常写法:* com.domain.service.impl.星.星(..)
aop:pointcut标签用于指定切入点表达式,属性id和expression,配在aop:aspect外面则可以在其后的切面都可以通过pointcut-ref属性使用
- 配置环绕通知
<aop:around method="aroundMethod" pointcut-ref="pointcutId"></aop:around>
//环绕通知: 即spring提供的一种在代码中手动设置增强代码的方式,而不需要挨个配置 public Object arountMethod(ProceedingJoinPoint pjp){ //ProceedingJoinPoint接口由spring提供 Object returnValue = null try{ preMethod(); //前置通知 Object[] args = pjp.getArgs(); //获取参数 returnValue = pjp.proceed(args); //明确调用业务层方法 afterReturningMethod(); //后置通知 return returnValue; } catch(Throwable t){ //不能写Exception afterThrowingMethod(); //异常通知 throw new RuntimeException(t); } finally{ afterMethod(); //最终通知 } }
注解
-
2.3.4.同xml方式
-
bean.xml中配置
要扫描的包
开启spring注解AOP支持(可以在配置类上用@EnableAspectJAutoProxy替代)
<context:component-scan base-package="com.domain"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
将AccountServiceImpl和Advice放入ioc容器
-
Advice类上加@Aspect
-
配置切入点表达式
在Adive类中新加私有方法private void ptId(),在其上加@Pointcut("execution(切入点表达式)")
-
在通知方法上加注解@Before("ptId()") @AfterReturning("ptId()") @Afterthrowing("ptId()") @After("ptId()") @Around("ptId()")
ptId()括号不能少
- 注解aop的各通知顺序可能会发生错乱,所以推荐使用环绕通知
Spring 事务控制(声明式)
事务处于业务层,有四大特性
(1)原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
(2)一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
(3)隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行 相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆, 必须串行化或序列化请 求,使得在同一时间仅有一个请求用于同一数据。
(4)持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
导入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
spring提供PlatformTransactionManager接口,里面提供commit()和rollback()方法
常用的实现类:DataSourceTransactionManager(常用)或HibernateTransactionManager(专用)
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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
-
配置数据源
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/day04_spring"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean>
-
配置业务层类和持久层类
<bean id="accountDao" class="全限定类名"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="accountService" class="全限定类名"> <property name="accountDao" ref="accountDao"></property> </bean>
-
配置事务管理器
<bean id="transactionManager" class="org.spring.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref=dataSource></property> </bean>
-
配置事务通知
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" read-only="false"/> <!-- 部分匹配优先级更高--> <tx:method name="find*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice>
isolation="" 隔离级别:默认使用数据库的默认隔离级别
propagation="" 传播行为:默认REQUIRED,表示一定会有事务,查询用SUPPORTS
read-only="" 是否只读:默认false,只有查询设为true
rollback-for="" 指定异常:产生该异常时回滚,其他异常不回滚,不设定表示全回滚
no-rollback-for="" 指定异常:产生该异常时不回滚,其他异常回滚,不设定表示全回滚
timeout="" 超时时间:默认-1,永不超时,以秒为单位 -
配置AOP
<aop:config> <!-- 配置切入点表达式 --> <aop:pointcut id="pt1" expression="execution(* com.whteway.service.impl.*.*(..))"/> <!-- 建立切入点与事务通知的对应关系 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/> </aop:config>
注解
-
配置事务管理器
-
开启spring对注解事务的支持
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
//创建事务管理器的配置类和DataSource的配置类 public class TransactionConfig{ @Bean(name="transactionManager") public PlatformTransactionManager createTransactionManager(DataSource dataSource){ return new DataSourceTransactionManager(dataSource); } } //在主配置类上加@EnableTransactionManagement // 和@Import({DataSourceConfig.class, TransactionConfig.class})
-
在需要事务支持的地方(类或方法)使用@Transactional
原理
对象的配置来决定。默认的策略是如果目标类是接口, 则使用JDK动态代理技术,否则使用Cglib来生成代理。
JDK 动态接口代理
- JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。 InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类 的代码,动态将横切逻辑和业务逻辑编制在一起。Proxy 利用 InvocationHandler 动态创建 一个符合某一接口的实例,生成目标类的代理对象。
13/04/2018 Page 131 of 283
CGLib 动态代理
- :CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库, 可以在运行期扩展 Java 类与实现 Java 接口,CGLib 封装了 asm,可以再运行期动态生成新 的 class。和 JDK 动态代理相比较:JDK 创建代理有一个限制,就是只能为接口创建代理实例, 而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。