一、HTTP介面切麵
1.web容器和應用容器
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,啟動自動代理的配置為:
,使用基於類的代理機制——CGLIB。
2.基於註解的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() { }
……
}
3.從切點JoinPoint獲取註解中的描述資訊,該方法參考自一文,程式碼如下:
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
, change to .
當AOP遇到雙背景關係
為什麼對Controller應用AOP的時候必須使用class-based的代理機制呢?這又涉及Spring中的兩種代理機制:Java Proxy和CGLIB,Java Proxy要求被代理的類必須實現某個介面;CGLIB則屬於基於子類的代理機制。參考What is the difference between JDK dynamic proxy and CGLib?