深入挖掘Spring系列 -- 依赖的来源(下)

简介: 深入挖掘Spring系列 -- 依赖的来源(下)

非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对象的时候借助名称查询,而不是类型查找。

目录
相关文章
|
2月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
40 4
|
4月前
|
缓存 Java 开发者
Spring循环依赖问题之Spring循环依赖如何解决
Spring循环依赖问题之Spring循环依赖如何解决
|
4月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
4月前
|
Java Spring
Spring循环依赖问题之构造器内的循环依赖如何解决
Spring循环依赖问题之构造器内的循环依赖如何解决
|
3月前
|
前端开发 Java 测试技术
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
|
4月前
|
JavaScript Java Maven
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
1899 1
|
4月前
|
Java Spring 容器
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
|
4月前
|
存储 缓存 Java
Spring循环依赖问题之循环依赖异常如何解决
Spring循环依赖问题之循环依赖异常如何解决
|
4月前
|
XML Java 数据格式
循环依赖问题之创建Bean的过程中发生异常,Spring会如何处理
循环依赖问题之创建Bean的过程中发生异常,Spring会如何处理
|
4月前
|
Java Maven Spring
Spring Boot中的依赖管理策略
Spring Boot中的依赖管理策略