SpringBoot中利用AOP和拦截器实现自定义注解

目录
  • 前言
  • Spring实现自定义注解
    • 1.引入相关依赖
    • 2.相关类
  • Java实现自定义注解
    • 通过Cglib实现
    • 通过JDk动态代理实现
    • Cglib和JDK动态代理的区别
  • 写在最后

前言

最近遇到了这样一个工作场景,需要写一批dubbo接口,再将dubbo接口注册到网关中,但是当dubbo接口异常的时候会给前端返回非常不友好的异常。所以就想要对异常进行统一捕获处理,但是对于这种service接口使用@ExceptionHandler注解进行异常捕获也是捕获不到的,应为他不是Controller的接口。这时就想到了自定义一个注解去实现异常捕获的功能。

Spring实现自定义注解

通过拦截器+AOP实现自定义注解的实现,在这里拦截器充当在指定注解处要执行的方法,aop负责将拦截器的方法和要注解生效的地方做一个织入(通过动态注解生成代理类实现)。

1.引入相关依赖

spring-boot-starter:spring的一些核心基础依赖

spring-boot-starter-aop:spring实现Aop的一些相关依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.相关类

1.自定义注解类

@Target({ElementType.TYPE})  //说明了Annotation所修饰的对象范围,这里,的作用范围是类、接口(包括注解类型) 或enum
@Retention(RetentionPolicy.RUNTIME)  //自定义注解的有效期,Runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Documented //标注生成javadoc的时候是否会被记录
public @interface EasyExceptionResult {
}

2.拦截器类

/**
 * MethodInterceptor是AOP项目中的拦截器(注:不是动态代理拦截器),
 * 区别与HandlerInterceptor拦截目标时请求,它拦截的目标是方法。
 */
public class EasyExceptionIntercepter implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        AnnotatedElement element=invocation.getThis().getClass();
        EasyExceptionResult easyExceptionResult=element.getAnnotation(EasyExceptionResult.class);
        if (easyExceptionResult == null) {
            return invocation.proceed();
        }
        try {
            return invocation.proceed();
        } catch (Exception rpcException) {
            //不同环境下的一个异常处理
            System.out.println("发生异常了");
            return null;
        }
    }
}

3.切点切面类

MethodInterceptor的实现类能作为切面的执行方式是应为Interceptor的父类是Advice。

@Configuration
public class EasyExceptionAdvisor {

    /**
     * 放在最后执行
     * 等待ump/日志等记录结束
     *
     * @return {@link DefaultPointcutAdvisor}对象
     */
    @Bean
    @Order(Integer.MIN_VALUE)
    public DefaultPointcutAdvisor easyExceptionResultAdvisor() {
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        //针对EasyExceptionResult注解创建切点
        AnnotationMatchingPointcut annotationMatchingPointcut = new AnnotationMatchingPointcut(EasyExceptionResult.class, true);
        EasyExceptionIntercepter interceptor = new EasyExceptionIntercepter();
        advisor.setPointcut(annotationMatchingPointcut);
        //在切点执行interceptor中的invoke方法
        advisor.setAdvice(interceptor);
        return advisor;
    }

}

4.自定义注解的使用

@Service
@EasyExceptionResult  //自定义异常捕获注解
public class EasyServiceImpl {

    public void testEasyResult(){
        throw new NullPointerException("测试自定义注解");
    }

}

5.效果

@SpringBootApplication
public class JdStudyApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context=SpringApplication.run(JdStudyApplication.class, args);
        EasyServiceImpl easyService=context.getBean(EasyServiceImpl.class);
        easyService.testEasyResult();
    }

}

至此就实现了通过spring实现自定义注解。

Java实现自定义注解

虽然通过Spring实现了自定义注解但是还有办法让我们不通过Spring也能实现自定义注解,毕竟注解是早于Spring的。

JDK中有一些元注解,主要有@Target,@Retention,@Document,@Inherited用来修饰注解,如下为一个自定义注解。

@Target({ElementType.TYPE})  //说明了Annotation所修饰的对象范围,这里,的作用范围是类、接口(包括注解类型) 或enum
@Retention(RetentionPolicy.RUNTIME)  //自定义注解的有效期,Runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Documented //标注生成javadoc的时候是否会被记录
public @interface EasyExceptionResult {
}

@Target

表明该注解可以应用的java元素类型

Target类型 描述
ElementType.TYPE 应用于类、接口(包括注解类型)、枚举
ElementType.FIELD 应用于属性(包括枚举中的常量)
ElementType.METHOD 应用于方法
ElementType.PARAMETER 应用于方法的形参
ElementType.CONSTRUCTOR 应用于构造函数
ElementType.LOCAL_VARIABLE 应用于局部变量
ElementType.ANNOTATION_TYPE 应用于注解类型
ElementType.PACKAGE 应用于包
ElementType.TYPE_PARAMETER 1.8版本新增,应用于类型变量)
ElementType.TYPE_USE 1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)

@Retention

表明该注解的生命周期

