Spring AOP
Spring 中 AOP 代理由 Spring 的 IoC 容器负责生成、管理,其依赖关系也由 IoC 容器负责管理。一些AOP的概念:
- 切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。
- 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
- 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。
- 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。
- 引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。
- 目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。
- AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
- 编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
一、基于代理的AOP实现
1. 代理类
public class ServiceHelper implements MethodBeforeAdvice, AfterReturningAdvice {
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("方法执行开始!");
}
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("方法执行结束!");
}
}
2. XML配置
<!-- 定义被代理者 -->
<bean id="user" class="com.aop.service.UserServiceImpl"></bean>
<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
<bean id="serviceHelper" class="com.aop.helper.ServiceHelper"></bean>
<!-- 定义切入点位置 -->
<bean id="servicePointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*"></property>
</bean>
<!-- 使切入点与通知相关联,完成切面配置 -->
<bean id="serviceHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="serviceHelper"></property>
<property name="pointcut" ref="servicePointcut"></property>
</bean>
<!-- 设置代理 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理的对象,有记录方法开始结束能力 -->
<property name="target" ref="user"></property>
<!-- 使用切面 -->
<property name="interceptorNames" value="serviceHelperAdvisor"></property>
<!-- 代理接口,service接口 -->
<property name="proxyInterfaces" value="com.aop.service.IUserService"></property>
</bean>
二、通过AspectJ提供的注解实现AOP
1. 切面代码
@Aspect
public class ServiceHelper{
@Pointcut("execution(* *.*())")
public void userPointcut(){}
@Before("userPointcut()")
public void before(){
System.out.println("方法执行开始!");
}
@AfterReturning("userPointcut()")
public void after(){
System.out.println("方法执行结束!");
}
}
2. XML配置
<aop:aspectj-autoproxy />
<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
<bean id="serviceHelper" class="com.aop.helper.ServiceHelper"></bean>
<!-- 定义被代理者 -->
<bean id="user" class="com.aop.service.UserServiceImpl"></bean>
三、使用Spring来定义纯粹的POJO切面
1.
public class ServiceHelper{
public void before(){
System.out.println("方法执行开始!");
}
public void after(){
System.out.println("方法执行结束!");
}
}
2. XML配置
2.1 写法一
<aop:config>
<aop:aspect ref="serviceHelper">
<aop:before method="before" pointcut="execution(* *.*(..))" />
<aop:after method="after" pointcut="execution(* *.*(..))" />
</aop:aspect>
</aop:config>
2.2. 写法二
<aop:config>
<aop:aspect ref="serviceHelper">
<aop:pointcut id="serviceHelpers" expression="execution(* *.*(..))" />
<aop:before pointcut-ref="serviceHelper" method="before" />
<aop:after pointcut-ref="serviceHelper" method="after" />
</aop:aspect>
</aop:config>
Springboot 实现AOP
其实搞明白AOP是什么之后(基于动态代理实现面向切面编程的一种思想),无论在哪里使用都很简单,但是由于最近做的好多项目都使用springboot,所以就在补充一下,超级无敌简单
依赖的包,pom中引入即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
springboot实现AOP有两种方式:一是申明的方式(基于XML),二是注解的方式(基于AspectJ),由于个人喜好只说说注解的方式实现AOP,
一、实现方式一(编写切入点表达式)
@Service
public class LogService {
private final Logger logger = LoggerFactory.getLogger(LogService.class);
public void search(){
logger.info("查询方法被执行");
}
}
@Aspect
@Component
public class LogAspect {
private final Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Pointcut("execution(public * com.dafeng.service.LogService.*(..))")//切入点描述 这个是切入点
public void logPointcut(){}//签名,可以理解成这个切入点的一个名称
@Before("logPointcut()") //在切入点的方法执行之前
public void before(JoinPoint joinPoint) {
logger.info("方法执行前。。。");
}
@After("logPointcut()") //在切入点的方法执行之后
public void after(JoinPoint joinPoint) {
logger.info("方法执行后。。。");
}
}
执行结果:
2020-03-01 13:40:38.894 INFO 35973 --- [nio-8080-exec-1] com.dafeng.service.LogAspect : 方法执行前。。。
2020-03-01 13:40:38.903 INFO 35973 --- [nio-8080-exec-1] com.dafeng.service.LogService : 查询方法被执行
2020-03-01 13:40:38.903 INFO 35973 --- [nio-8080-exec-1] com.dafeng.service.LogAspect : 方法执行后。。。
二、 实现方式二(自定义注解)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}
@Service
public class LogService {
private final Logger logger = LoggerFactory.getLogger(LogService.class);
@Log // 此处添加自定义注解
public void search(){
logger.info("查询方法被执行");
}
}
@Aspect
@Component
public class LogAspect {
private final Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Pointcut("@annotation(com.dafeng.annotation.Log)")//切入点描述使用自定义注解 这个是切入点
public void logPointcut(){}//签名,可以理解成这个切入点的一个名称
@Before("logPointcut()") //在切入点的方法执行之前要干的
public void before(JoinPoint joinPoint) {
logger.info("方法执行前。。。");
}
@After("logPointcut()") //在切入点的方法执行之后要干的
public void after(JoinPoint joinPoint) {
logger.info("方法执行后。。。");
}
}
执行结果:
2020-03-01 14:12:35.434 INFO 40304 --- [nio-8080-exec-1] com.dafeng.service.LogAspect : 方法执行前。。。
2020-03-01 14:12:35.441 INFO 40304 --- [nio-8080-exec-1] com.dafeng.service.LogService : 查询方法被执行
2020-03-01 14:12:35.442 INFO 40304 --- [nio-8080-exec-1] com.dafeng.service.LogAspect : 方法执行后。。。