Java框架之Spring IOC+AOP(AOP千字教程,一看就懂!!)

本文深入介绍了Spring的AOP(面向切面编程),包括其概念、用途和如何通过XML及注解方式实现。通过一个性能检测的例子展示了AOP如何减少代码冗余,同时提供了事务管理的场景来演示AOP的使用,涉及前置、后置、异常和最终通知。最后,展示了如何使用注解简化配置。
摘要由CSDN通过智能技术生成

AOP 面向切面编程

看本文章前最好先看Spring IOCSpring IOC

1 什么是AOP

AOP(Aspect Orient Programming):面向切面编程,他是作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。其实AOP问世的时间并不太长,AOP和OOP互为补充,面向切面编程将程序运行过程分解成各个切面。在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如性能检测、事务管理、安全检查、日志、缓存等,AOP已经成为一种非常常用的解决方案。

如果还没明白,咱们来一个 性能检测 的案例给大家说明。例如,我想统计业务方法执行的耗时。

public class TestService{
	public void add(){
		//新增的业务
	}
	void delete(){
		//删除的业务
	}
	//其他方法....
}

我现在想统计TestService中的每个方法执行时的耗时,可以对代码做如下修改:

public class TestService{
	public void add(){
		Long start = System.currentTimeMillis();
		//新增的业务
		Long end = System.currentTimeMillis();
		System.out.println("耗时为:"+(end-start));
	}
	void delete(){
		Long start = System.currentTimeMillis();
		//删除的业务
		Long end = System.currentTimeMillis();
		System.out.println("耗时为:"+(end-start));
	}
	//其他方法....
}

这样确实可以达到需求,但是代码量就很大了,每个业务方法都需要在首尾加代码,很明显不可取,如果有了AOP就可以将这部分公共的代码抽取出来,放在AOP中,可以在不修改源码的情况下做到功能的加强。

2 如何使用AOP-xml版

2.1 AspectJ实现AOP

AspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能。

<!--spring原有的依赖-->
...

<!--在原有spring依赖基础上,加入aspectj依赖-->
<dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>1.8.8</version>
</dependency>

2.2 AOP的基本概念

关于面向切面编程的一些术语:

  • 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用。

  • 切入点(Pointcut): 可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。

  • 切面(Aspect): 切面用于组织多个Advice,Advice放在切面中定义。

  • 增强处理(Advice): AOP框架在特定的切入点执行的增强处理。处理有"around",“before” 和 "after"等类型。也叫通知。

2.3 使用AOP

1.咱们以事务处理为场景来进行测试,准备一个事务管理器和一个业务类。

//业务接口
public interface IUserService {
    void delete();
    void update();
}
//业务实现
public class UserServiceImpl implements IUserService {
    @Override
    public void update() {
        System.out.println("用户修改成功。。。");
    }
    @Override
    public void delete() {
        System.out.println("用户删除成功。。。");
    }
}

事务管理器:模拟提供对于事务管理的相关方法。

public class TransactionManager {
    //前置通知方法
    public void begin(){
        System.out.println("开启事务....");
    }
    //后置通知方法
    public void commit(){
        System.out.println("提交事务...");
    }
    //异常通知方法
    public void rollback(Throwable e){
        System.out.println("回滚事务....错误原因是:"+e.getMessage());
    }
    //最终通知方法
    public void close(){
        System.out.println("关闭资源....");
    }
    //环绕通知方法(暂时不用)
    public void around(ProceedingJoinPoint pjp){
        try {
            begin();
            pjp.proceed(); //目标方法
            commit();
        }catch (Throwable e){
            rollback(e);
        }finally {
            close();
        }
    }
}

