Spring的依赖注入的方式

什么是依赖注入与控制反转

依赖注入:当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。然而采用依赖注入的方式,创建被调用者的工作不再由调用者来完成,创建被调用者的实例的工作由 IOC 容器来完成。然后注入调用者,称为依赖注入

控制反转:当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。然而采用控制反转的方式,创建被调用者的工作不再由调用者来完成,创建被调用者的实例的工作由 IOC 容器来完成。也就是把对象的创建,初始化,销毁的工作交给 spring ioc 容器来做。由 spring ioc 容器来管理对象的生命周期

依赖注入与自动装配的关系

依赖注入的本质就是装配,装配是依赖注入的具体行为

在传统的使用 xml 文件装配 bean 是一件很繁琐的事情,而且还需要找到对应类型的 bean才能装配,一旦 bean 很多,就不好维护了。为了解决这种问题,spring 使用注解来进行自动装配。自动装配就是开发人员不必知道具体要装配哪个 bean 的引用,这个识别的工作会由 spring 来完成。与自动装配配合的还有“自动检测”,这个动作会自动识别哪些类需要被配置成 bean,进而来进行装配

因此也可以这样理解:自动装配是为了将依赖注入自动化的一个简化配置的操作

例如不进行自动装配配置如下

<bean id="userDefault" class="cn.zhuoqianmingyue.ioc.di.autowire.UserDefault">
		<property name="country" ref="country"></property>
</bean>

<bean id="country" class="cn.zhuoqianmingyue.ioc.di.autowire.Country"></bean>

通过属性名称自动装配配置如下

<bean id="userByName" class="cn.zhuoqianmingyue.ioc.di.autowire.UserByName" autowire="byName"></bean>

<bean id="country" class="cn.zhuoqianmingyue.ioc.di.autowire.Country"></bean>

spring 在 3.X 之后,已推荐使用 JavaConfig 的形式来配置搭建项目,在此 spring 会使用注解 @Autowire 来自动装配。这也就是:自动装配是为了将依赖注入自动化的一个简化配置的操作

spring 基于 xml 文件形式的依赖注入的方式

  • 构造函数注入(常用)
  • setter 方法注入(常用)
  • p 命名空间注入(其实和 setter 注入原理一样,只是写法不一样)
  • spel 表达式注入
  • 复杂类型的注入(List,Map,Set 等集合的注入)<List>: 该标签用来装配可重复的 list 值;<Set>:该标签用来装配没有重复的 set 值;<Map>:该标签可用来注入键和值可以为任何类型的键值对;<props>:该标签支持注入键和值都是字符串类型的键值对

构造函数注入(实体类不需要 setter 方法)

实体类

public class User {
    
    private int id;
    
    private String username;
    
    private String password;
    
    public User() {
        System.out.println("User 类的无参构造函数被调用了");
    }
    
    public User(int id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
        System.out.println("User 类的有参构造函数被调用了");
    }
    
    @Override
    public String toString() {
        return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +'}';
    }
}

默认的构造函数是不带参数的构造函数。 Java 语言规定,如果类中没有定义任何构造函数,JVM 会自动为其生成一个默认的构造函数;反之,如果类中显式的定义了构造函数,JVM 则不会为其生成默认的构造函数。如果类中显式的声明了其他构造函数,如果未提供一个默认的构造函数,则属性注入时,会抛出异常

配置文件(通过 constructor-arg 标签 value 属性)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    <!--  第一种方式:构造函数注入  -->
    <bean id="user" class="com.atguigu.pojo.User">
        <constructor-arg value="1001"></constructor-arg>
        <constructor-arg value="张三"></constructor-arg>
        <constructor-arg value="123456"></constructor-arg>
    </bean>
</beans>

需要注意的是这里的标签的顺序要和构造方法中的参数顺序一致

测试类

public class Test {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) applicationContext.getBean("user");
        System.out.println(user);
    }
}

测试结果

在这里插入图片描述

