Spring IOC的四种注入方法

注入

Spring ⽀持的注⼊⽅式共有四种:set 注⼊、构造器注⼊、静态⼯⼚注⼊、实例化⼯⼚注⼊
注:注入前提均建立在建立好的spring环境内

1.Set方法注入-五种类型的注入

以业务对象JavaBean为主

1.1 业务对象JavaBean

第一步:创建dao包下的UserDao类

java 复制代码
package com.svt.dao;

public class UserDao {
    public void test(){
        System.out.println("UserDao Test...");
    }
}

第二步:属性字段提供set⽅法

原先写的方式都是手动实例化,比如private UserDao userDao=new UserDao();,这样会耦合过高,所以我们采用了新的形式:注入

java 复制代码
package com.svt.service;

import com.svt.dao.UserDao;

public class UserService {

    //手动实例化
    //private UserDao userDao=new UserDao();

    //业务逻辑对象JavaBean 提供set方法注入
    //在配置文件中还未告知set方法注入 所以userDao是空的
    //将这里bean对象实例化的过程交给IOC去做
    private UserDao userDao;

    //set方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void test(){
        System.out.println("UserService Test...");
        userDao.test();
    }
}

第三步:配置⽂件的bean标签设置property标签

通过property属性实现注入

java 复制代码
<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        Set方法注入
            通过property属性注入

    -->
    <bean id="userService" class="com.svt.service.UserService">
        <!--
            name:bean对象中属性字段的名称
            ref:指定bean标签的id属性值
        -->
        <property name="userDao" ref="userDao" />
    </bean>

    <bean id="userDao" class="com.svt.dao.UserDao"></bean>

</beans>

如果没有在资源文件夹xml内通过property属性实现注入,那么会出现空指针异常

第四步:测试

在UserService类中写测试方法

java 复制代码
public void test(){
        System.out.println("UserService Test...");
        //JavaBean对象
        userDao.test();
    }

再在测试类中调用

java 复制代码
import com.svt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Starter01 {
    public static void main(String[] args) {
        //获取Spring的上下文环境 BeanFactory也是可以的
        ApplicationContext ac=new ClassPathXmlApplicationContext("spring.xml");

        UserService userService= (UserService) ac.getBean("userService");
        userService.test();

    }
}

出现测试类中展现的内容则代表注入无误

1.2 常用对象String(日期类型)| 基本类型Integer

第一步:属性字段提供 set ⽅法

java 复制代码
    //常用对象String(日期类型)
    private String host;
    public void setHost(String host) {
        this.host = host;
    }

    //基本类型 Integer
    private Integer port;
    public void setPort(Integer port) {
        this.port = port;
    }

第二步:配置⽂件的 bean 标签设置 property 标签

java 复制代码
		<!-- value:具体的值(基本类型 常用对象|日期  集合)-->
        <!-- 常用对象 String-->
        <property name="host" value="127.0.0.1"/>
        <!-- 基本类型Integer-->
        <property name="port" value="8080"/>

第三步:测试

在UserService类中写测试方法

java 复制代码
public void test(){
        System.out.println("UserService Test...");
        //常用对象
        System.out.println(host);
        //基本类型
        System.out.println(port);
    }

再在测试类中调用

java 复制代码
import com.svt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Starter01 {
    public static void main(String[] args) {
        //获取Spring的上下文环境 BeanFactory也是可以的
        ApplicationContext ac=new ClassPathXmlApplicationContext("spring.xml");

        UserService userService= (UserService) ac.getBean("userService");
        userService.test();

    }
}

出现测试类中展现的内容则代表注入无误

1.3 集合类型和属性对象

第一步:属性字段提供set⽅法

java 复制代码
//List集合
    private List<String> list;
    public void setList(List<String> list) {
        this.list = list;
    }
    //List输出
    public void printList(){
        list.forEach(v-> System.out.println(v));//jdk8提供
    }

    //Set集合
    private Set<String> sets;
    public void setSets(Set<String> sets) {
        this.sets = sets;
    }
    //Set输出
    public void printSet(){
        sets.forEach(v-> System.out.println(v));
    }

    //Map集合
    private Map<String,Object> map;
    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
    //Map输出
    public void printMap(){
        map.forEach((k,v)-> System.out.println(k+"="+v));
    }

    //Properties属性对象
    private Properties properties;
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    //Properties输出
    public void printProperties(){
        properties.forEach((k,v)-> System.out.println(k+"="+v));
    }

