Maison  >  Article  >  Java  >  Comment utiliser AOP et les intercepteurs pour implémenter des annotations personnalisées dans SpringBoot

Comment utiliser AOP et les intercepteurs pour implémenter des annotations personnalisées dans SpringBoot

PHPz
PHPzavant
2023-05-29 19:58:00986parcourir

    Spring implémente des annotations personnalisées

    implémentation d'annotations personnalisées via intercepteur + AOP, où l'intercepteur agit comme une méthode à exécuter au niveau de l'annotation spécifiée, et aop est responsable de la prise en compte de la méthode de l'intercepteur et de l'annotation. un tissage en place (générer une implémentation de classe proxy via une annotation dynamique).

    1. Présentation des dépendances associées

    spring-boot-starter : quelques dépendances de base de base de spring

    spring-boot-starter-aop : quelques dépendances associées de spring implémentant 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. Classe d'annotation personnalisée

    @Target({ElementType.TYPE})  //说明了Annotation所修饰的对象范围,这里,的作用范围是类、接口(包括注解类型) 或enum
    @Retention(RetentionPolicy.RUNTIME)  //自定义注解的有效期,Runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
    @Documented //标注生成javadoc的时候是否会被记录
    public @interface EasyExceptionResult {
    }
    2. Classe Interceptor

    /**
     * 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. Classe d'aspect Pointcut

    La classe d'implémentation de MethodInterceptor peut être utilisée comme méthode d'exécution d'aspect car la classe parent d'Interceptor est 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. Utilisation d'annotations personnalisées

    @Service
    @EasyExceptionResult  //自定义异常捕获注解
    public class EasyServiceImpl {
     
        public void testEasyResult(){
            throw new NullPointerException("测试自定义注解");
        }
     
    }
    5. Effet

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

    Jusqu'à présent, des annotations personnalisées ont été mises en œuvre jusqu'au printemps. Comment utiliser AOP et les intercepteurs pour implémenter des annotations personnalisées dans SpringBoot

    Java implémente des annotations personnalisées

    Bien que les annotations personnalisées soient implémentées via Spring, il existe encore des moyens pour nous d'implémenter des annotations personnalisées sans utiliser Spring. Après tout, les annotations sont antérieures à Spring.

    Il existe certaines méta-annotations dans le JDK, principalement @Target, @Retention, @Document et @Inherited, qui sont utilisées pour modifier les annotations.

    @Target({ElementType.TYPE})  //说明了Annotation所修饰的对象范围,这里,的作用范围是类、接口(包括注解类型) 或enum
    @Retention(RetentionPolicy.RUNTIME)  //自定义注解的有效期,Runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
    @Documented //标注生成javadoc的时候是否会被记录
    public @interface EasyExceptionResult {
    }

    @Target

    Indique le type d'élément Java auquel cette annotation peut être appliquée

    Type de cibleDescriptionElementType.TYPES'applique aux classes, interfaces (y compris les annotations types) , et les énumérations ElementType.FIELDappliqués aux propriétés (y compris les constantes dans les énumérations) ElementType.METHODappliqué aux méthodes ElementType.PARAMETERappliqué aux paramètres de la méthode ElementType.CONSTRUCTORappliqué aux constructeursElementType.LOCAL_VARIABLEappliqué aux variables localesElementType.ANNOTATION_TYPEappliqué aux types d'annotationsElementType.PACK AGE appliqué au sacElementType.TYPE_PARAMETERNouveau dans la version 1.8, appliqué aux variables de type) ElementType.TYPE_USENouveau dans la version 1.8, appliqué à toutes les instructions qui utilisent des types (telles que les instructions de déclaration, les génériques et les instructions de conversion) Tapez )@Retention

    Indique le cycle de vie de l'annotation

    Le type de cycle de vieDescriptionRetentionPolicy.SOURCE est ignoré lors de la compilation ing,NonContenu dans la classeFichier RetentionPolicy.CLASS est ignoré lors du chargement de la JVM et est inclus dans le fichier de classe. La valeur par défaut RetentionPolicy.RUNTIME est chargée par la JVM et est incluse dans le fichier de classe et peut être. obtenu au moment de l'exécution. To @Document

    indique que l'élément marqué par cette annotation peut être documenté par Javadoc ou des outils similaires

    @Inherited

    indique que l'annotation utilisant l'annotation @Inherited est une sous-classe de la classe marquée La classe aura également cette annotation

    implémentée par Cglib

    Après avoir défini l'annotation, nous devons réfléchir à la manière de lier l'annotation et la classe ensemble pour obtenir l'effet souhaité pendant l'exécution. Ici. nous pouvons introduire le mécanisme de proxy dynamique, l'annotation veut effectuer l'opération avant que la méthode ne soit exécutée et une opération de tissage est effectuée lorsque la classe est compilée comme suit.

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

    Effet opérationnel :

    implémenté via le proxy dynamique JDkComment utiliser AOP et les intercepteurs pour implémenter des annotations personnalisées dans SpringBoot

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

    La différence entre le proxy dynamique Cglib et JDK

    Le proxy dynamique Java utilise le mécanisme de réflexion pour générer une classe anonyme qui implémente l'interface proxy et l'appelle avant d'appeler la méthode spécifique InvokeHandler à gérer.

    Le proxy dynamique cglib utilise le package open source asm pour charger le fichier de classe de la classe d'objet proxy et le traiter en modifiant son bytecode pour générer une sous-classe.

    1. Si l'objet cible implémente l'interface, le proxy dynamique du JDK sera utilisé par défaut pour implémenter AOP

    2 Si l'objet cible implémente l'interface, vous pouvez forcer l'utilisation de CGLIB pour implémenter AOP.

    3. Si l'objet cible n'implémente pas l'interface. , la bibliothèque CGLIB doit être utilisée, spring convertira automatiquement entre le proxy dynamique JDK et CGLIB

    Comment forcer l'utilisation de CGLIB pour implémenter AOP ?

    (1) Ajoutez la bibliothèque CGLIB, SPRING_HOME/cglib/*.jar (2) Ajoutez

    dans la configuration Spring file Quelle est la différence entre le proxy dynamique JDK et la génération de bytecode CGLIB ?

    (1) Le proxy dynamique JDK ne peut générer des proxys que pour les classes qui implémentent des interfaces, mais pas pour les classes (2) CGLIB implémente des proxys pour les classes, principalement pour générer une sous-classe pour la classe spécifiée et la couvrir Méthode

    Parce que c'est hérité, il est préférable de ne pas déclarer la classe ou la méthode comme finale

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Déclaration:
    Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer