AOP基本介绍
AOP的底层基于代理设计模式来实现的,通常用于拦截函数的执行, 并执行特定的代码,用于在多处代码中执行相同的事情。
什么地方使用AOP
- 在不同的地方做相同的事情,发现代码重复。比如说每个请求都需要进行日志打印
- 通常用于打印日志、处理异常、事物控制、性能统计
AOP包含哪几点
- 关注点 -> 重复代码
- 切面 -> 抽取重复代码
- 切入点 -> 拦截哪些方法
spring AOP实现基础,容器有如下几次扫描过程:
- 第一次,解析xml配置文件解析到类扫描的注解解析器,会在base-package指定的包及子包中扫描所有的类看类上是否有@Compontent,@Service,@Controller,@Repository注解,如果有,则spring容器创建该类的实例.
- 第二次,解析到aop的注解解析器,会在纳入spring管理的bean中,看哪个类上面是否有@Aspect注解
- 第三次,在有@Aspect注解的类的所有方法中查找@Pointcut,就会找到切入点表达式,根据切入点表达式,在纳入spring范围的bean内查找,看哪个bean符合切入点表达式,如果符合则创建代理对象.
- 当客户端访问某一个bean时,如果该bean有代理对象,则返回代理对象,否则返回该bean的对象
Spring AOP 的实现方式
- XML
- 事物管理 *add *del
- 注解
Spring AOP 通知类型
前置通知、后置通知、返回通知、异常通知、环绕通知
Spring AOP 注解实现需要用到的注解
- @Component
- @Aspect
- @Pointcut
- @Before
- @After
- @AfterReturning
- @AfterThrowing
- @Around
@Component
解释:就是spring注解里面的注解,泛指,只是没有像@Service、@Controller 一样,具有特殊意义。这个注解就是把切面类,纳入到spring容器管理,相当于。@Component=bean id="transaction" class="..Transaction",这个写法。
@Aspect
此注解的作用:标识这个类就是切面。@Aspect相当于xml文件中的aop配置标签:<aop:config> </aop:config>。然后切面里面,就有对应的切入点,通知等等。
@Pointcut
此注解的作用:标注一个切入点。此注解不能单独存在,必须依附在切面。一个切入点声明有两个部分:一个包含名字和任意参数的签名,还有一个切入点表达式,该表达式决定了我们关注那个方法的执行。作为切入点签名的方法必须返回void 类型:
@Pointcut("execution(\* com.lxk.spring.aop.annotation.PersonDaoImpl.\*(..))")
private void aa(){}//切入点签名
相当于如下:在xml配置切入点。
<aop:pointcut
expression="execution(\* cn.itcast.spring0401.aop.annotation.PersonDaoImpl.\*(..))"
id="aa()"/>
然后现在切入点就是这个aa(),方法啦。
AOP 注解直接实现
package com.yanzuoguang.lightframe.demo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* Created by yanzu on 2018/5/10.
*/
@Aspect // 定义切面
@Component // 注入到Spring容器中
public class AopDemo {
// Spring 前置通知
@Before("execution(* com.yanzuoguang.lightframe.controller..*.*(..))")
public void begin(JoinPoint joinPoint) {
System.out.println("前置通知" + joinPoint.toString()
+ "参数:" + Arrays.asList(joinPoint.getArgs()));
}
@After("execution(* com.yanzuoguang.lightframe.controller..*.*(..))")
public void commit(JoinPoint joinPoint) {
System.out.println("后置通知" + joinPoint.toString()
+ "参数:" + Arrays.asList(joinPoint.getArgs()));
}
@AfterReturning(value = "execution(* com.yanzuoguang.lightframe.controller..*.*(..))", returning = "ret")
public void afterReturning(JoinPoint joinPoint, Object ret) {
System.out.println("返回通知" + joinPoint.toString()
+ "参数:" + Arrays.asList(joinPoint.getArgs())
+ " 返回: " + ret);
}
@AfterThrowing(value = "execution(* com.yanzuoguang.lightframe.controller..*.*(..))", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
System.out.println("异常通知" + joinPoint.toString()
+ "参数:" + Arrays.asList(joinPoint.getArgs())
+ " 错误:" + ex.getClass().getName()
+ " :" + ex.getMessage());
}
@Around("execution(* com.yanzuoguang.lightframe.controller..*.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object ret = null;
try {
System.out.println("环绕通知前" + proceedingJoinPoint.toString()
+ "参数:" + Arrays.asList(proceedingJoinPoint.getArgs()));
ret = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
return ret;
} catch (Exception ex) {
ret = ex;
throw ex;
} finally {
System.out.println("环绕通知后" + proceedingJoinPoint.toString()
+ "参数:" + Arrays.asList(proceedingJoinPoint.getArgs())
+ " 返回: " + ret);
}
}
}
Aop注解切面实现
package com.yanzuoguang.lightframe.demo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* Created by yanzu on 2018/5/10.
*/
@Aspect // 定义切面
@Component // 注入到Spring容器中
public class AopDemo {
@Pointcut("execution(* com.yanzuoguang.lightframe.controller..*.*(..))")
public void cut(){
}
// Spring 前置通知
@Before("cut()")
public void begin(JoinPoint joinPoint) {
System.out.println("前置通知" + joinPoint.toString()
+ "参数:" + Arrays.asList(joinPoint.getArgs()));
}
@After("cut()")
public void commit(JoinPoint joinPoint) {
System.out.println("后置通知" + joinPoint.toString()
+ "参数:" + Arrays.asList(joinPoint.getArgs()));
}
@AfterReturning(value = "cut()", returning = "ret")
public void afterReturning(JoinPoint joinPoint, Object ret) {
System.out.println("返回通知" + joinPoint.toString()
+ "参数:" + Arrays.asList(joinPoint.getArgs())
+ " 返回: " + ret);
}
@AfterThrowing(value = "cut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
System.out.println("异常通知" + joinPoint.toString()
+ "参数:" + Arrays.asList(joinPoint.getArgs())
+ " 错误:" + ex.getClass().getName() + " :" + ex.getMessage());
}
@Around("cut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object ret = null;
try {
System.out.println("环绕通知前" + proceedingJoinPoint.toString()
+ "参数:" + Arrays.asList(proceedingJoinPoint.getArgs()));
ret = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
return ret;
} catch (Exception ex) {
ret = ex;
throw ex;
} finally {
System.out.println("环绕通知后" + proceedingJoinPoint.toString()
+ "参数:" + Arrays.asList(proceedingJoinPoint.getArgs())
+ " 返回: " + ret);
}
}
}
Aop XML实现
<aop:config>
<!-- 定义一个切入点表达式: 拦截哪些方法 -->
<aop:pointcut
id="pt"
expression="execution(* com.yanzuoguang.lightframe.controller..*(..))" />
<!-- 切面,aop为指定需要引用的Spring实体 -->
<aop:aspect ref="aop">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt" />
<!-- 前置通知: 在目标方法调用前执行 -->
<aop:before method="begin" pointcut-ref="pt" />
<!-- 后置通知: -->
<aop:after method="after" pointcut-ref="pt" />
<!-- 返回后通知 -->
<aop:after-returning method="afterReturning"
pointcut-ref="pt" />
<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing"
pointcut-ref="pt" />
</aop:aspect>
</aop:config>
AspectJ中的exection表达式小结:
基本语法格式为:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。
下面,我们给出各种使用execution()函数实例。
基本Demo: execution(* com.sample.service.impl..*.*(..))
解释如下:
- 符号含义 execution() 表达式的主体;
- 第一个
*
符号, 表示返回值的类型任意; com.sample.service.impl
指的是AOP所切的服务的包名,即,我们的业务部分- 包名后面的
..
表示当前包及子包 - 第二个
*
,表示类名,*
即所有类。此处可以自定义,下文有举例 .*(..)
表示任何方法名,括号表示参数,两个点表示任何参数类型
1. 通过方法签名定义切点
execution(public * *(..))
匹配所有目标类的public方法,但不匹配SmartSeller和protected voidshowGoods()方法
。第一个*
代表返回类型,第二个*
代表方法名,而..
代表任意入参的方法;
execution(* *To(..))
匹配目标类所有以To为后缀的方法。它匹配NaiveWaiter和NaughtyWaiter的greetTo()和serveTo()方法。第一个*
代表返回类型,而*To
代表任意以To为后缀的方法;
2. 通过类定义切点
execution(*com.baobaotao.Waiter.*(..))
匹配Waiter接口的所有方法,它匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()方法。第一个*
代表返回任意类型,com.baobaotao.Waiter.*
代表Waiter接口中的所有方法;
execution(*com.baobaotao.Waiter+.*(..))
匹配Waiter接口及其所有实现类的方法,它不但匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()这两个Waiter接口定义的方法,同时还匹配NaiveWaiter#smile()和NaughtyWaiter#joke()这两个不在Waiter接口中定义的方法。
3. 通过类包定义切点
在类名模式串中,“.*”
表示包下的所有类,而“..*”
表示包、子孙包下的所有类。
execution(* com.baobaotao.*(..))
匹配com.baobaotao包下所有类的所有方法;
execution(* com.baobaotao..*(..))
匹配com.baobaotao包、子孙包下所有类的所有方法,如com.baobaotao.dao,com.baobaotao.servier以及com.baobaotao.dao.user包下的所有类的所有方法都匹配。“..”
出现在类名中时,后面必须跟“*”
,表示包、子孙包下的所有类;
execution(* com..*.*Dao.find*(..))
匹配包名前缀为com的任何包下类名后缀为Dao的类中以find为前缀方法。如com.baobaotao.UserDao#findByUserId()、com.baobaotao.dao.ForumDao#findById()的方法都匹配切点。
4. 通过方法入参定义切点
切点表达式中方法入参部分比较复杂,可以使用“*”
和“..”
通配符,其中“*”
表示任意类型的参数,而“..”
表示任意类型参数且参数个数不限。
execution(* joke(String,int)))
匹配joke(String,int)方法,且joke()方法的第一个入参是String,第二个入参是int。它匹配NaughtyWaiter#joke(String,int)方法。如果方法中的入参类型是java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int);
execution(* joke(String,*)))
匹配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型,如joke(Strings1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,doubled2,String s3)则不匹配;
execution(* joke(String,..)))
匹配目标类中的joke()方法,该方法第 一个入参为String,后面可以有任意个入参且入参类型不限, 如joke(Strings1)、joke(String s1,String s2)和joke(String s1,double d2,Strings3)都匹配。
execution(* joke(Object+)))
匹配目标类中的joke()方法,方法拥有一个入参,且入参是Object类型或该类的子类。它匹配joke(Strings1)和joke(Client c)。如果我们定义的切点是execution(*joke(Object))
,
则只匹配joke(Object object)而不匹配joke(Stringcc)或joke(Client c)。
Spring事物
事物特性
- 原子性
- 一致性
- 隔离性
- 持久
事物实现
- 手动事物: 自己写 begin、commit
- 手写事物: 自己写 AOP 编程实现
- 声明事物: 通过XML声明事物
- 注解事物: @Transactional ,可以声明事物传递行为
SpringBoot 手写事物,SpringMVC将 PlatformTransactionManager 换成对应类型即可
package com.yanzuoguang.lightframe.demo.aop;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@Component
public class TransactionUtils {
@Autowired
private PlatformTransactionManager transactionManager;
//开启事物
public TransactionStatus begin() {
TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
System.out.println("开启事物");
return transaction;
}
//提交事物
public void commit(TransactionStatus transaction) {
transactionManager.commit(transaction);
System.out.println("提交事物");
}
//回滚事物
public void rollback(TransactionStatus transaction) {
transactionManager.rollback(transaction);
System.out.println("回滚事物");
}
}
XML声明式事物
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 2. JdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事物 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="*" read-only="false" />
</tx:attributes>
</tx:advice>
<!-- Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
<aop:config>
<aop:pointcut expression="execution(* com.itmayiedu02.service.*.*(..))"
id="pt" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
</aop:config>
Spring 7种事物传播行为
- PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
通常用的是 PROPAGATION_REQUIRED、 PROPAGATION_REQUIRES_NEW、 PROPAGATION_SUPPORTS、PROPAGATION_NOT_SUPPORTED
本文由 创作,采用 知识共享署名4.0 国际许可协议进行许可。本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。最后编辑时间为: 2020/05/13 06:48