Spring(5)-使用properties文件去获取BeanDefinition
本次我们使用properties文件在spring里面去读取Bean,当然这次不用我们手写BeanDefinitionReader,properties的解析器之前就有了
就是PropertiesBeanDefinitionReader,这个大佬就是去解析properties文件,从里面去创建GenericBeanDefinition,注册到spring工厂里面。
先看看这个Reader对properties文件格式的要求:
employee.(class)=MyClass // bean is of class MyClass employee.(abstract)=true // this bean can't be instantiated directly 如果是abstract的bean,不能直接被实例化 employee.group=Insurance // real property 真实属性 employee.usesDialUp=false // real property (potentially overridden) 真实属性(可能被覆盖) * 定义一个非抽象的bean,parent为抽象的employee, department属性为Sales salesrep.(parent)=employee // derives from "employee" bean definition 他的父类是employee salesrep.(lazy-init)=true // lazily initialize this singleton bean 懒加载初始化bean salesrep.manager(ref)=tony // reference to another bean 引用另外一个bean salesrep.department=Sales // real property 真实属性 techie.(parent)=employee // derives from "employee" bean definition techie.(scope)=prototype // bean is a prototype (not a shared instance) 这个bean是scope作用是多例,不共享 techie.manager(ref)=jeff // reference to another bean techie.department=Engineering // real property techie.usesDialUp=true // real property (overriding parent value) ceo.$0(ref)=secretary // inject 'secretary' bean as 0th constructor arg 注入ceo这个构造器的第一个参数 ceo.$1=1000000 // inject value '1000000' at 1st constructor arg 注入ceo这个构造器的第二个参数
看看BeanDefinitionReader这个接口,里面的代码
public interface BeanDefinitionReader { /** * Return the bean factory to register the bean definitions with. * <p>The factory is exposed through the BeanDefinitionRegistry interface, * encapsulating the methods that are relevant for bean definition handling. * * 获取beanDefinition的注册中心, * 为什么需要这个,因为读取到Bean definition后,需要存到这个里面去; * 如果不提供这个,我读了在哪放 */ BeanDefinitionRegistry getRegistry(); /** * @see org.springframework.core.io.support.ResourcePatternResolver * * 获取资源加载器 * 加载xml之类,当然作为一个接口,资源是可以来自于任何地方 */ @Nullable ResourceLoader getResourceLoader(); /** * 获取classloader */ @Nullable ClassLoader getBeanClassLoader(); /** * Return the BeanNameGenerator to use for anonymous beans * (without explicit bean name specified). * 获取bean名称生成器 */ BeanNameGenerator getBeanNameGenerator(); /** * 从指定的资源,加载bean definition * 从资源load bean definition */ int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; /** * 重载 */ int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException; /** * 重载 */ int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; /** * 重载 */ int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException; }
看下这个Reader,他就是使用指定的classloader,从指定的resource,读取,去注入对应BeanDefinition,
加载BeanDefinition
看下PropertiesBeanDefinitionReader的构造方法
/** * Create new PropertiesBeanDefinitionReader for the given bean factory. * @param registry the BeanFactory to load bean definitions into, * in the form of a BeanDefinitionRegistry * 调用父类,参数传入了bean definition 注册表调用父类,参数传入了bean definition 注册表 */ public PropertiesBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; // Determine ResourceLoader to use. if (this.registry instanceof ResourceLoader) { this.resourceLoader = (ResourceLoader) this.registry; } else { this.resourceLoader = new PathMatchingResourcePatternResolver(); } // Inherit Environment if possible if (this.registry instanceof EnvironmentCapable) { this.environment = ((EnvironmentCapable) this.registry).getEnvironment(); } else { this.environment = new StandardEnvironment(); } }
再看看loadBeanDefinitions是怎么实现的
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource), null); } public int loadBeanDefinitions(EncodedResource encodedResource, @Nullable String prefix) throws BeanDefinitionStoreException { if (logger.isTraceEnabled()) { logger.trace("Loading properties bean definitions from " + encodedResource); } //读取properties文件内容到props变量 Properties props = new Properties(); try { try (InputStream is = encodedResource.getResource().getInputStream()) { if (encodedResource.getEncoding() != null) { getPropertiesPersister().load(props, new InputStreamReader(is, encodedResource.getEncoding())); } else { getPropertiesPersister().load(props, is); } } //注册bean definition int count = registerBeanDefinitions(props, prefix, encodedResource.getResource().getDescription()); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + encodedResource); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException("Could not parse properties from " + encodedResource.getResource(), ex); } }
再看看registerBeanDefinitions()这个方法
public int registerBeanDefinitions(Map<?, ?> map, @Nullable String prefix, String resourceDescription) throws BeansException { if (prefix == null) { prefix = ""; } int beanCount = 0; for (Object key : map.keySet()) { if (!(key instanceof String)) { throw new IllegalArgumentException("Illegal key [" + key + "]: only Strings allowed"); } String keyString = (String) key; if (keyString.startsWith(prefix)) { // Key is of form: prefix<name>.property String nameAndProperty = keyString.substring(prefix.length()); // Find dot before property name, ignoring dots in property keys. int sepIdx ; int propKeyIdx = nameAndProperty.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX); if (propKeyIdx != -1) { sepIdx = nameAndProperty.lastIndexOf(SEPARATOR, propKeyIdx); } else { //用 . 做分割 sepIdx = nameAndProperty.lastIndexOf(SEPARATOR); } if (sepIdx != -1) { String beanName = nameAndProperty.substring(0, sepIdx); if (logger.isTraceEnabled()) { logger.trace("Found bean name '" + beanName + "'"); } if (!getRegistry().containsBeanDefinition(beanName)) { // If we haven't already registered it... // 如果之前没注册这个bean,则注册之,这里的prefix:prefix+beanName // ,其实就是从properties文件中筛选出beanName一致的key-value registerBeanDefinition(beanName, map, prefix + beanName, resourceDescription); ++beanCount; } } else { // Ignore it: It wasn't a valid bean name and property, // although it did start with the required prefix. if (logger.isDebugEnabled()) { logger.debug("Invalid bean name and property [" + nameAndProperty + "]"); } } } } return beanCount; }
再看看registerBeanDefinition()方法
这里面就是去校验properties文件里面那些 . () 包起来的值
主要是去遍历map,把property的key用.分割,前面的就是beanName,用beanName作为前缀
protected void registerBeanDefinition(String beanName, Map<?, ?> map, String prefix, String resourceDescription) throws BeansException { String className = null; String parent = null; String scope = BeanDefinition.SCOPE_SINGLETON; boolean isAbstract = false; boolean lazyInit = false; ConstructorArgumentValues cas = new ConstructorArgumentValues(); MutablePropertyValues pvs = new MutablePropertyValues(); String prefixWithSep = prefix + SEPARATOR; int beginIndex = prefixWithSep.length(); for (Map.Entry<?, ?> entry : map.entrySet()) { String key = StringUtils.trimWhitespace((String) entry.getKey()); if (key.startsWith(prefixWithSep)) { String property = key.substring(beginIndex); //核心属性,bean的ClassName if (CLASS_KEY.equals(property)) { className = StringUtils.trimWhitespace((String) entry.getValue()); } //parent属性 else if (PARENT_KEY.equals(property)) { parent = StringUtils.trimWhitespace((String) entry.getValue()); } //是否抽象bean definition else if (ABSTRACT_KEY.equals(property)) { String val = StringUtils.trimWhitespace((String) entry.getValue()); isAbstract = TRUE_VALUE.equals(val); } //scope else if (SCOPE_KEY.equals(property)) { // Spring 2.0 style scope = StringUtils.trimWhitespace((String) entry.getValue()); } else if (SINGLETON_KEY.equals(property)) { // Spring 1.2 style String val = StringUtils.trimWhitespace((String) entry.getValue()); scope = ("".equals(val) || TRUE_VALUE.equals(val) ? BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE); } else if (LAZY_INIT_KEY.equals(property)) { String val = StringUtils.trimWhitespace((String) entry.getValue()); lazyInit = TRUE_VALUE.equals(val); } ................
try {
//构造一个bean definition
AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(
parent, className, getBeanClassLoader());
bd.setScope(scope);
bd.setAbstract(isAbstract);
bd.setLazyInit(lazyInit);
//下面这两行,进行构造器注入和属性注入
bd.setConstructorArgumentValues(cas);
bd.setPropertyValues(pvs);
//注册
getRegistry().registerBeanDefinition(beanName, bd);
}
catch (ClassNotFoundException ex) {
throw new CannotLoadBeanClassException(resourceDescription, beanName, className, ex);
}
catch (LinkageError err) {
throw new CannotLoadBeanClassException(resourceDescription, beanName, className, err);
}
--看看createBeanDefinition这个方法,就是返回GenericBeanDefinition这个BeanDefinition的实现类
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
//parentName可能为空
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
//如果classLoader不为空,则使用以传入的classLoader
//同一虚拟机加载类对象,否则只是记录className
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
看下ProeprtiesBeanDefinitionReader里面定义的常量值
/** * Value of a T/F attribute that represents true. * Anything else represents false. Case seNsItive. */ public static final String TRUE_VALUE = "true"; /** * Separator between bean name and property name. * We follow normal Java conventions. */ public static final String SEPARATOR = "."; /** * Special key to distinguish {@code owner.(class)=com.myapp.MyClass}. */ public static final String CLASS_KEY = "(class)"; /** * Special key to distinguish {@code owner.(parent)=parentBeanName}. */ public static final String PARENT_KEY = "(parent)"; /** * Special key to distinguish {@code owner.(scope)=prototype}. * Default is "true". */ public static final String SCOPE_KEY = "(scope)"; /** * Special key to distinguish {@code owner.(singleton)=false}. * Default is "true". */ public static final String SINGLETON_KEY = "(singleton)"; /** * Special key to distinguish {@code owner.(abstract)=true} * Default is "false". */ public static final String ABSTRACT_KEY = "(abstract)"; /** * Special key to distinguish {@code owner.(lazy-init)=true} * Default is "false". */ public static final String LAZY_INIT_KEY = "(lazy-init)"; /** * Property suffix for references to other beans in the current * BeanFactory: e.g. {@code owner.dog(ref)=fido}. * Whether this is a reference to a singleton or a prototype * will depend on the definition of the target bean. */ public static final String REF_SUFFIX = "(ref)"; /** * Prefix before values referencing other beans. */ public static final String REF_PREFIX = "*"; /** * Prefix used to denote a constructor argument definition. */ public static final String CONSTRUCTOR_ARG_PREFIX = "$";
然后直接去,构造一个context继承AbstractRefreshableConfigApplicationContext,里面的loadBeanDefinitions这个方法,就行了,
public class PropertyContext extends AbstractRefreshableConfigApplicationContext { public PropertyContext() { } @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { PropertiesBeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(beanFactory); beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); String[] configResources = getConfigLocations(); beanDefinitionReader.loadBeanDefinitions(configResources); } public PropertyContext(ApplicationContext parent) { super(parent); } public PropertyContext(String configuration) { this(new String[]{configuration},true,null); } public PropertyContext(String[] configurations,Boolean refresh,@Nullable ApplicationContext parent) { super(parent); setConfigLocations(configurations); if (refresh){ refresh(); } } }
看看运行结果
但是呢,我刚才产生新的问题了,就是new PropertyContext的过程里,是在哪里去调用了loadBeanDefinitons这个方法啊,debug没找到,
这个疑问先放着,留着下次找到再来补下把
刚才百度,翻别人博客找到了,哈哈 https://blog.csdn.net/qq_41377914/article/details/107843774
1、第一步初始化用户使用的上下文类
{ public static void main( String[] args ) { PropertyContext propertyContext = new PropertyContext("beanDefinition.properties"); Map<String, Employee> result = propertyContext.getBeansOfType(Employee.class); System.out.println(result.size()); } }
2、在new的时候,会refresh一下,会调用抽象父类AbstractApplicationContext
public PropertyContext(String[] configurations,Boolean refresh,@Nullable ApplicationContext parent) { super(parent); setConfigLocations(configurations); if (refresh){ refresh(); } }
3、在refresh的抽象父类模板里,在创建beanFactory的时候,这个方法obtainFreshBeanFactory,方法里面去执行了loadBeanDefinitions方法
public void refresh() throws BeansException, IllegalStateException { synchronized(this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); this.prepareRefresh();
//就是这里 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); this.prepareBeanFactory(beanFactory); try { this.postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); this.invokeBeanFactoryPostProcessors(beanFactory); this.registerBeanPostProcessors(beanFactory); beanPostProcess.end(); this.initMessageSource(); this.initApplicationEventMulticaster(); this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var10) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10); } this.destroyBeans(); this.cancelRefresh(var10); throw var10; } finally { this.resetCommonCaches(); contextRefresh.end(); } } }
继续看这个创建DefaultListableBeanFactory里面的方法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { this.refreshBeanFactory(); return this.getBeanFactory(); } protected final void refreshBeanFactory() throws BeansException { if (this.hasBeanFactory()) { this.destroyBeans(); this.closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = this.createBeanFactory(); beanFactory.setSerializationId(this.getId()); this.customizeBeanFactory(beanFactory); this.loadBeanDefinitions(beanFactory); this.beanFactory = beanFactory; } catch (IOException var2) { throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2); } }
在refreshBeanFactory的过程中,执行了子类实现的loadBeanDefinitions方法。
原大佬博客地址:https://www.cnblogs.com/grey-wolf/p/12093929.html