基于注解的spring aop小例子

本文通过实战演示如何利用 Spring AOP 进行面向切面编程,解决了传统编程中非核心业务逻辑代码重复的问题,介绍了切面、连接点、通知等概念,并提供了基于注解的 AOP 实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

对于spring aop这个话题,大家肯定是觉得是老生长谈的话题了,不管是基于配置文件的aop还是基于注解的aop实现,网上有很多很好的文章。那些大牛们写的文章确实很好,有很多地方值得学习。但是别人写的东西是别人的,你看了别人的文章,并不代表是你已经掌握,还是要多动手,所以这里写了一份基于注解aop的小例子,顺便总结自己对aop的理解。

aop的由来

在aop出来之前,对于那些横贯于核心业务代码的非核心代码。估计没有很好地解决方案,只能在每一个核心代码模块中重复的写那些非核心业务逻辑,像日志记录,事务控制等等。这些不重要的代码充斥在核心逻辑中,导致代码的耦合度很高,不利于后续代码的优化。因而aop面向切面编程的思想被提出来,aop是和oop那种竖向拓展思想对立的横向拓展。将各个核心业务模块的公共代码抽取出来放在一起,然后织入到核心业务逻辑模块。具体可以看下图,来查看aop的演进史。
这里写图片描述
从上图可以看到aop的优势。

spring aop和aspectj的简单小比较

spring aop的本质是动态代理,不管是基于jdk动态代理还是基于cglib的动态代理,spring aop都是属于在类运行期织入切面,但是这两种方式都是有一定的局限性,如果对于这个局限性不是很了解,可以参见这篇总结 java动态代理而aspectj是另外的一个aop框架,他是在类编译期织入切面。具体的织入时期可以参见下图
这里写图片描述
可以看到切面是在被代理类之前进行编译的,然后被织入。对于那些被定义为final类型且实现接口的类,显然spring aop是不合适的,这时候我们就可以运用aspectj来实现面向切面编程。

基于注解的spring aop小实现

由于spring版本不同可能对需要引入的jar不一致,这里就不列举需要的jar包了,感兴趣的可以自己去结合自己使用spring版本查找一下
首先定义一个接口,由被代理类来实现

/**
 - 
 - @ClassName: AopService
 - @Description: TODO(核心逻辑类实现的接口)
 - @author 爱琴孩
*/
public interface AopService {
    public void aopPrint();
}

被代理类,实现上面的接口,这里面就是所谓核心逻辑的位置

/**
 - @ClassName: AopServiceImpl
 - @Description: TODO(核心逻辑实现类)
 - @author 爱琴孩
*/
@Service(value="aopServiceImpl")
public class AopServiceImpl implements AopService{
    @Override
    public void aopPrint() {
        // TODO Auto-generated method stub
        System.out.println("这里处理核心的业务逻辑!");
    }
}

接下来是实现aop的切面

/**
 - @ClassName: AopAspect
 - @Description: TODO(定义一个切面)
 - @author 爱琴孩
*/
@Aspect
@Component
public class AopAspect {
    //定义全局切点
    @Pointcut("execution(* aiqinhai.base.service.impl.AopServiceImpl.*(..))")
    public void commonPointCut(){}
    //目标类方法执行之前触发的通知
    @Before(value = "commonPointCut()")
    public void before(){
        System.out.println("执行业务逻辑之前,切面预先执行准备工作!");
    }
    //不管目标类是否正常执行,都会触发的通知
    @After(value = "commonPointCut()")
    public void after(){
        System.out.println("执行业务逻辑之后,进行清除工作");
    }
    //目标类中方法发生异常时触发的通知
    @AfterThrowing(value = "commonPointCut()")
    public void afterThrow(){
        System.out.println("发生异常之后执行的切面通知");
    }
    //目标类中方法成功执行时触发的通知
    @AfterReturning(value = "commonPointCut()")
    public void  afterReturn(){
        System.out.println("正常运行业务逻辑之后的切面通知");
    }
    //环绕通知
    @Around(value = "commonPointCut()")
    public void around(ProceedingJoinPoint pointJoin) throws Throwable{
        System.out.println("环绕通知的前置通知");
        pointJoin.proceed();
        System.out.println("环绕通知的后置通知");
    }
}

上面的切面需要注意的有以下几点
- @Aspect注解是非Component类型注解,所以如果只是加上@Aspect注解,spring容器扫描不到,这样切面是织入不到对应的对象里面,所以我手动加上了@Component注解,或者你在spring容器中配置这个切面,配置方法和普通的javabean没什么区别。spring aop主要是迎合ioc来实现相关功能的,所以舍弃了一部分功能,导致功能上好像没有aspectj强大。
- @Aspect这个注解原本不是spring里面的,而是由另外的aop框架aspectj中的,在spring2.0中引入该注解,要想用这个注解需要在spring容器中开启对aspectj的声明,对于proxy-target-class属性,是用来选择是基于jdk的动态代理还是cglib的动态代理,在默认情况下proxy-target-class是false,这种情况下,是对开启jdk动态代理,如果为false是开启cglib动态代理,后来随着spring版本的提高,spring会根据运行类来自动选择是jdk动态代理还是使用cglib动态代理,我使用的spring4.3.4,所以这个属性配不配是无关紧要的,这点我在本地测试过,是可以实现的。
- 对于切面中的通知类型,这里就不详细叙述了,可以结合上面的代码和下面的测试结果来查看
测试类

/**
* 
* @ClassName: AopController
* @Description: TODO(测试类)
* @author 爱琴孩
*/
@Controller
@RequestMapping("/AopController")
public class AopController {
    @Autowired
    private AopService aopService;

    @RequestMapping("/testAop")
    public String testAop(){
        aopService.aopPrint();
        return "login";
    }
}

运行结果
这里写图片描述

总结

上面只是基于注解的spring aop简单小例子,小伙伴们可以自己去尝试,期间可能会有切面不执行或者报错出现,这时候静下心一步步检查,你会收获另一番风景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱琴孩

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

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

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

打赏作者

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

抵扣说明:

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

余额充值