第二步:配置⽂件的bean标签设置property标签

java 复制代码
<!--        List集合-->
        <property name="list">
            <list>
                <value>江苏</value>
                <value>浙江</value>
                <value>上海</value>
            </list>
        </property>

        <!--        Set集合-->
        <property name="sets">
            <set>
                <value>江苏JS</value>
                <value>浙江ZJ</value>
                <value>上海SH</value>
            </set>
        </property>

        <!--        Map集合-->
        <property name="map">
            <map>
                <entry>
                    <key>
                        <value>周杰伦</value>
                    </key>
                    <value>晴天</value>
                </entry>
                <entry>
                    <key>
                        <value>林俊杰</value>
                    </key>
                    <value>美人鱼</value>
                </entry>
                <entry>
                    <key>
                        <value>陈奕迅</value>
                    </key>
                    <value>富士山下</value>
                </entry>
            </map>
        </property>

        <!--        Properties属性对象-->
        <property name="properties">
            <props>
                <prop key="js">江苏</prop>
                <prop key="zj">浙江</prop>
                <prop key="sh">上海</prop>
            </props>
        </property>

第三步:测试

与上面一样

java 复制代码
public void test(){
        System.out.println("UserService Test...");
        //List集合类型
        printList();
        //Set集合类型
        printSet();
        //Map集合类型
        printMap();
        //Properties属性对象
        printProperties();
    }

2.构造器方法注入-种形式的注入

2.1 单个Bean对象作为参数

第一步:JavaBean对象

java 复制代码
package com.svt.service;

import com.svt.dao.UserDao02;

/**
 * 构造器方法注入
 *  需要提供带参构造
 */
public class UserService02 {
    private UserDao02 userDao02;
    /* 构造器注入*/

    public UserService02(UserDao02 userDao02) {
        this.userDao02 = userDao02;
    }
    public void test(){
        System.out.println("UserService Test...");
    }
}

第二步:XML配置

java 复制代码
<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        构造器注入
            设置构造器所需要的参数
            通过constructor-arg标签设置构造器的参数
                name:属性名称
                ref:要注入的bean对象对应的bean标签的id属性值
    -->
    <bean id="userService02" class="com.svt.service.UserService02">
        <constructor-arg name="userDao02" ref="userDao02"></constructor-arg>
    </bean>

    <bean id="userDao02" class="com.svt.dao.UserDao02"></bean>

</beans>

第三步:测试

java 复制代码
import com.svt.service.UserService02;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Starter02 {
    public static void main(String[] args) {
        //获取Spring的上下文环境 BeanFactory也是可以的
        ApplicationContext ac=new ClassPathXmlApplicationContext("spring02.xml");

        UserService02 userService02= (UserService02) ac.getBean("userService02");
        userService02.test();

    }
}

出现测试类中展现的内容则代表注入无误

2.2 多个Bean对象作为参数

第一步:JavaBean对象

java 复制代码
package com.svt.service;

import com.svt.dao.UserDao;
import com.svt.dao.UserDao02;

/**
 * 构造器方法注入
 *  需要提供带参构造
 */
public class UserService02 {
    private UserDao02 userDao02;
    /* 构造器注入*/

    /*public UserService02(UserDao02 userDao02) {
        this.userDao02 = userDao02;
    }*/

    private UserDao userDao;

    public UserService02(UserDao02 userDao02, UserDao userDao) {
        this.userDao02 = userDao02;
        this.userDao = userDao;
    }

    public void test(){
        System.out.println("UserService Test...");
        userDao02.test();
        userDao.test();
    }
}

第二步:XML配置

java 复制代码
<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        构造器注入
            设置构造器所需要的参数
            通过constructor-arg标签设置构造器的参数
                name:属性名称
                ref:要注入的bean对象对应的bean标签的id属性值
                value:数据具体的值
                index:参数的位置(从0开始)一般不用 但是可以确定参数是哪个位置的
    -->
    <bean id="userService02" class="com.svt.service.UserService02">
        <constructor-arg name="userDao02" ref="userDao02"></constructor-arg>
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>

    </bean>

    <bean id="userDao02" class="com.svt.dao.UserDao02"></bean>
    <bean id="userDao" class="com.svt.dao.UserDao"></bean>

