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

2023-05-30 0 3,025

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();
    }
 
}

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

至此就实现了通过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();
        }
 
    }

运行效果:

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

通过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

资源下载此资源下载价格为1小猪币,终身VIP免费,请先
由于本站资源来源于互联网,以研究交流为目的,所有仅供大家参考、学习,不存在任何商业目的与商业用途,如资源存在BUG以及其他任何问题,请自行解决,本站不提供技术服务! 由于资源为虚拟可复制性,下载后不予退积分和退款,谢谢您的支持!如遇到失效或错误的下载链接请联系客服QQ:442469558

:本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可, 转载请附上原文出处链接。
1、本站提供的源码不保证资源的完整性以及安全性,不附带任何技术服务!
2、本站提供的模板、软件工具等其他资源,均不包含技术服务,请大家谅解!
3、本站提供的资源仅供下载者参考学习,请勿用于任何商业用途,请24小时内删除!
4、如需商用,请购买正版,由于未及时购买正版发生的侵权行为,与本站无关。
5、本站部分资源存放于百度网盘或其他网盘中,请提前注册好百度网盘账号,下载安装百度网盘客户端或其他网盘客户端进行下载;
6、本站部分资源文件是经压缩后的,请下载后安装解压软件,推荐使用WinRAR和7-Zip解压软件。
7、如果本站提供的资源侵犯到了您的权益,请邮件联系: 442469558@qq.com 进行处理!

猪小侠源码-最新源码下载平台 Java教程 SpringBoot中怎么利用AOP和拦截器实现自定义注解 http://www.20zxx.cn/763240/xuexijiaocheng/javajc.html

猪小侠源码,优质资源分享网

常见问题
  • 本站所有资源版权均属于原作者所有,均只能用于参考学习,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担
查看详情
  • 最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,建议提前注册好百度网盘账号,使用百度网盘客户端下载
查看详情

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务