首頁 >Java >java教程 >Springboot外掛程式如何開發

Springboot外掛程式如何開發

王林
王林轉載
2023-05-16 09:31:051338瀏覽

    一背景

    專案新增監控系統,對各個系統進行監控介面呼叫情況,初期的時候是在各個專案公共引用的依賴包裡面新增aop切面來完成對各個系統的介面呼叫進行監控,但是這樣有缺點,一是不同項目的介面路徑不同,導致aop切面要寫多個切面路徑,二是一些不需要進行監控的系統,因為引入了公共包也被監控了,這樣侵入性就太強了。為了解決這個問題,就可以透過springboot的可插拔屬性了。

    二監控日誌外掛程式開發

    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程式設計中的動態代理.實作該介面可以對需要增強的方法進行增強.

    我們注意到我的切面執行類別並沒有增加任何@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)來處理了。

    透過以上處理就實現了監控插件的處理,然後再使用時,只需要將這個項目引入到不同需要監控的項目上就可以了。

    以上是Springboot外掛程式如何開發的詳細內容。更多資訊請關注PHP中文網其他相關文章!

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