Java EE3-我独自整合(第三章:Spring DI 入门案例)

依赖注入DI(Dependency Injection)的两种方式

Spring中给Bean属性赋值有两种方式:

  • 构造注入(Constructor-based Dependency Injection):通过构造方法给bean的属性赋值。所以要求bean的类中必须提供对应参数的构造方法。相当于以前创建对象时new Student(1,"张三");
  • 设值注入,又称setter注入(Setter-based Dependency Injection):通过Bean的setter 方法赋值。所以要求Bean中属性必须提供setter方法。相当于以前的:Student student= new Student(); student.setld(1);student.setName("张三");
  • 接下来通过将上次对工厂的测试方法一分为三进行演示

构造注入

  • 必须在Bean中提供有参构造方法(无参构造方法最好也提供上,这个位置不用,但是其他位置如果没有通过构造注入,默认是调用无参构造的,会发生异常)

添加有参构造

java 复制代码
    public Student(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

配置Spring

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"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd">

        <!--Spring框架的核心配置文件-->
        <!--一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象
            id      唯一标识
            class   类型的具体名称,必须写包名.类名
        -->
<!--        <bean id="student" class="com.tianshi.domain.Student"/>-->

        <!--配置静态工厂-->
        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory" factory-method="getStudent">
                <!--DI构造注入
                    用来确定给哪个属性赋值(可同时使用):
                        index:参数在构造参数表中的下标,从0开始计数
                        name:构造方法参数名称
                        type:构造方法参数类型
                    设置属性的值(只用一个):
                        value:要传入的构造方法参数值(简单类型)
                        ref:要传入的构造方法参数(引用类型)
                    建议用index+name+value/ref完成配置
                -->
                <constructor-arg index="0" name="id" value="100"/>
                <constructor-arg index="1" name="name" value="小明"/>
        </bean>

        <!--配置实例工厂-->
        <bean id="studentFactory" class="com.tianshi.factory.StudentInstanceFactory"></bean>

        <bean id="studentWithInstanceFactory" class="com.tianshi.domain.Student"
        factory-bean="studentFactory" factory-method="getStudent">

        </bean>

        <!--配置FactoryBean工厂-->
        <bean id="studentWithFactoryBean" class="com.tianshi.factory.StudentFactoryBean">

        </bean>
</beans>

测试

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentWithStaticFactoryTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student=(Student)applicationContext.getBean("studentWithStaticFactory");
        System.out.println(student);
    }
}

C名称空间注入

  • Spring 框架提供更加方便的配置方案,此注入方式基于构造方法, 仅简化了配置,此配置方案在一定程度上,降低了配置文件的可读性

添加有参构造

java 复制代码
    /**
     * 常用4:实例工厂+多例
     * 用途:不同工厂做不同初始化、策略模式、Spring 原型 Bean
     */
    public Student getStudent(){
        return new Student();
    }
    public Student getStudent(Integer id,String name){
        return new Student(id,name);
    }
java 复制代码
    /**
     * 常用5:实例工厂+单例
     * 用途:一个工厂实例对应一个唯一对象
     */
    private Student student;
    //无参构造
    public StudentInstanceFactory() {}

    //无参获取
    public Student getStudent() {
        if (student == null) {
            student = new Student();
        }
        return student;
    }

    //有参获取
    public Student getStudent(Integer id,String name) {
        if (student == null) {
            student = new Student(id, name);
        }
        return student;
    }

配置Spring

记得在beans前面加上这一行,即提供名字空间配置信息

xml 复制代码
       xmlns:c="http://www.springframework.org/schema/c"
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"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd">

        <!--Spring框架的核心配置文件-->
        <!--一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象
            id      唯一标识
            class   类型的具体名称,必须写包名.类名
        -->