</beans>

如果在写完JavaBean对象而没有在配置文件中添加bean则会出现以下报错内容,像上面xml文件中一样添加 <constructor-arg name="userDao" ref="userDao"></constructor-arg>即可

第三步:测试

在UserService02中的test测试方法中新增了两行输出语句

java 复制代码
public void test(){
        System.out.println("UserService Test...");
        userDao02.test();
        userDao.test();
    }

出现测试类中展现的内容则代表注入无误

2.3 Bean对象和常⽤对象作为参数

第一步:加入常用对象String(举例)

java 复制代码
	private UserDao02 userDao02;
    /* 构造器注入*/

    private UserDao userDao;
    
    private String user;

    public UserService02(UserDao02 userDao02, UserDao userDao, String user) {
        this.userDao02 = userDao02;
        this.userDao = userDao;
        this.user = user;
    }

第二步:XML配置

java 复制代码
<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        构造器注入
            设置构造器所需要的参数
            通过constructor-arg标签设置构造器的参数
                name:属性名称
                ref:要注入的bean对象对应的bean标签的id属性值
                value:数据具体的值
                index:参数的位置(从0开始)一般不用 但是可以确定参数是哪个位置的
    -->
    <bean id="userService02" class="com.svt.service.UserService02">
        <constructor-arg name="userDao02" ref="userDao02"></constructor-arg>
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
        <constructor-arg name="user" value="user" index=""></constructor-arg>

    </bean>

    <bean id="userDao02" class="com.svt.dao.UserDao02"></bean>
    <bean id="userDao" class="com.svt.dao.UserDao"></bean>

</beans>

第三步:测试

在UserService02中的test测试方法中新增了一行输出语句

java 复制代码
public void test(){
        System.out.println("UserService Test...");
        userDao02.test();
        userDao.test();
        System.out.println(user);
    }

出现测试类中展现的内容则代表注入无误

2.4 循环依赖问题

产生原因:Bean 通过构造器注⼊,之间彼此相互依赖对⽅导致 bean ⽆法实例化。比如一个IOC容器要去实例化A,但是A中有B的注入,于是IOC就先去找B实例化,此时B中又有A的注入,IOC又要去找A先进行初始化,这样一套下来IOC不知道是先找A还是B就会出现循环依赖的问题。

问题示例

  • 代码示例
java 复制代码
public class AccountService {
    private AccountDao accountDao;

    public AccountService(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void test(){
        System.out.println("AccountService Test...");
        accountDao.test();
    }
}

public class AccountDao {
    private AccountService accountService;

    public AccountDao(AccountService accountService) {
        this.accountService = accountService;
    }

    public void test(){
        System.out.println("AccountDao Test...");
    }
}
  • xml配置示例
java 复制代码
<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    循环依赖问题-->
    <bean id="acountService" class="com.svt.service.AccountService">
        <constructor-arg name="accountDao" ref="accountDao"></constructor-arg>
    </bean>
    <bean id="accountDao" class="com.svt.dao.AccountDao">
        <constructor-arg name="accountService" ref="acountService"></constructor-arg>
    </bean>
</beans>

此时AccountDao 注入了AccountService ,而AccountService 也注入了AccountDao ,这就造成了循环依赖

问题解决

如果出现循环依赖 需要通过set注入解决,下面就依据上面的循环注入代码进行解决

  • 解决代码示例
java 复制代码
public class AccountDao {
    private AccountService accountService;
    
    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    public void test(){
        System.out.println("AccountDao Test...");
    }
}

public class AccountService {
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void test(){
        System.out.println("AccountService Test...");
        accountDao.test();
    }
}
  • 解决xml配置示例
java 复制代码
<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--    如果出现循环依赖 需要通过set注入解决-->

    <bean id="accountService" class="com.svt.service.AccountService">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <bean id="accountDao" class="com.svt.dao.AccountDao">
        <property name="accountService" ref="accountService"></property>
    </bean>
    <!--
        构造器必须等待构造器参数中的实例化实例化好后才会实例化本身
        set方法注入:先实例化对象,再找被注入的set进去,就不会造成循环依赖
    -->
</beans>

原因

