美文网首页
每日一面 - Spring Boot 中的 Applicatio

每日一面 - Spring Boot 中的 Applicatio

作者: 干货满满张哈希 | 来源:发表于2021-01-21 00:48 被阅读0次

    ApplicationContext 是 spring 用来容纳管理 beans 以及其生命周期的容器。ApplicationContext 的分层规定了bean的界限以及可以复用的 bean。关于 ApplicationContext 层级可以参考官方文档(http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-build-an-application-context-hierarchy),这里我们通过一个简单的例子来说明下 ApplicationContext 层级以及其中的bean界限,例如某些 bean 可以被多个 ApplicationContext 共享,同时某些 bean 只在某个 ApplicationContext 生效,不同 ApplicationContext 可以声明同名或者同类型的bean这样。我们将实现一个下图所示的 ApplicationContext 结构:

    image

    我们会实现,一个 parent context 与三个对应 child context 的结构。

    首先定义Parent context:

    Bean类:

    package com.test.spring.context.bean;
    
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    public class RootBean {
        private Stirng name;
    }
    

    Context类:

    import com.hopegaming.scaffold.spring.context.bean.RootBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @PropertySource(value = "classpath:/root.yaml", factory = YamlPropertyLoaderFactory.class)
    public class RootContext {
        @Bean
        public RootBean getFatherBean() {
            RootBean rootBean = new RootBean();
            rootBean.setName("root");
            return rootBean;
        }
    }
    

    root.yml:

    # 配置这些主要是将actuator相关接口暴露出来。
    management:
      endpoint:
        health:
          show-details: always
      endpoints:
        jmx:
          exposure:
            exclude: '*'
        web:
          exposure:
            include: '*'
    

    由于我们使用了yml,这里需要我们自定义一个YamlPropertyLoaderFactory用于加载yml配置:

    package com.test.spring.context.config;
    
    import org.springframework.boot.env.YamlPropertySourceLoader;
    import org.springframework.core.env.PropertySource;
    import org.springframework.core.io.support.DefaultPropertySourceFactory;
    import org.springframework.core.io.support.EncodedResource;
    
    import java.io.IOException;
    
    public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
        @Override
        public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
            if (resource == null){
                return super.createPropertySource(name, resource);
            }
    
            return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()).get(0);
        }
    }
    

    定义child context的公共Bean类:

    package com.test.spring.context.bean;
    
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    public class ChildBean {
        private RootBean fatherBean;
        private String name;
    }
    

    定义ChildContext1:

    package com.test.spring.context.config.child1;
    
    import com.hopegaming.scaffold.spring.context.bean.ChildBean;
    import com.hopegaming.scaffold.spring.context.bean.RootBean;
    import com.hopegaming.scaffold.spring.context.config.YamlPropertyLoaderFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.PropertySource;
    
    @SpringBootApplication(scanBasePackages = {"com.test.spring.context.controller"})
    @PropertySource(value = "classpath:/bean-config-1.yaml", factory = YamlPropertyLoaderFactory.class)
    public class ChildContext1 {
        @Bean
        public ChildBean getChildBean(@Value("${spring.application.name}") String name, RootBean fatherBean) {
            ChildBean childBean = new ChildBean();
            childBean.setFatherBean(fatherBean);
            childBean.setName(name);
            return childBean;
        }
    }
    
    

    bean-config-1.yaml

    server:
      port: 8080
    spring:
      application:
        name: child1
    
    

    接下来分别是ChildContext2,ChildContext3的:

    package com.test.spring.context.config.child2;
    
    import com.hopegaming.scaffold.spring.context.bean.ChildBean;
    import com.hopegaming.scaffold.spring.context.bean.RootBean;
    import com.hopegaming.scaffold.spring.context.config.YamlPropertyLoaderFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.PropertySource;
    
    @SpringBootApplication(scanBasePackages = {"com.test.spring.context.controller"})
    @PropertySource(value = "classpath:/bean-config-2.yaml", factory = YamlPropertyLoaderFactory.class)
    public class ChildContext2 {
        @Bean
        public ChildBean getChildBean(@Value("${spring.application.name}") String name, RootBean fatherBean) {
            ChildBean childBean = new ChildBean();
            childBean.setFatherBean(fatherBean);
            childBean.setName(name);
            return childBean;
        }
    }
    
    server:
      port: 8081
    spring:
      application:
        name: child2
    
    management:
      endpoint:
        health:
          show-details: always
      endpoints:
        jmx:
          exposure:
            exclude: '*'
        web:
          exposure:
            include: '*'
    
    package com.test.spring.context.config.child3;
    
    import com.hopegaming.scaffold.spring.context.bean.ChildBean;
    import com.hopegaming.scaffold.spring.context.bean.RootBean;
    import com.hopegaming.scaffold.spring.context.config.YamlPropertyLoaderFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.PropertySource;
    
    @SpringBootApplication(scanBasePackages = {"com.test.spring.context.controller"})
    @PropertySource(value = "classpath:/bean-config-3.yaml", factory = YamlPropertyLoaderFactory.class)
    public class ChildContext3 {
        @Bean
        public ChildBean getChildBean(@Value("${spring.application.name}") String name, RootBean fatherBean) {
            ChildBean childBean = new ChildBean();
            childBean.setFatherBean(fatherBean);
            childBean.setName(name);
            return childBean;
        }
    }
    
    server:
      port: 8082
    spring:
      application:
        name: child3
    
    management:
      endpoint:
        health:
          show-details: always
      endpoints:
        jmx:
          exposure:
            exclude: '*'
        web:
          exposure:
            include: '*'
    

    测试接口TestController

    package com.test.spring.context.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Locale;
    
    @RestController
    public class TestController {
        @Autowired
        private ChildBean childBean;
    
        @RequestMapping("/test")
        public ChildBean getChildBean() {
            return childBean;
        }
    }
    

    启动类:

    package com.test.spring.context;
    
    import com.hopegaming.scaffold.spring.context.config.child1.ChildContext1;
    import com.hopegaming.scaffold.spring.context.config.child2.ChildContext2;
    import com.hopegaming.scaffold.spring.context.config.child3.ChildContext3;
    import com.hopegaming.scaffold.spring.context.config.root.RootContext;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.context.ConfigurableApplicationContext;
    
    public class ContextMain {
        public static void main(String[] args) {
            SpringApplicationBuilder appBuilder =
                    new SpringApplicationBuilder()
                            .sources(RootContext.class)
                            //第一个子context用child,剩下的都用sibling
                            .child(ChildContext1.class)
                            .sibling(ChildContext2.class)
                            .sibling(ChildContext3.class);
            ConfigurableApplicationContext applicationContext = appBuilder.run();
        }
    }
    
    

    启动后,访问http://127.0.0.1:8080/test返回:

    {"fatherBean":{"name":"root"},"name":"child1"}
    

    访问http://127.0.0.1:8081/test返回:

    {"fatherBean":{"name":"root"},"name":"child2"}
    

    访问http://127.0.0.1:8082/test返回:

    {"fatherBean":{"name":"root"},"name":"child3"}
    

    访问http://127.0.0.1:8080/actuator/beans会有类似于下面的返回(省略了不关心的bean):

    {
        "contexts": {
            "application-1": {
                "beans": {
                    "getChildBean": {
                        "aliases": [],
                        "scope": "singleton",
                        "type": "com.hopegaming.scaffold.spring.context.bean.ChildBean",
                        "resource": "com.hopegaming.scaffold.spring.context.config.child2.ChildContext2",
                        "dependencies": [
                            "getFatherBean"
                        ]
                    },
                    "childContext2": {
                        "aliases": [],
                        "scope": "singleton",
                        "type": "com.hopegaming.scaffold.spring.context.config.child2.ChildContext2$$EnhancerBySpringCGLIB$$26f80b15",
                        "resource": null,
                        "dependencies": []
                    }
                    .......
                },
                "parentId": "application"
            },
            "application": {
                "beans": {
                    "getFatherBean": {
                        "aliases": [],
                        "scope": "singleton",
                        "type": "com.hopegaming.scaffold.spring.context.bean.RootBean",
                        "resource": "com.hopegaming.scaffold.spring.context.config.root.RootContext",
                        "dependencies": []
                    },
                    "rootContext": {
                        "aliases": [],
                        "scope": "singleton",
                        "type": "com.hopegaming.scaffold.spring.context.config.root.RootContext$$EnhancerBySpringCGLIB$$18d9c26f",
                        "resource": null,
                        "dependencies": []
                    }
                    .......
                },
                "parentId": null
            }
        }
    }
    

    通过这个例子,想必大家对于 ApplicationContext 层级有了一定的理解

    相关文章

      网友评论

          本文标题:每日一面 - Spring Boot 中的 Applicatio

          本文链接:https://www.haomeiwen.com/subject/igtrzktx.html