后端:Spring、Spring Boot-实例化Bean&依赖注入(DI)

文章目录

    • [1. 实例化Bean](#1. 实例化Bean)
    • [2. 使用FactoryBean](#2. 使用FactoryBean)
    • [3. 依赖注入(DI)](#3. 依赖注入(DI))
      • [3.1 @AutoWired 属性注入(查找顺序:先类型,后名字)](#3.1 @AutoWired 属性注入(查找顺序:先类型,后名字))
      • [3.2 @AutoWired 在构造函数&参数上的使用](#3.2 @AutoWired 在构造函数&参数上的使用)
      • [3.3 @Inject和@Resource 进行依赖注入](#3.3 @Inject和@Resource 进行依赖注入)
      • [3.4 @Value 进行注入](#3.4 @Value 进行注入)

1. 实例化Bean

默认使用无参构造函数,如果在这个Bean下定义了一个有参的构造方法(没有写无参构造方法),实例化时使用的是这个有参构造方法;如果有多个有参的构造方法(没有写无参构造方法),此时实例化时会报错,因为不知道使用哪个构造方法。

java 复制代码
package com.lize.demo.dao;


import com.lize.demo.TestBean;
import com.lize.demo.TestBean2;
import org.springframework.stereotype.Component;

@Component("UserDao")
public class UserDao {

    private TestBean tb;
    private TestBean2 tb2;
//    public UserDao(){
//        System.out.println("构造函数");
//    }

    public UserDao(TestBean tb){
        System.out.println("有参的构造函数"+tb);
        this.tb = tb;
    }

    public UserDao(TestBean tb,TestBean2 tb2){
        System.out.println("有参的构造函数"+tb+tb2);
        this.tb = tb;
        this.tb2 = tb2;
    }

    public void printUserDao(){

        System.out.println("UserDao");
    }

}
java 复制代码
package com.lize.demo;

import com.lize.demo.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

    @Autowired
    private UserDao ud;

    @Test
    void contextLoads() {

        ud.printUserDao();
    }

}

报错信息如下:

此时如果要实例化有参的Bean,可以使用注解@Bean的方式来进行,如下:

java 复制代码
package com.lize.demo.dao;


import com.lize.demo.TestBean;
import com.lize.demo.TestBean2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

public class UserDao {
    private TestBean tb;
    private TestBean2 tb2;

    public UserDao(TestBean tb){
        System.out.println("有参的构造函数"+tb);
        this.tb = tb;
    }

    public UserDao(TestBean tb,TestBean2 tb2){
        System.out.println("有参的构造函数"+tb+tb2);
        this.tb = tb;
        this.tb2 = tb2;
    }

    public void printUserDao(){

        System.out.println("UserDao");
    }

}
java 复制代码
package com.lize.demo.config;


import com.lize.demo.TestBean;
import com.lize.demo.TestBean2;
import com.lize.demo.dao.UserDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;

@Configuration
public class SpringConnfig {
    @Bean
    public UserDao getUserDao(TestBean tb,TestBean2 tb2){
        return new UserDao(tb,tb2);
    }
}

运行结果如下:

2. 使用FactoryBean

定义一个类,让其实现FactoryBean这个接口,并重写其下方法,如下:

java 复制代码
package com.lize.demo.service;


import com.lize.demo.TestBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Service;


@Service("UserService")
public class UserService implements FactoryBean {

    @Override
    public Object getObject() throws Exception {
        return new TestBean();
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}
java 复制代码
package com.lize.demo;

import com.lize.demo.dao.UserDao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);

        System.out.println(context.getBean("UserService"));
    }

}

此时的打印结果如下:

上述打印结果为TestBean,而不是UserService这个Bean。如果要获取UserService这个Bean,可以通过类型获取,如下:

还有一种做法就是在第一种的基础上,通过字符串获取Bean,字符串前面加上"&"符号,如下:

如果想通过类型获取TestBean这个Bean,可以在getObjectType方法下添加对应的类型信息,如下:

运行结果:

总结一下:

使用FactoryBean来实例化Bean。

  • FactoryBean是一个接口;
  • 需要有一个Bean,一旦这个Bean实现FactoryBean就成为了特殊的Bean;
  • 需要实现两个方法
    • getObject,当通过Bean实际名获取到的Bean就是getObject返回的对象(伪装);
    • getObjectType,想通过获取对应的类型去获取这个伪装的Bean,就需要返回getObject返回的对象的类型;
  • 可以自由控制Bean的构造方法来实例化Bean

3. 依赖注入(DI)

3.1 @AutoWired 属性注入(查找顺序:先类型,后名字)

使用这个注解,首先会通过类型去容器中查找是否有这个Bean,如果没有,再通过名字去查找是否有这个Bean。

直接在类上添加注解@Component定义Bean,名字为testBean3

java 复制代码
package com.lize.demo;


import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
//@Primary
public class TestBean3 {
    
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "TestBean3{" +
                "name='" + name + '\'' +
                '}';
    }
}

使用配置类定义Bean,名字为:TestBean31

java 复制代码
package com.lize.demo.config;


import com.lize.demo.TestBean3;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConnfig {

    @Bean
    public TestBean3 TestBean31(){
        return new TestBean3();
    }
}

上述定义了两个类型相同的Bean。

java 复制代码
package com.lize.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class TestBean4 {

    @Autowired
    private TestBean3 testBean3;

    @Override
    public String toString() {
        return "TestBean4{" +
                "testBean3=" + testBean3 +
                '}';
    }
}

在TestBean4 中引入这个Bean,然后在单元测试中输出结果如下:

java 复制代码
package com.lize.demo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

    @Autowired
    private TestBean4 tb4;

    @Test
    void contextLoads() {

        System.out.println(tb4);
    }
}

运行结果如下:

可以看到,此时因为有两个Bean类型相同,因此采用名字去查找Bean,在TestBean4中使用的Bean名字为testBean3,因此输出的结果中的Bean为直接在类上添加注解@Component的那个Bean(name的值默认为空)。如果把TestBean4中的那个Bean的名字修改为TestBean31,那么此时的输出结果就是通过配置类定义的那个Bean了。

java 复制代码
package com.lize.demo.config;


import com.lize.demo.TestBean3;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConnfig {

    @Bean
    public TestBean3 TestBean31(){
        TestBean3 testBean3 = new TestBean3();
        testBean3.setName("TestBean31");
        return testBean3;
    }

}
java 复制代码
package com.lize.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class TestBean4 {

    @Autowired
    private TestBean3 TestBean31;

    @Override
    public String toString() {
        return "TestBean4{" +
                "testBean3=" + TestBean31 +
                '}';
    }
}

如果通过名字还是查找不到,比如把TestBean4中的引入那个Bean的名字修改为tb3,那么此时就会报错了。

此时可以在定义Bean的那个类上添加注解 @Primary,表示主要的。

另外一种解决方法就是在这个TestBean4引入的那个Bean下指明到底是哪个Bean(使用注解 @Qualifier),如下:

3.2 @AutoWired 在构造函数&参数上的使用

如果一个Bean定义了多个有参的构造函数,但是没有定义默认的构造函数(无参构造函数),此时在另外一个类中引入这个Bean,然后在单元测试中输出这个Bean,会报错,如下:

java 复制代码
package com.lize.demo.dao;


import com.lize.demo.TestBean;
import com.lize.demo.TestBean2;
import org.springframework.stereotype.Component;

@Component("UserDao")
public class UserDao {

    private TestBean tb;

    private TestBean2 tb2;
//    public UserDao(){
//        System.out.println("构造函数");
//    }

    public UserDao(TestBean tb){
        System.out.println("有参的构造函数"+tb);
        this.tb = tb;
    }

    public UserDao(TestBean tb,TestBean2 tb2){
        System.out.println("有参的构造函数"+tb+tb2);
        this.tb = tb;
        this.tb2 = tb2;
    }

    public void printUserDao(){

        System.out.println("UserDao");
    }

}

如果此时想要正常输出,可以在对应的构造函数上面添加注解@AutoWired ,如下,此时正常输出。

TestBean、TestBean2如下形式:

如果想要为构造函数中的参数设置为不必须的,需要在参数上面设置 @Autowired(required = false),直接在构造函数上设置是不生效的,因此会报错(下面没有给出),如下:

此时打印结果为null。

另外,还可以写在单元测试的方法上面,如下:

Spring会自动调用@Autowired的方法进行自动注入,在没有调用set的方法的前提下,此时调用get的结果不为null,如下:

java 复制代码
package com.lize.demo.dao;


import com.lize.demo.TestBean;
import com.lize.demo.TestBean2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("UserDao")
public class UserDao {

    private TestBean tb;

    private TestBean2 tb2;
//    public UserDao(){
//        System.out.println("构造函数");
//    }

    @Autowired
    public UserDao(@Autowired(required = false) TestBean tb){
        System.out.println("有参的构造函数"+tb);
        this.tb = tb;
    }

    @Autowired
    public void setTb2(TestBean2 tb2){
        this.tb2 = tb2;
    }

    public TestBean2 getTb2(){
        return tb2;
    }

    public UserDao(TestBean tb,TestBean2 tb2){
        System.out.println("有参的构造函数"+tb+tb2);
        this.tb = tb;
        this.tb2 = tb2;
    }

    public void printUserDao(){

        System.out.println("UserDao");
    }

}

3.3 @Inject和@Resource 进行依赖注入

@Resource优先根据名字进行查找,找不到再根据类型查找。
@inject不能设置required=false属性,另外还需要添加额外的依赖。
推荐使用构造函数进行注入,或者@Resource进行注入

3.4 @Value 进行注入

基本数据类型的注入

java 复制代码
package com.lize.demo.entity;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class User {
    @Value("lize")
    private String name;
    @Value("19")
    private Integer age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
java 复制代码
package com.lize.demo;

import com.lize.demo.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

    @Test
    void contextLoads(@Autowired User user) {

        System.out.println(user);
    }

}

如果想通过从文件中的数据进行注入,如下,新建a.properties

在Spring Boot项目中,如果想获取配置文件application.properties中的数据,不需要使用@PropertySource指定路径文件,如下:

如果在数据文件获取不到对应数据,在Spring Boot项目中会报错(解决方法为在变量名后面加入":"填写默认值),但是在Spring中会指定把值直接注入到对应变量。

复杂数据类型的注入,使用spel表达式的方式进行注入,如下:

java 复制代码
package com.lize.demo.entity;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

@Component
public class User {

    @Value("#{{'语文':'98'}}")
    private Map<String,String> score;
    @Value("#{'王者,原神'}")
    private List<String> like_games;

    @Override
    public String toString() {
        return "User{" +
                "score=" + score +
                ", like_games=" + like_games +
                '}';
    }
}
相关推荐
所待.3834 分钟前
JavaEE之线程初阶(上)
java·java-ee
Winston Wood8 分钟前
Java线程池详解
java·线程池·多线程·性能
手握风云-12 分钟前
数据结构(Java版)第二期:包装类和泛型
java·开发语言·数据结构
许苑向上15 分钟前
Dubbo集成SpringBoot实现远程服务调用
spring boot·后端·dubbo
喵叔哟32 分钟前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构
尘浮生38 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
郑祎亦1 小时前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis
不是二师兄的八戒1 小时前
本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
java·docker·php
爱编程的小生1 小时前
Easyexcel(2-文件读取)
java·excel
带多刺的玫瑰2 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法