  • 构造器注入:必须等待构造器参数中的实例化实例化好后才会实例化本身
  • set方法注入:先实例化对象,再找被注入的set进去,就不会造成循环依赖

3.静态工厂注入(了解)

第一步:创建工厂类

new factory->new StaticFactory

java 复制代码
package com.svt.factory;

import com.svt.dao.TypeDao;

//静态工厂
public class StaticFactory {
    //定义静态方法
    public static TypeDao createTypeDao(){
        return new TypeDao();
    }
}

第二步:创建dao实体类

java 复制代码
package com.svt.dao;

public class TypeDao {
    public void test(){
        System.out.println("TypeDao Test...");
    }
}

第三步:创建TypeService,注入TypeDao

java 复制代码
package com.svt.service;

import com.svt.dao.TypeDao;

public class TypeService {
    //注入TypeDao
    private TypeDao typeDao;

    public void setTypeDao(TypeDao typeDao) {
        this.typeDao = typeDao;
    }

    public void test(){
        System.out.println("TypeService Test...");
        typeDao.test();
    }
}

第四步:xml配置

java 复制代码
<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--    静态工厂-->
    <!--    定义bean对象-->
    <bean id="typeService" class="com.svt.service.TypeService">
        <property name="typeDao" ref="typeDao"></property>
    </bean>
<!--    静态工厂注入:通过静态构造实例化需要被注入的bean对象-->
    <bean id="typeDao" class="com.svt.factory.StaticFactory" factory-method="createTypeDao"></bean>
</beans>

第五步:测试

java 复制代码
import com.svt.service.TypeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Starter02 {
    public static void main(String[] args) {
        //获取Spring的上下文环境 BeanFactory也是可以的
        ApplicationContext ac=new ClassPathXmlApplicationContext("spring02.xml");

        TypeService typeService= (TypeService) ac.getBean("typeService");
        typeService.test();

    }
}

出现测试类中展现的内容则代表注入无误

4.实例化工厂注入(了解)

实例化工厂注入与静态工厂注入大致一样,有以下几点不同

4.1 定义实例化工厂,写非静态方法

静态工厂里写的是静态方法,实例化工厂里写的非静态方法

java 复制代码
package com.svt.factory;

import com.svt.dao.TypeDao;

//定义实例化工厂
public class InstanceFactory {
    public TypeDao createTypeDao(){
        return new TypeDao();
    }
}

4.2 修改xml配置

java 复制代码
<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--    定义bean对象-->
    <bean id="typeService" class="com.svt.service.TypeService">
        <property name="typeDao" ref="typeDao"></property>
    </bean>
    
<!--    实例化工厂注入:通过实例化工厂需要被注入的bean对象-->
    <bean id="instanceFactory" class="com.svt.factory.InstanceFactory"></bean>
    <bean id="typeDao" factory-bean="instanceFactory" factory-method="createTypeDao"></bean>
</beans>

其余与静态工厂一致,测试类实现结果也与静态工厂一致

静态工厂注入与实例化工厂注入本质上还是set注入,只是本来bean对象是通过构造器去实例化的,现在更换了以下,想要静态工厂注入就把实例化的方式变成静态工厂,实例化工厂同理,两者用的不多,理解即可

注入方式的选择

开发项⽬中set⽅式注⼊⾸选

使⽤构造注⼊可以在构建对象的同时⼀并完成依赖关系的建⽴,对象⼀建⽴则所有的⼀切也就准备好

了,但如果要建⽴的对象关系很多,使⽤构造器注⼊会在构建函数上留下⼀⻓串的参数,且不易记忆,这时使⽤Set注⼊会是个不错的选择。

使⽤Set注⼊可以有明确的名称,可以了解注⼊的对象会是什么,像setXXX()这样的名称会⽐记忆Constructor上某个参数的位置代表某个对象更好。

相关推荐
Amarantine、沐风倩✨3 分钟前
设计一个监控摄像头物联网IOT(webRTC、音视频、文件存储)
java·物联网·音视频·webrtc·html5·视频编解码·七牛云存储
dazhong201229 分钟前
PLSQL 客户端连接 Oracle 数据库配置
数据库·oracle
路在脚下@1 小时前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
啦啦右一1 小时前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
森屿Serien1 小时前
Spring Boot常用注解
java·spring boot·后端
苹果醋32 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader3 小时前
深入解析 Apache APISIX
java·apache
了一li3 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
菠萝蚊鸭3 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
码农君莫笑3 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio