栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

搭建大型分布式服务(三十)面试官:工作3年了你还没写过SpringBoot Starter?

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

搭建大型分布式服务(三十)面试官:工作3年了你还没写过SpringBoot Starter?

系列文章目录

文章目录

系列文章目录前言

一、本文要点二、开发环境三、创建项目四、测试一下五、小结


前言

面试官总喜欢问:你写过SpringBoot Starter吗?

自己动手写一个SpringBoot,难吗?真的不难,网上随便一搜,各种教程文章一大堆。

那为什么面试官还是喜欢问呢?

其实我们可以换位思考一下,候选人工作好些年了,早已经度过了复制粘贴,到复制改写年龄,应当具备封装能力,甚至定制能力。假设你没有任何亮点,这么多CRUD工程师,我为什么要选你呢?

这里不详细讲SpringBoot Starter的原理,从面试官角度来看,你能回答出SpringBoot启动加载流程、类加载原理、bean对象生成、SpringBoot IoC容器托管Bean等,基本可以过关。但更多面试官会一步一步深挖,直到你回答不出为止。所以光靠临急抱佛脚,死记硬背肯定是不行的。

本文通过项目实战方式,加深一下我们的记忆。假定一个业务场景,项目需要在application.properties/application.yml 中自定义配置变量来获取ip,用来生成不同的日志文件名称。其中random是springboot内置的,myVar是我们拓展的。

my.log.prefix=monitor_${myVar.ip}
my.log.prefix=mopnitor_${myVar.yyyyMMddHHmmss}
my.log.prefix=monitor _${random.int(10)}

一、本文要点

前面的文章《搭建大型分布式服务(十九)面试官:你做过Spring框架功能拓展吗?》 已经介绍如何使用SpringBoot拓展,在yml/properties配置文件自定义变量。本文将这一功能封装成SpringBoot的starter,演示SpringBoot的starter的编写过程。系列文章完整目录

springboot 拓展springboot 自定义配置变量springboot application配置文件获取服务IPspringboot application配置文件获取当前时间 + 格式化EnvironmentPostProcessor 外置配置logback 读取springboot配置 二、开发环境

jdk 1.8maven 3.6.2springboot 2.4.3mybatis 1.3.0idea 2020 三、创建项目

1、创建一个基础maven工程,打包方式jar。



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.4.3
         
    
    com.mmc.lesson
    var-spring-boot-starter
    0.0.1-SNAPSHOT
    var-spring-boot-starter
    jar
    Demo project for Spring Boot
    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

        
            org.springframework.boot
            spring-boot-autoconfigure
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.8
                    1.8
                
            
            
                org.apache.maven.plugins
                maven-source-plugin
                
                    
                        attach-sources
                        verify
                        
                            jar-no-fork
                        
                    
                
            
        
    



2、在resources目录下创建meta-INF,新建factory文件,编写以下内容,使spring框架能感知到新增的SPI,配置如下。

org.springframework.boot.env.EnvironmentPostProcessor=
com.mmc.lesson.var.starter.MyValuePropertySourceEnvironmentPostProcessor

3、编写MyValuePropertySourceEnvironmentPostProcessor.java ,实现EnvironmentPostProcessor 、Order 接口,这样可以更高优先级在spring容器refresh前先实现我们自定义的配置的注入。

public class MyValuePropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {

    private Log logger;

    
    public MyValuePropertySourceEnvironmentPostProcessor() {
        logger = LogFactory.getLog(MyValuePropertySourceEnvironmentPostProcessor.class);
    }

    
    public MyValuePropertySourceEnvironmentPostProcessor(Log logger) {
        this.logger = logger;
    }

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

        MyValuePropertySource.addToEnvironment(environment, this.logger);
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 2;
    }
}

4、编写 MyValuePropertySource.java ,定义我们在application.yml / application.properties 的变量前缀 ${myVar} 。

public class MyValuePropertySource extends PropertySource {

    
    public static final String MY_PROPERTY_SOURCE_NAME = "myVar";

    private static final String PREFIX = "myVar.";

