前言
AOP定义:面向切面编程
AOP的使用只存在于一些特定的场合(具有横切逻辑的应用场合),横切逻辑这个解释可能比较抽象,咱们说得再具体一点,AOP可以用于事务管理,日志管理,性能监测,权限校验等地方
Spring的AOP是通过代理模式实现的。实际生活中常见的代理场景如:房屋中介、黄牛售卖火车票。
代理分为静态代理和动态代理,Spring使用动态代理来完成AOP。
生成动态代理的两种方式:JDK动态代理(只支持实现接口的类)、CGLIB动态代理
一、XML版实现AOP
通过一个简单的案例演示AOP
如下为了实现用户注册和管理员注册,需要开启事务、关闭事务等大量重复的代码
public class UserServiceImpl implements IUserService {
public void save() {
try {
System.out.println("开启事务");
System.out.println("用户注册");
System.out.println("提交事务");
}catch (Exception e){
e.printStackTrace();
System.out.println("回滚事务");
}finally {
System.out.println("关闭事务");
}
}
}
public class DeptServiceImpl implements IDeptService{
public void save() {
try {
System.out.println("开启事务");
System.out.println("管理员注册");
System.out.println("提交事务");
}catch (Exception e){
e.printStackTrace();
System.out.println("回滚事务");
}finally {
System.out.println("关闭事务");
}
}
}
使用AOP简化上面的代码
将事务代码抽取成一个事务方法的类
public class TxManager {
public void start(){
System.out.println("开启事务");
}
public void conmit(){
System.out.println("提交事务");
}
public void rollback(Throwable throwable){
System.out.println("回滚事务,错误信息是:"+throwable);
}
public void close(){
System.out.println("关闭事务");
}
}
业务类只留下自己特有的方法即可
public class UserServiceImpl implements IUserService {
public void save() {
System.out.println("用户注册");
}
}
配置SpringTest-Context.xml
如果有2个以上的切面增强,Spring可能会造成增强乱序
<bean id="userService" class="cn.itsource._03_aop.service.impl.UserServiceImpl"/>
<bean id="txManager" class="cn.itsource._03_aop.TxManager"/>
<!--开启aop-->
<aop:config>
<!--pointcut:切点,id:随便取,expression:要切的方法-->
<!--第一个*表示所有的返回值
第二个*表示所有的以I开头,service结尾的类
两个..表示所有的参数,不同形参的save-->
<aop:pointcut id="userPoincut" expression="execution(* cn.itsource._03_aop.service.I*Service.save(..))"/>
<!--aspect:切面-->
<aop:aspect ref="txManager">
<!--在业务代码之前-->
<aop:before method="start" pointcut-ref="userPoincut"/>
<!--在业务代码之后-->
<aop:after-returning method="conmit" pointcut-ref="userPoincut"/>
<!--业务代码出现异常后 throwing:配置错误信息-->
<aop:after-throwing method="rollback" pointcut-ref="userPoincut" throwing="throwable"/>
<!--最终执行-->
<aop:after method="close" pointcut-ref="userPoincut"/>
</aop:config>
编写配置测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SpringTest {
@Autowired
private IUserService service;
@Test
public void test(){
service.save();
}
}
使用环绕增强
环绕增强是我们自己来控制切面的顺序,就不会出现乱序
public class TxManager {
/**
*环绕增强
* @param joinPoint
*
* ProceedingJoinPoint能获取业务类
*/
public void around(ProceedingJoinPoint joinPoint){
try {
start();
//反射机制获取业务方法
joinPoint.proceed();
conmit();
}catch (Throwable e){
rollback(e);
}finally {
close();
}
}
}
配置xml
<!--开启aop-->
<aop:config>
<!--pointcut:切点,id:随便取,expression:要切的方法-->
<!--第一个*表示所有的返回值
第二个*表示所有的以I开头,service结尾的类
两个..表示所有的参数,不同形参的save-->
<aop:pointcut id="userPoincut" expression="execution(* cn.itsource._03_aop.service.I*Service.save(..))"/>
<!--aspect:切面-->
<aop:aspect ref="txManager">
<!--around:环绕切面,不会发生顺序问题。上面的配置多个切面方法spring可能发生顺序问题-->
<aop:around method="around" pointcut-ref="userPoincut"/>
</aop:aspect>
</aop:config>
测试方法和上面的一样,但是需要注意,环绕增强和普通顺序增强不能同时出现,会重复执行
二、注解版实现AOP
实现类
@Service
public class UserServiceImpl implements IUserService {
public void save() {
System.out.println("用户注册");
}
}
配置SpringTest-Context.xml
<!--开启注解扫描-->
<context:component-scan base-package="cn.itsource._04_aopanno"/>
<!--支持aop注解-->
<aop:aspectj-autoproxy/>
事务类,普通顺序增强的注解实现,这种方式也是可能会乱序
@Component //声明bean
@Aspect //声明该类是切面类
public class TxManager {
//配置切点,通过一个空方法加注解实现,注解里的配置和xml的一样
@Pointcut("execution(* cn.itsource._04_aopanno.service.I*Service.save(..))")
public void poincut(){}
@Before("poincut()")
public void start(){
System.out.println("开启事务");
}
@AfterReturning("poincut()")
public void conmit(){
System.out.println("提交事务");
}
@AfterThrowing(pointcut = "poincut()",throwing = "throwable")
public void rollback(Throwable throwable){
System.out.println("回滚事务,错误信息是:"+throwable);
}
@After("poincut()")
public void close(){
System.out.println("关闭事务");
}
}
环绕增强,由于自己配置增强的顺序,所以不会出现乱序
@Component //声明bean
@Aspect //声明该类是切面类
public class TxManager {
//配置切点,通过一个空方法加注解实现,注解里的配置和xml的一样
@Pointcut("execution(* cn.itsource._04_aopanno.service.I*Service.save(..))")
public void poincut(){}
/**
* @param joinPoint
* ProceedingJoinPoint能获取业务类
*/
@Around("poincut()")
public void around(ProceedingJoinPoint joinPoint){
try {
start();
//反射机制获取业务方法
joinPoint.proceed();
conmit();
}catch (Throwable e){
rollback(e);
}finally {
close();
}
}
}