<!--        <bean id="student" class="com.tianshi.domain.Student"/>-->

        <!--配置静态工厂-->
        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"
              factory-method="getStudent">
                <!--DI构造注入
                    用来确定给哪个属性赋值(可同时使用):
                        index:参数在构造参数表中的下标,从0开始计数
                        name:构造方法参数名称
                        type:构造方法参数类型
                    设置属性的值(只用一个):
                        value:要传入的构造方法参数值(简单类型)
                        ref:要传入的构造方法参数(引用类型)
                    建议用index+name+value/ref完成配置
                -->
                <constructor-arg index="0" name="id" value="100"/>
                <constructor-arg index="1" name="name" value="小明"/>
        </bean>

        <!--配置实例工厂-->
        <!--C名称空间注入
            基于参数索引的配置:
                c:_索引数="值" 注入简单类型数据值
                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象
            基于参数名的配置:
                c:参数名="值" 注入简单类型的数据值
                c:参数名="bean的id" 注入Spring容器中其他的bean对象
-->
        <bean id="studentFactory" class="com.tianshi.factory.StudentInstanceFactory"/>
        <bean id="studentWithInstanceFactory" class="com.tianshi.domain.Student"
        factory-bean="studentFactory" factory-method="getStudent" c:_0="101" c:name="李四">

        </bean>

        <!--配置FactoryBean工厂-->
        <bean id="studentWithFactoryBean" class="com.tianshi.factory.StudentFactoryBean">

        </bean>
</beans>

测试

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentWithInstanceFactoryTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) applicationContext.getBean("studentWithInstanceFactory");
        System.out.println(student);
    }
}

设值注入

设值注入要求类型中必须提供 Setter 方法,且一般配合无参构造完成注入

Student类(提供Serializable标记接口告诉JVM该类可被序列化反序列化)

java 复制代码
package com.tianshi.domain;

import java.io.Serializable;
import java.util.Objects;

public class Student implements Serializable {

    private Integer id;     //身份唯一标识
    private String name;    //姓名

    public Student() {
    }

    public Student(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer id() {
        return id;
    }

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

    public String name() {
        return name;
    }

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

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

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id) && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

泛型接口FactoryBean重写

  • 使用有参注入必须提供setter方法,该泛型接口不认你写的重载方法
java 复制代码
package com.tianshi.factory;

import com.tianshi.domain.Student;
import org.springframework.beans.factory.FactoryBean;

/**
 * 基于Spring定义的工厂Bean接口,定义工厂类型
 * 接口泛型代表当前工厂生产的产品的类型
 *
 * 此种方式让代码和Spring框架耦合到一起,如果不适用spring框架,当前代码不可用
 */
public class StudentFactoryBean implements FactoryBean<Student> {
    //使用有参注入必须提供setter方法,该泛型接口不认你写的重载方法
    private Integer id;
    private String name;

    public Integer id() {
        return id;
    }

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

    public String name() {
        return name;
    }

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

    /**
     * 工厂方法
     * @return 产品对象
     * @throws Exception
     */
    @Override
    public  Student getObject() throws Exception {
        return new Student(id, name);
    }

    /**
     * 工厂生产的产品是什么
     * @return 产品类型对象
     */
    @Override
    public Class<?> getObjectType() {
        return Student.class;
    }
}

配置Spring

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"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd">

        <!--Spring框架的核心配置文件-->
        <!--一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象
            id      唯一标识
            class   类型的具体名称,必须写包名.类名
        -->
<!--        <bean id="student" class="com.tianshi.domain.Student"/>-->

        <!--配置静态工厂-->
        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"
              factory-method="getStudent">
                <!--DI构造注入
                    用来确定给哪个属性赋值(可同时使用):
                        index:参数在构造参数表中的下标,从0开始计数
                        name:构造方法参数名称
                        type:构造方法参数类型
                    设置属性的值(只用一个):
                        value:要传入的构造方法参数值(简单类型)
                        ref:要传入的构造方法参数(引用类型)
                    建议用index+name+value/ref完成配置
                -->
                <constructor-arg index="0" name="id" value="100"/>
                <constructor-arg index="1" name="name" value="小明"/>
        </bean>

