基于注解的spring aop小例子

来源:互联网 发布:淘宝上没有s 编辑:程序博客网 时间:2024/06/06 21:44

前言

对于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@Componentpublic 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简单小例子,小伙伴们可以自己去尝试,期间可能会有切面不执行或者报错出现,这时候静下心一步步检查,你会收获另一番风景。

原创粉丝点击