生命周期类型 描述
RetentionPolicy.SOURCE 编译时被丢弃,不包含在类文件中
RetentionPolicy.CLASS JVM加载时被丢弃,包含在类文件中,默认值
RetentionPolicy.RUNTIME 由JVM 加载,包含在类文件中,在运行时可以被获取到

@Document

表明该注解标记的元素可以被Javadoc 或类似的工具文档化

@Inherited

表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解

通过Cglib实现

在我们定义好注解之后就需要考虑如何将注解和类绑定到一起,在运行期间达到我们想要的效果,这里就可以引入动态代理的机制,将注解想要做的操作在方法执行前,类编译时就进行一个织入的操作如下。

public static void main(String[] args) {
        Class easyServiceImplClass=EasyServiceImpl.class;
        //判断该对象是否有我们自定义的@EasyExceptionResult注解
        if(easyServiceImplClass.isAnnotationPresent(EasyExceptionResult.class)){
            final EasyServiceImpl easyService=new EasyServiceImpl();
            //cglib的字节码加强器
            Enhancer enhancer=new Enhancer();
            将目标对象所在的类作为Enhaner类的父类
            enhancer.setSuperclass(EasyServiceImpl.class);
            通过实现MethodInterceptor实现方法回调,MethodInterceptor继承了Callback
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    try{
                        method.invoke(easyService, args);
                        System.out.println("事务结束...");
                    }catch (Exception e){
                        System.out.println("发生异常了");
                    }
                    return proxy;
                }
            });

            Object obj= enhancer.create();;
            EasyServiceImpl easyServiceProxy=(EasyServiceImpl)obj;
            easyServiceProxy.testEasyResult();
        }

    }

运行效果:

通过JDk动态代理实现

public class EasyServiceImplProxy implements InvocationHandler {

    private EasyServiceImpl target;

    public void setTarget(EasyServiceImpl target)
    {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 这里可以做增强
        System.out.println("已经是代理类啦");
        try{
            return  method.invoke(proxy, args);
        }catch (Exception e){
            System.out.println("发生异常了");
            return null;
        }
    }

