springboot aop 自定义注解,spring aop 自定义注解

  springboot aop 自定义注解,spring aop 自定义注解

  00-1010前言Spring实现自定义注释1。相关性介绍2。用相关类实现自定义注释Java通过JDk动态代理实现Cglib。最后说明了Cglib和JDK动态代理的区别。

  00-1010最近遇到这样一个工作场景,需要写一批dubbo接口,然后在网关注册dubbo接口。但是,当dubbo接口出现异常时,它会向前端返回一个非常不友好的异常。所以我们想用统一的方式来捕捉异常,但是对于这个服务接口使用@ExceptionHandler注释是捕捉不到异常的,因为它不是控制器的接口。这时候我想到了定制一个注释来实现异常捕捉的功能。

  00-1010通过拦截器aop实现定制注释,其中拦截器充当在指定注释处执行的方法。AOP负责编织拦截器的方法和注释生效的地方(通过动态注释生成代理类实现)。

  00-1010 Spring-Boot-Starter:Spring的一些核心基础依赖项

  Spring-Boot-Starter-Aop:Spring实现Aop的一些相关依赖

  依赖关系groupIdorg.springframework.boot/groupId artifact id spring-boot-starter/artifact id/依赖关系groupIdorg.springframework.boot/groupId artifact id spring-boot-starter-AOP/artifact id/dependency

  

目录

1.自定义注解类

 

  @Target({ElementType。TYPE}) //解释由批注修饰的对象的范围。这里的作用域是类、接口(包括注释类型)或者enum @ retention(retention policy . runtime)//自定义注释的有效期。运行时:注释不仅保存在类文件中,在jvm加载类文件后,仍然有一个@ Documented//2.拦截器类指示javadoc在生成时是否会被记录。

  /** * MethodInterceptor是AOP项目中的拦截器(注意:不是动态代理拦截器)。*拦截目标时与HandlerInterceptor的请求不同。它拦截的目标是一个方法。*/public class easyexceptioninterceptor实现method interceptor { @ Override public Object invoke(method invocation invocation)抛出Throwable { AnnotatedElement element=invocation . getthis()。getClass();EasyExceptionResult EasyExceptionResult=element . get annotation(EasyExceptionResult . class);if(easyExceptionResult==null){ return invocation . proceed();} try { return invocation . proceed();} catch(Exception RPC Exception){//不同环境下的一个异常处理程序System.out.println(“发生了异常”);返回null} } }3.切点切面类

  MethodInterceptor的实现类可以作为切面执行,因为拦截器的父类是Advice。

  @配置

  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.效果

 

  

@SpringBootApplicationpublic 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_PARAMETER1.8版本新增,应用于类型变量)ElementType.TYPE_USE1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)@Retention

  表明该注解的生命周期

  生命周期类型描述RetentionPolicy.SOURCE编译时被丢弃,不包含在类文件中RetentionPolicy.CLASSJVM加载时被丢弃,包含在类文件中,默认值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自定义注解内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: