Fork me on GitHub

Programming Design Notes

Spring Web MVC + AspectJ 記錄 IP

| Comments

因為 AspectJ 需要用介面去制作一個代理 (proxy)(如果有使用 CGLib 可以不使用介面 (interface)),所以先制作一個介面 (interface),這個 Spring Web controller 的介面 ,介名為 com.blogsport.lawpronotes.controller.MyController。
MyController 程式碼:
package com.blogsport.lawpronotes.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

public interface MyController {
@RequestMapping
public String genenalProcess();
@RequestMapping(method=RequestMethod.GET, params="view=iphone")
public String IPhoneView();
}

在 method 上需要加上註解,不然該 method 無法被 Spring Web Controller 調用。

新增一個 Class 去實作這個介面,命名為 IndexController。
IndexController 程式碼:
package com.hkforum.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/index")
public class IndexController implements MyController {
@RequestMapping
public String genenalProcess() {
return "index";
}

@RequestMapping
public String IPhoneView() {
return "iphoneIndex";
}
}

實作的 method 也需要加上註解,作用是要 Aspect 辨識出該 method 是用戶要求調用的方法。

現在新增個一個 Class,命名為 com.blogsport.lawpronotes.logger.WebLogger,用來記錄用戶使用了那一個 method 和 IP 地址。
WebLogger 程式碼:
package com.blogsport.lawpronotes.logger;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
public class WebLogger {

@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void anyRequestMappingMethod() {
}

@Pointcut("execution(* com.hkforum.web.controller.*.*(..))")
public void anyControllerMethod() {
}

@Pointcut("execution(public * *(..))")
public void anyPublicMethod() {
}

@Pointcut("anyControllerMethod() && anyPublicMethod() && anyRequestMappingMethod()")
public void anyInteractionMethod() {
}

@Before("anyInteractionMethod()")
public void recordIP(JoinPoint pjp) {
String remoteAddress = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes()).getRequest().getRemoteAddr();
System.out.println(pjp.toString() + " ip:" + remoteAddress);
}
}

截取有 RequestMapping 註解的 method:
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")

截取所有 Web Controller 的 Class:
@Pointcut("execution(* com.hkforum.web.controller.*.*(..))")

截取所有公開 method:
@Pointcut("execution(public * *(..))")

截取合乎以上條件的方法:
@Pointcut("anyControllerMethod() && anyPublicMethod() && anyRequestMappingMethod()")

因為 Web Controller bean 不是一般的 Spring Bean,所以不會在 applicationContext.xml 加上 Aspect,而且加了也不能截取 Web Controller Bean。
現在要更改 dispatcherServlet-servlet.xml。
請加上以下程式碼:
<aop:aspectj-autoproxy />
<bean id="webLogger" class="com.hkforum.web.logger.WebLogger" />
<bean id="indexController" class="com.hkforum.web.controller.IndexController" />

完整 dispatcherServlet-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

<aop:aspectj-autoproxy />
<bean id="webLogger" class="com.hkforum.web.logger.WebLogger" />
<bean id="indexController" class="com.hkforum.web.controller.IndexController" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>

**請自行新增 index.jsp 和 iphoneIndex.jsp 到 /WEB-INF/views/ 資料夾下。

用瀏覽器打開 index.*, Console 會顯示:
execution(String com.hkforum.web.controller.MyController.genenalProcess()) ip:127.0.0.1
如果是 index.*?view=iphone,Console 會顯示:
execution(String com.hkforum.web.controller.MyController.IPhoneView()) ip:127.0.0.1

以這種方式來制作截取記錄的好處是可以將記錄程式碼和控制程式碼分開,而且能夠截取到多個 method,又可以減少多餘的程式碼,又比較好維護。

相關書籍: Spring Recipes: A Problem-Solution ApproachSpring in ActionSpring Web Recipes: A Problem-Solution Approach to Spring Framework Web Development Technologies