zoukankan      html  css  js  c++  java
  • 和spring cloud/boot 学习如何管理自己的组件

    案例,

    功能:

    需要写一个往kafka上报数据的组建。

    当组建启动时,需要建立如下资源:

    1, 和kafka建立若干条连接

    2, 启动一个线程池

    3, 启动上报一个缓冲区

    问题如下:

    1, 如何在spring工程中引入该组件,并注入到spring容器中

    2, 如间接被引用到此JAR包(如 引用的工程有引用到此组建JAR),或只是想用到里面数据类型,并不打算用功能时,如何避免资源会随着引入而自行启动造成资源浪费

    3, 组建的配置如何统一管理问题

    4, 如何管理众多JAR包依赖,如, 此组建开发要用到kafka的0.11.0.2,有天需要升级到1.0.0

    这些问题其实在spring cloud中都有比较好的解决方案,如 zuul, 后面也是仿造zuul的解决

    一, 如何使该组建被spring工程引入

    SPI方式,

    如 zuul 的通过

    META-INFspring.factories 

    进行引入 ,指定引导目录

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration,
    org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration

    故 组建也定义如下

    META-INFspring.factories 

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    com.zhenai.security.report.SecurityAutoConfiguration

    在SecurityAutoConfiguration中,根据需要对bean进行初始化,和相关资源的启动。如 启动连接,启动本地线程池等。

    但这里的问题是,只要引入了这个JAR包(包括间接引入该JAR包),那么所有工程都会平白无故的去连kafka,去启动一些无用的线程池

    二, 如何屏蔽间接被引用到此JAR包的工程启动相关资源

    解决这个问题,spring cloud和spring boot还稍有不同,先看spring cloud.

    Marker方式

    spring cloud标签模式

    先看zuul是怎么做的, 如要在工程里启动ZUUL,一般会在main类里加入@EnableZuulProxy 标签,如下:

    @SpringBootApplication
    @EnableEurekaClient
    @EnableZuulProxy
    public class Application {
    ...  ....
    }

    需要引入@EnableZuulProxy 标签

    @EnableZuulProxy的源码如下 :
    
    @EnableCircuitBreaker
    @EnableDiscoveryClient
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(ZuulProxyMarkerConfiguration.class)
    public @interface EnableZuulProxy {
    }

    看到 @Import(ZuulProxyMarkerConfiguration.class), ZuulProxyMarkerConfiguration只做了一件事,引入一个maker标签

    如下:

    @Configuration
    public class ZuulProxyMarkerConfiguration {
        @Bean
        public Marker zuulProxyMarkerBean() {
            return new Marker();
        }
    
        class Marker {
        }
    }

    这个Maker对象用作是否启动启用该配置,从而控制了资源是否启动,如 ZuulServerAutoConfiguration

    @Configuration
    @EnableConfigurationProperties({ ZuulProperties.class })
    @ConditionalOnClass(ZuulServlet.class)
    @ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
    // Make sure to get the ServerProperties from the same place as a normal web app would
    @Import(ServerPropertiesAutoConfiguration.class)
    public class ZuulServerAutoConfiguration {
    ...  ...
    }

    根据是否有Marker进行相关类的注入,是否启动。

    故:

    此处,案例中的组件也选用了这种方式,如

    @EnableZASecurityReport 标签

    当需要启动时,在main类里加入标签即可,如

    @EnableZASecurityReport
    public class Application {
    
    ...  ....
    
    }

    后续kafka的连接类,线程池,缓冲区等是否分配都可以根据相关标识进行管理,如spi入口类SecurityAutoConfiguration :

    @Configuration
    @EnableConfigurationProperties({ SecurityReportProperties.class })
    @ConditionalOnBean(SecurityProxyMarkerConfiguration.Marker.class)
    public class SecurityAutoConfiguration {
    ...  ... 
    }

    这样,如果在main启动类中,只要未加入@EnableZASecurityReport,那么即使引入了组件的JAR包,相关资源也不会被启动。

    starter方式

    还有一种方式,即,spring boot用的比较多的start方式

    spring boot的所有配置都在spring-boot-autoconfigure/META-INF/spring.factories里,通过@ConditionalOnBean特定类是否引入来判断是否启动资源。

    如: spring-boot-starter-data-redis

    首先通过spring.factories,引入Redis的引导类

    org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,
    org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,
    org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,

    如:

    RedisAutoConfiguration

    @Configuration
    @ConditionalOnClass({ RedisOperations.class })
    @EnableConfigurationProperties(RedisProperties.class)
    @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
    public class RedisAutoConfiguration {
     ... ...
    }

    当工程需要用到Redis时,通过Maven引入相关类

    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-redis</artifactId>  
    </dependency>

    spring-boot-starter-data-redis, 其实是一个空项目,有个spring.provides,通过它引入redis相关的JAR包,然后使@ConditionalOnClass生效,从而完成对Redis的JAR环境的初始化。

    三, 配置如何统一管理问题

    一个组件(JAR包)出来后,配置会比较多,比如该项目涉及到kafka配置,线程池配置等一大堆,传统方式是去写个相关说明文档,一堆配置项会让使用起来很是麻烦。

    1, 约定优于配置

    在spring cloud/boot中,最让人好用的就是 约定优于配置。记住约定,少写配置

    如在zuul中, 只需要配置几个必须项,其它都是约定项

    如,约定配置文件即application.yml( 或 bootstrap.yml):

    zuul:
     debug:
      routes:
        ZHENAI-CLIENT:
          path: /test/**

    简单配置下routes就可以启动,如果要找到zuul的配置的约定值,可以直接寻找总配置类ZuulProperties,

    ZuulProperties里,包含了所有配置项,并通过配置对象的方式进行模块话的划分如:

    ZuulRoute相关,Host相关,HystrixSemaphore相关等

    (也是一种默认约定)

    故,

    在组件中,也可以模仿简化下配置。 此组件核心功能就是上报,比配项目应该只是kafka的地址,要启用,只需要

    report.kafkaConfig.servers=X.X.X.X:9092

    即可,若要详细配置,和约定值,用一个统一配置文件管 ReportProperties.java

    里面注明约定配置的值

    2,运用spring的自动装配功能

    运用@ConfigurationProperties标签进行自动装配。这个所有基本功能不细说。

    详细可查看ZuulProperties里。

    好处在于:

    1, 可以实现动态配置,如 配置 map,list,甚至enums等

    2,如果配合spring cloud config,可以实现动态热更新

    四,统一管理JAR包的依赖

    参考spring cloud/boot 里,JAR文件统一在spring-boot-dependencies的项目里单独管理,而版本间的兼容,依靠了开源项目http://platform.spring.io/platform/ 来做管理,故很少存在版本冲突。

    作为自研的组件,最好依赖到的第三方jar都由spring boot去同理管理版本号,而需要用到的其它jar,可用建立个dependencies项目单独管理起来,不再自己工程能写版本号,方便统一升级维护。

    总结下:

    1,  如何给spring /spring boot 项目提供组件会比较好

    用SPI方式,方便平滑引用

    2,如何避免不需要用到组件的项目误引用JAR后,自动启动组件相关资源

    1, 提供@EnableXXX标签模式,注入一个marker标签,在启动时通过@ConditionalOnBean来判断

    2,starter方式,配置与类分开,@ConditionalOnBean来判断,同时引用时才启动会

    3,组件的配置如何统一管理

    1, 约定大于配置,简化配置。 为每个组件统一一个组件的XXXProperties.java,并提供约定值

    2,自动装配模式

    4, 如何统一管理JAR包,防止JAR版本冲突等

    交给spring boot统一管理,其它版本号统一在父工程(或加入dependencies工程) 管理版本

    加个广告,新的一年,打算把公众号维护起来,质量做起来。

    欢迎关注下,谢谢

  • 相关阅读:
    2020软件工程个人作业06——软件工程实践总结作业
    git上传文件夹内容
    git常用命令(部分)
    java命令行输入参数
    2020软件工程作业05
    软件工程——问题清单
    2020软件工程作业04
    2020软件工程作业03
    2020软件工程作业02
    问题清单
  • 原文地址:https://www.cnblogs.com/springsource/p/8440986.html
Copyright © 2011-2022 走看看