通常来说,谈到Spring的特性,绕不过去的就是DI(依赖注入)和AOP(切面),在将bean的系列中,说了DI的多种使用姿式;接下来看一下AOP的玩法git
<!-- more -->github
在实际使用以前有必要了解一下什么是AOP,以及AOP的几个基本概念正则表达式
链接点,好比方法调用,方法执行,字段设置/获取、异常处理执行、类初始化、甚至是 for 循环中的某个点spring
但 Spring AOP 目前仅支持方法执行 (method execution)安全
简单来讲,Spring AOP中,PointCut就是那个被拦截的方法框架
切点,用来描述知足什么规则的方法会被拦截dom
@Before("execution(public * com.git.hui.demo.base.bean.*.*(..))")
@Around("@annotation(parameterCheck)")
切面是切点和通知的结合。通知和切点共同定义了关于切面的所有内容,它是何时,在什么时候和何处完成功能maven
引入容许咱们向现有的类添加新的方法或者属性spring-boot
组装方面来建立一个被通知对象。这能够在编译时完成(例如使用AspectJ编译器),也能够在运行时完成。Spring和其余纯Java AOP框架同样,在运行时完成织入。
简单来说就是生成一个代理类,在调用被拦截的方法时,实际上执行的是代理类,这个代理类内部执行切面逻辑
首先是基本环境的搭建, 先贴上必要的xml配置, 使用aop须要引入包: spring-boot-starter-aop
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
首先建立一个被拦截的bean: com.git.hui.boot.aop.demo.DemoBean
,以下
@Component public class DemoBean { /** * 返回随机的字符串 * * @param time * @return */ public String randUUID(long time) { try { System.out.println("in randUUID before process!"); return UUID.randomUUID() + "|" + time; } finally { System.out.println("in randUUID finally!"); } } }
接着在启动类中,执行
@SpringBootApplication public class Application { public Application(DemoBean demoBean) { String ans = demoBean.randUUID(System.currentTimeMillis()); System.out.println("----- ans: " + ans + "---------"); } public static void main(String[] args) { SpringApplication.run(Application.class); } }
在实际使用以前,须要建立一个切面,用@Aspect
声明,其次切面也须要做为bean托付给Spring容器管理
@Aspect @Component public class AnoAspcet { }
在方法调用以前,须要执行一些操做,这个时候可使用 @Before
注解来声明before advice
一种可以使用姿式以下,咱们的切点直接在注解中进行定义,使用正则表达式的方式
@Before("execution(public * com.git.hui.boot.aop.demo.*.*(*))") public void doBefore(JoinPoint joinPoint) { System.out.println("do in Aspect before method called! args: " + JSON.toJSONString(joinPoint.getArgs())); }
在方法调用完毕以后,再执行一些操做,这个时候after就能够派上用场,为了考虑切点的通用性,咱们能够考虑声明一个切点,使用@Pointcut
注解
@Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(*))") public void point() { }
使用pointcut的方式也比较简单,以下
@After("point()") public void doAfter(JoinPoint joinPoint) { System.out.println("do in Aspect after method called! args: " + JSON.toJSONString(joinPoint.getArgs())); }
在正常返回结果以后,再次执行,这个也挺有意思的,一般使用这个advice时,通常但愿获取返回结果,那么应该怎么处理呢?
org.aspectj.lang.annotation.AfterReturning#returning
指定返回结果对应参数name/** * 执行完毕以后,经过 args指定参数;经过 returning 指定返回的结果,要求返回值类型匹配 * * @param time * @param result */ @AfterReturning(value = "point() && args(time)", returning = "result") public void doAfterReturning(long time, String result) { System.out.println("do in Aspect after method return! args: " + time + " ans: " + result); }
这个也比较常见,在方法执行先后干一些事情,好比常见的耗时统计,日志打印,安全控制等,不少都是基于around advice实现的
使用这个advice须要注意的是传入参数类型为 ProceedingJoinPoint
,须要在方法内部显示执行org.aspectj.lang.ProceedingJoinPoint#proceed()
来表示调用方法
@Around("point()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("do in Aspect around ------ before"); Object ans = joinPoint.proceed(); System.out.println("do in Aspect around ------- over! ans: " + ans); return ans; }
执行以后输出以下
do in Aspect around ------ before do in Aspect before method called! args: [1551433188205] in randUUID before process! in randUUID finally! do in Aspect around ------- over! ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205 do in Aspect after method called! args: [1551433188205] do in Aspect after method return! args: 1551433188205 ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205 ----- ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205---------
从输出结果上,能够看到每一个advice的使用范围,固然也带来了一些疑问
以上这些问题留在下一篇进行介绍
一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛
尽信书则不如,以上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
一灰灰blog
知识星球