    /**
     * 生成代理类
     * @return 代理类
     */
    public Object CreatProxyedObj()
    {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

Cglib和JDK动态代理的区别

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

如何强制使用CGLIB实现AOP?

(1)添加CGLIB库,SPRING_HOME/cglib/*.jar

(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

JDK动态代理和CGLIB字节码生成的区别?

(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类

(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法

因为是继承,所以该类或方法最好不要声明成final

写在最后

@ExceptionHandler注解的使用可参考文章Java实现优雅的参数校验方法详解

到此这篇关于SpringBoot中利用AOP和拦截器实现自定义注解的文章就介绍到这了,更多相关SpringBoot自定义注解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解使用Spring AOP和自定义注解进行参数检查

    引言 使用SpringMVC作为Controller层进行Web开发时,经常会需要对Controller中的方法进行参数检查.本来SpringMVC自带@Valid和@Validated两个注解可用来检查参数,但只能检查参数是bean的情况,对于参数是String或者Long类型的就不适用了,而且有时候这两个注解又突然失效了(没有仔细去调查过原因),对此,可以利用Spring的AOP和自定义注解,自己写一个参数校验的功能. 代码示例 注意:本节代码只是一个演示,给出一个可行的思路,并非完整的解决

  • 详解使用Spring Boot的AOP处理自定义注解

    上一篇文章Java 注解介绍讲解了下Java注解的基本使用方式,并且通过自定义注解实现了一个简单的测试工具:本篇文章将介绍如何使用Spring Boot的AOP来简化处理自定义注解,并将通过实现一个简单的方法执行时间统计工具为样例来讲解这些内容. AOP概念 面向侧面的程序设计(aspect-oriented programming,AOP,又译作面向方面的程序设计.观点导向编程.剖面导向程序设计)是计算机科学中的一个术语,指一种程序设计范型.该范型以一种称为侧面(aspect,又译作方面)的语

  • SpringBoot使用自定义注解实现权限拦截的示例

    本文介绍了SpringBoot使用自定义注解实现权限拦截的示例,分享给大家,具体如下: HandlerInterceptor(处理器拦截器) 常见使用场景 日志记录: 记录请求信息的日志, 以便进行信息监控, 信息统计, 计算PV(page View)等 性能监控: 权限检查: 通用行为: 使用自定义注解实现权限拦截 首先HandlerInterceptor了解 在HandlerInterceptor中有三个方法: public interface HandlerInterceptor { //

  • spring AOP自定义注解方式实现日志管理的实例讲解

    今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在applicationContext-mvc.xml中要添加的 <mvc:annotation-driven /> <!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 --> <context:component-scan base-package=&qu

  • Spring AOP 实现自定义注解的示例

    自工作后,除了一些小项目配置事务使用过 AOP,真正自己写 AOP 机会很少,另一方面在工作后还没有写过自定义注解,一直很好奇注解是怎么实现他想要的功能的,刚好做项目的时候,经常有人日志打得不够全,经常出现问题了,查日志的才发现忘记打了,所以趁此机会,搜了一些资料,用 AOP + 自定义注解,实现请求拦截,自定义打日志,玩一下这两个东西,以下是自己完的一个小例子,也供需要的同学参考. 1. 注解如下: package cn.bridgeli.demo.annotation;   import j

  • SpringBoot中利用AOP和拦截器实现自定义注解

    目录 前言 Spring实现自定义注解 1.引入相关依赖 2.相关类 Java实现自定义注解 通过Cglib实现 通过JDk动态代理实现 Cglib和JDK动态代理的区别 写在最后 前言 最近遇到了这样一个工作场景,需要写一批dubbo接口,再将dubbo接口注册到网关中,但是当dubbo接口异常的时候会给前端返回非常不友好的异常.所以就想要对异常进行统一捕获处理,但是对于这种service接口使用@ExceptionHandler注解进行异常捕获也是捕获不到的,应为他不是Controller的

  • 在SpringBoot中该如何配置拦截器

    拦截器也是我们经常需要使用的,在SpringBoot中该如何配置呢 拦截器不是一个普通属性,而是一个类,所以就要用到java配置方式了.在SpringBoot官方文档中有这么一段说明: 翻译: 如果你想要保持Spring Boot 的一些默认MVC特征,同时又想自定义一些MVC配置(包括:拦截器,格式化器, 视图控制器.消息转换器 等等),你应该让一个类实现 WebMvcConfigurer ,并且添加 @Configuration 注 解,但是千万不要加 @EnableWebMvc 注解.如果

  • SpringBoot 拦截器和自定义注解判断请求是否合法

    应用场景举例: 当不同身份的用户请求一个接口时,用来校验用户某些身份,这样可以对单个字段数据进行精确权限控制,具体看代码注释 自定义注解 /** * 对比请求的用户身份是否符合 * @author liuyalong * @date 2020/9/25 16:03 */ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface CompareUser { /** * The name

  • 详解Java拦截器以及自定义注解的使用

    目录 1,设置预处理,设置不需要拦截的请求 2.UserTokenInterceptor ,securityInterceptor分别处理不同的请求拦截,执行不同的拦截逻辑. 3.关于注解的使用 总结 1,设置预处理,设置不需要拦截的请求 @Component public class MyWebConfig implements WebMvcConfigurer { private final UserTokenInterceptor userTokenInterceptor; private

  • 在spring-boot工程中添加spring mvc拦截器

    1. 认识拦截器 Spring MVC的拦截器(Interceptor)不是Filter,同样可以实现请求的预处理.后处理.使用拦截器仅需要两个步骤: 实现拦截器 注册拦截器 1.1 实现拦截器 实现拦截器可以自定义实现HandlerInterceptor接口,也可以通过继承HandlerInterceptorAdapter类,后者是前者的实现类.下面是拦截器的一个实现的例子,目的是判断用户是否登录.如果preHandle方法return true,则继续后续处理. public class L

  • 利用spring的拦截器自定义缓存的实现实例代码

    本文研究的主要是利用spring的拦截器自定义缓存的实现,具体实现代码如下所示. Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.本文利用Memcached 的实例和spring的拦截器实现缓存自定义的实现.利用拦截器读取自定义的缓存标签,key值的生成策略. 自定义的Cacheable package com.jeex.sci; @Target(ElementT

  • Java中过滤器 (Filter) 和 拦截器 (Interceptor)的使用

    1.过滤器 (Filter) 过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter注解实现对特定URL拦截,看到Filter 接口中定义了三个方法. init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次.注意:这个方法必须执行成功,否则过滤器会不起作用. doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter. destroy(): 当容器销毁 过滤器

  • SpringBoot中通过AOP整合日志文件的实现

    目录 1.导入相关的依赖 2.log4j2 日志文件 3.dao层的接口以及实现类 4.Service层业务实现类 5.Controller层接口控制类 6.编写业务类增强类,加入一个日志文件记录 7.运行测试,查看结果 1.导入相关的依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter&

  • SpringBoot中的Condition包下常用条件依赖注解案例介绍

    目录 一.@ConditionalOnClass() Spring中存在指定class对象时,注入指定配置 1.首先引入pom依赖 2.实体类测试对象 3.定义@ConditionalOnClass()配置类 4.启动类测试 二.注入指定配置 1.首先引入pom依赖 2.实体类测试对象 3.定义@ConditionalOnMissingClass()配置类 4.启动类测试 三.加载指定配置 1.首先引入pom依赖 2.实体类测试对象 2.1 引入条件判断实体类 3.定义@ConditionalO

  • 详解SpringBoot AOP 拦截器(Aspect注解方式)

    常用用于实现拦截的有:Filter.HandlerInterceptor.MethodInterceptor 第一种Filter属于Servlet提供的,后两者是spring提供的,HandlerInterceptor属于Spring MVC项目提供的,用来拦截请求,在MethodInterceptor之前执行. 实现一个HandlerInterceptor可以实现接口HandlerInterceptor,也可以继承HandlerInterceptorAdapter类,两种方法一样.这个不在本文

随机推荐

其他