什么是AOP
AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能饿统一维护的一种技术
AOP是OOP的延续,是软件开发中的一个热点,也是一个Spring框架中的一个重要内容,是函数式编程的一种衍生范型,利用AOP可以对业务逻辑的各种部分进行隔离,从而使得业务逻辑各部分之间的耦合读降低,提高程序的可重用性,同时提高了开发效率
功能:
1.事务 2.日记录 3.权限控制
AOP的优势
优势:减少重复代码,提高开发效率,并且便于维护
AOP关键术语
通知类型:
AOP将抽取出来的共性功能称为通知,通知类型:以通知在上下文中的具体位置作为划分
前置通知:Before
后置通知:After
返回通知:After-returning
异常通知:After-throwing
环绕通知:Around
Spring AOP
①. AOP 在Spring中的作用
提供声明式事务;允许用户自定义切面
②. AOP 的基本概念
横切关注点:跨越应用程序多个模块的方法或功能。即与我们业务逻辑无关,但需要我们关注的部分就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
- Aspect(切面):横切关注点被模块化的特殊对象。通常是一个类,里面可以定义切入点和通知
- Weaving(织入):把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时,类加载时和运行时完成。Spring和其它纯Java AOP框架一样,在运行时完成织入
- Advice(通知):AOP在特定的切入点上执行的增强处理,是切面必须要完成的工作,也是类中的一个方法
- Target(目标):被通知对象
- AOP(代理):AOP框架创建的对象,代理就是目标对象的加强。Spring中的 AOP 代理可以是 JDK 动态代理,也可以是 CGLIB 代理,前者基于接口,后者基于子类
- JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
- Pointcut(切入点):就是带有通知的连接点,与切入点匹配的执行点
③. 使用Spring实现Aop
以下是用AOP实现事务(Transaction)
方式一(xml版):
<!-- 获取数据源-->
<bean id="data" class="com.alibaba.druid.pool.DruidDataSource" >
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/javaee230809db?serverTimezone=UTC" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="data" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.apesource.dao" />
</bean>
<!--扫描注入对象的注解-->
<context:component-scan base-package="com.apesource"></context:component-scan>
<!-- 配置一个事务管理器 -->
<bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入DataSource -->
<property name="dataSource" ref="data"></property>
</bean>
<!-- 事务的配置 -->
<tx:advice id="txAdvice" transaction-manager="tm">
<!-- 指定方法名称:是业务核心方法
read-only:是否是只读事务。默认false,不只读。
isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。
propagation:指定事务的传播行为。
timeout:指定超时时间。默认值为:-1。永不超时。
rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。没有默认值,任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚。没有默认值,任何异常都回滚。
-->
<tx:attributes>
<tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut expression="execution(* com.apesource.service.*.*(..))" id="pt1"/>
<!-- 在aop:config标签内部:建立事务的通知和切入点表达式的关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
方式二(注解版):
<bean id="data" class="com.alibaba.druid.pool.DruidDataSource" >
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/javaee230809db?serverTimezone=GMT" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="data" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.apesource.dao" />
</bean>
<!--扫描注入对象的注解-->
<context:component-scan base-package="com.apesource"/>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="data"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
/**
* 转账业务
* */
@Override
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public void transfer(String sourceName, String targetName, int money) {
//2.1查询转账人与被转账人的信息
Account sourceAccount = dao.findAccountByName(sourceName);
Account targetAccount = dao.findAccountByName(targetName);
//2.2开始转账
sourceAccount.setAmoney(sourceAccount.getAmoney()-money);
targetAccount.setAmoney(targetAccount.getAmoney()+money);
//2.3修改数据库
dao.updateAccountById(sourceAccount);
//模拟转账事故
int a = 10/0;
dao.updateAccountById(targetAccount);
}