        <!--配置实例工厂-->
        <!--C名称空间注入
            基于参数索引的配置:
                c:_索引数="值" 注入简单类型数据值
                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象
            基于参数名的配置:
                c:参数名="值" 注入简单类型的数据值
                c:参数名="bean的id" 注入Spring容器中其他的bean对象
-->
        <bean id="studentFactory" class="com.tianshi.factory.StudentInstanceFactory" c:_0="101" c:name="李四"/>
        <bean id="studentWithInstanceFactory" class="com.tianshi.domain.Student"
        factory-bean="studentFactory" factory-method="getStudent"/>

        <!--配置FactoryBean工厂-->
        <!--设值注入
            标签属性:
                name:要注入数据的property属性名
                value:为属性赋值(简单数据类型)
                ref:为属性赋值(引用数据类型)
        -->
        <bean id="studentWithFactoryBean" class="com.tianshi.factory.StudentFactoryBean">
                <property name="id" value="102"/>
                <property name="name" value="王五"/>
        </bean>
</beans>

测试

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentWithFactoryBeanTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) applicationContext.getBean("studentWithFactoryBean");
        System.out.println(student);
    }
}

P名称空间注入

配置Spring

记得在beans前面加上这一行,即提供名字空间配置信息

xml 复制代码
xmlns:p="http://www.springframework.org/schema/p"
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"
       xmlns:c="http://www.springframework.org/schema/c"
       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">

        <!--Spring框架的核心配置文件-->
        <!--一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象
            id      唯一标识
            class   类型的具体名称,必须写包名.类名
        -->
<!--        <bean id="student" class="com.tianshi.domain.Student"/>-->

        <!--配置静态工厂-->
        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"
              factory-method="getStudent">
                <!--DI构造注入
                    用来确定给哪个属性赋值(可同时使用):
                        index:参数在构造参数表中的下标,从0开始计数
                        name:构造方法参数名称
                        type:构造方法参数类型
                    设置属性的值(只用一个):
                        value:要传入的构造方法参数值(简单类型)
                        ref:要传入的构造方法参数(引用类型)
                    建议用index+name+value/ref完成配置
                -->
                <constructor-arg index="0" name="id" value="100"/>
                <constructor-arg index="1" name="name" value="小明"/>
        </bean>

        <!--配置实例工厂-->
        <!--C名称空间注入
            基于参数索引的配置:
                c:_索引数="值" 注入简单类型数据值
                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象
            基于参数名的配置:
                c:参数名="值" 注入简单类型的数据值
                c:参数名="bean的id" 注入Spring容器中其他的bean对象
-->
        <bean id="studentFactory" class="com.tianshi.factory.StudentInstanceFactory" c:_0="101" c:name="李四"/>
        <bean id="studentWithInstanceFactory" class="com.tianshi.domain.Student"
        factory-bean="studentFactory" factory-method="getStudent"/>

        <!--配置FactoryBean工厂-->
        <!--设值注入
            标签属性:
                name:要注入数据的property属性名
                value:为属性赋值(简单数据类型)
                ref:为属性赋值(引用数据类型)
        -->
        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">
                <property name="id" value="102"/>
                <property name="name" value="王五"/>
        </bean>

        <!--P名称空间注入
            仅基于参数名,无基于索引:
                p:属性名 = "简单数据"
                p:属性名-ref = "引用数据,其他bean标签的id"
        -->
        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>
</beans>

测试

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentWithFactoryBeanTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //第一个工厂
        Student student1 = (Student) applicationContext.getBean("studentWithFactoryBean1");
        System.out.println(student1);
        //第二个工厂
        Student student2 = (Student) applicationContext.getBean("studentWithFactoryBean2");
        System.out.println(student2);
    }
}

