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 结构:
我们会实现,一个 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 层级有了一定的理解