Spring AOP 之一:基本概念与流程
2017-06-26 00:00
603 查看
摘要: 本文主要介绍Spring的AOP,并且尝试使用尽可能简介的方式描述一下Spring的轮廓,以便以对Spring AOP...
Pointcut:Joinpoint的表达式,表示拦截哪些方法。一个Pointcut对应多个Joinpoint
Advice: 通知,要切入的逻辑
Before Advice: 在方法(连接点)前切入
After Advice: 在方法(连接点)后切入,抛出异常时也会切入
After Returning Advice: 在方法(连接点)返回后切入,抛出异常则不会切入
After Throwing Advice: 在方法(连接点)抛出异常时切入
Around Advice: 在方法(连接点)执行前后切入,可以中断或忽略原有流程的执行
Aspect:切面,整个切入逻辑的抽象
上面的概念如果是第一次接触,那么就显得非常抽象。没有关系,先抛开上面的概念,先来了解一下Spring AOP(或者更加广义的说面向切面编程究竟是什么)。
可以这样理解:AOP是为了非侵入式的处理一类逻辑。举一个老生常谈的例子:记录方法执行时间,很多方法可能都需要这个功能,很显然我们不愿意去为每一个方法都写记录时间的代码,这些代码会重复,会和业务逻辑耦合,这是代码的"bad smell"。
记录时间方法这就是一类逻辑,我们可以把这一类逻辑处理为一个Aspect(切面),当然这部分逻辑只是切面中的Advice(通知)部分,我们还需要指定那些方法(Joinpont)需要执行这个记录时间逻辑(Advice)。(Advice 其实包含3部分,什么地点,什么时间,做什么工作)
所以AOP简单总结:在什么地点(Joinponit,多个地点Pointcut)什么时间做什么(Advice),Advice封装了什么时候做、什么地方和做什么。
下图是一个工程的整体的目录结构图,可以对整体结构有一个了解。
![](https://static.oschina.net/uploads/img/201706/26202840_Vfc4.png)
下面的例子都使用的是AspectJ的注解的方式处理的,为的是尽量让代码简化清晰,让我们专注在AOP上,而不是各种配置上。
业务逻辑部分是非常简单的就是获取100除以div的结果,每一个方法都可以作为一个Joinpoint,但是Spring AOP只是抽象了Pointcut,所以我们下面看一下Pointcut。
上面是单独定义Pointcut,为了方便多出引用,当然也可以直接写在Advice中。我们把@Pointcut("this(cn.freemethod.business.HaveResultBusiness)")中的this(cn.freemethod.business.HaveResultBusiness)部分称作为Pointcut表达式。定义的就是什么地方,Pointcut的表达式有很多类型,例如上面的this表达式,还有expression表达式等,本文不详细介绍,下一篇文章会仔细介绍。
@Pointcut("this(cn.freemethod.business.HaveResultBusiness)")表达式表示的是:实现了cn.freemethod.business.HaveResultBusiness接口的所有方法(连接点,Joinpoint)。
上面就是最核心的Aspect类了,@Aspect表明这个类是一个切面,注意要和@Componet之类的组合使用。在这个Aspect中定义了5中类型的Advice(通知),每一个Advice都使用了cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()这一个Pointcut。
@Before是在Pointcut之前执行,@After是在Pointcut之后执行。
@Around封装了连接点(方法)的处理过程,可以在方法前后注入逻辑
@AfterThrowing是在抛出异常之后执行,可以通过throwing = "ex"把异常注入进来
@AfterReturning是在返回之后执行,可以通过returning = "retVal"的方式把返回值注入进来。
@EnableAspectJAutoProxy是对AspectJ的支持,@ComponentScan配置扫描cn.freemethod及其子包。
打印容器中的bean名字,这个查错常用,为了不影响输出已经注释了。
Spring AOP 之一:基本概念与流程
Spring AOP 之二:Pointcut注解表达式
Spring AOP 之三:通知(Advice)方法参数
基本概念
Joinpoint:拦截点,连接点。如:业务逻辑模块中的方法或异常Pointcut:Joinpoint的表达式,表示拦截哪些方法。一个Pointcut对应多个Joinpoint
Advice: 通知,要切入的逻辑
Before Advice: 在方法(连接点)前切入
After Advice: 在方法(连接点)后切入,抛出异常时也会切入
After Returning Advice: 在方法(连接点)返回后切入,抛出异常则不会切入
After Throwing Advice: 在方法(连接点)抛出异常时切入
Around Advice: 在方法(连接点)执行前后切入,可以中断或忽略原有流程的执行
Aspect:切面,整个切入逻辑的抽象
上面的概念如果是第一次接触,那么就显得非常抽象。没有关系,先抛开上面的概念,先来了解一下Spring AOP(或者更加广义的说面向切面编程究竟是什么)。
可以这样理解:AOP是为了非侵入式的处理一类逻辑。举一个老生常谈的例子:记录方法执行时间,很多方法可能都需要这个功能,很显然我们不愿意去为每一个方法都写记录时间的代码,这些代码会重复,会和业务逻辑耦合,这是代码的"bad smell"。
记录时间方法这就是一类逻辑,我们可以把这一类逻辑处理为一个Aspect(切面),当然这部分逻辑只是切面中的Advice(通知)部分,我们还需要指定那些方法(Joinpont)需要执行这个记录时间逻辑(Advice)。(Advice 其实包含3部分,什么地点,什么时间,做什么工作)
所以AOP简单总结:在什么地点(Joinponit,多个地点Pointcut)什么时间做什么(Advice),Advice封装了什么时候做、什么地方和做什么。
实例
上面的概念部分还是过于抽象,下面我们通过一个实例,把概念和实例一一对应来讲,我尽量简化一些概念的东西,先着重于流程部分,这样有利于从整体上把握AOP,而不是限于概念之中。下图是一个工程的整体的目录结构图,可以对整体结构有一个了解。
![](https://static.oschina.net/uploads/img/201706/26202840_Vfc4.png)
下面的例子都使用的是AspectJ的注解的方式处理的,为的是尽量让代码简化清晰,让我们专注在AOP上,而不是各种配置上。
业务方法
public interface HaveResultBusiness { Integer getResult(Integer div); }
import org.springframework.stereotype.Service; import cn.freemethod.business.HaveResultBusiness; @Service public class HaveResultBusinessImpl implements HaveResultBusiness { @Override public Integer getResult(Integer div) { System.out.println("HaveResultBusinessImpl getResult..."); Integer result = 100 / div; return result; } }
业务逻辑部分是非常简单的就是获取100除以div的结果,每一个方法都可以作为一个Joinpoint,但是Spring AOP只是抽象了Pointcut,所以我们下面看一下Pointcut。
Pointcut
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; @Aspect public class HaveResultBusinessPointcut { @Pointcut("this(cn.freemethod.business.HaveResultBusiness)") // @Pointcut("execution(* cn.freemethod.business.HaveResultBusiness.*(..))") public void haveResultBusinessPointcut(){}; }
上面是单独定义Pointcut,为了方便多出引用,当然也可以直接写在Advice中。我们把@Pointcut("this(cn.freemethod.business.HaveResultBusiness)")中的this(cn.freemethod.business.HaveResultBusiness)部分称作为Pointcut表达式。定义的就是什么地方,Pointcut的表达式有很多类型,例如上面的this表达式,还有expression表达式等,本文不详细介绍,下一篇文章会仔细介绍。
@Pointcut("this(cn.freemethod.business.HaveResultBusiness)")表达式表示的是:实现了cn.freemethod.business.HaveResultBusiness接口的所有方法(连接点,Joinpoint)。
Aspect 切面
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class TimingAspect { @Before("cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()") // @Before("execution(* cn.freemethod.business.HaveResultBusiness.*(..))") public void before() { System.out.println("TimingAspect before..."); } @Around("cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { long start = System.currentTimeMillis(); Object retVal = pjp.proceed(); long end = System.currentTimeMillis(); System.out.println("time elapse:" + (end - start)); return retVal; } @After("cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()") public void after() { System.out.println("TimingAspect after..."); } @AfterThrowing(pointcut = "cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()", throwing = "ex") public void doRecoveryActions(Exception ex) { System.out.println("@AfterThrowing:" + ex.getMessage()); } @AfterReturning(pointcut = "cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()", returning = "retVal") public void doAccessCheck(Object retVal) { System.out.println("@AfterReturning:"+retVal); } }
上面就是最核心的Aspect类了,@Aspect表明这个类是一个切面,注意要和@Componet之类的组合使用。在这个Aspect中定义了5中类型的Advice(通知),每一个Advice都使用了cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()这一个Pointcut。
@Before是在Pointcut之前执行,@After是在Pointcut之后执行。
@Around封装了连接点(方法)的处理过程,可以在方法前后注入逻辑
@AfterThrowing是在抛出异常之后执行,可以通过throwing = "ex"把异常注入进来
@AfterReturning是在返回之后执行,可以通过returning = "retVal"的方式把返回值注入进来。
配置
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan(basePackages = {"cn.freemethod"}) public class AspectConfig { }
@EnableAspectJAutoProxy是对AspectJ的支持,@ComponentScan配置扫描cn.freemethod及其子包。
工具
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; //@Component public class ApplicationContextProvider implements ApplicationContextAware { private static ApplicationContext context; public void setApplicationContext(ApplicationContext context) throws BeansException { ApplicationContextProvider.context=context; String[] names = context.getBeanDefinitionNames(); for(String name : names){ System.out.println(name); } } public static ApplicationContext getApplicationContext() { return context; } }
打印容器中的bean名字,这个查错常用,为了不影响输出已经注释了。
启动
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import cn.freemethod.business.HaveResultBusiness; import cn.freemethod.config.AspectConfig; public class AnnotationConfigStart { public static void main(String[] args) { AbstractApplicationContext context = new AnnotationConfigApplicationContext(AspectConfig.class); HaveResultBusiness haveResultBusiness = context.getBean(HaveResultBusiness.class); Integer result = haveResultBusiness.getResult(100); System.out.println(result); haveResultBusiness.getResult(0); context.close(); } }
部分输出
HaveResultBusinessImpl getResult... time elapse:0 TimingAspect after... @AfterReturning:1 1 TimingAspect before... HaveResultBusinessImpl getResult... TimingAspect after... @AfterThrowing:/ by zero java.lang.ArithmeticException: / by zero at cn.freemethod.business.impl.HaveResultBusinessImpl.getResult(HaveResultBusinessImpl.java:13)
附录
参考
关于Spring的注解配置可以参考:Spring注解配置pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.freemethod</groupId> <artifactId>aop</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <spring_version>4.1.6.RELEASE</spring_version> <cglib_version>3.2.4</cglib_version> <javassist_version>3.12.1.GA</javassist_version> <junit_version>4.9</junit_version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring_version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring_version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring_version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit_version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring_version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.11</version> </dependency> </dependencies> </project>
资源链接
完整工程代码Spring AOP 之一:基本概念与流程
Spring AOP 之二:Pointcut注解表达式
Spring AOP 之三:通知(Advice)方法参数
相关文章推荐
- Spring AOP:基本概念,基础接口,基础类,基本流程
- Spring AOP基本概念
- Spring AOP的基本概念
- Spring学习之Aop的基本概念
- Spring中AOP基本概念及配置方式
- Spring2.5学习笔记2-AOP-基本概念
- spring学习笔记(9)AOP基本概念
- spring-AOP基本概念
- 循序渐进之Spring AOP(2) - 基本概念
- spring中AOP基本概念(14)
- springAOP中的基本概念
- 我所理解的Spring AOP的基本概念
- 深入理解Spring AOP之基本概念
- Spring AOP—1、AOP基本概念
- Spring中的AOP(一)——AOP基本概念和Spring对AOP的支持
- (spring-第16回【AOP基础篇】)基本概念
- Java程序员从笨鸟到菜鸟之(七十四)细谈Spring(六)spring之AOP基本概念和配置详解
- 8.4.3: Spring的AOP---AOP的基本概念
- 细谈Spring(六)之AOP基本概念和配置详解
- 5、spring 入门—Spring AOP基本概念