各种类型属性的注入配置方式

  • 无论是构造注入还是设值注入都提供了 value 和 ref 进行设置值,这两个属性只能给属性赋予简单数据类型或其他 bean 的引用
  • 如果类的属性是数组、集合等类型需要通过下面 方式进行设置,这些标签都是或的子标签
  • 一旦使用了子标签方式,就不能对或设置 value 属性或 ref 属性,且需要在 Student 类中提供对应名称、对应类型的属性

新增Book类型

java 复制代码
package com.tianshi.domain;

public class Book {
    private Integer id;
    private String name;

    public Book() {
    }

    public Book(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer id() {
        return id;
    }

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

    public String name() {
        return name;
    }

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

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

修改Student类型

java 复制代码
package com.tianshi.domain;

import java.io.Serializable;
import java.util.*;

public class Student implements Serializable {

    private Integer id;     //身份唯一标识
    private String name;    //姓名

    //数组
    private String[] hobbies;
    //List集合
    private List<Book> books;
    //Set集合
    private Set<String> set;
    //Map集合
    private Map<Integer,String> map;
    //引用类型
    private Book book;

    public Student() {
    }

    public Student(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer id() {
        return id;
    }

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

    public String name() {
        return name;
    }

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

    public List<Book> books() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }

    public String[] hobbies() {
        return hobbies;
    }

    public void setHobbies(String[] hobbies) {
        this.hobbies = hobbies;
    }

    public Set<String> set() {
        return set;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public Map<Integer, String> map() {
        return map;
    }

    public void setMap(Map<Integer, String> map) {
        this.map = map;
    }

    public Book book() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", hobbies=" + Arrays.toString(hobbies) +
                ", books=" + books +
                ", set=" + set +
                ", map=" + map +
                ", book=" + book +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id) && Objects.equals(name, student.name) && Objects.deepEquals(hobbies, student.hobbies) && Objects.equals(books, student.books) && Objects.equals(set, student.set) && Objects.equals(map, student.map) && Objects.equals(book, student.book);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, Arrays.hashCode(hobbies), books, set, map, book);
    }
}

Book实例工厂

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

import com.tianshi.domain.Book;
import com.tianshi.domain.Student;

public class BookInstanceFactory {
    private Book book;
    public BookInstanceFactory() {
    }
    public Book getBook() {
        if (book == null) {
            book = new Book();
        }
        return book;
    }
    public Book getBook(Integer id,String name) {
        if (book == null) {
            book = new Book(id,name);
        }
        return book;
    }
}

配置Spring

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"
       xmlns:c="http://www.springframework.org/schema/c"
       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">

        <!--Spring框架的核心配置文件-->
        <!--一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象
            id      唯一标识
            class   类型的具体名称,必须写包名.类名
        -->
<!--        <bean id="student" class="com.tianshi.domain.Student"/>-->

        <!--配置静态工厂-->
<!--        //唯一一个静态工厂(构造注入)-->
        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"
              factory-method="getStudent">
                <!--DI构造注入
                    用来确定给哪个属性赋值(可同时使用):
                        index:参数在构造参数表中的下标,从0开始计数
                        name:构造方法参数名称
                        type:构造方法参数类型
                    设置属性的值(只用一个):
                        value:要传入的构造方法参数值(简单类型)
                        ref:要传入的构造方法参数(引用类型)
                    建议用index+name+value/ref完成配置
                -->
                <constructor-arg index="0" name="id" value="100"/>
                <constructor-arg index="1" name="name" value="小明"/>
        </bean>

        <!--配置实例工厂两个-->
        <!--C名称空间注入
            基于参数索引的配置:
                c:_索引数="值" 注入简单类型数据值
                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象
            基于参数名的配置:
                c:参数名="值" 注入简单类型的数据值
                c:参数名="bean的id" 注入Spring容器中其他的bean对象
-->
<!--        //第一个实例工厂(C名称空间注入)-->
        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>
        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"
        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>
<!--        //Book的实例工厂-->
        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>
        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book"/>
