非Spring容器的外部化配置
简单来说,这类配置主要是我们在工作中常用的一些@Value相关配置注入。来看一段代码案例:
package org.idea.spring.bean.source.resource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.io.Resource; import java.io.IOException; import java.io.InputStream; /** * @Author linhao * @Date created in 11:28 上午 2021/5/3 */ @Configuration @PropertySource(value = "META-INF/default.properties",encoding = "UTF-8") public class ResourceInjectDemo { @Value("${user.id}") private long id; @Value("${user.nickname}") private String name; @Value("${user.resource:classpath://default.properties}") private Resource resource; public static void main(String[] args) throws IOException { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(ResourceInjectDemo.class); annotationConfigApplicationContext.refresh(); ResourceInjectDemo resourceInjectDemo = annotationConfigApplicationContext.getBean(ResourceInjectDemo.class); System.out.println(resourceInjectDemo.id); System.out.println(resourceInjectDemo.name); String fileName = resourceInjectDemo.resource.getFilename(); System.out.println(fileName); annotationConfigApplicationContext.close(); } } 复制代码
配置文件:
网络异常,图片无法展示
|
上边的这段代码比较简单,主要就是针对一些外部化的资源加载进行配置。
如果需要避免注入属性为空的情况,可以在配置的时候加入冒号,代码截图如下:
网络异常,图片无法展示
|
最后小结
关于本篇文章着重介绍了Spring内部的三种依赖来源渠道,这里我们抽取几道经典问题来进行分析探讨。
依赖注入和依赖查找来源是否相同
不相同,依赖查找的来源只局限于Spring BeanDefinition和手动注入的Sington对象,但是对于Resolvable Dependency对象以及@Value这类外部化配置的对象并不支持。
当Spring容器初始化之后还能注册bean吗
可以的,这里我贴一段自己实践出来的代码供大家参考:
package org.idea.spring.bean.source; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.idea.spring.bean.beandefinitionbuilder.User; /** * @Author linhao * @Date created in 4:44 下午 2021/5/3 */ public class AddBeanAfterRefreshDemo { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(AddBeanAfterRefreshDemo.class); annotationConfigApplicationContext.refresh(); try { User user0 = (User) annotationConfigApplicationContext.getBean("user"); System.out.println("user0 is "+user0); }catch (Exception b){ b.printStackTrace(); } System.out.println("启动后手动注入bean对象"); BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class); beanDefinitionBuilder.setScope("prototype"); beanDefinitionBuilder .addPropertyValue("id",2) .addPropertyValue("name","idea"); annotationConfigApplicationContext.registerBeanDefinition("user",beanDefinitionBuilder.getBeanDefinition()); User user1 = (User) annotationConfigApplicationContext.getBean("user"); User user2 = annotationConfigApplicationContext.getBean(User.class); BeanDefinition beanDefinition = annotationConfigApplicationContext.getBeanDefinition("user"); System.out.println(beanDefinition.getScope()); System.out.println(user1==user2); annotationConfigApplicationContext.close(); } } 复制代码
通过这段代码实践发现其实关于BeanDefinition定义的bean在容器进行初始化之后依旧可以动态注入,不过这里有个点需要注意下:
如果将案例代码稍微修改:
package org.idea.spring.bean.source; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.idea.spring.bean.beandefinitionbuilder.User; /** * @Author linhao * @Date created in 4:44 下午 2021/5/3 */ public class AddBeanAfterRefreshDemo { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(AddBeanAfterRefreshDemo.class); annotationConfigApplicationContext.refresh(); try { User user0 = (User) annotationConfigApplicationContext.getBean(User.class); System.out.println("user0 is "+user0); }catch (Exception b){ b.printStackTrace(); } System.out.println("启动后手动注入bean对象"); BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class); beanDefinitionBuilder.setScope("prototype"); beanDefinitionBuilder .addPropertyValue("id",2) .addPropertyValue("name","idea"); annotationConfigApplicationContext.registerBeanDefinition("user",beanDefinitionBuilder.getBeanDefinition()); User user1 = (User) annotationConfigApplicationContext.getBean(User.class); User user2 = annotationConfigApplicationContext.getBean(User.class); BeanDefinition beanDefinition = annotationConfigApplicationContext.getBeanDefinition("user"); System.out.println(beanDefinition.getScope()); System.out.println(user1==user2); annotationConfigApplicationContext.close(); } } 复制代码
修改后的代码就会在运行的时候抛出异常。这是因为在Spring容器底层会有一个Map专门记录不同的beanClass类型对应不同的beanName集合:
代码位于:
org.springframework.beans.factory.support.DefaultListableBeanFactory /** Map of singleton and non-singleton bean names, keyed by dependency type. */ private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64); 复制代码
如果第一次尝试通过类型获取Bean失败了的话,后续的多次尝试获取也会出现失败。
在实际工作中我们可能比较少会遇到需要在容器初始化之后再去手动注入Bean对象的情况。如果有需要的时候记得重复调用Bean对象的时候借助名称查询,而不是类型查找。