什么是AOP?
- 概念
AOP:Aspect Oriented Programing即面向切面编程
简单来说:它就是把我们程序中重复性的代码抽取出来,在需要执行的时候,使用动态代理技术,在不修改源代码的情况下,对我们的方法进行增强。
AOP的作用和优势
- 作用
在程序运行期间,不修改源码对方法进行增强 - 优势
减少重复代码
提高开发效率
方便维护
此处存在的问题:
事务被自动控制了,我们使用了connection对象的setAutoCommit(true)
此方式控制事务,只能控制一条,如果业务方法一次性要执行多条sql语句,这种方法就无法实现功能了。如下图:
当我们执行时,由于执行有异常,转账失败。但是因为我们是每次执行持久层方法都是独立事务,导致无法实现事务控制(不符合事务的一致性)。
解决办法:
让业务层来控制事务的提交和回滚。
事务控制
开始事务:beginTransaction( )
提交事务:commit( )
回滚事务:rollback( )
释放连接:release( )
通知
前置通知、后置通知、异常通知、最终通知、环绕通知
动态代理
- 特点:
字节码随用随创建,随用随加载。
它与静态代理的区别,静态代理是字节码一上来就创建好了,并完成加载。
装饰者模式就是静态代理的一种方式。 - 动态代理常用的两种方式:
1、基于接口的动态代理
提供者:JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口。
2、基于子类的动态代理
提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
要求:被代理类不能用 final 修饰的类(最终类)。
spring中基于xml的aop配置
1、把通知Bean也交给spring来处理
2、使用aop:config标签表明开始aop的配置
3、使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一标识
ref属性:是指定通知类bean的id
4、在aop:aspect标签的内部使用对应的标签配置通知的类型
aop:before:表示配置前置通知
method:用于指定Logger类中哪个方法是前置通知
pointcut:用于指定切入点表达式,该表达式的含义是对业务层中哪些方法进行增强
完整的写法:execution(void com.gk.service.impl.AccountServiceImpl.saveAccount())
缩写部分:execution(* com.gk.service.impl.*.*())
全通配写法:
* *..*.*(..)
实际开发中切入点表达式的通常写法:
* com.gk.service.impl.*.*(..)
5、aop:point-ref id用于指定切入点表达式的唯一标识,expression属性用于指定表达式内容
aop:pointcut expression="* com.gk.service.impl.*.*(..))" id="pt1"/>
也可以写在aop:aspect外面(必须是在aop:aspect标签之前),即所有切面可用。
6、aop:around 用于配置环绕通知
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
spring 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数。
其中有一个proceed()方法明确调用业务层方法(切入点方法)
基于注解的AOP配置
约束aop和context
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
在配置文件中指定spring要扫描的包:
< !-- 告知 spring,在创建容器时要扫描的包 --> < context:component-scan base-package=“com.itheima”>< /context:component-scan >
在通知类上使用@ Aspect注解声明为切面
以及一个@Around注解
@PointCut注解一定要注意下面通知调用的时候要使用pt1()加上括号,不然调用不成功;
开启spring对注解AOP的支持
< aop:aspectj-autoproxy >< /aop:aspectj-autoproxy >
完全不使用xml配置
@Configuration
@ComponentScan(basePackages="com.gk")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
事务控制
TransactionDefinition
它是事务的定义信息对象,里面有如下方法:
1、获取事务对象名称
- String getName()
2、获取事务隔离级:反映事务提交并发访问时的处理态度 - int getIsolationLevel()
3、获取事务传播行为
- int getPropagationBehaviour()
4、获取事务超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
- int getTimeout()
5、获取事务是否只读:建议查询时设置为只读。
- boolean isReadOnly()
读写型事务:增加、删除、修改开启事务
只读型事务:执行查询时,也会开启事务
基于xml的声明式事务控制
一、环境搭建
- 拷贝必要的jar包到工程中
- 创建spring的配置文件并导入约束
- 准备数据库表和实体类
- 编写业务层接口和实现类
- 编写Dao接口和实现类
- 在配置文件中配置业务层和持久层
二、配置步骤
- 配置事务管理器
<!-- 配置一个事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入 DataSource -->
<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>
- 配置AOP切入点表达式
- 配置切入点表达式和事务通知的对应关系
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut expression="execution(* com.gk.service.impl.*.*(..))"
id="pt1" />
<!-- 建立通知和切入点表达式的对应关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1" />
</aop:config>
基于注解的声明式事务控制
一、环境搭建
- 拷贝必备的 jar 包到工程的 lib 目录
- 创建 spring 的配置文件导入约束并配置扫描的包
- 创建数据库表和实体类
- 创建业务层接口和实现类并使用注解让 spring 管理
- 创建 Dao 接口和实现类并使用注解让 spring 管理
二、配置步骤
- 配置事务管理器并注入数据源
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
- 在业务层使用@Transactional 注解
@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
//只读型事务的配置
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
}
- 在配置文件中开启 spring 对注解事务的支持
<tx:annotation-driven transaction-manager="transactionManager"/>