<!--        //第二个实例工厂(各种类型的属性注入的配置)-->
        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>
        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"
              factory-bean="studentFactory2" factory-method="getStudent">
                <property name="id" value="105"/>
                <property name="name" value="王"/>
                <!--数组array标签,一个标签代表一个数组-->
                <property name="hobbies">
                        <array>
                                <!--数组中的简单数据,按照属性依次从0开始赋值-->
                                <value>0下标</value>
                                <value>1下标</value>
                                <value>2下标</value>
                                <!--ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值-->
                                <!--<ref bean="beanId"></ref>-->
                        </array>
                </property>
                <!--List集合,list标签,一个标签代表一个集合对象-->
                <property name="books">
                        <list>
                                <!--0下标位置对象-->
                                <ref bean="bookWithInstanceFactory"></ref>
                                <!--配置一个局部的bean对象,不需要命名(不写id),局部有效-->
                                <bean class="com.tianshi.domain.Book">
                                        <property name="id" value="2"></property>
                                        <property name="name" value="算法导论"/>
                                </bean>
                                <ref bean="bookWithInstanceFactory"></ref>
                        </list>
                </property>
                <!--Set集合,set标签-->
                <property name="set">
                        <set>
                                <value>set值1</value>
                                <value>set值2</value>
                                <value>set值3</value>
                        </set>
                </property>
                <!--Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对-->
                <property name="map">
                        <map>
                                <!--entry有属性 key:简单类型键值,key-ref:引用类型键值
                    value:简单类型value值,value-ref:引用类型value值-->
                                <entry key="1" value="value1"></entry>
                                <entry key="2" value="value2"></entry>
                                <entry key="3" value="value3"></entry>
                        </map>
                </property>
                <!--引用类型属性,使用ref注入-->
                <property name="book" ref="bookWithInstanceFactory"/>
        </bean>
        <!--配置FactoryBean工厂两个-->
        <!--设值注入
            标签属性:
                name:要注入数据的property属性名
                value:为属性赋值(简单数据类型)
                ref:为属性赋值(引用数据类型)
        -->
<!--        //第一个FactoryBean工厂(设值注入)-->
        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">
                <property name="id" value="102"/>
                <property name="name" value="王五"/>
        </bean>

        <!--P名称空间注入
            仅基于参数名,无基于索引:
                p:属性名 = "简单数据"
                p:属性名-ref = "引用数据,其他bean标签的id"
        -->
<!--        //第二个FactoryBean工厂(P名称空间注入)-->
        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>
</beans>

测试

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentWithInstanceFactoryTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //实例工厂1
        Student student1 = (Student) applicationContext.getBean("studentWithInstanceFactory1");
        System.out.println(student1);
        //实例工厂2
        Student student2 = (Student) applicationContext.getBean("studentWithInstanceFactory2");
        System.out.println(student2);
    }
}

依赖注入方式的选择

  • 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
  • 可选依赖使用setter注入进行,灵活性强
  • Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  • 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入 完成可选依赖的注入
  • 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用 构造器注入
  • 自己开发的模块推荐使用setter注入

依赖注入的自动装配

  • 在Spring中,允许Bean的自动注入,loC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
  • 自动装配有两种方式进行配置:局部配置(推荐)、全局配置

局部配置&全局配置

给book工厂注入,测试发现,使用ref引用类型的地方有了参数

xml 复制代码
<!--        //Book的实例工厂-->
        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>
        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>
复制代码
<!--局部的依赖注入自动装配
    在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。
    可以设置的值:
      default : 默认的,此是默认值。默认策略就是全局策略
      no : 不使用自动装配逻辑。
      byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入
        有可能,类型不匹配
      byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入
        有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。
        最常用。一般情况下,一个配置文件,不会配置多个同类型bean
      constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,
        基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。
-->

