Hello,你们好,今天来给你们讲一讲Spring中的AOP,面向切面编程,它在Spring的整个体系中占有着重要地位。本文仍是以实践为主,注解切入注入,OK,文章结构:java
提到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的静态植入,不是本文重点,因此只提一提。程序员
谈到Spring AOP,老程序员应该比较清楚,以前的Spring AOP没有使用@Aspect这一套注解和aop:config这一套XML解决方案,而是开发者本身定义一些类实现一些接口,并且配置贼恶心。本文就再也不演示了,后来Spring痛下决心,把AspectJ"整合"进了Spring当中,并开启了aop命名空间。如今的Sping AOP能够算是朗朗乾坤。上例子以前,仍是把AOP的概念都提一提:spring
网上有不少概念,什么链接点,织入,目标对象,引入什么的。我我的以为,在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>
复制代码
小狗类,会说话:bash
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;
}
}
复制代码
切面类:app
@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方法:maven
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()切入进去.ui
而后说下Spring AOP支持的几种相似于@Before的AspectJ注解:
@Before("execution(...)")
public void before(JoinPoint joinPoint){
System.out.println("...");
}
复制代码
@AfterReturning(value="execution(...)",returning = "returnVal")
public void AfterReturning(JoinPoint joinPoint,Object returnVal){
System.out.println("我是后置通知...returnVal+"+returnVal);
}
复制代码
@AfterThrowing(value="execution(....)",throwing = "e")
public void afterThrowable(Throwable e){
System.out.println("出现异常:msg="+e.getMessage());
}
复制代码
@After("execution(...)")
public void after(JoinPoint joinPoint) {
System.out.println("最终通知....");
}
复制代码
@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,而是到包名就中止了,要用两个".."来表示包下的任意类:
具体详细语法,你们若是有需求自行google了,我最经常使用的就是这俩了。要么按照包来定位,要么按照具体类来定位.
在使用切入点时,还能够抽出来一个@Pointcut来供使用:
/** * 使用Pointcut定义切点 */
@Pointcut("execution(...)")
private void myPointcut(){}
/** * 应用切入点函数 */
@After(value="myPointcut()")
public void afterDemo(){
System.out.println("最终通知....");
}
复制代码
能够避免重复的execution在不一样的注解里写不少遍...
好了,Spring AOP 基于AspectJ注解如何实现AOP给你们分享完了,其实不是很难,由于博主比较懒,主说了主要内容,像切入点表达式要说其实有不少东西能够说,还有加强级别的排序问题,好比一个Aspect类中定义了多个@Before,谁先调用,由于博主以为这些知识点不是很重要,用的很是少,因此就没提了。这一期把基于注解的分享完了,下一次准备把Spring AOP基于XML的配置讲一讲,而后说一些实际的运用场景。包括多个切面命中一个切点时,切面级别的调用顺序等都会提一提。而后把Spring AOP底层使用的JDK动态代理和CGLib动态代理都稍微说一说。好了,这一期就到这里了..Over,Have a good day .