Spring

参考视频:B站狂神,写这个只是方便个人复习,怎么写是我自己的事,我能看懂就行,没要求非要让你看!白嫖还挑刺,是很没有风度的事情。希望做个有风度的“五好青年”!


6、依赖注入

6.1 构造器注入

  • 之前的案例已经说过了。

6.2 Set 注入 (重点)

依赖注入(Set注入!)。

  • 依赖 : Bean对象的创建依赖于容器!
  • 注入 : Bean对象中的所有属性,由容器来设置和装配。

【环境搭建】

  1. 复杂类型
  • Address.java
package com.github.subei.pojo;

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}
  1. 真实测试对象
  • Student.java
package com.github.subei.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobby;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobby() {
        return hobby;
    }

    public void setHobby(List<String> hobby) {
        this.hobby = hobby;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public String getWife() {
        return wife;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address +
                ", books=" + Arrays.toString(books) +
                ", hobby=" + hobby +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}
  1. 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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.github.subei.pojo.Student">
        <!-- 第一种:普通值注入,value -->
        <property name="name" value="subei"/>
    </bean>

</beans>
  1. 测试类
import com.github.subei.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.getName());
    }
}


2、Bean注入

  • 注意点:这里的值是一个引用,ref
    <bean id="address" class="com.github.subei.pojo.Address">
        <property name="address" value="成都"/>
    </bean>

    <bean id="student" class="com.github.subei.pojo.Student">
        <!-- 第一种:普通值注入,value -->
        <property name="name" value="subei"/>
        <!-- 第二种:Bean注入,ref -->
        <property name="address" ref="address"/>
        
    </bean>

3、数组注入

    <bean id="student" class="com.github.subei.pojo.Student">
        <!-- 第三种:数组注入 -->
        <property name="books">
            <array>
                <value>Mybatis</value>
                <value>Spring</value>
                <value>SpringMVC</value>
            </array>
        </property>
    </bean>

4、List注入

    <bean id="student" class="com.github.subei.pojo.Student">
        <!-- 第四种:list注入 -->
        <property name="hobby">
            <list>
                <value>家里蹲</value>
                <value>精神萎靡</value>
                <value>无法沟通</value>
            </list>
        </property>
    </bean>

5、Map注入

    <bean id="student" class="com.github.subei.pojo.Student">
		<!-- 第五种:Map注入 -->
        <property name="card">
            <map>
                <entry key="学生证" value="20201014"/>
                <entry key="身份证" value="14253686"/>
            </map>
        </property>

    </bean>

6、set注入

    <bean id="student" class="com.github.subei.pojo.Student">
		<!-- 第六种:set注入 -->
        <property name="games">
            <set>
                <value>保卫萝卜1</value>
                <value>保卫萝卜2</value>
                <value>保卫萝卜3</value>
            </set>
        </property>

    </bean>

7、Null注入

    <bean id="student" class="com.github.subei.pojo.Student">
		<!-- 第七种:null注入 -->
        <property name="wife">
            <null/>
        </property>

    </bean>

8、Properties注入

    <bean id="student" class="com.github.subei.pojo.Student">
        <!-- 第八种:Properties注入 -->
        <property name="info">
            <props>
                <prop key="学号">20210106</prop>
                <prop key="性别">保密</prop>
                <prop key="姓名">subei</prop>
            </props>
        </property>
    </bean>
  • 测试
import com.github.subei.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.toString());
    }
}


6.3 拓展方式注入

  • 我们可以使用p命令空间和C命令空间进行注入
  • 官方解释:

p命名和c命名注入翻译:

  • p-namespace 允许使用bean元素的属性(而不是嵌套的<property/>元素)来描述协作 Bean 的属性值,或同时使用这两者。
  • c-namespace 允许使用内联属性来配置构造函数参数,而不是嵌套的constructor-arg元素。
  • User.java
package com.github.subei.pojo;

