Heim  >  Artikel  >  Java  >  So entwickeln Sie ein Springboot-Plug-in

So entwickeln Sie ein Springboot-Plug-in

王林
王林nach vorne
2023-05-16 09:31:051261Durchsuche

    Hintergrund

    Das Projekt fügte ein Überwachungssystem hinzu, um die Schnittstellenaufrufe jedes Systems zu überwachen. In der Anfangsphase wurden AOP-Aspekte zu den Abhängigkeitspaketen hinzugefügt, auf die jedes Projekt allgemein verweist, um die Schnittstellenaufrufe jedes Systems zu vervollständigen. Die Überwachung weist jedoch Mängel auf. Erstens sind die Schnittstellenpfade verschiedener Projekte unterschiedlich, was dazu führt, dass mehrere Aspektpfade für den AOP-Aspekt geschrieben werden müssen. Zweitens werden aufgrund der Einführung von auch einige Systeme überwacht, die nicht überwacht werden müssen Öffentliche Pakete, daher ist das Eindringen zu stark. Um dieses Problem zu lösen, können Sie die steckbaren Attribute von Springboot verwenden.

    Zweite Überwachungsprotokoll-Plug-in-Entwicklung

    1 Erstellen Sie eine neue AOP-Aspekt-Ausführungsklasse MonitorLogInterceptor

    @Slf4j
    public class MonitorLogInterceptor extends MidExpandSpringMethodInterceptor<MonitorAspectAdviceProperties> {
       @Override
       public Object invoke(MethodInvocation methodInvocation) throws Throwable {
           Object result = null;
           HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
           //拿到请求的url
           String requestURI = request.getRequestURI();
           if (StringUtils.isEmpty(requestURI)) {
               return result;
           }
           try {
               result = methodInvocation.proceed();
           } catch (Exception e) {
               buildRecordData(methodInvocation, result, requestURI, e);
               throw e;
           }
           //参数数组
           buildRecordData(methodInvocation, result, requestURI, null);
           return result;

    Wir können sehen, dass sie MidExpandSpringMethodInterceptor8742468051c85b06f0a0af9e3e506b5cMidExpandSpringMethodInterceptor8742468051c85b06f0a0af9e3e506b5c

    @Slf4j
    public abstract class MidExpandSpringMethodInterceptor<T> implements MethodInterceptor {
        @Setter
        @Getter
        protected T properties;
        /**
         * 主动注册,生成AOP工厂类定义对象
         */
        protected String getExpression() {
            return null;
        }
        @SuppressWarnings({"unchecked"})
        public AbstractBeanDefinition doInitiativeRegister(Properties properties) {
            String expression = StringUtils.isNotBlank(this.getExpression()) ? this.getExpression() : properties.getProperty("expression");
            if (StringUtils.isBlank(expression)) {
                log.warn("中台SpringAop插件 " + this.getClass().getSimpleName() + " 缺少对应的配置文件 或者 是配置的拦截路径为空 导致初始化跳过");
                return null;
            }
            BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(AspectJExpressionPointcutAdvisor.class);
            this.setProperties((T) JsonUtil.toBean(JsonUtil.toJson(properties), getProxyClassT()));
            definition.addPropertyValue("advice", this);
            definition.addPropertyValue("expression", expression);
            return definition.getBeanDefinition();
        }
        /**
         * 获取代理类上的泛型T
         * 单泛型 不支持多泛型嵌套
         */
        private Class<?> getProxyClassT() {
            Type genericSuperclass = this.getClass().getGenericSuperclass();
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            return (Class<?>) parameterizedType.getActualTypeArguments()[0];
        }
    }

    而最终是实现了MethodInterceptor,这个接口是 方法拦截器,用于Spring AOP编程中的动态代理.实现该接口可以对需要增强的方法进行增强.

    我们注意到我的切面执行类并没有增加任何@Compont和@Service等将类注入到spring的bean中的方法,那他是怎么被注入到bean中的呢,因为使用了spi机制

    SPI机制的实现在项目的资源文件目录中,增加spring.factories文件,内容为

    com.dst.mid.common.expand.springaop.MidExpandSpringMethodInterceptor=
      com.dst.mid.monitor.intercept.MonitorLogInterceptor

    这样就可以在启动过程直接被注册,并且被放到spring容器中了。还有一个问题就是,切面执行类有了,切面在哪里呢。

    @Configuration
    @Slf4j
    @Import(MidExpandSpringAopAutoStarter.class)
    public class MidExpandSpringAopAutoStarter implements ImportBeanDefinitionRegistrar {
        private static final String BEAN_NAME_FORMAT = "%s%sAdvisor";
        private static final String OS = "os.name";
        private static final String WINDOWS = "WINDOWS";
        @SneakyThrows
        @SuppressWarnings({"rawtypes"})
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            // 1 获取MidExpandSpringMethodInterceptor类的所有实现集合
            List<MidExpandSpringMethodInterceptor> list = SpringFactoriesLoader.loadFactories(MidExpandSpringMethodInterceptor.class, null);
            if (!CollectionUtils.isEmpty(list)) {
                String expandPath;
                Properties properties;
                BeanDefinition beanDefinition;
                // 2 遍历类的所有实现集合
                for (MidExpandSpringMethodInterceptor item : list) {
                    // 3 获取资源文件名称 资源文件中存储需要加入配置的
                    expandPath = getExpandPath(item.getClass());
                    // 4 加载资源文件
                    properties = PropertiesLoaderUtils.loadAllProperties(expandPath + ".properties");
                    // 5 赋值beanDefinition为AspectJExpressionPointcutAdvisor
    
                    if (Objects.nonNull(beanDefinition = item.doInitiativeRegister(properties))) {
                        // 6 向容器中注册类  注意这个beanname是不存在的,但是他赋值beanDefinition为AspectJExpressionPointcutAdvisor是动态代理动态生成代理类所以不会报错
                        registry.registerBeanDefinition(String.format(BEAN_NAME_FORMAT, expandPath, item.getClass().getSimpleName()), beanDefinition);
                    }
                }
            }
        }
        /**
         * 获取资源文件名称
         */
        private static String getExpandPath(Class<?> clazz) {
            String[] split = clazz.getProtectionDomain().getCodeSource().getLocation().getPath().split("/");
            if (System.getProperty(OS).toUpperCase().contains(WINDOWS)) {
                return split[split.length - 3];
            } else {
                return String.join("-", Arrays.asList(split[split.length - 1].split("-")).subList(0, 4));
            }
        }
    }

    这个就是切面注册类的处理,首先实现了ImportBeanDefinitionRegistrar,实现他的registerBeanDefinitions方法可以将想要注册的类放入spring容器中,看下他的实现

    • 1 获取MidExpandSpringMethodInterceptor类的所有实现集合

    • 2 遍历类的所有实现集合

    • 3 获取资源文件名称 资源文件中存储需要加入配置的

    • 4 加载资源文件

    • 5 赋值beanDefinition为AspectJExpressionPointcutAdvisor

    • 6 向容器中注册类 注意这个beanname是不存在的,但是他赋值beanDefinition为AspectJExpressionPointcutAdvisor是动态代理动态生成代理类所以不会报错

    看到这里,还有一个问题ImportBeanDefinitionRegistrar实际上是将类注册到容器中,但是还需要一个步骤就是他要被容器扫描才行,以往的方式是项目中通过路径扫描,但是我们是插件,不能依赖于项目,而是通过自己的方式处理,这时候就需要用@Import(MidExpandSpringAopAutoStarter.class)rrreee

    und schließlich MethodInterceptor implementiert ist ein Methoden-Interceptor, der für dynamische Proxys in der Spring-AOP-Programmierung verwendet wird. Die Implementierung dieser Schnittstelle kann die Methoden verbessern, die verbessert werden müssen.

    Wir haben festgestellt, dass meine Aspektausführungsklasse keine @Component und @Service usw. hinzugefügt hat. Fügen Sie die Klasse ein In die Methode in der Spring Bean, wie wird sie in die Bean injiziert, da der SPI-Mechanismus verwendet wird🎜🎜Die Implementierung des SPI-Mechanismus befindet sich im Ressourcendateiverzeichnis des Projekts, fügen Sie die Datei spring.factories hinzu, der Inhalt ist 🎜
    🎜com.dst.mid.common.expand.springaop.MidExpandSpringMethodInterceptor=
    com.dst.mid.monitor.intercept.MonitorLogInterceptor🎜
    🎜Auf diese Weise kann es direkt während des Startvorgangs registriert werden und in den Federbehälter gelegt. Eine weitere Frage ist: Wo sind die Aspekte, nachdem die Aspektausführungsklasse verfügbar ist? 🎜rrreee🎜Dies ist die Verarbeitung von Aspektregistrierungsklassen. Zuerst wird ImportBeanDefinitionRegistrar implementiert, indem Sie die Methode registerBeanDefinitions implementieren Container. Schauen Sie sich seine Implementierung an 🎜
  • 🎜3 Rufen Sie den Namen der Ressourcendatei ab. Die Ressourcendatei speichert die Konfiguration, die hinzugefügt werden muss. 🎜
  • 🎜4 Laden Sie die Ressourcendatei 5 Weisen Sie die BeanDefinition AspectJExpressionPointcutAdvisor zu Es werden keine Fehler gemeldet🎜
  • 🎜🎜Angesichts dessen gibt es ein weiteres Problem.ImportBeanDefinitionRegistrar registriert die Klasse tatsächlich im Container, aber ein weiterer Schritt besteht darin, dass sie vom Container gescannt werden muss. Die vorherige Methode bestand darin, den Pfad im Projekt zu durchsuchen, aber wir sind Plug-Ins und können uns nicht auf das Projekt verlassen. Stattdessen müssen Sie es auf Ihre eigene Weise handhaben. Zu diesem Zeitpunkt müssen Sie @ verwenden. Import(MidExpandSpringAopAutoStarter.class) um damit umzugehen. 🎜🎜Durch die obige Verarbeitung wird die Verarbeitung des Überwachungs-Plug-Ins realisiert. Wenn Sie es verwenden, müssen Sie dieses Projekt nur noch in verschiedene Projekte einführen, die überwacht werden müssen. 🎜

    Das obige ist der detaillierte Inhalt vonSo entwickeln Sie ein Springboot-Plug-in. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen