zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Spring Framework 源码学习笔记(五)

2023-06-13 09:11:16 时间

Chapter 05

Section 01 - @Value赋值的应用

entity包下新建一个实体了News,

public class News {
    private Integer id;
    private String content;

    public News() {
        System.out.println("无参构造方法被调用");
    }

    public News(Integer id, String content) {
        System.out.println("有参构造方法被调用");
        this.id = id;
        this.content = content;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "News{" +
                "id=" + id +
                ", content='" + content + ''' +
                '}';
    }
}

新建一个配置类BeanValueConfig

@Configuration
public class BeanValueConfig {

    @Bean
    public News news(){
        return new News();
    }
}

新建一个测试类BeanValueConfigTest

public class BeanValueConfigTest {

    @Test
    public void getBeanValued(){
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanValueConfig.class);
        System.out.println("IoC容器初始化完成");
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }

        News news = (News) context.getBean("news");
        System.out.println(news);
    }
}

执行该测试,控制台打印如下,News的属性都为空

使用@Value进行赋值,可以赋值基本数据类型或者Spring的EL表达式

修改News实体类属性部分代码如下,增加@Value注解,并添加值

@Value("1")
private Integer id;
@Value("某头部主播发布道歉信")
private String content;

执行BeanValueConfigTest测试,方法控制台打印如下

News对象创建完成之后两个属性都已经被赋值,但是这种情况数据和代码之间的耦合度比较高,因此可以将数据放在配置文件当中,实现数据和代码的解耦。在resources目录下新建config.properties配置文件

news.id=1
news.content=某头部主播各大平台账号被封

修改News实体类属性定义部分的代码

@Value("${news.id}")
private Integer id;
@Value("${news.content}")
private String content;

修改BeanValueConfig,加载配置文件,设置编码

@Configuration
@PropertySource(value = "classpath:config.properties", encoding = "utf-8")
public class BeanValueConfig {

    @Bean
    public News news(){
        return new News();
    }
}

执行测试,控制台输出如下,中文可以正常输出

如果不设置编码格式,中文会出现乱码

配置文件被加载后是放在环境变量中即org.springframework.core.env.Environment类中,可以通过容器获取environemnt对象,通过getProperty获取配置文件中配置项的值 修改测试方法,增加获取environment对象的代码

// 获取环境变量
Environment environment = context.getEnvironment();
System.out.println(environment);
System.out.println("new.content配置项的值为:" + environment.getProperty("news.content"));

执行测试,查看控制台打印的代码

Section 02 - @Autowired @Resource @Qualifier @Primary自动装配

项目中新建三个包dao,service,controller,分别增加三个类

@Repository
public class PersonDao {

}
@Service
public class PersonService {

    // 自动装配的Bean personDao
    @Autowired
    private PersonDao personDao;

    public PersonDao getBeanByAutowire(){
        return personDao;
    }
}
@Controller
public class PersonController {

    @Autowired
    private PersonService personService;
}

新增配置类BeanAutoAssembleConfig,将新建的三个包中的Bean注册到容器中

@ComponentScan(basePackages = {"com.citi.dao","com.citi.service","com.citi.controller"})
public class BeanAutoAssembleConfig {
    
}

新建测试类BeanAutoAssembleConfigTest,增加方法isSameBean()判断PersonService中使用@Autowire装配的PersonDao和从容器中获取的PersonDao对象是否为同一个对象,

public class BeanAutoAssembleConfitTest {

    @Test
    public void isSameBean(){
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanAutoAssembleConfig.class);
        System.out.println("IoC容器初始化完成");
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }

        PersonDao personDaoFromCon = (PersonDao) context.getBean("personDao");
        PersonService personService = (PersonService) context.getBean("personService");
        PersonDao personDaoFromAuto = personService.getBeanByAutowire();
        if (personDaoFromCon == personDaoFromAuto){
            System.out.println("@Autowire自动装配的Bean和从容器中获取的Bean是同一个Bean");
        } else {
            System.out.println("@Autowire自动装配的Bean和从容器中获取的Bean 不是 同一个Bean");
        }

    }
}

控制台打印如下,说明@Autowire装配的Bean和容器中的Bean是同一个Bean

在BeanAutoAssembleConfig中使用@Bean标签再注入一个PersonDao

@ComponentScan(basePackages = {"com.citi.dao","com.citi.service","com.citi.controller"})
public class BeanAutoAssembleConfig {

    @Bean("personDao2")
    public PersonDao personDao(){
        return new PersonDao();
    }
}

在测试方法中,用从容器中获取bean 的name为personDao2的Bean,与PersonService的@Autowire的Bean进行比较

public class BeanAutoAssembleConfitTest {

    @Test
    public void isSameBean(){
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanAutoAssembleConfig.class);
        System.out.println("IoC容器初始化完成");
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }

        PersonDao personDaoFromCon = (PersonDao) context.getBean("personDao2");
        PersonService personService = (PersonService) context.getBean("personService");
        PersonDao personDaoFromAuto = personService.getBeanByAutowire();
        System.out.println(personDaoFromCon);
        System.out.println(personDaoFromAuto);
        if (personDaoFromCon == personDaoFromAuto){
            System.out.println("@Autowire自动装配的Bean和从容器中获取的Bean是同一个Bean");
        } else {
            System.out.println("@Autowire自动装配的Bean和从容器中获取的Bean 不是 同一个Bean");
        }

    }
}

执行测试方法,控制台打印出不是同一个Bean,即persongDao2与personDao不是同一个Bean

@Autowire默认装配的Bean的类型装配的,如果需要指定的Bean进行自动装配则要使用@Qualifier("personDao2")指定Bean的name,修改PersonService类

@Service
public class PersonService {

    // 自动装配的Bean personDao
    @Qualifier("personDao2")
    @Autowired
    private PersonDao personDao;

    public PersonDao getBeanByAutowire(){
        return personDao;
    }
}

再次执行测试类,控制台打印出是同一个Bean

@Resource与@Autowire可以起到相同的作用,不同的是@Resource默认是按照Bean的name导入的 修改PersonService,使用@Resource注入,这里PersonDao的bean name为personDao2

@Service
public class PersonService {

    // 自动装配的Bean personDao
    //@Qualifier("personDao")
    //@Autowired
    @Resource
    private PersonDao personDao2;

    public PersonDao getBeanByAutowire(){
        return personDao2;
    }

}

执行测试类,控制台打印如下

因为测试方法中从容器中获取的是bean name为personDao2的Bean,与PersonService装配的Bean为同一个Bean,可以确定@Resource是按照Bean的name来装配的,@Resource不支持@Primary, 当容器中不存在Bean时,使用@Autowire注解可以声明request=false,这时不会报错,而@Resource不支持request=false

@Resource装配顺序:

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
  3. 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。

@Primary注解是作用在配置类上的,在注入Bean的方法上增加@Primary注解,当@Qualifier()与@Primary同时存在时,@Qulifier注解的功能不受影响 修改配置类如下,优先注入personDao2这个Bean

@ComponentScan(basePackages = {"com.citi.dao","com.citi.service","com.citi.controller"})
public class BeanAutoAssembleConfig {

    @Primary
    @Bean("personDao2")
    public PersonDao personDao(){
        return new PersonDao();
    }
}

PersonService类代码修改如下,指定装配的Bean name为personDao

@Service
public class PersonService {

    // 自动装配的Bean personDao
    @Qualifier("personDao")
    @Autowired
    //@Resource
    private PersonDao personDao2;

    public PersonDao getBeanByAutowire(){
        return personDao2;
    }

}

测试类代码如下,增加从容器中根据类型获取Bean的方法

public class BeanAutoAssembleConfitTest {

    @Test
    public void isSameBean(){
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanAutoAssembleConfig.class);
        System.out.println("IoC容器初始化完成");
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }

        PersonDao personDaoFromCon = (PersonDao) context.getBean("personDao2");
        PersonService personService = (PersonService) context.getBean("personService");
        PersonDao personDaoFromAuto = personService.getBeanByAutowire();
        System.out.println(personDaoFromCon);
        System.out.println(personDaoFromAuto);
        if (personDaoFromCon == personDaoFromAuto){
            System.out.println("@Autowire自动装配的Bean和从容器中获取的Bean是同一个Bean");
        } else {
            System.out.println("@Autowire自动装配的Bean和从容器中获取的Bean 不是 同一个Bean");
        }

         PersonDao bean = context.getBean(PersonDao.class);
         System.out.println(bean.getClass().getName());

    }
}

控制台正常打印,连个注解没有冲突@Qualifier执行装配的Bean的name

如果此时将@Primary注释,再次执行测试类,控制台报错

No qualifying bean of type 'com.citi.dao.PersonDao' available: expected single matching bean but found 2: personDao,personDao2,没有@Primary注解,根据类型获取Bean的时候就会报错,因为有两个Bean,容器并不知道你要的是哪一个

Section 03 - @Inject

使用@Inject注解要导入相应的maven依赖

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

将PersonService中的@Autowire注解注释,使用@Inject注解,注释掉测试方法中注释掉根据类型获取PersonDao对象的代码,然后执行测试,同样可以装配Bean

@Resource和@Inject都是JSR250规范,@Autowire是属于Spring的注解。