2.使用AOP为业务类增加事务管理功能

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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">
    <!--创建业务类的bean-->
    <bean id="userServiceImpl" class="包路径.UserServiceImpl"></bean>
    <!--创建事务管理器的bean  就是切面-->
    <bean id="txManager" class="包路径.TransactionManager"></bean>

    <!--aop的配置-->
    <aop:config>
        <!--切入点,id:为切入点的唯一标识,expression:切入点表达式 
			该表达式会切到指定包下名叫UserServiceImpl类中的方法(方法名不限,参数类型和个数不限,返回值不限)
		-->
        <aop:pointcut id="myPointcut" expression="execution(* 包路径.UserServiceImpl.*(..))"/>
        <!--切面 ,ref:关联事务管理器的id -->
        <aop:aspect ref="txManager">
            <!--前置通知-->
            <aop:before method="begin" pointcut-ref="myPointcut"></aop:before>
            <!--后置通知,在发生异常时不会执行 -->
            <aop:after-returning method="commit" pointcut-ref="myPointcut"></aop:after-returning>
            <!--异常通知,异常时才会执行,throwing="e":将异常信息传递给通知方法 -->
            <aop:after-throwing method="rollback" pointcut-ref="myPointcut" throwing="e"></aop:after-throwing>
            <!--最终通知,正常和异常都要执行-->
            <aop:after method="close" pointcut-ref="myPointcut"></aop:after>
            <!--环绕通知-->
            <!--<aop:around method="around" pointcut-ref="myPointcut"></aop:around>-->
        </aop:aspect>
    </aop:config>
</beans>

3.测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MyTest {
    @Autowired
    private IUserService userService;
    @Test
    public void t1(){
        userService.update(); 
    }
}
//执行结果:
开启事务....
用户修改成功。。。
提交事务...
关闭资源....

进行测试后会发现使用AOP可以将公共代码抽离到外部AOP的切面中,这样原有代码就不需要做修改。分别可以实现:前置,后置,异常,最终等通知,也可以使用环绕通知来实现,建议测试环绕通知时注释掉前4种通知,方便查看。

3 如何使用AOP-注解版

1.准备业务类

//业务接口
public interface IUserService {
    void delete();
    void update();
}
//业务实现
@Service
public class UserServiceImpl implements IUserService {
    @Override
    public void update() {
        System.out.println("用户修改成功。。。");
    }
    @Override
    public void delete() {
        System.out.println("用户删除成功。。。");
    }
}

2.准备事务管理器,并设置 切入点等相关配置

@Component
@Aspect // 配置切面类,相当于:<aop:aspect ref="txManager">
public class TxManager {
    //配置切入点
    @Pointcut("execution(* 包路径.UserServiceImpl.*(..))")
    public void myPointcut(){}

    //前置通知, 使用切入点传入方法名即可
    @Before("myPointcut()")
    public void begin(){
        System.out.println("开启事务....");
    }
    //后置通知
    @AfterReturning("myPointcut()")
    public void commit(){
        System.out.println("提交事务...");
    }
    //异常通知
    @AfterThrowing(value = "myPointcut()",throwing = "e")
    public void rollback(Throwable e){
        System.out.println("回滚事务....错误原因是:"+e.getMessage());
    }
    //最终通知
    @After("myPointcut()")
    public void close(){
        System.out.println("关闭资源....");
    }
    //环绕通知
    //@Around("myPointcut()")
    public void around(ProceedingJoinPoint pjp){
        try {
            begin();
            pjp.proceed(); //目标方法
            commit();
        }catch (Throwable e){
            rollback(e);
        }finally {
            close();
        }
    }
}

3.使用包扫描把 业务类 和 切面配置类 都交给Spring管理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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">
    <!--包扫描-->
    <context:component-scan base-package="包路径"/>
    <!--开启注解版aop-->
    <aop:aspectj-autoproxy/>
</beans>

4.测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyTest {
    @Autowired
    private IUserService userService;
    @Test
    public void t1(){
        userService.update();
    }
}

到此,AOP的两种使用,咱们就有个大致了解。
那么AOP的底层是怎么实现的呢?是动态代理实现。

我亦无他,惟手熟尔

学java找他CSDN:码赛客1024

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别来无恙blwy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值