局部配置依赖全局配置

  • 全局即beans(标签中追加default-autowire),局部即bean(标签中追加autowire),皆有五个值,详解如上
  • 局部配置的优先级大于全局配置的优先级,全局使用byType,局部使用default/全局使用default,局部使用byType,注解掉使用ref属性的标签,仍然可以引用,完成自动装配
方法一:
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!--
  使用名字空间的时候,必须在根标签beans中,增加对应的配置内容,就是
  xmlns:c="http://www.springframework.org/schema/c"
  xml namespace = " 名字空间编写配置方案的描述内容,约束配置的方式。 "

  p名字空间,必须配置  xmlns:p="http://www.springframework.org/schema/p"

  全局自动装配策略在beans标签上增加属性。 default-autowire
  属性的可配置值和局部自动装配一样,除default外,其他和局部自动装配策略含义完全相同。
  default : 默认策略,就是不自动装配。等同no。
  no
  byType
  byName
  constructor

  建议直接配置全局策略。常用byType。在特定的bean标签上,可以做单独的设置。局部优先级更高。
-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       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"
       default-autowire="byType">

    <!--自动装配-->
    <!--局部的依赖注入自动装配
        在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。
        可以设置的值:
          default : 默认的,此是默认值。默认策略就是全局策略
          no : 不使用自动装配逻辑。
          byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入
            有可能,类型不匹配
          byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入
            有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。
            最常用。一般情况下,一个配置文件,不会配置多个同类型bean
          constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,
            基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。
    -->
    <bean id="student" class="com.tianshi.beans.Student" autowire="default">
        <property name="id" value="100"/>
        <property name="name" value="张三"/>
        <!--<property name="book" ref="book"/>-->
    </bean>
    <bean id="book" class="com.tianshi.beans.Book">
        <property name="id" value="100"/>
        <property name="name" value="spring in action"/>
    </bean>
    <!--<bean id="book1" class="com.tianshi.beans.Book">
        <property name="id" value="200"/>
        <property name="name" value="thinking in java"/>
    </bean>-->

    <!--DI注入方式: 2. 设置注入,配合无参构造或有参构造都可以完成。 -->
    <!--<bean id="student" class="com.tianshi.beans.Student">
        &lt;!&ndash; 设置注入,相关的配置标签是 property
             标签属性:
               name :要注入数据的property属性名是什么。
               value : 为属性赋值,必须是简单类型数据
               ref : 为属性赋值,必须是引用类型数据
         &ndash;&gt;
        <property name="id" value="10"/>
        <property name="name" value="王五"/>
        &lt;!&ndash;数组, array标签,一个标签代表一个数组&ndash;&gt;
        <property name="hobbies">
            <array>
                &lt;!&ndash;数组中的一个简单数据,按照配置属性依次从0下标开始赋值&ndash;&gt;
                <value>0下标</value>
                <value>1下标</value>
                <value>2下标</value>
                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;
                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;
            </array>
        </property>
        &lt;!&ndash;List集合, list标签,一个标签代表一个集合对象&ndash;&gt;
        <property name="books">
            <list>
                &lt;!&ndash;0下标位置对象&ndash;&gt;
                <ref bean="book"></ref>
                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效。&ndash;&gt;
                <bean class="com.tianshi.beans.Book">
                    <property name="id" value="20"/>
                    <property name="name" value="Spring框架技术"/>
                </bean>
                &lt;!&ndash;value只能赋予简单类型数据。&ndash;&gt;
                &lt;!&ndash;<value>简单数据值</value>&ndash;&gt;
            </list>
        </property>
        &lt;!&ndash;Set集合:set标签&ndash;&gt;
        <property name="set" >
            <set>
                &lt;!&ndash;也有ref和bean配置方式。参考list配置&ndash;&gt;
                <value>set值1</value>
                <value>set值2</value>
                <value>set值3</value>
            </set>
        </property>
        &lt;!&ndash;Map集合:map标签和entry子标签。一个map标签代表一个Map集合。一个entry标签代表map中的一个键值对&ndash;&gt;
        <property name="map">
            <map>
                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值
                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;
                <entry key="key1" value="value1"></entry>
                <entry key="key2" value="value2"></entry>
                <entry key="key3" value="value3"></entry>
            </map>
        </property>
        &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;
        <property name="book" ref="book"></property>
    </bean>-->

    <!--<bean id="book" class="com.tianshi.beans.Book">
        <property name="id" value="10"/>
        <property name="name" value="spring in action"/>
    </bean>
