spring aop拦截controller方法

背景

开发的web应用程序涉及到校验采用的spring校验框架,在controller的方法中到处都要写校验处理,异常处理,能否减少这部分冗余代码。

问题:

这是表单提交的处理

 1     @RequestMapping(value = "/edit", method = RequestMethod.POST)
 2     public String edit(@Valid FormBean formBean, BindingResult bindingResult, Model model) {
 3         if (bindingResult.hasErrors()) {
 4             fillModel(model);
 5             return "resource";
 6         }
 7         try {
 8             service.edit(formBean);
 9         } catch (BusinessException e) {
10             fillModel(model);
11             BindingResultUtil.reject(bindingResult, e);
12             return "resource";
13         }
14         return "redirect:/resources";
15     }

校验检查到出错,需要些代码判断出错,返回到原页面;而且还需要把有些必要的信息回填,比如下拉列表。

这是ajax请求的处理:

 1     @ResponseBody
 2     @RequestMapping(value = "/resource", method = RequestMethod.POST)
 3     public Map<String, Object> save(@Valid FormBean formBean, BindingResult bindingResult) {
 4         Map<String, Object> resultMap = new HashMap<String, Object>();
 5         if (bindingResult.hasErrors()) {
 6             return BindingResultUtil.convertToMap(bindingResult);
 7         }
 8         try {               service.save(formBean);
 9             return resultMap;
10         } catch (BusinessException e) {
11             log.error("save resource failed", e);
12             BindingResultUtil.reject(bindingResult, e);
13             return BindingResultUtil.convertToMap(bindingResult);
14         }
15     }    

本来只需要返回一个void就好,但是为了把校验信息输出,不得不返回一个Map,把检验的结果放入到map中。 到处都是这样的相同结构的代码,那么没有办法对这种情况进行改善?能不能统一进行处理校验信息、异常错误信息,controller的方法程序只需要写主业务逻辑。

方案1:

使用Filter拦截处理,但仔细观察其方法public void doFilter(ServletRequest request, ServletResponse response,FilterChain filterChain)

,只传递request,response,无法利用spring已有的校验框架BindingResult的获取校验结果,所以该方案不可行。

方案2:

spring aop自带的拦截功能,找到这篇文章http://www.uroot.com/archives/490 充分利用Spring3 MVC AOP和Annotation特性,自定义拦截和注解,实现登陆验证的最佳实践。

方案3:

利用aspectJ进行拦截。本文也重点介绍方案3的配置:

需要进行如下配置:

1.pom.xml文件配置相关依赖:

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.1</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.1</version>
        </dependency>

2.spring-servlet.xml

<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
    ">

    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <beans:bean id="validInterceptor" class="com.test.interceptor.ValidInterceptor" />
    <aop:config>
       <aop:pointcut id="validPoint" expression="execution(public * com.test.controller.*.*(..)) "/>
       <aop:advisor pointcut-ref="validPoint" advice-ref="validInterceptor"/>
     </aop:config> 

注意红色部分的配置,里面配置拦截哪些controller的哪些方法可以被拦截

3.自定义拦截器

public class ValidInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        Valid validAnnotation = findAnnotation(invocation.getMethod().getParameterAnnotations(), Valid.class);
        if (validAnnotation != null) {
          handleValidationError();
        }
        return invocation.proceed();
    }

思路是拦截@Valid注解,检查BindingResult是否有错误,有错误就提前返回。对于表单提交与ajax提交,是有不同的处理方式。表单提交可能有回调动作,比如fillModel(),需要告诉拦截器需要返回哪个页面。对于ajax请求呢,返回值类型是不同的,可能是Map,可能是void,没有办法返回错误信息的Map类,因此需要把json的校验错误信息写入到Response中。

4. 对处理表单提交 定义注解,配置一旦出错,应该返回到哪个错误页面。ajax请求则没有必要处理。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandleFormSubmitValid {
    String view();
    String callback() default "";
}

5.最后修改controller的方法

对于表单提交:

   @RequestMapping(value = "/edit", method = RequestMethod.POST)
   @HandleFormSubmitValid(view="resource", callback="fillModel")  public String edit(@Valid FormBean formBean, BindingResult bindingResult, Model model) {
        service.edit(courseFormBean);                return "redirect:/resources";
    }

ajax请求:

    @ResponseBody
    @RequestMapping(value = "/resource", method = RequestMethod.POST)
    public void save(@Valid FormBean formBean, BindingResult bindingResult) {
        service.save(formBean);
    }

Controller代码是不是清爽了很多?没有丑陋的异常处理,检验处理,没有莫名其妙的Map作为返回值, 世界清静多了!

当然在实现的过程还遇到了其他的问题:

比如spring框架,居然没有办法获得HttpServletResponse,最后我不得不写了一个Filter或者mvc的interceptors,将其放到ThreadLocal里面。

写ajax请求写Response的时候,需要注意编码和contentType,如下:

        HttpServletResponse response = WebContext.getResponse();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().write(ajaxJsonResponse);

