基本概念
-
Joinpoint:拦截点,链接点。如:业务逻辑模块中的方法或异常java
-
Pointcut:Joinpoint的表达式,表示拦截哪些方法。一个Pointcut对应多个Joinpointspring
-
Advice: 通知,要切入的逻辑express
-
Before Advice: 在方法(链接点)前切入apache
-
After Advice: 在方法(链接点)后切入,抛出异常时也会切入编程
-
After Returning Advice: 在方法(链接点)返回后切入,抛出异常则不会切入maven
-
After Throwing Advice: 在方法(链接点)抛出异常时切入ide
-
Around Advice: 在方法(链接点)执行先后切入,能够中断或忽略原有流程的执行工具
-
Aspect:切面,整个切入逻辑的抽象this
上面的概念若是是第一次接触,那么就显得很是抽象。没有关系,先抛开上面的概念,先来了解一下Spring AOP(或者更加广义的说面向切面编程到底是什么)。.net
能够这样理解:AOP是为了非侵入式的处理一类逻辑。举一个老生常谈的例子:记录方法执行时间,不少方法可能都须要这个功能,很显然咱们不肯意去为每个方法都写记录时间的代码,这些代码会重复,会和业务逻辑耦合,这是代码的"bad smell"。
记录时间方法这就是一类逻辑,咱们能够把这一类逻辑处理为一个Aspect(切面),固然这部分逻辑只是切面中的Advice(通知)部分,咱们还须要指定那些方法(Joinpont)须要执行这个记录时间逻辑(Advice)。(Advice 其实包含3部分,什么地点,什么时间,作什么工做)
因此AOP简单总结:在什么地点(Joinponit,多个地点Pointcut)什么时间作什么(Advice),Advice封装了何时作、什么地方和作什么。
实例
上面的概念部分仍是过于抽象,下面咱们经过一个实例,把概念和实例一一对应来说,我尽可能简化一些概念的东西,先着重于流程部分,这样有利于从总体上把握AOP,而不是限于概念之中。
下图是一个工程的总体的目录结构图,能够对总体结构有一个了解。
下面的例子都使用的是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>