首頁  >  文章  >  Java  >  SpringBoot中怎麼利用AOP和攔截器實現自訂註解

SpringBoot中怎麼利用AOP和攔截器實現自訂註解

PHPz
PHPz轉載
2023-05-29 19:58:00986瀏覽

    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元素類型

    ##套用於方法#ElementType.PARAMETER套用於方法的形參考#ElementType.CONSTRUCTORElementType.LOCAL_VARIABLEElementType.ANNOTATION_TYPE##應用於註解類型ElementType.PACKAGE套用到套件ElementType.TYPE_PARAMETER1.8版本新增,套用於類型變量)ElementType.TYPE_USE1.8版本新增,套用於任何使用類型的語句中(例如聲明語句、泛型和強制轉換語句中的類型) @Retention
    Target類型 描述
    ElementType.TYPE 套用於類別、介面(包括註解類型)、枚舉
    ElementType.FIELD 套用於屬性(包括枚舉中的常數)
    #ElementType.METHOD
    ##套用於建構子
    套用於局部變數

    #表示該註解的生命週期

    ##生命週期類型描述編譯時被丟棄,不包含在類別檔案中JVM載入時被丟棄,包含在類別檔案中,預設值由JVM加載,包含在類別文件中,在運行時可以被獲取到#@Document
    RetentionPolicy.SOURCE
    #RetentionPolicy.CLASS
    RetentionPolicy.RUNTIME
    ##表明該註解標記的元素可以被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動態代理程式的差異SpringBoot中怎麼利用AOP和攔截器實現自訂註解

    # 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設定檔中加入

    JDK動態代理程式和CGLIB字節碼產生的差別?

    (1)JDK動態代理只能對實作了介面的類別生成代理,而不能針對類別

    (2)CGLIB是針對類別實作代理,主要是對指定的類別產生一個子類,覆寫其中的方法

    因為是繼承,所以該類別或方法最好不要宣告成final 

    以上是SpringBoot中怎麼利用AOP和攔截器實現自訂註解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除