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

10. Spring 配置元信息

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

10. Spring 配置元信息

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 {
​
    
    AnnotationMetadata getMetadata();
​
    
    @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 元素属性默认值使用场景
profilenullSpring Profile 配置值
default-lazy-initdefault当 outter beans "default-lazy-init" 属性存在时,继承该值,否则为"false"
default-mergedeafult当 outter beans "default-merge" 属性存在时,继承该值,否则为"false"
default-autowiredefault当 outter beans "default-autowire" 属性存在时,继承该值,否则为"false"
default-autowire-candidatesnull默认 Spring Beans 名称 pattern
default-init-methodnull默认 Spring Beans 自定义初始化方法
default-destroy-methodnull默认 Spring Beans 自定义销毁方法

上面这些属性,都是在 XML 的 beans 标签中设置的

Spring XML 配置元信息——应用上下文相关

XML 元素使用场景
激活 Spring 注解驱动
Spring @Component 以及自定义注解扫描
激活 Spring LoadTimeWeaver
暴露 Spring Beans 作为 JMX Beans
将当前平台作为 MBeanServer
加载外部化配置资源作为 Spring 属性配置
利用外部化配置资源覆盖 Spring 属性值

可以在 BeanDefinitionParserDelegate 中查看其解析规则

10.5 基于 XML 文件装载 Spring Bean 配置元信息

Spring Bean 配置元信息

XML 元素使用场景
单 XML 资源下的多个 Spring Beans 配置
单个 Spring Bean 定义(BeanDefinition)配置
为 Spring Bean 定义(BeanDefinition)映射别名
加载外部 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  elements will cause recursion in this method. In
    // order to propagate and preserve  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 属性
$nn 表示第 n+1 个构造器参数

底层实现:PropertiesBeanDefinitionReader

不推荐使用,配置很复杂并且局限性很大

10.7 基于 Java 注解装载 Spring Bean 配置元信息

Spring 模式注解:

Spring 注解场景说明起始版本
@Repository数据仓储模式注解2.0
@Component通用组件模式注解2.5
@Service服务模式注解2.5
@ControllerWeb 控制器模式注解2.5
@Configuration配置类模式注解3.0

Spring Bean 依赖注入注解

Spring 注解场景说明起始版本
@AutowiredBean 依赖注入,支持多种依赖查找方式2.5
@Qualifier细粒度的 @Autowired 依赖查找2.5
Java 注解场景说明起始版本
@Resource类似于 @Autowired2.5
@Inject类似于 @Autowired2.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 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 元素 或 InitializingBean2.5
@PreDestroy替换 XML 元素 或 DisposableBean2.5

CommonAnnotationBeanPostProcessor 中处理这两个注解

10.8 Spring Bean 配置元信息底层实现

Spring BeanDefinition 解析与注册

实现场景实现类起始版本
XML 资源XmlBeanDefinitionReader1.0
Properties 资源PropertiesBeanDefinitionReader1.0
Java 注解AnnotatedBeanDefinitionReader3.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 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 元素 3.0
@Import导入 Configuration Class3.0
@ComponentScan扫描指定 package 下标注 Spring 模式注解的类3.1

@ImportResource 导入 xml 文件,@Import 导入 Java 类(配置类或者普通类)

10.10.2 Spring IoC 属性配置注解

Spring 注解场景说明起始版本
@PropertySource配置属性抽象 PropertySource3.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





    

    
        
        
        
    

    

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 测试



    
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
beansspring-beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
contextspring-contexthttp://www.springframework.org/schema/context/spring-context.xsd
aopspring-aophttp://www.springframework.org/schema/sop/spring-aop.xsd
txspring-txhttp://www.springframework.org/schema/tx/spring-tx.xsd
utilspring-beanshttp://www.springframework.org/schema/util/spring-util.xsd
toolspring-beanshttp://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

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

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

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