容器的核心抽象类:
功能类:
注:MutablePropertySources内维护着一个CopyOnWriteArrayList< PropertySource < ? > >类型的属性源列表
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext =
new ClassPathXmlApplicationContext("config/Bootstrap_${env}.xml");
}
ClassPathXmlApplicationContext 是基于类路径下的XML配置的容器,容器的启动是通过new ClassPathXmlApplicationContext(…)创建的,下面分步概述主要步骤:
注:所有的步骤都发生在ClassPathXmlApplicationContext 的构造器内
注:从步骤 2 之后,便都属于AbstractApplicationContext.refresh()方法的逻辑,AbstractApplicationContext是一个高度的抽象类。refresh()该方法是一个模板方法,是容器刷新的骨架,后续讲 Spring Boot 容器启动时,也会使用它,逻辑大同小异
3 prepareRefresh(),容器刷新前的准备工作,设置一些Ioc容器状态标识,比如closed、active等状态字段
4 obtainFreshBeanFactory(),创建DefaultListableBeanFactory,加载(load)并注册(register)BeanDefinition
5 prepareBeanFactory(ConfigurableListableBeanFactory):我们说过ApplicationContext添加了许多企业级功能,前面已经完成了BeanFactory的创建和配置文件的解析与注册,而ApplicationContext的拓展也由此展开。
6 postProcessBeanFactory(ConfigurableListableBeanFactory),此时所有beanDefjnition都已加载,但还没有实例化任何bean。该方法默认无代码,具体的逻辑交给特定的ApplicationContext实现类去完成。
7 invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory),调用上下文中注册为的BeanFactoryPostProcessor。因为此时还没有开始实例化任何Bean,Spring允许BeanFactoryPostProcessor在容器实例化任何Bean之前读取配置元数据,并且可以修改它。这也是为什么Spring可以根据< context:property-placeholder location=“xxxx.properties”/>中的配置信息,替换掉BeanDefinition中的 ${xxx} 模样的值。
8 registerBeanPostProcessors(ConfigurableListableBeanFactory),注册BeanPostProcessor,这里只是注册
9 初始化国际化,即i18n
10 初始化Application中的事件多播器,即applicationEventMulticaster【ApplicationEventMulticaster】
11 onRefresh(),该方法默认无代码,但是在启动Web类型的容器时,很重要,Servlet的启动就是在该方法中实现的
12 finishBeanFactoryInitialization(ConfigurableListableBeanFactory),完成这个上下文的BeanFactory的初始化,其中包括对ConversionService的设置、BeanDefinition冻结以及实例化所有非延迟的单例Bean,因为Bean实例化涉及的太多,会单独总结!
12 finishRefresh(),通过调用LifecycleProcessor的onRefresh()和发布ContextRefreshedEvent事件,完成对ApplicationContext的刷新工作。
注:BeanDefinitionRegistry继承自AliasRegistry,因此他除了可以注册BeanDefinition之外,还可以注册Alias与BeanName之间的映射关系。
默认元素< bean >的解析分三步:
NamespaceHandlerResolver是如何根据命名空间地址,解析出其对应的NamespaceHander的呢?
我们来看Aop命名空间对应的AopNamespaceHandler是如何注册的:
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
// 保存 Label -> BeanDefinitionParser的映射,用于自定义元素的解析
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
// 报存 Label -> BeanDefinitionDecorator的映射
// decorators用于自定义元素的装饰,比如scoped-proxy
// attributeDecorators用于自定义属性的装饰
private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();
private final Map<String, BeanDefinitionDecorator> attributeDecorators = new HashMap<>();
}
## 命名空间地址=自定义NamespaceHandler的全限定类名
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
## xsd公网地址=xsd文件目录路径
https\://www.springframework.org/schema/aop/spring-aop.xsd=org/springframework/aop/config/spring-aop.xsd
Spring的NamesapceHandlerResolver其实也是扫描所有META-INF下的spring.handlers文件,并将其中的配置提取出来,入缓存。这样当给定命名空间地址时,别可以快速的返回其对应的NamespaceHandler了~~
并且,解析Element的工作也并不是由NamespaceHandler来做的,当我们调用parse(Element element, ParserContext parserContext)方法进行元素解析时,它底层通过NamespaceHandlerSupport 中的缓存Map,根据Element的Tag,找到适合的BeanDefinitionParser来对其进行解析。
1 createApplicationContext():创建SpplicationmContext
2 prepareContext(…):加载Bean
3 refreshContext(context):
4 afterRefresh(context, applicationArguments):默认无实现
@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
@java.lang.annotation.Inherited
@org.springframework.boot.SpringBootConfiguration
@org.springframework.boot.autoconfigure.EnableAutoConfiguration
@org.springframework.context.annotation.ComponentScan(excludeFilters = {@org.springframework.context.annotation.ComponentScan.Filter(type = org.springframework.context.annotation.FilterType.CUSTOM, classes = {org.springframework.boot.context.TypeExcludeFilter.class}), @org.springframework.context.annotation.ComponentScan.Filter(type = org.springframework.context.annotation.FilterType.CUSTOM, classes = {org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter.class})})
public @interface SpringBootApplication {
@org.springframework.core.annotation.AliasFor(annotation = org.springframework.boot.autoconfigure.EnableAutoConfiguration.class)
java.lang.Class<?>[] exclude() default {};
@org.springframework.core.annotation.AliasFor(annotation = org.springframework.boot.autoconfigure.EnableAutoConfiguration.class)
java.lang.String[] excludeName() default {};
@org.springframework.core.annotation.AliasFor(annotation = org.springframework.context.annotation.ComponentScan.class, attribute = "basePackages")
java.lang.String[] scanBasePackages() default {};
@org.springframework.core.annotation.AliasFor(annotation = org.springframework.context.annotation.ComponentScan.class, attribute = "basePackageClasses")
java.lang.Class<?>[] scanBasePackageClasses() default {};
}
自动化配置的根源,即是@EnableAutoConfiguration注解
当Spring中开启了注解以及component-scan,那么会注册一个很重要的BeanFactoryPostProcessor:
当BeanFactory加载并注册完BeanDefinition后,便会调用所有的BeanFactoryPostprocessor,BeanFactoryPostprocessor共有两个比较重要的接口:
ConfigurationClassPostProcessor是一个BeanDefinitionRegistryPostProcessor,Spring会先执行postProcessBeanDefinitionRegistry(…),然后执行postProcessBeanFactory(…)方法。
主要看postProcessBeanDefinitionRegistry(…),该方法根据注册表中的由@Configuration注解标准的BeanDefinition,进而派生出更多的BeanDefinition。
Bean的加载主要是通过BeanFactory的getBean(Name)来展开的,核心的步骤总结如下:
4.1 beforeSingletonCreation(beanName):创建Bean实例之前,先将BeanName插入singletonsCurrentlyInCreation【Set< BeanName>】缓存中,表示该Bean处于创建中了
4.2 createBean(BeanName,BeanDefinition,Args):Bean实例化以及依赖注入
4.3 afterSingletonCreation(BeanName):创建Bean实例之后,再将BeanName移出singletonsCurrentlyInCreation【Set< BeanName>】缓存中,以示该Bean创建完成
4.4 addSingleton(BeanName,Bean):它将实例化后的单例对象注册到了singletonObjects【ConcurrentHashMap
在大多数应用程序场景中,容器中的大多数Bean都是单例的。当一个单例Bean需要与另一个单例Bean协作,或者一个非单例Bean需要与另一个非单例Bean协作时,通常通过将一个bean定义为另一个Bean的属性来处理这种依赖性。但是当Bean的生命周期不同时,就会出现问题,比如:单Bbean A需要使用非单例(原型)Bean B。容器只创建一次单例Bean,因此只得到一次设置属性的机会。容器不能每次需要Bean B的新实例时都向bean A提供一个。
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// 方法的签名规则:
// [abstract] theMethodName(no-arguments);
@Lookup("command") // 注解形式,value的值,是Command的BeanName
protected abstract Command createCommand();
}
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
bean>
public class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
public Command createCommand(){
//
};
}
实现替换器
public class CommandReplacer implements MethodReplacer implements ApplicationContextAware{
private ApplicationContext applicationContext;
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
return this.applicationContext.getBean("command", Command.class);;
}
}
xml配置
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<replaced-method name="createCommand" replacer="commandReplacer">
replaced-method>
bean>
<bean id="commandReplacer" class="a.b.c.CommandReplacer"/>
Spring IoC容器不仅管理对象(bean)的实例化,还支持协作者(或依赖项)的连接。如果您想将(例如)一个HTTP Request-Scope的Bean注入到另一个更长的作用域的Bean中,您可以选择注入一个AOP代理来代替Request-Scope的bean。也就是说,您需要注入一个代理对象,它与作用域对象公开相同的公共接口,但也可以从相关作用域(如HTTP请求)检索实际目标对象,并将方法调用委托给实际对象。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
<aop:scoped-proxy/>
bean>
<bean id="userService" class="com.something.SimpleUserService">
<property name="userPreferences" ref="userPreferences"/>
bean>
beans>
public class SimpleMovieLister {
// 将对实际对象的依赖,转换为对Provider的依赖,ObjectFactory的使用与之类似
private Provider<MovieFinder> movieFinder;
@Inject
public void setMovieFinder(Provider<MovieFinder> movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.get().findMovies(...);
// ...
}
}
在定义使用静态工厂方法创建的Bean时,使用class属性指定包含静态工厂方法的类,使用factory-method属性指定工厂方法本身的名称。您应该能够调用这个方法(带有可选参数,如后面所述)并返回一个活动对象,该对象被视为与通过构造函数创建对象是一样的。这种bean定义的一个用途是在遗留代码中调用静态工厂。
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
与通过静态工厂方法实例化类似,使用实例工厂方法实例化将从容器中调用现有bean的非静态方法来创建新bean。要使用这种机制,将class属性保留为空,并在factory-bean属性中指定当前(或父或父)容器中bean的名称,该容器包含要调用来创建对象的实例方法。使用factory-method属性设置工厂方法本身的名称。下面的例子展示了如何配置这样一个bean:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
一个工厂类也可以包含多个工厂方法哟哟