内容简介:Spring核心系列之AOP(一)
Hello,大家好,今天来给大家讲一讲Spring中的AOP,面向切面编程,它在Spring的整个体系中占有着重要地位。本文还是以实践为主,注解切入注入,OK,文章结构:
- @AspectJ 详解
- Spring AOP - AspectJ注解
1. @AspectJ 的由来
提到AspectJ,其实很多人是有误解的,很多人只知道在Spring中使用Aspect那一套注解,以为是Spring开发的这一套注解,这里我觉得有责任和大家澄清一下。 AspectJ是一个AOP框架,它能够对 java 代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器),可以这样说AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言,更幸运的是,AspectJ与java程序完全兼容,几乎是无缝关联,因此对于有java编程基础的工程师,上手和使用都非常容易. 其实AspectJ单独就是一门语言,它需要专门的编译器(ajc编译器). Spring AOP 与ApectJ的目的一致,都是为了统一处理横切业务,但与AspectJ不同的是,Spring AOP并不尝试提供完整的AOP功能(即使它完全可以实现),Spring AOP 更注重的是与Spring IOC容器的结合,并结合该优势来解决横切业务的问题,因此在AOP的功能完善方面,相对来说AspectJ具有更大的优势,同时,Spring注意到AspectJ在AOP的实现方式上依赖于特殊编译器(ajc编译器),因此Spring很机智回避了这点, 转向采用动态代理技术的实现原理来构建Spring AOP的内部机制(动态织入),这是与AspectJ(静态织入)最根本的区别 。在AspectJ 1.5后,引入@Aspect形式的注解风格的开发,Spring也非常快地跟进了这种方式,因此Spring 2.0后便使用了与AspectJ一样的注解。请注意, Spring 只是使用了与 AspectJ 5 一样的注解,但仍然没有使用 AspectJ 的编译器,底层依是动态代理技术的实现,因此并不依赖于 AspectJ 的编译器 。 所以,大家要明白,Spring AOP虽然是使用了那一套注解,其实实现AOP的底层是使用了动态代理(JDK或者CGLib)来动态植入。至于AspectJ的静态植入,不是本文重点,所以只提一提。
2. Spring AOP - AspectJ注解
谈到Spring AOP,老 程序员 应该比较清楚,之前的Spring AOP没有使用@Aspect这一套注解和 aop:config 这一套XML解决方案,而是开发者自己定义一些类实现一些接口,而且配置贼恶心。本文就不再演示了,后来Spring痛下决心,把AspectJ"整合"进了Spring当中,并开启了aop命名空间。现在的Sping AOP可以算是朗朗乾坤。上例子之前,还是把AOP的概念都提一提:
- 切点:定位到具体方法的一个表达式
- 切面: 切点+建言
- 建言(增强):定位到方法后干什么事
网上有很多概念,什么连接点,织入,目标对象,引入什么的。我个人觉得,在Java的Spring AOP领域,完全不用管。别把自己绕晕了。就按照我说的这三个概念就完事了,好了,先来搞个例子: maven依赖:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.2.3.RELEASE</version> </dependency> //下面这两个aspectj的依赖是为了引入AspectJ的注解 <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.12</version> </dependency> //Spring AOP底层会使用CGLib来做动态代理 <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency>
小狗类,会说话:
public class Dog { private String name; public void say(){ System.out.println(name + "在汪汪叫!..."); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
切面类:
@Aspect //声明自己是一个切面类 public class MyAspect { /** * 前置通知 */ //@Before是增强中的方位 // @Before括号中的就是切入点了 //before()就是传说的增强(建言):说白了,就是要干啥事. @Before("execution(* com.zdy..*(..))") public void before(){ System.out.println("前置通知...."); } }
这个类是重点,先用@Aspect声明自己是切面类,然后before()为增强, @Before(方位)+切入点 可以具体定位到具体某个类的某个方法的方位. Spring配置文件:
//开启AspectJ功能. <aop:aspectj-autoproxy /> <bean id="dog" class="com.zdy.Dog" /> <!-- 定义aspect类 --> <bean name="myAspect" class="com.zdy.MyAspect"/>
然后Main方法:
ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext.xml"); Dog dog =(Dog) ac.getBean("dog"); System.out.println(dog.getClass()); dog.say();
输出结果:
class com.zdy.Dog$$EnhancerBySpringCGLIB$$80a9ee5f 前置通知.... null在汪汪叫!...
说白了,就是把切面类丢到容器,开启一个AdpectJ的功能, Spring AOP就会根据切面类中的(@Before+切入点)定位好具体的类的某个方法(我这里定义的是com.zdy包下的所有类的所有方法),然后把增强before()切入进去.
然后说下Spring AOP支持的几种类似于@Before的AspectJ注解:
- 前置通知@Before : 前置通知通过@Before注解进行标注,并可直接传入切点表达式的值,该通知在目标函数执行前执行,注意JoinPoint,是Spring提供的静态变量,通过joinPoint 参数,可以获取目标对象的信息,如类名称,方法参数,方法名称等,该参数是可选的。
@Before("execution(...)") public void before(JoinPoint joinPoint){ System.out.println("..."); }
- 后置通知@AfterReturning : 通过@AfterReturning注解进行标注,该函数在目标函数执行完成后执行,并可以获取到目标函数最终的返回值returnVal,当目标函数没有返回值时,returnVal将返回null,必须通过returning = “returnVal”注明参数的名称而且必须与通知函数的参数名称相同。请注意,在任何通知中这些参数都是可选的,需要使用时直接填写即可,不需要使用时,可以完成不用声明出来。
@AfterReturning(value="execution(...)",returning = "returnVal") public void AfterReturning(JoinPoint joinPoint,Object returnVal){ System.out.println("我是后置通知...returnVal+"+returnVal); }
- 异常通知 @AfterThrowing :该通知只有在异常时才会被触发,并由throwing来声明一个接收异常信息的变量,同样异常通知也用于Joinpoint参数,需要时加上即可.
@AfterThrowing(value="execution(....)",throwing = "e") public void afterThrowable(Throwable e){ System.out.println("出现异常:msg="+e.getMessage()); }
- 最终通知 @After :该通知有点类似于finally代码块,只要应用了无论什么情况下都会执行.
@After("execution(...)") public void after(JoinPoint joinPoint) { System.out.println("最终通知...."); }
- 环绕通知 @Around : 环绕通知既可以在目标方法前执行也可在目标方法之后执行,更重要的是环绕通知可以控制目标方法是否指向执行,但即使如此,我们应该尽量以最简单的方式满足需求,在仅需在目标方法前执行时,应该采用前置通知而非环绕通知。案例代码如下第一个参数必须是ProceedingJoinPoint,通过该对象的proceed()方法来执行目标函数,proceed()的返回值就是环绕通知的返回值。同样的,ProceedingJoinPoint对象也是可以获取目标对象的信息,如类名称,方法参数,方法名称等等
@Around("execution(...)") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("我是环绕通知前...."); //执行目标函数 Object obj= (Object) joinPoint.proceed(); System.out.println("我是环绕通知后...."); return obj; }
然后说下一直用"..."忽略掉的切入点表达式,这个表达式可以不是exection(..),还有其他的一些,我就不说了,说最常用的execution:
//scope :方法作用域,如public,private,protect //returnt-type:方法返回值类型 //fully-qualified-class-name:方法所在类的完全限定名称 //parameters 方法参数 execution(<scope> <return-type> <fully-qualified-class-name>.*(parameters))
<fully-qualified-class-name>.*(parameters)
注意这一块,如果没有精确到class-name,而是到包名就停止了,要用两个".."来表示包下的任意类:
- execution(* com.zdy..*(..)):com.zdy包下所有类的所有方法.
- execution(* com.zdy.Dog.*(..)): Dog类下的所有方法.
具体详细语法,大家如果有需求自行google了,我最常用的就是这俩了。要么按照包来定位,要么按照具体类来定位.
在使用切入点时,还可以抽出来一个@Pointcut来供使用:
/** * 使用Pointcut定义切点 */ @Pointcut("execution(...)") private void myPointcut(){} /** * 应用切入点函数 */ @After(value="myPointcut()") public void afterDemo(){ System.out.println("最终通知...."); }
可以避免重复的execution在不同的注解里写很多遍...
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- CSS核心技术详解-核心概念
- WTC测评:核心团队从业经验,核心代码尚未开源
- AI 产品开发的核心原则:以研究为核心驱动
- Knative 核心概念介绍:Build、Serving 和 Eventing 三大核心组件
- 区块链技术六大核心算法,读懂六大核心算法就变成区块链专家
- webpack怎么能只是会用呢,核心中的核心tapable了解下?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Deep Learning
Ian Goodfellow、Yoshua Bengio、Aaron Courville / The MIT Press / 2016-11-11 / USD 72.00
"Written by three experts in the field, Deep Learning is the only comprehensive book on the subject." -- Elon Musk, co-chair of OpenAI; co-founder and CEO of Tesla and SpaceX Deep learning is a for......一起来看看 《Deep Learning》 这本书的介绍吧!