spring之AOP学习简单笔记
Aop概念
面向切面编程 是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
利用AOP可以对业务逻辑的各个部分进行隔离,将那些与业务无关,却为业务模块所共同调用的逻辑 封装起来,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
常见应用场景
事务管理、性能监视、权限控制、安全检查、缓存 、日志等
Aop原理的动态代理实现【理解】
【1】JDK 本身的动态代理 Proxy 类; 【2】采用 cglib的enhancer增加类
JDK本身动态代理,要求必须提供 对应的接口
Cglib的增强可以不用任何接口
AOP的入门
导入jar包
spring-aop-4.2.4.RELEASE.jar
<!--配置aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
编写目标类 及其对应的接口
IUserService 和 UserService
编写切面类
AspectObj
Spring容器进行相应配置
<!-- new 目标类--> |
进行测试
//读取配置文件 |
切入点表达式
execution()
语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)
修饰符,一般省略
public 公共方法
* 任意
返回值,不能省略
void 返回没有值
String 返回值字符串
* 任意
包,[省略]
com.qf.crm 固定包
com.qf.crm.*.service crm包下面子包任意 (例如:com.qf.crm.service)
com.qf.crm.. crm包下面的所有子包(含自己)
com.qf.crm.*.service.. crm包下面任意子包,固定目录service,service目录任意包
类,[省略]
UserServiceImpl 指定类
*Impl 以Impl结尾
User* 以User开头
* 任意
方法名,不能省略
addUser 固定方法
add* 以add开头
*Do 以Do结尾
* 任意
(参数)
() 无参
(int) 一个整型
(int ,int) 两个
(..) 参数任意
throws ,可省略,一般不写。
案例:
execution**(public * com.qf.service.impl.*.*(..))**
within:
匹配包或子包中的方法
pointcutexp包里的任意类: within(com.test.spring.aop.pointcutexp.*)
pointcutexp包和所有子包里的任意类:within(com.test.spring.aop.pointcutexp..*)
this:
匹配实现接口的代理对象中的方法
用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
案例:实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类:this(com.test.spring.aop.pointcutexp.Intf)
当一个实现了接口的类被AOP的时候,用getBean方法必须cast为接口类型,不能为该类的类型
target:
匹配实现接口的目标对象中的方法
用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配
args:
匹配参数格式符合标准的方法
参数为String类型(运行是决定)的方法: args(String)
bean(id)
对指定的bean所有的方法
@target:
用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@within:
用于匹配所有持有指定注解类型内的方法;
带有@Transactional标注的所有类的任意方法:
@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)
@annotation:
用于匹配当前执行方法持有指定注解的方法;
带有@Transactional标注的任意方法:@annotation(org.springframework.transaction.annotation.Transactional) @within和@target针对类的注解,@annotation是针对方法的注解
@args:
用于匹配当前执行的方法传入的参数持有指定注解的执行;
参数带有@Transactional标注的方法:@args(org.springframework.transaction.annotation.Transactional)
该注解标注在参数上
通知类型
before:前置通知(应用:各种校验、日志)
after:最终通知(应用:清理现场)
afterReturning:后置通知(应用:常规数据处理)
around:环绕通知(应用:十分强大,可以做任何事情)
afterThrowing:抛出异常通知(应用:包装异常信息)
基于注解的实现方式
目标类
UserService
切面类
AnnotationAspect
配置文件:
<context:component-scan base-package="com.aspect" />
<aop:aspectj-autoproxy />
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:Anno.xml")
public class TestAnno {
@Autowired
private IUserService user;
@Test
public void testAdvice(){
user.selectUser();
}
}
SpringAOP的事务
事务的隔离问题: 脏读 、不可重复读、幻读、可串行化
事务的传播属性:在两个业务之间如何共享事务
案例 : 相互转账问题
增加相应的事务jar包
<!-- 数据库连接jar--> |
配置数据库jdbc 连接
Jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=admin |
配置数据源 dataSources
<!--读取 jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--spring的JDBC: 数据库连接-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}" />
</bean>
增加spring的jdbc操作
<!-- 采用spring 自身的 jdbc解决方案 : 后续用 mybatis的解决方案 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
<property name="dataSource" ref="dataSource" />
</bean>
在dao中实现数据库的两个操作:
一个增加账户金额的;另外一个减少账户金额的
配置文件:增加jdbcTemplate的属性注入
<bean id="accountDaoImpl" class="com.hzqf.dao.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
在service中调用dao的操作。并完成 转账业务
配置文件 : 增加 dao的属性注入
<bean id="accountService" class="com.hzqf.service.impl.AccountService" >
<property name="accountDao" ref="accountDaoImpl" />
</bean>
事务管理配置步骤:
增加事务管理器
<!-- 1.增加事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
配置业务方法的切面
<!-- 2.配置所有需要被事务管理器管理的 目标类及其方法-->
<aop:config>
<!-- 哪些类的哪些方法参与事务 -->
<aop:pointcut id="allMethods" expression="execution(* com.hzqf.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allMethods" />
</aop:config>
配置业务管理中的方法及其对应的事务属性
<!--3.沿用事务管理器,来管理 需要进行事务操作的各种方法-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--事务属性主要配置方案:
isolation: 表示事务隔离级别: 取值 default 等
propagation:表示事务的传播级别:取值 REQUIRED 等
no-rollback-for:表示就算有里面的异常也不进行回滚;
rollback-for:表示有里面的异常就需要回滚;
timeout: 执行超时时间; 秒级别
read-only:是否只读
-->
<!--转账业务-->
<tx:method name="transaction" isolation="DEFAULT" propagation="REQUIRED" />
<!-- 增加业务 -->
<tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" />
<!-- 查询业务,无需事务,所以设置为只读-->
<tx:method name="select*" read-only="true"/>
</tx:attributes>
</tx:advice>
采用注解的方式:
增加事务管理器
<!-- 1.增加事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
增加事务注解支持
<!-- 开启注解功能的 事务管理; 后续,只要在对应需要处理业务的方法上,增加 @Transaction注解 即可-->
<tx:annotation-driven transaction-manager="txManager" />
在需要增加事务管理的方法类上加上对应的@Transaction 注解
@Transactional //采用注解的方式进行事务控制
public void transaction(final String firstName, final String secondName, final int account) {
//第二个人扣款
userDao.accountOut(secondName,account);
//int i = 1 /0 ;
//第一个人收款
userDao.accountIn(firstName,account);
}