public class User {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

}
  1. P命名空间注入 : 需要在头文件中加入约束文件
 导入约束 : xmlns:p="http://www.springframework.org/schema/p"
 
    <!-- p命名空间注入,可以直接注入属性的值: property -->
    <bean id="user" class="com.github.subei.pojo.User" p:name="subei" p:age="21" />



  1. c 命名空间注入 : 需要在头文件中加入约束文件
 导入约束 : xmlns:c="http://www.springframework.org/schema/c"

    <!-- c命名空间注入,可以通过构造器注入: construct-args -->
    <bean id="user2" class="com.github.subei.pojo.User" c:name="subei" c:age="22" />
  • 发现问题:爆红了,没有写有参构造!

  • 解决:把有参、无参构造器加上。
  • 由此可知:c 就是所谓的构造器注入!

  • 测试代码
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
        User user = context.getBean("user2",User.class);
        System.out.println(user);
    }

6.4 Bean的作用域

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。

简单地讲,bean就是由IoC容器初始化、装配及管理的对象。

Scope Description
singleton (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。
prototype 将单个 bean 定义的作用域限定为任意数量的对象实例。
request 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext中有效。
session 将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
application 将单个 bean 定义的范围限定为ServletContext的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
websocket 将单个 bean 定义的范围限定为WebSocket的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。

6.4.1 Singleton(单例)

单例模式(Spring默认机制)

  • 下图显示了单例作用域如何工作:

当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。


  • 注意,Singleton作用域是Spring中的缺省作用域。

  • 要在XML中将bean定义成singleton,可以这样配置:

    <bean id="user" class="com.github.subei.pojo.User"/>

    <!-- 以下内容是等价的,尽管是多余的(默认为单例作用域) -->
    <bean id="user2" class="com.github.subei.pojo.User" scope="singleton"/>
  • 测试
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
        User user = context.getBean("user2",User.class);
        User user2 = context.getBean("user2",User.class);
        System.out.println(user);

        System.out.println(user==user2);
    }

6.4.2 Prototype(原型)

原型模式:每次从容器中get的时候,都会产生一个新对象!

每次对特定 bean 提出请求时,bean 部署的非单一原型范围都会导致创建一个新 bean 实例。也就是说,将 Bean 注入到另一个 Bean 中,或者您可以通过容器上的getBean()方法调用来请求它。通常,应将原型作用域用于所有有状态 Bean,将单例作用域用于 StatelessBean。

下图说明了 Spring 原型范围:


在XML中将bean定义成prototype,可以这样配置:

    <bean id="user2" class="com.github.subei.pojo.User" scope="prototype"/>  

  • 其余的request、session、application、这些个只能在web开发中使用到!

6.4.3 Request

Spring 容器通过为每个 HTTP 请求使用loginAction bean 定义来创建LoginAction bean 的新实例。也就是说,loginAction bean 的作用域是 HTTP 请求级别。您可以根据需要更改创建实例的内部状态,因为从同一loginAction bean 定义创建的其他实例看不到这些状态更改。它们特定于单个请求。当请求完成处理时,将限制作用于该请求的 Bean。

  • 考虑以下 XML 配置来定义 bean :
<bean id="loginAction" class="com.something.LoginAction" scope="request"/>

使用注解驱动的组件或 Java 配置时,可以使用@RequestScope注解 将组件分配给request范围。以下示例显示了如何执行此操作:

@RequestScope
@Component
public class LoginAction {
    // ...
}

6.4.4 Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

  • 考虑下面bean定义:
 <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

在单个 HTTP Session的生存期内,Spring 容器通过使用userPreferences bean 定义来创建UserPreferences bean 的新实例。换句话说,userPreferences bean 的作用域实际上是 HTTP Session级别。与请求范围的 Bean 一样,您可以根据需要任意更改所创建实例的内部状态,因为知道其他 HTTP Session实例(也使用从相同userPreferences Bean 定义创建的实例)不会看到这些状态更改,因为它们特定于单个 HTTP Session。当最终丢弃 HTTP Session时,也将丢弃作用于该特定 HTTP Session的 bean。

  • 使用注解驱动的组件或 Java 配置时,可以使用@SessionScope注解 将组件分配给session范围。
