基于Spring AOP自定义注解拦截Controller方法

开篇 - 前奏

最近项目后台针对报表查询进行优化,为了避免打开页面慢的问题,新增首次加载不返回数据的需求。由于后台报表较多,如果每个方法添加判断修改量较大且代码重复,故采用自定义注解的形式处理。

后台前端使用Datatables插件进行实现,每次表格渲染插件会自动传递参数draw,初始draw=1, 每次加载值递增。

Datatables是一款jquery表格插件。它是一个高度灵活的工具,可以将任何HTML表格添加高级的交互功能。

  • 分页,即时搜索和排序
  • 几乎支持任何数据源:DOM, javascript, Ajax 和 服务器处理
  • 支持不同主题 DataTables, jQuery UI, Bootstrap, Foundation
  • 各式各样的扩展: Editor, TableTools, FixedColumns ……
  • 丰富多样的option和强大的API
  • 支持国际化
  • 超过2900+个单元测试
  • 免费开源 ( MIT license )! 商业支持
  • 更多特性请到官网查看

正文 - 间奏

配置

请求拦截需要在控制层Controller进行处理,所以需要开启cglib实现动态代理

<!-- 报表首次渲染方法拦截 -->
<aop:config proxy-target-class="true">
	<!--切入点-->
	<aop:pointcut id="methodPoint" expression="execution(* com.test.demo.web.controller..*.*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)"/>
	<!--通知-->
    <aop:advisor pointcut-ref="methodPoint" advice-ref="drawInterceptor"/>
</aop:config>

注意事项

因为Spring的Bean扫描和Spring-MVCBean扫描是分开的, 两者的Bean位于两个不同的Application,即父子容器, 而且Spring-MVCBean扫描要早于Spring的Bean扫描, 所以当Controller Bean生成完成后, 再执行Spring的Bean扫描,Spring会发现要被AOP代理的Controller Bean已经在容器中存在, 配置AOP就无效了。

同样这样的情况也存在于数据库事务中, 如果ServiceBean扫描配置在spring-mvc.xml中, 而数据库事务管理器配置在application.xml中, 会导致数据库事务失效, 原理一样。

所以这里 ,我们需要把AOP放置在Controller扫描配置的文件中。

示例

Spring的配置文件application.xml配置如下:

<context:component-scan base-package="cn.test.demo.dao" />
<context:component-scan base-package="cn.test.demo.service" />

Spring MVC的配置文件spring-mvc.xml

<!-- 扫描类包,将spring注解的类自动扫描为Bean,同时完成Bean的注入. -->
<context:component-scan base-package="cn.test.demo.controller" />
<!-- aop配置 -->
<aop:config proxy-target-class="true">
	<aop:pointcut id="methodPoint" expression="..."></aop:pointcut>
	<aop:advisor pointcut-ref="methodPoint" advice-ref="drawInterceptor"/>
</aop:config>
  • aop:configproxy-target-class="true" 开启Cglib动态代理
  • aop:pointcut切入点配置
  • aop:advisor通知接口配置

处理

自定义注解
@Target({ METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Draw {
    String message() default "{com.test.demo.web.annotation.Draw.message}";
}
java中元注解解释
  • @Target 描述注解的使用范围

    public enum ElementType {
     
        TYPE, // 类、接口、枚举类
     
        FIELD, // 成员变量(包括:枚举常量)
     
        METHOD, // 成员方法
     
        PARAMETER, // 方法参数
     
        CONSTRUCTOR, // 构造方法
     
        LOCAL_VARIABLE, // 局部变量
     
        ANNOTATION_TYPE, // 注解类
     
        PACKAGE, // 可用于修饰:包
     
        TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
     
        TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
    
    }
    
  • @Retention 描述注解保留的时间范围

public enum RetentionPolicy {
    SOURCE,    // 源文件保留
    CLASS,       // 编译期保留,默认值
    RUNTIME   // 运行期保留,可通过反射去获取注解信息
}
  • @Documented 描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息

  • @Inherited 使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)

方法拦截器实现类
@Configuration
public class DrawInterceptor implements MethodInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(DrawInterceptor.class);

    @Autowired
    private HttpServletRequest request;
    /**
     * 首次渲染
     */
    private static final String FIRST_DRAW = "1";

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 判断方法是否加了@Draw 注解
        if (invocation.getMethod().isAnnotationPresent(Draw.class)) {
            String drawValue = request.getParameter("draw");
            if (FIRST_DRAW.equals(drawValue)) {
                logger.info("请求{}首次渲染,返回空数据", request.getRequestURI());
                // 空结果
                return AjaxResultUtil.assembleDataGridResult(new PaginationResult(new Pagination(0, 0), 0, Collections.EMPTY_LIST));
            }
        }
        //执行被拦截的方法
        return invocation.proceed();
    }
}

使用

在对应请求的处理方法上使用 @DrawSpring会自动扫描进行拦截处理。

注:被注解的类需要放到前面我们指定的cn.test.demo.controlle 下,否则不生效。

示例

@Draw
@RequestMapping("/page")
public VO getPage(){
    ...
    return vo;
}

结尾 - 尾声

至此,我们已完成如下事情

  • AOP支持并配置
  • 自定义注解
  • 注解扫描并处理
  • 应用

感悟 - 高潮

自定义注解是本小笨蛋儿第一次在企业实际场景中应用,也非常深刻体验到抽取、封装思想带来的便利,也算攻破了自定义注解这一技术,可很方便控制和修改,将通用部分抽取并封装可极大提升开发效率。

其实受益颇多的还是这种抽取的思想,技术很容易从各大网站博客学习,随学随用,但是思想是一步步从个人经验和前人经验借鉴提升的,一旦思想境界达到一定程度,对于任何新旧技术都不会成为瓶颈。

知行合一,举一反三,方成正果~~

最后引用名人劝解后人的名言:学吧,学无止境,太深了。 ——乾隆赠予兴跃神话

😄😄 加油~

更多信息可关注我的公众号 Java知识星球 ,不定时推送更多原创技术知识文章。

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值