-->
    <!-- p名字空间配置方式:
         p:属性名 = "简单数据"
         p:属性名-ref = "引用数据,其他bean标签的id"
     -->
    <!--<bean id="studentWithPNS" class="com.tianshi.beans.Student"
          p:id="20" p:name="赵六"></bean>-->

    <!-- DI的注入方式: 1. 构造注入 -->
    <!--<bean id="student" class="com.tianshi.beans.Student">
        &lt;!&ndash; 构造注入的配置方案,使用标签完成配置信息
             index : 参数在构造方法参数表中的下表。从0开始计数。
             name : 构造方法参数名称
             type : 构造方法参数类型。
             value : 要传入的构造方法参数值。注意,只能给八种基本类型和对应包装类和字符串赋值。统称简单类型。
             ref : 要传入的构造方法参数。注意,只能给引用类型赋值。不是简单类型的。引用其他已经配置的bean的名字
             建议使用 index + value 或 ref 即可。
             如果有多个构造方法,参数表的数量一样,但是类型不同,建议使用 index + name + value 或 ref 完成配置。
             value和ref只能使用一个。  index, name, type 可以同时使用。
         &ndash;&gt;
        <constructor-arg index="0" name="id"
                         type="java.lang.Integer" value="1" />
        <constructor-arg index="1" value="张三" />
    </bean>-->

    <!-- C名称空间配置
         使用C名称空间后,标签bean新增属性。 c:xxxx,用于完成构造注入
         c:_下标 : 从0开始。 简单类型赋值
         c:_下标-ref : 从0开始。 引用类型赋值
         c:构造方法参数名 : 简单类型赋值
         c:构造方法参数名-ref : 引用类型赋值
     -->
   <!-- <bean id="studentWithCNS" class="com.tianshi.beans.Student"
          c:_0="2" c:name="李四"></bean>-->

</beans>
方法二:
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"
       xmlns:c="http://www.springframework.org/schema/c"
       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"
        default-autowire="default">

        <!--Spring框架的核心配置文件-->
        <!--一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象
            id      唯一标识
            class   类型的具体名称,必须写包名.类名
        -->
<!--        <bean id="student" class="com.tianshi.domain.Student"/>-->

        <!--配置静态工厂-->
<!--        //唯一一个静态工厂(构造注入)-->
        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"
              factory-method="getStudent">
                <!--DI构造注入
                    用来确定给哪个属性赋值(可同时使用):
                        index:参数在构造参数表中的下标,从0开始计数
                        name:构造方法参数名称
                        type:构造方法参数类型
                    设置属性的值(只用一个):
                        value:要传入的构造方法参数值(简单类型)
                        ref:要传入的构造方法参数(引用类型)
                    建议用index+name+value/ref完成配置
                -->
                <constructor-arg index="0" name="id" value="100"/>
                <constructor-arg index="1" name="name" value="小明"/>
        </bean>

        <!--配置实例工厂两个-->
        <!--C名称空间注入
            基于参数索引的配置:
                c:_索引数="值" 注入简单类型数据值
                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象
            基于参数名的配置:
                c:参数名="值" 注入简单类型的数据值
                c:参数名="bean的id" 注入Spring容器中其他的bean对象
-->
<!--        //第一个实例工厂(C名称空间注入)-->
        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>
        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"
        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>
<!--        //Book的实例工厂-->
        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>
        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>
