<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近遇到了這樣一個工作場景,需要寫一批dubbo介面,再將dubbo介面註冊到閘道器中,但是當dubbo介面異常的時候會給前端返回非常不友好的異常。所以就想要對異常進行統一捕獲處理,但是對於這種service介面使用@ExceptionHandler註解進行異常捕獲也是捕獲不到的,應為他不是Controller的介面。這時就想到了自定義一個註解去實現異常捕獲的功能。
通過攔截器+AOP實現自定義註解的實現,在這裡攔截器充當在指定註解處要執行的方法,aop負責將攔截器的方法和要註解生效的地方做一個織入(通過動態註解生成代理類實現)。
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>
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實現自定義註解。
雖然通過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註解的註解,所標記的類的子類也會擁有這個註解
在我們定義好註解之後就需要考慮如何將註解和類繫結到一起,在執行期間達到我們想要的效果,這裡就可以引入動態代理的機制,將註解想要做的操作在方法執行前,類編譯時就進行一個織入的操作如下。
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(); } }
執行效果:
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); } }
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自定義註解內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45