疑问(重点

从测试结果来看,并没有打印

public User() {
	System.out.println("User 类的无参构造函数被调用了");
}

也就是说在 bean 实例化时,它的无参构造器没有被调用。为什么呢?

答案且看: spring源码之实例化 createBeanInstance 方法解读

配置文件 constructor-arg 标签属性另外几种写法

指定角标方式进行注入
<bean id="userIndex" class="cn.zhuoqianmingyue.ioc.di.constructor.User">
	<constructor-arg index="1" value="30"/>
	<constructor-arg index="0" value="zhouqianmingyue"/>
</bean>
指定类型方式进行注入
<bean id="userType" class="cn.zhuoqianmingyue.ioc.di.constructor.User">
	<constructor-arg type="java.lang.Integer" value="30"/>
	<constructor-arg type="java.lang.String" value="zhouqianmingyue"/>
</bean>
指定引用类型进行注入
<bean id="userHasClass" class="cn.zhuoqianmingyue.ioc.di.constructor.User">
	<constructor-arg type="java.lang.Integer" value="30"/>
	<constructor-arg type="java.lang.String" value="zhouqianmingyue"/>
	<constructor-arg type="cn.zhuoqianmingyue.ioc.di.constructor.Country" ref="country"/>
</bean>
	
<bean id="country" class="cn.zhuoqianmingyue.ioc.di.constructor.Country">
	<property name="name" value="CHINA"/>
</bean>

setter 方法注入(实体类必须要 setter 方法)

实体

public class User {
    
    private int id;
    
    private String username;
    
    private String password;
    
    public User() {
        System.out.println("User 类的无参构造函数被调用了");
    }
    
    public void setId(int id) {
        this.id = id;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    
    @Override
    public String toString() {
        return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +'}';
    }
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--  第二种方式:set 方法注入  -->
    <bean id="user1" class="com.atguigu.pojo.User">
        <property name="id" value="1002"></property>
        <property name="password" value="123123"></property>
        <property name="username" value="李四"></property>
    </bean>
<beans>

测试类

public class Test {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) applicationContext.getBean("user1");
        System.out.println(user);
    }
}

测试结果

在这里插入图片描述

疑问(重点

User 实体类中只有一个无参构造器,在 bean 实例化时,无参构造器被调用了

结合第一个疑问,可以得出以下结论

  • spring 在实例化 bean 时,如果同时存在无参与有参构造器,那么有参构造器会被调用,无参构造器不会调用
  • 在使用构造器进行依赖注入时,必须要有参构造器,无参构造器可有可无;否则,在属性注入时,会抛异常
  • 在使用 setter 方法进行依赖注入时,必须要有无参构造器,有参构造器可有可无;否则,在属性注入时,会抛异常;如果什么构造器都没有,bean 是无法完成实例化的,进而无法完成依赖注入

为什么会得出这样的结论,请查看: spring源码之实例化 createBeanInstance 方法解读

P 命名空间注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--  第三种注入方式:p 命名空间注入  -->
    <bean id="user2" class="com.atguigu.pojo.User"
          p:id="1003" p:username="王五" p:password="@123">
    </bean>
</beans>

spel 表达式注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--  第四种注入方式:spel 表达式注入  -->
    <bean id="user3" class="com.atguigu.pojo.User">
        <property name="id" value="#{1004}"></property>
        <property name="username" value="#{'李欢'}"></property>
        <property name="password" value="#{'jffdkj'}"></property>
    </bean>
</beans>

复杂类型注入

list 类型

<bean id="user" class="cn.zhuoqianmingyue.ioc.di.User">
	<property name="address">
		<list>
			<value>北京</value>
			<value>河北</value>
		</list>
	</property>
</bean>

set 类型

<bean id="user" class="cn.zhuoqianmingyue.ioc.di.User">
	<property name="address2">
		<set>
			<value>北京</value>
			<value>河北</value>
		</set>
	</property>
</bean>

map 类型

<bean id="user" class="cn.zhuoqianmingyue.ioc.di.User">
	<property name="addressMap">
		<map>
			<entry key="BEIJING" value="北京"/>
			<entry key="HEBEI" value="河北"/>
		</map>
	</property>
</bean>

静态工厂方法注入

public class UserFactory {
	
	// 静态方法
	public static User getUser1() {
		return new User();
	}
}

xml 配置文件

<!-- 使用静态工厂实例化 user -->
<bean id="user1" class="ioc.service.UserFactory" factory-method="getUser1"></bean>

实例工厂方法注入

public class UserFactory {
	
	// 普通方法
	public User getUser2() {
		return new User();
	}
}

xml 配置文件

<!-- 使用实例工厂实例化 user -->
<bean id="userFactory" class="ioc.service.UserFactory"></bean>
<bean id="user2" factory-bean="userFactory" factory-method="getUser2"></bean>

使用 autowire 标签注入

public class Teacher {

    private Student student;

    public Teacher() {
        System.out.println("Teacher 的无参构造器被调用了");
    }

    public Teacher(Student student) {
        System.out.println("Teacher 的有参构造器被调用了");
        this.student = student;
    }

    public void echo() {
        System.out.println("I'm a student : " + student);
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "student=" + student +
                '}';
    }
}

public class Student {

    private String name;

    public Student() {
        System.out.println("Student 的无参构造器被调用了");
    }

    public Student(String name) {
        System.out.println("Student 的有参构造器被调用了");
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

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

xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.atguigu.pojo.Student">
        <property name="name" value="小吉"></property>
    </bean>

    <bean id="teacher" class="com.atguigu.pojo.Teacher" autowire="byType"></bean>
</beans>

spring 基于注解形式的依赖注入方式

@Bean 注解

@Configuration
public class RabbitmqConfig {

    @Bean
    public Queue queue() {
        return new Queue(Constant.ES_QUEUE);
    }
    
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange(Constant.ES_EXCHAGE);
    }
    
    @Bean
    public Binding binding(Queue queue, DirectExchange directExchange) {
        return BindingBuilder.bind(queue).to(directExchange).with(Constant.ES_BIND_KEY);
    }
}

@Autowired 注解

用在构造器上

@Controller
public class SoakController extends BaseController {
 
	private SoakService soakService;
	
	@Autowired 
	public SoakController(SoakService soakService) {
		this.soakService = soakService;
	}
}

用在方法上

@Controller
public class SoakController extends BaseController {
 
	private SoakService soakService;
	
	@Autowired 
	public void setSoakService(SoakService soakService) {
		this.soakService = soakService;
	}
}

用在参数上

@Controller
public class SoakController extends BaseController {
 
	private SoakService soakService;
	
	public SoakController(@Autowired SoakService soakService) {
		this.soakService = soakService;
	}
}

用在字段上(较为常用)

@Controller
public class SoakController extends BaseController {
 
	@Autowired
	private SoakService soakService;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值