第一 編寫切點
首先我們在Springmvc的controller層中定義一個測試用的切點
package com.lin.controller;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@RequestMapping("/sys/login")
@Controller
public class LoginController {
@RequestMapping(value="/t")
@ResponseBody
public Map<String,Object> test(HttpServletRequest request, HttpServletResponse response){
Map<String,Object> map=new HashMap<String, Object>();
System.out.println("我是切點");
return map;
}
}
第二 定義切面
Spring 藉助AspectJ的切點表達式語言來定義Spring切面。
下面我們來看一個切點表達式,這個表達式能夠設置當test(..)方法執行時觸發通知的調用。
我們使用execution()指示器選擇LoginController的test()方法。方法表達式以“*”號開始,表明我們不關心方法返回值的類型。然後,我們指定了全限定類名和方法名。對於方法參數列表,我們使用兩個點好(..)表明切點要選擇任意的 test()方法,無論該方法的參數是什麼。
編寫切面POJO類
package com.lin.common.aop;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Component
@Aspect
public class Audience {
@Before("execution(* com.lin.controller.LoginController.test(..))")
public void before(){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest(); //獲取request 可以從中獲取參數或cookie
System.out.println("我是切面");
}
}
@Component (把普通pojo實例化到spring容器中,相當於配置文件中的<bean id="" class=""/>)
Audience類使用@Aspect註解進行了標註。該註解表明Audience不僅僅是一個POJO,還是一個切面。Audience類中的方法都使用註解來定義切面的具體行爲。
Spring使用AspectJ註解來聲明通知方法
@Before 通知方法還會在目標方法調用之前執行
@Around 通知方法會將目標方法封裝起來
@AfterThrowing 通知方法會在目標方法拋出異常後調用
@AfterReturning 通知方法會在目標方法返回後調用
@After 通知方法會在目標方法返回或拋出異常後調用
第三 在XML中,通過Spring的aop命名空間啓用AspectJ代理
在Springmvc中使用AOP我們需要將啓用代理放在Spring-mvc.xml中,否則代理不生效。
原因(摘自其他博客,該部分內容原地址:http://blog.csdn.net/my_nice_life/article/details/52910718):
(部分借鑑)CGLIB代理配置在了applicationContext.xml 核心配置文件中,該配置文件會被ContextLoaderListenerclass加載,Spring會創建一個WebApplicationContext上下文,稱爲父上下文(父容器) ,保存在ServletContext中,key爲WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。
而spring-mvc.xml是DispatcherServlet,可以同時配置多個,每個 DispatcherServlet有一個自己的上下文對象(WebApplicationContext),稱爲子上下文(子容器),子上下文可以訪問父上下文中的內容,但父上下文不能訪問子上下文中的內容。 它也保存在 ServletContext中,key是”org.springframework.web.servlet.FrameworkServlet.CONTEXT”+Servlet名稱
當spring加在父容器的時候就會去找切入點,但是這個時候切入的controller是在子容器中的,父容器是無法訪問子容器,所以就攔截不到。如果將上述的配置文件放到spring-mvc.xml中,那麼問題就解決了。我已經測試可以通過URL訪問觸發切點了。
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
<!-- Spring aop 命名空間-->
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<!-- 掃描controller(controller層注入) -->
<context:component-scan base-package="com.lin.controller"/>
<!-- 開啓spring對註解驅動的支持 -->
<mvc:annotation-driven/>
<!-- 啓動AspectJ自動代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
測試結果