AOP
spring支持
// springboot 配置方式
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
// XML 配置方式
<aop:aspectj-autoproxy/>
定义切面
@Component
@Aspect
public class MyTest {
}
概念
- joinPoint(连接点):可执行方法就是连接点。
- pointCut(切入点):用来描述连接点的一个形容词,可以确定哪些连接点是需要使用advice的。
- advice(通知,有人翻译成“增强”,个人觉得通知更形象):before advice:在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常;after return advice:在一个 join point 正常返回后执行的 advice;after throwing advice:当一个 join point 抛出异常后执行的 advice;after(final) advice:无论一个join point是正常退出还是发生了异常, 都会被执行的 advice;around advice:在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice。
完整示例
@Aspect //定义一个切面
@Component // 系统启动自动注入spring容器
@Slf4j // 日志支持
public class LogRecordAspect {
private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);
// 定义切点Pointcut
// public : 指方法类型,只有public的方法才会拦截
// * : 指返回类型,*表示所有返回类型都拦截
// org.jeecg.modules.*.*.*Controller.*(..))
// 包路径-----------------类名--------方法及其参数
// PS. 一个点代表下一层目录,两个点代表下层所有子目录
@Pointcut("execution(public * org.jeecg.modules.*.*.*Controller.*(..))")
public void excudeService() {
}
@Around("excudeService()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
String url = request.getRequestURL().toString();
String method = request.getMethod();
String uri = request.getRequestURI();
String queryString = request.getQueryString();
logger.info("请求开始, 各个参数, url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString);
// result的值就是被拦截方法的返回值,pjp就是被拦截的方法,pjp.proceed()就是调用被拦截方法并返回了Object类型的result
Object result = pjp.proceed();
// 在方法拦截之后添加自定义业务逻辑
logger.info("请求结束,controller的返回值是 " + result);
return result;
}
}

@Pointcut executions 表达式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
括号中各个pattern分别表示:
- 修饰符匹配(modifier-pattern?)表示方法描述符,public private protected,可省略
- 返回值匹配(ret-type-pattern)可以为*表示任何返回值,全路径的类名等
- 类路径匹配(declaring-type-pattern?)
- 方法名匹配(name-pattern)可以指定方法名 或者 *代表所有, set* 代表以set开头的所有方法
- 参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“*”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(*,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(..)表示零个或多个任意参数
- 异常类型匹配(throws-pattern?)
- 其中后面跟着“?”的是可选项
表达式是一个由两个空格组成的一段字符串,第一段是方法修饰符,第二段是返回类型,第三段是包名+方法名+方法参数
举例说明:
随意公共方法的运行:
execution(public * *(..))
不论什么一个以“set”開始的方法的运行:
execution(* set*(..))
AccountService 接口的随意方法的运行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的随意方法的运行:
execution(* com.xyz.service.*.*(..))
定义在service包和全部子包里的随意类的随意方法的运行:
execution(* com.xyz.service..*.*(..))
定义在pointcutexp包和全部子包里的JoinPointObjP2类的随意方法的运行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
***> 最靠近(..)的为方法名,靠近.*(..))的为类名或者接口名,如上例的JoinPointObjP2.*(..))
SpringMVC Interceptor
代码示例
public class WxDictAspect implements HandlerInterceptor {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(WxDictAspect.class);
/**
* 进入handler之前(Controller)
* 返回false,则不会进入handler,也不会触发后面两个事件
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.err.print("preHandle");
return true;
}
/**
* handler(Controller) return 之后 & preHandle返回了true
* ModelAndView 是controller的返回值,controller返回类型是ModelAndView 才能在这接到
*/
@Override
@ResponseBody
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 修改handler返回的数据
Result<?> res = (Result<?>) modelAndView.getModel().get("result");
res.setMessage("(キ`゚Д゚´)!!");
System.err.print("postHandle");
}
/**
* postHandle处理完 & preHandle返回了true
* 多用来回收资源或者异常处理(参数列表最后一个是异常)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (null != ex) {
logger.error(ex.getMessage());
}
}
}
spring-mvc.xml 配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/testController.do*" />
<mvc:exclude-mapping path="/websocket.do"/>
<mvc:exclude-mapping path="/websocket/sockjs.do"/>
<bean class="com.myproject.wx.enhance.aspect.WxDictAspect"></bean>
</mvc:interceptor>
</mvc:interceptors>
SpringMvc架构 aop 在controller层失效
因为Spring的Bean扫描和Spring-MVC的Bean扫描是分开的, 两者的Bean位于两个不同的Application, 而且Spring-MVC的Bean扫描要早于Spring的Bean扫描, 所以当Controller Bean生成完成后, 再执行Spring的Bean扫描,Spring会发现要被AOP代理的Controller Bean已经在容器中存在, 配置AOP就无效了.
同样这样的情况也存在于数据库事务中, 如果Service的Bean扫描配置在spring-mvc.xml中, 而数据库事务管理器配置在application.xml中, 会导致数据库事务失效, 原理一样.
所以这里 ,我们需要把AOP放置在Controller扫描配置的文件中.

本文介绍 Spring 中的 AOP 概念与应用实践,包括如何通过注解和 XML 方式配置 AOP,定义切面、切入点及不同类型的增强。同时提供了一个完整的日志记录切面示例,并解释了 Spring MVC 中的拦截器配置。
1172