<!--        //第二个实例工厂(各种类型的属性注入的配置)-->
        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>
        <!--自动装配-->
        <!--局部的依赖注入自动装配
            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。
            可以设置的值:
              default : 默认的,此是默认值。默认策略就是全局策略
              no : 不使用自动装配逻辑。
              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入
                有可能,类型不匹配
              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入
                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。
                最常用。一般情况下,一个配置文件,不会配置多个同类型bean
              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,
                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。
        -->
        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"
              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">
                <property name="id" value="105"/>
                <property name="name" value="王"/>
                <!--数组array标签,一个标签代表一个数组-->
                <property name="hobbies">
                        <array>
                                <!--数组中的简单数据,按照属性依次从0开始赋值-->
                                <value>0下标</value>
                                <value>1下标</value>
                                <value>2下标</value>
                                <!--ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值-->
                                <!--<ref bean="beanId"></ref>-->
                        </array>
                </property>
                <!--List集合,list标签,一个标签代表一个集合对象-->
                <property name="books">
                        <list>
                                <!--0下标位置对象-->
                                <ref bean="bookWithInstanceFactory"></ref>
                                <!--配置一个局部的bean对象,不需要命名(不写id),局部有效-->
                                <bean class="com.tianshi.domain.Book">
                                        <property name="id" value="2"></property>
                                        <property name="name" value="算法导论"/>
                                </bean>
                                <ref bean="bookWithInstanceFactory"></ref>
                        </list>
                </property>
                <!--Set集合,set标签-->
                <property name="set">
                        <set>
                                <value>set值1</value>
                                <value>set值2</value>
                                <value>set值3</value>
                        </set>
                </property>
                <!--Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对-->
                <property name="map">
                        <map>
                                <!--entry有属性 key:简单类型键值,key-ref:引用类型键值
                    value:简单类型value值,value-ref:引用类型value值-->
                                <entry key="1" value="value1"></entry>
                                <entry key="2" value="value2"></entry>
                                <entry key="3" value="value3"></entry>
                        </map>
                </property>
                <!--引用类型属性,使用ref注入-->
<!--                <property name="book" ref="bookWithInstanceFactory"/>-->
        </bean>
        <!--配置FactoryBean工厂两个-->
        <!--设值注入
            标签属性:
                name:要注入数据的property属性名
                value:为属性赋值(简单数据类型)
                ref:为属性赋值(引用数据类型)
        -->
<!--        //第一个FactoryBean工厂(设值注入)-->
        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">
                <property name="id" value="102"/>
                <property name="name" value="王五"/>
        </bean>

        <!--P名称空间注入
            仅基于参数名,无基于索引:
                p:属性名 = "简单数据"
                p:属性名-ref = "引用数据,其他bean标签的id"
        -->
<!--        //第二个FactoryBean工厂(P名称空间注入)-->
        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>
</beans>

自动装配的特性

  • 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  • 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,否则有发生异常的可能,推荐使用
  • 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置 耦合,不推荐使用
  • 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
相关推荐
Ttang232 小时前
Java爬虫:Jsoup+OkHttp实战指南
java·爬虫·okhttp
李庆政3702 小时前
OkHttp的基本使用 实现GET/POST请求 authenticator自动认证 Cookie管理 请求头设置
java·网络协议·http·okhttp·ssl
Chan162 小时前
SpringAI:MCP 协议介绍与接入方法
java·人工智能·spring boot·spring·java-ee·intellij-idea·mcp
dllxhcjla2 小时前
苍穹外卖2
java
迷藏4942 小时前
**发散创新:Go语言中基于上下文的优雅错误处理机制设计与实战**在现代后端开发中,**错误处理**早已不是简单
java·开发语言·后端·python·golang
杰克尼2 小时前
知识点总结--day10(Spring-Cloud框架)
java·开发语言
gelald2 小时前
Spring - AOP 原理
java·后端·spring
zwqwyq2 小时前
springboot与springcloud对应版本
java·spring boot·spring cloud
okiseethenwhat3 小时前
Java 内部类详解
java·开发语言