对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点html
类是对物体特征的抽象,切面就是对横切关注点的抽象。 通知+切点 意思就是全部要被应用到加强(advice)代码的地方。(包括方法的方位信息)java
被拦截到的点,由于Spring只支持方法类型的链接点,因此在Spring中链接点指的就是被拦截的方法,实际上链接点还能够是字段或者构造器git
对链接点进行拦截的定义github
所谓通知指的就是指拦截到链接点以后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类 这玩意也叫 加强 在逻辑层次上包括了咱们抽取的公共逻辑和方位信息。由于Spring只能方法级别的应用AOP,也就是咱们常见的before,after,after-returning,after-throwing,around五种,意思就是在方法调用先后,异常时候执行我这段公共逻辑呗。web
代理的目标对象spring
将切面应用到目标对象并致使代理对象建立的过程。 好比根据Advice中的方位信息在指定切点的方法先后,执行加强。这个过程Spring 替咱们作好了。利用的是CGLIB动态代理技术。spring-boot
在不修改代码的前提下,引入能够在运行期为类动态地添加一些方法或字段this
上面那一堆看不懂对吗? 我也不太懂。 来看张图 阿里云
切面一共有五种通知spa
前置通知(Before advice) :在某链接点(JoinPoint)以前执行的通知, 但这个通知不能阻止链接点前的执行。在方法调用以前发出通
@Before("execution(* com.slife.java8.aspect.AspectTest.test())") public void beforeTest() { System.out.println("执行 方法 以前 调用----"); }
后通知(After advice) :当某链接点退出的时候执行的通知(不管是正常 返回仍是异常退出)。 不考虑方法运行的结果 。在方法调用以后发出通
@After("execution(* com.slife.java8.aspect.AspectTest.test())") public void afterTest() { System.out.println(); System.out.println("执行 方法 以后 调用----"); }
方法正常返回后,调用通知。在方法调用后,正常退出发出通
@AfterReturning("execution(* com.slife.java8.aspect.AspectTest.test())") public void afterReturningTest() { System.out.println(); System.out.println("执行 方法 AfterReturning 调用----"); }
抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行 的通知。在方法调用时,异常退出发出通
@AfterThrowing("execution(* com.slife.java8.aspect.AspectTest.test())") public void afterThrowingTest() { System.out.println(); System.out.println("执行 方法 AfterThrowing 调用----"); }
环绕通知(Around advice) :包围一个链接点的通知,相似Web中Servlet 规范中的Filter的doFilter方法。能够在方法的调用先后完成自定义的行为,也能够选择不执行。在方法调用以前和以后发出通
@Around("execution(* com.slife.java8.aspect.AspectTest.test())") public void aroundTest() { System.out.println(); System.out.println("执行 方法 先后 调用----"); }
2017-10-27 19:51:51.605 DEBUG 15592 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : Last-Modified value for [/aspecttest] is: -1 执行 方法 以前 调用---- JoinpointTest++++执行我正常流水线的业务逻辑 执行 方法 以后 调用---- 执行 方法 AfterReturning 调用---- 2017-10-27 19:51:51.622 DEBUG 15592 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling 2017-10-27 19:51:51.622 DEBUG 15592 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : Successfully completed request
切入点指示符用来指示切入点表达式目的,在Spring AOP中目前只有执行方法这一个链接点,Spring AOP支持的AspectJ切入点指示符以下:
args() 定制join-point去匹配那些参数为指定类型的方法的执行动做。 @args() 定制join-point去匹配那些参数被指定类型注解的方法的执行动做 execution() 开始匹配在其内部编写的定制 this() 定制join-pont去匹配由AOP代理的Bean引用的指定类型的类。 target() 定制join-point去匹配特定的对象,这些对象必定是指定类型的类。 @target() 定制join-point去匹配特定的对象,这些对象要具备的指定类型的注解。 within() 定制join-point在必须哪个包中。 @within() 定制join-point在必须由指定注解标注的类中。 @annotation 定制链接点具备指定的注解。 只有execution用来执行匹配,其余标志符都只是为了限制/定制他们所要匹配的链接点的位置。
*
:匹配任何数量字符。
..
:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
+
:匹配指定类型的子类型;仅能做为后缀放在类型模式后边。
java.lang.String 匹配String类型; java.*.String 匹配java包下的任何“一级子包”下的String类型; 如匹配java.lang.String,但不匹配java.lang.ss.String java..* 匹配java包及任何子包下的任何类型; 如匹配java.lang.String、java.lang.annotation.Annotation java.lang.*ing 匹配任何java.lang包下的以ing结尾的类型; java.lang.Number+ 匹配java.lang包下的任何Number的自类型; 如匹配java.lang.Integer,也匹配java.math.BigInteger
注解? 修饰符? 返回值类型 类型声明?方法名(参数列表) 异常列表? 注解:可选,方法上持有的注解,如@Deprecated; 修饰符:可选,如public、protected; 返回值类型:必填,能够是任何类型模式;“*”表示全部类型; 类型声明:可选,能够是任何类型模式; 方法名:必填,可使用“*”进行模式匹配; 参数列表:“()”表示方法没有任何参数;“(..)”表示匹配接受任意个参数的方法,“(..,java.lang.String)”表示匹配接受java.lang.String类型的参数结束,且其前边能够接受有任意个参数的方法;“(java.lang.String,..)” 表示匹配接受java.lang.String类型的参数开始,且其后边能够接受任意个参数的方法;“(*,java.lang.String)” 表示匹配接受java.lang.String类型的参数结束,且其前边接受有一个任意类型参数的方法; 异常列表:可选,以“throws 异常全限定名列表”声明,异常全限定名列表若有多个以“,”分割,如throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException。 匹配Bean名称:可使用Bean的id或name进行匹配,而且可以使用通配符“*”;
AspectJ使用 且(&&)、或(||)、非(!)来组合切入点表达式。在Schema风格下,因为在XML中使用“&&”须要使用转义字符“&&”来代替之,因此很不方便,所以Spring ASP 提供了and、or、not来代替&&、||、!。
使用JoinPoint获取:Spring AOP提供使用org.aspectj.lang.JoinPoint类型获取链接点数据,任何通知方法的第一个参数均可以是JoinPoint(环绕通知是ProceedingJoinPoint,JoinPoint子类),固然第一个参数位置也能够是JoinPoint.StaticPart类型,这个只返回链接点的静态部分。
AOP 、IOC 作为Spring 的支柱,使用场景很是普遍。
##三、事务
动态代理 Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。所以,AOP代理能够直接使用容器中的其它bean实例做为目标,这种关系可由IOC容器的依赖注入提供。Spring建立代理的规则为:
一、默认使用Java动态代理来建立AOP代理,这样就能够为任何接口实例建立代理了
二、当须要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
@Aspect // FOR AOP @Order(-99) // 控制多个Aspect的执行顺序,越小越先执行 @Component public class AdviceTest { @Pointcut(value="execution(* com.slife.java8.aspect.AspectTest.test())") public void poincut(){ } @Before("poincut()") public void beforeTest() { System.out.println("执行 方法 以前 调用----"); } }
这样就完成了在spring boot 项目中定义使用本身的aop
CGLIB动态代理技术 有时候你会发现 你的配置和我同样,可是aop没有生效,这颇有多是SpringMVC的配置的代理模式不对。
方法里的xxxService对象若是使用autowared注入,没法启动aspect, 可是 xxxService = ctx.getBean("xxxxx")获取,是能够启用aspect的
这个时候 xxxService 并非注入进来的,即便有 @Autowired 注解,这时的注解没有任何做用。 只有 Spring 生成的对象才有 AOP 功能,由于 Spring 生成的代理对象才有 AOP 功能。
配置spring.aop.proxy-target-class=true
/** * Created by chen on 2017/10/27. * <p> * Email 122741482@qq.com * <p> * Describe: */ @Service public class JoinpointTest { public void JoinpointTest(){ System.out.println("**********JoinpointTest*****************"); } public void test(){ System.out.println("JoinpointTest++++执行我正常流水线的业务逻辑"); } }
@Aspect // FOR AOP @Order(-99) // 控制多个Aspect的执行顺序,越小越先执行 @Component public class AdviceTest { @Pointcut(value="execution(* com.slife.java8.aspect.AspectTest.test())") public void poincut(){ } @Before("poincut()") public void beforeTest() { System.out.println("执行 方法 以前 调用----"); } @After("execution(* com.slife.java8.aspect.AspectTest.test())") public void afterTest() { System.out.println(); System.out.println("执行 方法 以后 调用----"); } @Around("execution(* com.slife.java8.aspect.AspectTest.test())") public void aroundTest() { System.out.println(); System.out.println("执行 方法 先后 调用----"); } @AfterReturning("execution(* com.slife.java8.aspect.AspectTest.test())") public void afterReturningTest() { System.out.println(); System.out.println("执行 方法 AfterReturning 调用----"); } @AfterThrowing("execution(* com.slife.java8.aspect.AspectTest.test())") public void afterThrowingTest() { System.out.println(); System.out.println("执行 方法 AfterThrowing 调用----"); } @Before("execution(* com.slife.java8..*test*(..))") public void aspecttest1() { System.out.println(); System.out.println("执行 方法aspecttest1 Before 调用----"); } }
个人官网
个人官网http://guan2ye.com 个人CSDN地址http://blog.csdn.net/chenjianandiyi 个人简书地址http://www.jianshu.com/u/9b5d1921ce34 个人githubhttps://github.com/javanan 个人码云地址https://gitee.com/jamen/ 点击获取阿里云优惠券https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=vf2b5zld&utm_source=vf2b5zld