    private static final Log logger = LogFactory.getLog(MyValuePropertySource.class);

    
    public MyValuePropertySource() {
        this(MY_PROPERTY_SOURCE_NAME);
    }

    
    public MyValuePropertySource(String name) {
        super(name, new MyLogValue());
    }

    
    public static void addToEnvironment(ConfigurableEnvironment environment, Log logger) {

        MutablePropertySources sources = environment.getPropertySources();
        PropertySource existing = sources.get(MY_PROPERTY_SOURCE_NAME);
        if (existing != null) {
            logger.trace("RandomValuePropertySource already present");
            return;
        }
        MyValuePropertySource randomSource = new MyValuePropertySource(MY_PROPERTY_SOURCE_NAME);
        if (sources.get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME) != null) {
            sources.addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, randomSource);
        } else {
            sources.addLast(randomSource);
        }
        logger.trace("MyValuePropertySource add to Environment");
    }

    @Override
    public Object getProperty(String name) {
        if (!name.startsWith(PREFIX)) {
            return null;
        }
        logger.trace(LogMessage.format("Generating property for '%s'", name));
        return getValue(name.substring(PREFIX.length()));
    }

    
    private Object getValue(String type) {

        if (type.equalsIgnoreCase("ip")) {

            return getSource().getIp();
        }
        return null;
    }
}

5、编写MyLogValue.java ,实现 ${myVar.ip} 的取值逻辑。

@Data
public class MyLogValue {

    
    public String getIp() {

        return IpUtil.getLocalIP();
    }
}
class IpUtil {

    
    static String getLocalIP() {
        String sIP = "";
        InetAddress ip = null;
        try {
            // 如果是Windows操作系统
            if (isWindowsOS()) {
                ip = InetAddress.getLocalHost();
                // 如果是Linux操作系统
            } else {
                boolean bFindIP = false;
                Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
                while (netInterfaces.hasMoreElements()) {
                    if (bFindIP) {
                        break;
                    }
                    NetworkInterface ni = netInterfaces.nextElement();
                    if (ni.isLoopback() || ni.isVirtual() || !ni.isUp()) {
                        continue;
                    }
                    // ----------特定情况,可以考虑用ni.getName判断
                    // 遍历所有ip
                    Enumeration ips = ni.getInetAddresses();
                    while (ips.hasMoreElements()) {
                        ip = ips.nextElement();
                        if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() // 127.开头的都是lookback地址
                                && !ip.getHostAddress().contains(":")) {
                            bFindIP = true;
                            break;
                        }
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (null != ip) {
            sIP = ip.getHostAddress();
        }

        return sIP;
    }

    
    private static boolean isWindowsOS() {
        boolean isWindowsOS = false;
        String osName = System.getProperty("os.name");
        if (osName.toLowerCase().contains("windows")) {
            isWindowsOS = true;
        }
        return isWindowsOS;
    }
}

6、整体工程目录。

四、测试一下

1、修改 logback-spring.xml 配置。

    
    

 
        
        ${log.path}/${monitor.file.prefix}.log
        
        
            [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%level] [%logger{50}:%L] - %msg%n
            
            UTF-8
        
        
        
            
            ${log.path}/monitor/monitor-%d{yyyy-MM-dd}.%i.zip
            
                500MB
            
            
            7
            7GB
            true
        
    

2、重点来了,修改application.properties,增加我们自定义的变量myVar.ip。

# 日志路径
logging.file.path=./logs
# 日志名称前缀
my.log.prefix=monitor_${myVar.ip}

3、编写测试用例。

@SpringBootTest
class EnvDemoApplicationTests {

    @Value("${my.log.prefix}")
    private String prefix;

    @Test
    void contextLoads() {

        System.out.println("-----------------------------");
        System.out.println(prefix);
    }

}

4、这样就可以发布到仓库了。

# 打包
mvn clean install -Dmaven.test.skip=true
# 发布
mvn deploy -Dmaven.test.skip=true
五、小结

至此,我们就成功编写一个SpringBoot的Starter啦,本文作为下一个系列开篇起着非常重要的作用。下一篇《搭建大型分布式服务(三十一)基于JOLT实现业务型轻量级ETL工具》

加我加群一起交流学习!更多干货下载、项目源码和大厂内推等着你

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/776318.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号