一、HTTP接口切面
-
web容器和应用容器
mvc-context-hierarchy
IoC是Spring框架中的核心概念之一,如果要在Spring MVC中使用Spring的这一特性,也需要构建类似的容器。在Spring MVC中,主要通过web.xml进行Web相关的配置,例如配置DispatchServlet,以便应对url请求到具体的controller方法的映射。当DispatcherServlet启动的时候,它会创建一个对应的web应用上下文,负责加载web相关的组件,例如控制器、视图解析器以及处理器映射。在web.xml中,还有个常用配置:ContextLoaderListener,会创建一个Spring应用上下文,用于加载应用中的其他bean,例如各种Service、各种数据库访问层。借用官方文档-mvc servlet的一张图,可以看出,由ContextLoaderListener启动的容器为Root容器(父容器)、由DispatcherServlert启动的容器为Web容器(子容器),并且,子容器可以看到父容器中的bean,反之则不可。正是因为这个原因,在Controller层应用AOP技术的时候,要注意两点:(1)xxx-servlet.xml中的component-scan配置,要确定只扫描web组件、yyyyApplicationContext.xml中的component-scan配置,要排除掉web组件;(2)在xxx-servlet.xml中配置AOP,启动自动代理的配置为:<aop:aspectj-autoproxy proxy-target-class="true"/>
,使用基于类的代理机制——CGLIB。
-
基于注解的AOP应用
我这里所谓基于注解的AOP技术,是指利用自定义注解标注要织入的切点,这种方式比较灵活,可以对指定接口中的某几个方法进行切面。例如,在这次处理日志埋点的需求中,我只给公开的接口加了注解:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BizLogAnnotation {
String description() default "";
}
然后在xxxAspect中给出如下切点定义即可:
@Aspect
@Component
public class DubboLogAspect {
@Pointcut(value = "@annotation(com.xxxx.dubbo.log.BizLogAnnotation)")
public void hsfMethod() { }
……
}
public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
description = method.getAnnotation(SystemControllerLog.class).description();
break;
}
}
}
return description;
}
二、RPC服务接口切面
- 普通Service的AOP
三、Log4j日志配置
- 配置依赖,log4j + slf4j依赖
- 配置log4j.properties
- 修改代码中的Logger
四、补充资料
- 在阅读官方文档的时候,发现官方文档中也有对Controller AOP的阐述。
In some cases a controller may need to be decorated with an AOP proxy at runtime. One example is if you choose to have @Transactional annotations directly on the controller. When this is the case, for controllers specifically, we recommend using class-based proxying. This is typically the default choice with controllers. However if a controller must implement an interface that is not a Spring Context callback (e.g. InitializingBean, *Aware, etc), you may need to explicitly configure class-based proxying. For example with <tx:annotation-driven/>, change to <tx:annotation-driven proxy-target-class="true"/>.
- 当AOP遇到双上下文
- 为什么对Controller应用AOP的时候必须使用class-based的代理机制呢?这又涉及Spring中的两种代理机制:Java Proxy和CGLIB,Java Proxy要求被代理的类必须实现某个接口;CGLIB则属于基于子类的代理机制。参考What is the difference between JDK dynamic proxy and CGLib?
网友评论