spring aop即切面编程是spring的一重要功能,主要用于完成系统级别的任务。例如:系统日志的记录,权限管理等。这种编程方式的好处是将我们的业务处理和系统级别的问题隔离开。
spring aop术语
- 通知advice:定义切面的任务和何时调用该通知。主要有前置通知(@Before)、后置通知(@After)、返回通知(@AfterReturning)、异常通知(@AfterThrowing)、环绕通知(@Around)
- 连接点JoinPoint:程序执行过程中通知会被执行的点,通常是调用某个方法。
- 切点Pointcut:可以理解成切点的集合,切点的定义会匹配通知所要织入的一个或多个连接点。通常使用类名和方法名或正则表达式所匹配的类或方法名来指定这些切点。
- 切面Aspect:切面是通知和切点的结合
定义切点
execution:匹配连接点的执行方法
@Pointcut("execution(* com.example.service..*.*(..))"):第一个*标识匹配任意返回值的方法,..标识0个以上,第一个..表示当前包及其子包,第二个*标识任意类,第三个*标识任意方法,最后一个..标识任意参数。
within:限定匹配方法的连接点
@Pointcut("within(com.example.service.*)")表示匹配service包下的任意连接点
this:用来限制AOP代理必须是指定的类型
@Pointcut("this(com.example.service.UserService)")标识aop只能代理UserService
bean
@Pointcut("bean(userServiceImpl)")限定bean的id为userServiceImpl
@annotation限定被指定注解修饰的方法
常用execution,其他限定一般很少使用。可以使用and或or来连接多个表达式。
SpringBoot环境下使用AOP
定义切面:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Configuration;
@Aspect
@Configuration
public class LogAspect {
@Pointcut("execution(* com.example.test.aop.LogService.*(..))")
public void pointCut(){};
@Around("pointCut()")
public void aroundPoint(ProceedingJoinPoint joinPoint){
System.out.println("around aop before...");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("around aop after...");
}
@Before("pointCut()")
public void beforePoint(){
System.out.println("before aop ...");
}
@After("pointCut()")
public void afterPoint(){
System.out.println("after aop before...");
}
@AfterReturning(pointcut = "pointCut()",returning = "returnObj")
public void afterReturningPoint(JoinPoint joinPoint,Object returnObj){
System.out.println("afterReturn aop" + returnObj);
}
@AfterThrowing(pointcut = "pointCut()",throwing = "error")
public void afterThrowPoint(JoinPoint joinPoint,Throwable error){
System.out.println("afterThrowing aop " + error);
}
}
定义service:
import org.springframework.stereotype.Service;
@Service
public class LogService {
public void insert(){
System.out.println("start insert data to database...");
}
public String update(){
System.out.println("start to update...");
return "update success";
}
public void query(){
System.out.println("start to query...");
throw new RuntimeException("query error");
}
}
定义测试类:
import com.heyu.test.Application;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class TestService {
@Autowired
private LogService logService;
@Test
public void testInsert(){
logService.insert();
}
@Test
public void testUpdate(){
logService.update();
}
@Test
public void testQuery(){
logService.query();
}
}
测试testInsert()输出结果:
可以发现各类通知的执行顺序如下:
around before通知 -> before通知 -> 被拦截方法 -> around after通知 -> after通知 -> afterRetruning通知
如果在spring MVC环境,则先将所有的注解去掉,再在配置文件中增加如下代码:
<!-- 配置需要添加aop的类bean -->
<bean id="logService" class="com.example.test.aop.LogService" />
<!-- 配置切面bean -->
<bean id="logAspect" class="com.example.test.aop.LogAspect" />
<!-- 配置aop -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut id="pointCut" expression="execution(* com.example.test.aop.LogService.*(..))"/>
<!-- 配置切点 -->
<aop:aspect ref="logAspect" order="1">
<aop:before method="beforePoint" pointcut-ref="pointCut"/>
<aop:after method="afterPoint" pointcut-ref="pointCut"/>
<aop:after-throwing method="afterThrowPoint" pointcut-ref="pointCut" throwing="error"/>
<aop:after-returning method="afterReturningPoint" pointcut-ref="pointCut" returning="returnObj"/>
<aop:around method="aroundPoint" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
注:如果想获取代理方法参数,可观察JoinPoint类中的方法,这里具体就不介绍了