@SessionScope
@Component
public class UserPreferences {
    // ...
}

7、自动装配Bean

  • 自动装配是Spring满足bean依赖一种方式!
  • Spring会在上下文中自动寻找,并自动给bean装配属性!

在Spring中有三种自动装配的方式:

  1. 在xml中显示的配置;
  2. 在java中显示配置;
  3. 隐式的自动装配bean。【重要!】

7.1 测试

  • 环境搭建:一个人有两个宠物!

  • Cat.java
package com.github.subei.pojo;

public class Cat {
    public void shout(){
        System.out.println("喵!喵!喵!");
    }
}
  • Dog.java
package com.github.subei.pojo;

public class Dog {
    public void shout(){
        System.out.println("汪!汪!汪!");
    }
}
  • People.java
package com.github.subei.pojo;

public class People {
    private Cat cat;
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}
  • 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cat" class="com.github.subei.pojo.Cat"/>
    <bean id="dog" class="com.github.subei.pojo.Dog"/>

    <bean id="people" class="com.github.subei.pojo.People">
        <property name="name" value="哇哈哈"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>

    </bean>

</beans>
  • 测试类
import com.github.subei.pojo.People;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        People people = context.getBean("people", People.class);
        people.getCat().shout();
        people.getDog().shout();
    }
}

7.2 ByName自动装配

    <!--
    byName:会自动在容器上下文中查找,和自己对象set方法后而的值对应的beanid!
    -->
    <bean id="people" class="com.github.subei.pojo.People" autowire="byName">
        <property name="name" value="哇哈哈"/>

    </bean>

7.3 ByType自动装配

    <bean class="com.github.subei.pojo.Cat"/>
    <bean class="com.github.subei.pojo.Dog"/>


    <!--
    byName:会自动在容器上下文中查找,和自己对象set方法后而的值对应的beanid!
    byType:会自动在容器上下文中查找,和自己对象属性类型机同的bean!
    -->
    <bean id="people" class="com.github.subei.pojo.People" autowire="byType">
        <property name="name" value="哇哈哈"/>

    </bean>

小结:

  • byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
  • bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!

7.4 使用注解实现自动装配

  • JDK 1.5支持的注解,Spring2.5就支持注解了!
  • The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML.
  • 要使用注解须知:
    • 导入约束,context约束
    • <mark>配置注解的支持;context:annotation-config/</mark>
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

@Autowired

  • 修改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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/context/spring-aop.xsd">

    <!-- 开启注解的支持 -->
    <context:annotation-config/>

    <bean id="cat" class="com.github.subei.pojo.Cat"/>
    <bean id="dog" class="com.github.subei.pojo.Dog"/>
    <bean id="people" class="com.github.subei.pojo.People"/>


</beans>
  • 修改People.java文件
package com.github.subei.pojo;

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

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}
  • 运行

  • 直接在属性上使用!也可以在set方式上使用!
  • 使用Autowired我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname!
  • 科普一下:
@Nullable  字段标记了这个注解,说明这个字段可以为null

  • 测试代码
package com.github.subei.pojo;

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

public class People {
    // 如果显示定义了Autowtred的requtred属性为false,说明这个对象可以为null.否则不允许为空。
    @Autowired(required = false)
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}

  • 如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!

package com.github.subei.pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class People {

    @Autowired
    @Qualifier(value = "cat111")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog222")
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}

@Resource

package com.github.subei.pojo;

import javax.annotation.Resource;

public class People {
    @Resource(name="cat2")
    private Cat cat;
    @Resource
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}

小结,@Resource 和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上!
  • @Autowired 通过byType的方式实现,而且必须要求这个对象存在!
  • @Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的倩况下,就报错!<mark>【常用】</mark>
  • 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byname的方式实现。

注解一时爽,维护火葬场!!!!