支持作者
最便宜的卫生纸
第一 编写切点
首先我们在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访问触发切点了。
配置文件
<span style="color:#000000"><?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></span>
测试结果