ホームページ  >  記事  >  Java  >  Springbootプラグインの開発方法

Springbootプラグインの開発方法

王林
王林転載
2023-05-16 09:31:051306ブラウズ

    #1 背景

    プロジェクトでは、各システムのインターフェイス呼び出しを監視する監視システムを追加しました。初期の頃は、依存関係パッケージに基づいていました。各プロジェクトによって共通に参照されます。AOP アスペクトは、各システムのインターフェイス呼び出しを監視するために追加されます。ただし、これには欠点があります。まず、異なるプロジェクトのインターフェイス パスが異なるため、AOP アスペクトに対して複数のアスペクト パスを記述する必要があります。第 2 に、一部のシステムは監視する必要がありませんが、パブリック パッケージが導入されて監視されるため、煩雑になりすぎます。この問題を解決するには、springboot のプラグ可能属性を使用します。

    2 番目の監視ログ プラグインの開発

    1 新しい AOP アスペクト実行クラス 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;

    を作成します。MidExpandSpringMethodInterceptor8742468051c85b06f0a0af9e3e506b5c## が実装されていることがわかります。 #

    @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 プログラミングの動的プロキシに使用されるメソッド インターセプターです。このインターフェイスを実装すると、強化する必要があるメソッドを強化できます。

    私のアスペクト実行クラスは、Spring Bean にクラスを注入するための @Component や @Service などのメソッドを追加していないことに気付きました。spi メカニズムが使用されているため、Bean にどのように注入されるのでしょうか。

    SPI を実装するにはこのメカニズムでは、プロジェクトのリソース ファイル ディレクトリに spring.factories ファイルを

    com.dst.mid.common.expand.springaop.MidExpandSpringMethodInterceptor=\

    com.dst.mid という内容で追加します。 .monitor.intercept.MonitorLogInterceptor

    このようにして、起動プロセス中に直接登録し、Spring コンテナーに配置できます。もう 1 つの質問は、アスペクト実行クラスが利用できるようになったのですが、アスペクトはどこにあるのかということです。

    @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 のすべての実装コレクションを走査しますclass

    • 3 リソース ファイルの名前を取得します。リソース ファイルには、追加する必要がある構成が保存されます。

    • 4 リソースをロードします。ファイル

    • 5 beanDefinition の割り当て AspectJExpressionPointcutAdvisor

    • 6 のコンテナにクラスを登録します。この Bean 名は存在しませんが、beanDefinition が割り当てられることに注意してください。これは動的プロキシであり、プロキシ クラスを動的に生成するため、エラーは報告されません

    これを見ると、別の問題があります

    ImportBeanDefinitionRegistrar実際には、クラスをコンテナに追加しますが、もう 1 つの手順が必要です。つまり、コンテナによってスキャンする必要があります。前の方法は、プロジェクトはパスをスキャンしますが、プラグインであるためプロジェクトに依存できません。代わりに、現時点では、@Import(MidExpandSpringAopAutoStarter.class) を使用して処理する必要があります。

    以上の処理により監視プラグインの処理が実現するので、利用する際には監視が必要な他のプロジェクトにこのプロジェクトを導入するだけで済みます。

    以上がSpringbootプラグインの開発方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。