您好,登錄后才能下訂單哦!
本篇內容主要講解“SpringBoot中怎么利用AOP和攔截器實現自定義注解”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“SpringBoot中怎么利用AOP和攔截器實現自定義注解”吧!
通過攔截器+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
到此,相信大家對“SpringBoot中怎么利用AOP和攔截器實現自定義注解”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。