需要改进的地方:

1.controller方法中有参数bindingResult在拦截器中有被使用,但在controller方法中没有被用到,有可能被认为是无用参数,给去掉,则检验拦截功能会失败。理想的情况应该是去掉bindingResult,在拦截器中对formBean进行检验。2.表单提交需要配置回调函数名称,有可能回调函数被重构改成另外一个名称,拦截功能也会失败。
时间: 2024-10-29 19:11:45

spring aop拦截controller方法的相关文章

spring aop 拦截业务方法,实现权限控制

难点:aop类是普通的java类,session是无法注入的,那么在有状态的系统中如何获取用户相关信息呢,session是必经之路啊,获取session就变的很重要.思索很久没有办法,后来在网上看到了解决办法. 思路是:      i. SysContext  成员变量 request,session,response     ii. Filter 目的是给 SysContext 中的成员赋值     iii.然后在AOP中使用这个SysContext的值   要用好,需要理解  ThreadL

Spring AOP根据JdbcTemplate方法名动态设置数据源

说明:现在的场景是,采用数据库(Mysql)复制(binlog)的方式在两台不同服务器部署并配置主从(Master-Slave)关系: 并需要程序上的数据操作方法来访问不同的数据库,比如,update方法访问主数据库服务器,query方法访问从数据库服务器. 即把"增删改"和"查"分开访问两台服务器,当然两台服务器的数据库同步事先已经配置好. 然而程序是早已完成的使用Spring JdbcTemplate的架构,如何在不修改任何源代码的情况下达到<本文标题&g

【框架】[Spring]AOP拦截-使用切点:AspectJExpressionPointcut-切点语言

转载请注明出处:http://blog.csdn.net/qq_26525215 本文源自[大学之旅_谙忆的博客] 用AspectJExpressionPointcut实现的切点比JdkRegexpMethodPointcut实现切点的好处就是,在设置切点的时候可以用切点语言来更加精确的表示拦截哪个方法! 可以精确到返回参数,参数类型,方法名. 当然,也可以模糊匹配. 这里用纯Java的方式和配置xml的方法都来演示一遍. 需要的包什么的就不解释了,如不动,请参考前面的. 首先,准备好原型对象P

Spring MVC中 controller方法返回值

1.返回ModelAndView 定义ModelAndView对象并返回,对象中可添加model数据.指定view 2.返回String 1.表示返回逻辑视图名 model对象通过 model.addAttribute("xxx",model)进行设定 2.redirect重定向: redirect重定向特点:浏览器地址栏中的url会变化.修改提交的request数据无法传到重定向的地址.因为重定向后重新进行request(request无法共享) 3.forward页面转发: 通过f

Spring AOP拦截对Controller的请求时的配置失败

简单的说,就是父子容器的问题,将AOP的配置信息放在applicationContext.xml中,该配置文件被ContextLoaderListener加载,Spring会创建一个WebApplicationContext的上下文,称为父上下文(父容器),保存在 ServletContext中,key值为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE.而spring-mvc.xml是DispatcherServlet 的

使用Spring AOP预处理Controller的参数

实际编程中,可能会有这样一种情况,前台传过来的参数,我们需要一定的处理才能使用,比如有这样一个Controller @Controller public class MatchOddsController { @Autowired private MatchOddsServcie matchOddsService; @RequestMapping(value = "/listOdds", method = RequestMethod.GET, produces = {MediaType

Spring AOP—注解配置方法的使用

Spring除了支持Schema方式配置AOP,还支持注解方式:使用@AspectJ风格的切面声明. 1 启用对@AspectJ的支持 Spring默认不支持@AspectJ风格的切面声明,为了支持需要使用如下配置: 这样Spring就能发现@AspectJ风格的切面并且将切面应用到目标对象. 2 声明切面 @AspectJ风格的声明切面非常简单,使用@Aspect注解进行声明: 然后将该切面在配置文件中声明为Bean后,Spring就能自动识别并进行AOP方面的配置: 该切面就是一个POJO,

SpringMVC中配置AOP拦截controller 失效

来源:http://www.oschina.net/question/222929_124314 目测大部分同学的aop失效都是因为在springmvc的配置文件里面扫描了类,那么spring去扫描的时候发现内存中已经有了对象,就不会在对类进行aop增强.所以当我们确定在那一层切入的时候,那么在springmvc的配置文件中,应该要排除欲切入的层. <!-- 扫描的时候过滤掉Service层,aop要在service进行切入! --> <context:component-scan ba

SpringMVC拦截器中通过反射得到Controller方法注解时ClassCastException解决方案

错误应用场 在Controller中,我们自定义了一个@Auth注解来实现权限控制功能,如: @Auth(verifyLogin=false,verifyURL=false) @RequestMapping("/login") public ModelAndView login(HttpServletRequest request,HttpServletResponse response) throws Exception{ Map<String,Object> conte