AOP 面向切面编程,相对于OOP面向对象编程。
spring AOP存在的目的是为了解耦。AOP可以让一组类共享相同的行为。在OOP中只能通过继承类和实现接口,来使代码的耦合度增强,且类继承只能是单继承,阻碍更多的行为添加到一组类上,AOP弥补了OOP的不足。
Spring支持AspectJ的注解式切面编程。
1、使用@AspectJ声明一个切面。
2、使用@After、@Before、@Around定义建言(adivice),可以直接将拦截规则(切点)作为参数。
3、其中@After、@Before、@Around参数的拦截规则为切点(PointCut),为了使切点复用,可以使用@PointCut专门定义拦截规则,然后在@After、@Before、@Around的参数中调用。
4、其中符合条件的每一个被拦截处为连接点(JoinPoint)。
AspectJ提供了五种定义通知的标注:
@Before
- :前置通知,在调用目标方法之前执行通知定义的任务
@After
- :后置通知,在目标方法执行结束后,无论执行结果如何都执行通知定义的任务
@After-returning
- :后置通知,在目标方法执行结束后,如果执行成功,则执行通知定义的任务
@After-throwing
- :异常通知,如果目标方法执行过程中抛出异常,则执行通知定义的任务
@Around
- :环绕通知,在目标方法执行前和执行后,都需要执行通知定义的任务
本示例将演示基于注解拦截和基于方法规则拦截两种方式,演示一种模拟记录操作的日志系统的实现。其中注解式拦截能够很好的控制要拦截的粒度和获得更丰富的信息,
Spring本身在事务处理(@transcational)和数据缓存(@Cacheable等)上面都使用此种形式拦截。
一、添加AOP支持
[html] view plain copy
print
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-aop</artifactId>
- <version>4.1.6.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjrt</artifactId>
- <version>1.8.3</version>
- </dependency>
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjweaver</artifactId>
- <version>1.8.5</version>
- </dependency>
二、编写拦截规则的注解
[java] view plain copy
print
- package com.chenfeng.xiaolyuh.aop.annotation;
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 拦截规则注解
- * 注解本生没有功能,就和XML一样。注解和XML都是一种元数据,元数据即解释数据的数据,这就是所谓的配置。
- * 注解的功能来自用这个注解的地方。
- */
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface Action {
- String name();
- }
三、编写使用注解的被拦截类
[java] view plain copy
print
?
- package com.chenfeng.xiaolyuh.aop.service;
- import org.springframework.stereotype.Service;
- import com.chenfeng.xiaolyuh.aop.annotation.Action;
- /**
- * 编写使用注解的被拦截类
- * @ClassName DemoAnnotationService
- * @author yuhao.wang
- * @Date 2017年3月10日 下午3:41:18
- * @version 1.0.0
- */
- @Service
- public class DemoAOPAnnotationService {
- @Action(name="编写使用注解的被拦截类")
- public void add() {
- "写使用注解的被拦截类DemoAnnotationService:add");
- }
- }
四、编写使用方法规则被拦截的类
[java] view plain copy
print
?
- package com.chenfeng.xiaolyuh.aop.service;
- import org.springframework.stereotype.Service;
- /**
- * 编写使用方法规则被拦截类
- * @ClassName DemoMethodService
- * @author yuhao.wang
- * @Date 2017年3月10日 下午3:41:18
- * @version 1.0.0
- */
- @Service
- public class DemoAOPMethodService {
- public void add() {
- "使用方法规则被拦截类DemoMethodService:add");
- }
- }
五、编写切面
[java] view plain copy
print
?
- package com.chenfeng.xiaolyuh.aop.aspectj;
- import java.lang.reflect.Method;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.After;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.aspectj.lang.annotation.Pointcut;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.springframework.stereotype.Component;
- import com.chenfeng.xiaolyuh.aop.annotation.Action;
- /**
- * 编写切面
- * @ClassName LogAspect
- * @author yuhao.wang
- * @Date 2017年3月10日 下午3:46:11
- * @version 1.0.0
- */
- @Aspect// 通过@Aspect声明一个切面
- @Component // 通过@Component让此切面成为Spring容器管理的Bean
- public class LogAspect {
- // 通过@Pointcut注解声明一个切点
- @Pointcut("@annotation(com.chenfeng.xiaolyuh.aop.annotation.Action)")
- public void annotationPointCut(){};
- // 通过@After注解声明一个建言,并使用@Pointcut定义的切点
- @After("annotationPointCut()")
- public void after(JoinPoint joinPoint) {
- // 通过反射可以获得注解上的属性,然后做日志记录相关的操作
- MethodSignature signature = (MethodSignature) joinPoint.getSignature();
- Method method = signature.getMethod();
- class);
- "注解式拦截器:" + action.name());
- }
- // 通过@Before注解声明一个建言,此建言直接使用拦截规则作为参数
- @Before("execution(* com.chenfeng.xiaolyuh.aop.service.DemoAOPMethodService.*(..))")
- public void before(JoinPoint joinpoint) {
- MethodSignature signature = (MethodSignature) joinpoint.getSignature();
- Method method = signature.getMethod();
- "拦截规则式拦截器:" + method.getName());
- }
- }
六、配置类
[java] view plain copy
print
- package com.chenfeng.xiaolyuh.aop.config;
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.EnableAspectJAutoProxy;
- import org.springframework.context.annotation.FilterType;
- import com.chenfeng.xiaolyuh.bean.config.BeanConfig;
- @Configuration // 声明当前类是一个配置类,相当于Spring配置的XML文件
- // 包扫描,并排除了对BeanConfig的扫描
- @ComponentScan(basePackages={"com.chenfeng.xiaolyuh"}, excludeFilters={@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=BeanConfig.class)})
- @EnableAspectJAutoProxy // 使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持
- public class AopConfig {
- }
七、测试类
[java] view plain copy
print
- package com.chenfeng.xiaolyuh.test;
- import org.junit.After;
- import org.junit.Test;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- import com.chenfeng.xiaolyuh.aop.config.AopConfig;
- import com.chenfeng.xiaolyuh.aop.service.DemoAOPAnnotationService;
- import com.chenfeng.xiaolyuh.aop.service.DemoAOPMethodService;
- /**
- * Created by yuhao.wang on 2017/3/9.
- */
- public class SpringAopTest {
- new AnnotationConfigApplicationContext(AopConfig.class);
- @Test
- public void contextTest() {
- class);
- class);
- annotationService.add();
- methodService.add();
- }
- @After
- public void closeContext() {
- context.close();
- }
- }
参考: https://www.tianmaying.com/tutorial/spring-aop-point-advice