10.1 Spring 配置元信息
-
Spring Bean 配置元信息——BeanDefinition
-
Spring Bean 属性元信息——PropertyValues
-
Spring 容器配置元信息
-
Spring 外部化配置元信息+PropertySource
-
Spring Profile 元信息——@Profile
10.2 Spring Bean 配置元信息
-
GenericBeanDefinition:通用型 BeanDefinition
-
RootBeanDefinition:无 Parent 的 BeanDefinition 或者合并后的 BeanDefinition
-
AnnotatedBeanDefinition:注解标注的 BeanDefinition
GenericBeanDefinition 和 RootBeanDefinition 都继承了 AbstractBeanDefinition,AbstractBeanDefinition 提供了对于 Definiton 的写操作,GenericBeanDefinition 简单重写了 setParent 方法,RootBeanDefinition 则增加了许多字段(IoC 中 BeanDefitnition 通常需要 merge,merge 后的 BeanDefinition 就是 RootBeanDefinition,也叫做 mbd),这些字段用于创建 Bean 时提供一些辅助性操作(提升性能等)
DefaultListableBeanFactory 中存储所有 BeanDefinition,AbstractBeanDefinition 中存储所有 mergedBeanDefinition。
public interface AnnotatedBeanDefinition extends BeanDefinition { /** * Obtain the annotation metadata (as well as basic class metadata) * for this bean definition's bean class. * @return the annotation metadata object (never {@code null}) */ AnnotationMetadata getMetadata(); /** * Obtain metadata for this bean definition's factory method, if any. * @return the factory method metadata, or {@code null} if none * @since 4.1.1 */ @Nullable MethodMetadata getFactoryMethodMetadata(); }
AnnotatedBeanDefinition 可以通过反射或者 ASM 方式获取 AnnotationMetadata,Spring 5.2 及以后 ASM 方式标记过时,新增 SimpleAnnotationMetadataReadingVisitor 类使用
getFactoryMethodMetadata 元数据是否来自某个方法,比如 Factory 中方法,可有可无,因为并不是每个 Bean 都是通过 Factory method 来生成的
10.3 Spring Bean 属性元信息
-
Bean 属性元信息——PropertyValues
-
可修改实现——MutablePropertyValues
-
元素成员——PropertyValue
-
-
Bean 属性上下文存储——AttributeAccessor
-
Bean 元信息元素——BeanMetadataElement
属性元信息中后两个属性上下文存储以及 Bean 元信息元素两种元信息都是起辅助作用的,上下文信息可以在创建 Bean 的过程中进行使用,元信息元素可以标记 BeanDefinition 的来源,具体使用可参照下例:
public class BeanConfigurationMetadataDemo { public static void main(String[] args) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class); builder.addPropertyValue("name", "bar"); // 获取 AbstractBeanDefinition AbstractBeanDefinition definition = builder.getBeanDefinition(); // 附加属性(不影响 Bean populate、initialize) definition.setAttribute("name", "foo"); // 当前 BeanDefinition 来自哪里,辅助作用 definition.setSource(BeanConfigurationMetadataDemo.class); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.registerBeanDefinition("user", definition); factory.addBeanPostProcessor(new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof User && beanName.equals("user")) { User user = (User) bean; BeanDefinition beanDefinition = factory.getBeanDefinition("user"); if (beanDefinition.getSource() == BeanConfigurationMetadataDemo.class) { String name = (String) beanDefinition.getAttribute("name"); user.setName(name); return user; } } return bean; } }); System.out.println(factory.getBean("user", User.class)); } } // 运行结果: User{name='foo', age=0}
10.4 Spring 容器配置元信息
Spring XML 配置元信息——beans 元素相关
beans 元素属性 | 默认值 | 使用场景 |
---|---|---|
profile | null | Spring Profile 配置值 |
default-lazy-init | default | 当 outter beans "default-lazy-init" 属性存在时,继承该值,否则为"false" |
default-merge | deafult | 当 outter beans "default-merge" 属性存在时,继承该值,否则为"false" |
default-autowire | default | 当 outter beans "default-autowire" 属性存在时,继承该值,否则为"false" |
default-autowire-candidates | null | 默认 Spring Beans 名称 pattern |
default-init-method | null | 默认 Spring Beans 自定义初始化方法 |
default-destroy-method | null | 默认 Spring Beans 自定义销毁方法 |
上面这些属性,都是在 XML 的 beans 标签中设置的
Spring XML 配置元信息——应用上下文相关
XML 元素 | 使用场景 |
---|---|
<context:annotation-config/> | 激活 Spring 注解驱动 |
<context:component-scan/> | Spring @Component 以及自定义注解扫描 |
<context:load-time-weaver/> | 激活 Spring LoadTimeWeaver |
<context:mbean-export/> | 暴露 Spring Beans 作为 JMX Beans |
<context:mbean-server/> | 将当前平台作为 MBeanServer |
<context:property-placeholder/> | 加载外部化配置资源作为 Spring 属性配置 |
<context:property-override/> | 利用外部化配置资源覆盖 Spring 属性值 |
可以在 BeanDefinitionParserDelegate 中查看其解析规则
10.5 基于 XML 文件装载 Spring Bean 配置元信息
Spring Bean 配置元信息
XML 元素 | 使用场景 |
---|---|
<beans:beans> | 单 XML 资源下的多个 Spring Beans 配置 |
<beans:bean> | 单个 Spring Bean 定义(BeanDefinition)配置 |
<beans:alias> | 为 Spring Bean 定义(BeanDefinition)映射别名 |
<beans:import> | 加载外部 Spring XML 资源配置 |
底层实现:XmlBeanDefinitionReader
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { // ... // 从输入流注册 BeanDefinition return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); // ... } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { // ... // 获取 DOM 操作需要的 Document 元素 Document doc = doLoadDocument(inputSource, resource); // 执行注册 int count = registerBeanDefinitions(doc, resource); // ... } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 创建 DocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 保存现有 BeanDefinition 数量 int countBefore = getRegistry().getBeanDefinitionCount(); // 注册 BeanDefinitions documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 返回注册 BeanDefinition 数量 return getRegistry().getBeanDefinitionCount() - countBefore; } @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); } protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { // 获取设置的 profiles 跟 环境变量中 profile 进行比较,相同才往后执行,不同就注解返回不解析 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); // 真正解析的地方 parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; } // XML -> BeanDefinition protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } // Spring 默认解析的元素 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } } // 自定义 XML 解析 @Nullable public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); }
10.6 基于 Properties 文件装载 Spring Bean 配置元信息(不推荐)
Spring Bean 配置元信息
Properties 属性名 | 使用场景 |
---|---|
(class) | Bean 类全限定名 |
(abstract) | 是否为抽象的 BeanDefinition |
(parent) | 指定 parent BeanDefinition 名称 |
(lazy-init) | 是否为延迟初始化 |
(ref) | 引用其他 Bean 的名称 |
(scope) | 设置 Bean 的 scope 属性 |
$n | n 表示第 n+1 个构造器参数 |
底层实现:PropertiesBeanDefinitionReader
不推荐使用,配置很复杂并且局限性很大
10.7 基于 Java 注解装载 Spring Bean 配置元信息
Spring 模式注解:
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@Repository | 数据仓储模式注解 | 2.0 |
@Component | 通用组件模式注解 | 2.5 |
@Service | 服务模式注解 | 2.5 |
@Controller | Web 控制器模式注解 | 2.5 |
@Configuration | 配置类模式注解 | 3.0 |
Spring Bean 依赖注入注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@Autowired | Bean 依赖注入,支持多种依赖查找方式 | 2.5 |
@Qualifier | 细粒度的 @Autowired 依赖查找 | 2.5 |
Java 注解 | 场景说明 | 起始版本 |
---|---|---|
@Resource | 类似于 @Autowired | 2.5 |
@Inject | 类似于 @Autowired | 2.5 |
AutowiredAnnotationBeanPostProcessor 处理 @Autowired、@Value、@Inject 注解
CommonAnnotationBeanPostProcessor 处理 @Resource 注解,javax.xml.ws.WebServiceRef注解、javax.ejb.EJB,主要处理 j2se 或者 Java EE 相关的注解或者 API
Spring Bean 条件装配注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@Profile | 配置化条件装配 | 3.1 |
@Conditional | 编程条件装配 | 4.0 |
@Profile 通过 @Conditional 实现
实现 Condition 接口
class ProfileCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 读取注解所有的信息 MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { for (Object value : attrs.get("value")) { if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) { return true; } } return false; } return true; } }
Spring Bean 生命周期回调注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@PostConstructor | 替换 XML 元素 <bean init-method="..."/> 或 InitializingBean | 2.5 |
@PreDestroy | 替换 XML 元素 <bean destroy-method="..."/> 或 DisposableBean | 2.5 |
CommonAnnotationBeanPostProcessor 中处理这两个注解
10.8 Spring Bean 配置元信息底层实现
Spring BeanDefinition 解析与注册
实现场景 | 实现类 | 起始版本 |
---|---|---|
XML 资源 | XmlBeanDefinitionReader | 1.0 |
Properties 资源 | PropertiesBeanDefinitionReader | 1.0 |
Java 注解 | AnnotatedBeanDefinitionReader | 3.0 |
Spring 注解驱动主要开始于 Spring 2.5 版本
Java 注解和类相关,并不和资源相关,因此 AnnotatedBeanDefinitionReader 和 BeanDefinitionReader 这个接口是没有关系的,因为 BeanDenitionReader 是基于资源(Resource)的,XmlBeanDefinitionReader 和 PropertiesBeanDefinitionReader 都属于 BeanDenitionReader 实现
10.8.1 Spring XML 资源 BeanDefinition 解析与注册
核心 API——XmlBeanDefinitionReader
-
资源——Resource
-
底层——BeanDefinitionDocumentReader
-
XML 解析——Java DOM Level 3 API
-
BeanDefinition 解析——BeanDefinitionParserDelegate
-
BeanDefinition 注册——BeanDefinitionRegistrar
-
10.8.2 Spring Properties 资源 BeanDefinition 解析与注册
核心 API——PropertiesBeanDefinitionReader
-
资源
-
字节流——Resource
-
字符流——EncodedResource
-
-
底层
-
存储——java.util.Properties
-
BeanDefinition 解析——API 内部实现
-
BeanDefinition 注册——BeanDefinitionRegistry
-
10.8.3 Spring Java 注册 BeanDefinition 解析与注册
核心 API——AnnotatedBeanDefinitionReader
-
资源——类对象 java.lang.Class
-
底层
-
条件评估——ConditionEvaluattor
-
Bean 范围解析——ScopeMetadataResolver
-
BeanDefinition 解析——内部 API 实现
-
BeanDefinition 处理——AnnotationConfigUtils.processCommonDefinitionAnnotations
-
BeanDefinition 注册——BeanDefinitionRegistry
-
public class AnnotatedBeanDefinitionReader { // Bean 注册容器 private final BeanDefinitionRegistry registry; // Bean 名称生成器 private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE; // Scope 解析器,是否单例以及代理模式 private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); // 评估器,评估是否需要跳过注册 private ConditionEvaluator conditionEvaluator; // ... }
10.9 基于 XML 文件装载 Spring IoC 容器配置元信息
spring.handlers 文件中配置解析命名空间的 handler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
spring.schema 文件中配置命名空间对应的 xsd 文件
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
handler 继承 NamespaceHandlerSupport,重写 init 方法,init 方法内部注册具体的解析类
public class UtilNamespaceHandler extends NamespaceHandlerSupport { private static final String SCOPE_ATTRIBUTE = "scope"; @Override public void init() { // 注册解析 list、map 等的 parser 类 registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser()); registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser()); registerBeanDefinitionParser("list", new ListBeanDefinitionParser()); registerBeanDefinitionParser("set", new SetBeanDefinitionParser()); registerBeanDefinitionParser("map", new MapBeanDefinitionParser()); registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser()); } // ... }
解析类继承 AbstractSingleBeanDefinitionParser,重写 doParse 方法
private static class ListBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return ListFactoryBean.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { List<Object> parsedList = parserContext.getDelegate().parseListElement(element, builder.getRawBeanDefinition()); builder.addPropertyValue("sourceList", parsedList); String listClass = element.getAttribute("list-class"); if (StringUtils.hasText(listClass)) { builder.addPropertyValue("targetListClass", listClass); } String scope = element.getAttribute(SCOPE_ATTRIBUTE); if (StringUtils.hasLength(scope)) { builder.setScope(scope); } } }
此处解析完成,BeanDefinitionBuilder 属性也设置完成,返回 AbstractBeanDefinitionParser#parse 方法将结果封装成 BeanDefinitionHolder 进行注册。整体流程如下:
XmlBeanDefinitionReader#loadBeanDefinition(Resource) -> doLoadBeanDefinitions() -> doLoadDocument() -> registerBeanDefinitions() -> DefaultBeanDefinitionDocumentReader#registerBeanDefinitions() -> doRegisterBeanDefinitions() -> parseBeanDefinitions() -> BeanDefinitionParserDelegate#parseCustomElement() -> 根据 namespaceUri 获取 handler -> NamespaceHandlerSupport#parse -> 查找 parser -> BeanDefinitionParser#parse()(此处将parseInternal 解析完成的 BeanDefinition 封装成 BeanDefinitionHolder 进行注册) -> parseInternal() -> doParse()(子类重写的方法,解析逻辑在此实现)
parseCustonElement 流程:
-
获取 namespace
-
通过 namespace 获取 handler
-
构造 ParserContext
-
解析元素,获取 BeanDefinition
10.10 基于 Java 注解装载 Spring IoC 容器配置元信息
10.10.1 Spring IoC 容器装配注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@ImportResource | 替换 XML 元素 <import> | 3.0 |
@Import | 导入 Configuration Class | 3.0 |
@ComponentScan | 扫描指定 package 下标注 Spring 模式注解的类 | 3.1 |
@ImportResource 导入 xml 文件,@Import 导入 Java 类(配置类或者普通类)
10.10.2 Spring IoC 属性配置注解
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@PropertySource | 配置属性抽象 PropertySource | 3.1 |
@PropertySources | @PropertySource 集合注解 | 4.0 |
从 Java 8 开始,一个类上支持同时使用多个 @PropertySource 注解
10.11 基于 Extensible XML authoring 扩展原理
扩展步骤:
-
编写 XML Schema 文件:定义 XML 结构
-
自定义 NamespaceHandler 实现:命名空间绑定
-
自定义 BeanDefinitionParser 实现:XML 元素与 BeanDefinition 解析
-
注册 XML 扩展:命名空间与 XML Schema 映射
10.11.1 定义 xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://gcl.com/schema/users" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://gcl.com/schema/users"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> <xsd:complexType name="User"> <xsd:attribute name="id" type="xsd:long" use="required"/> <xsd:attribute name="name" type="xsd:string" use="required"/> <xsd:attribute name="age" type="xsd:int"/> </xsd:complexType> <xsd:element name="user" type="User"/> </xsd:schema>
10.11.2 定义 handler
public class CustomHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("user", new CustomParser()); } }
http\://gcl.com/schema/users=com.gcl.configuration.metadata.handler.CustomHandler
10.11.3 定义 parser
public class CustomParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return User.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { setAttribute("name", element, builder); setAttribute("age", element, builder); } private void setAttribute(String attributeName, Element element, BeanDefinitionBuilder builder) { String attribute = element.getAttribute(attributeName); if (StringUtils.hasText(attribute)) { builder.addPropertyValue(attributeName, attribute); } } }
10.11.4 注册 xml 扩展
http\://gcl.com/schema/users.xsd=META-INF/com/gcl/configuration/metadata/users.xsd
10.11.5 测试
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:users="http://gcl.com/schema/users" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://gcl.com/schema/users http://gcl.com/schema/users.xsd" > <users:user id="1" name="foo" age="18"/> </beans>
public class CustomXmlParserDemo { public static void main(String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions("classpath:META-INF/users.xml"); System.out.println(factory.getBean(User.class)); } } // 运行结果: User{name='foo', age=18}
10.12 基于 Properties 资源装载外部化配置
-
注解驱动
-
@org.springframework.context.annotation.PropertySource
-
@org.springframework.context.annotation.PropertySources
-
-
API 编程
-
org.springframework.core.env.PropertySource
-
org.springframework.core.env.PropertySources
-
Java 8 支持重复注解功能,也就是支持一个类上重复使用相同的注解,因此可以使用多个 @PropertySource 替换 @PropertySources 注解
10.13 基于 YAML 资源装载外部化配置
API 编程
-
org.springframework.beans.factory.config.YamlProcessor
-
org.springframework.beans.factory.config.YamlMapFactoryBean
-
org.springframework.beans.factory.config.YamlPropertiesFactoryBean
-
10.14 面试题
10.14.1 Spring 内建 XML Schema 常见有哪些
命名空间 | 所属模块 | Schema 资源 URL |
---|---|---|
beans | spring-beans | http://www.springframework.org/schema/beans/spring-beans.xsd |
context | spring-context | http://www.springframework.org/schema/context/spring-context.xsd |
aop | spring-aop | http://www.springframework.org/schema/sop/spring-aop.xsd |
tx | spring-tx | http://www.springframework.org/schema/tx/spring-tx.xsd |
util | spring-beans | http://www.springframework.org/schema/util/spring-util.xsd |
tool | spring-beans | http://www.springframework.org/schema/tool/spring-tool.xsd |
10.14.2 Spring 配置元信息具体有哪些
-
Bean 配置元信息:通过媒介(如 XML、Properties等),解析 BeanDefinition
-
IoC 容器配置元信息:通过媒介(如:XML、Properties等),控制 IoC 容器行为,比如注解驱动、AOP等
-
外部化配置:通过资源抽象(如:Properties、YAML等),控制 PropertySource
-
Spring Profile:通过外部化配置,提供条件分支流程
10.14.3 Extensible XML authoring 的缺点
-
高复杂度:开发人员要熟悉 XML Schema,spring.handlers、spring.schemas 以及 Spring API
-
嵌套元素支持较弱:通常需要使用方法递归或者其嵌套解析的方式处理嵌套元素
-
XML 处理性能较差:Spring XML 基于 DOM Level 3 API 实现,该 API 便于理解,然而性能较差
-
XML 框架移植性较差:很难适配高性能和便利性的 XML 框架,如 JAXB