不断的学习,我们才能不断的前进
一个好的程序员是那种过单行线马路都要往两边看的人

Spring--IoC容器

1. 简介

  1. IoC概述
    Spring Ioc指的是控制反转(IoC),IoC也被称为依赖注入(DI)。在此过程中,对象仅通过构造函数参数,工厂方法的参数或在构造或从工厂方法返回后在对象实例上设置的属性来定义其依赖项(即,与它们一起使用的其他对象)。然后,容器在创建bean时注入那些依赖项。
    在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。Bean是由Spring IoC容器实例化,组装和管理的对象。否则,bean仅仅是应用程序中许多对象之一。Bean及其之间的依赖关系反映在容器使用的配置元数据中。

IOC的解释参考文章
IOC 就是把对象的创建、赋值和管理交给容器来实现。只需要知道对象的名称即可,对象的创建和复制交给容器来解决,降低了系统的耦合度。可以通过XML配置 和 注解两种方式来实现。
IOC 避免了创建重复的对象,造成资源浪费;更改实现类需要更改很多地方

  1. 容器概述
    org.springframework.context.ApplicationContext 接口代表IoC容器,并负责实际化。 容器通过读取配置元数据来获取有关要实例化,配置和组装哪些对象的指令。
    Spring提供了一些ApplicationContext的实例;在独立应用程序中,通常通过ClassPathXmlApplicationContext 或 FileSystemXmlApplicationContext创建实例

2. 使用过程

  1. xml 配置bean
<?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="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>  
</beans>

id 属性是标识单个bean定义的字符串。
class属性定义Bean的类型,并使用完全限定的类名。
proerty 是用来实例化容器

  1. 实例话容器,使用ClassPathXmlApplicationContext实例化容器,参数是配置文件名称
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

配置文件中加载的时候,容器内(Bean)管理的对象就已经初始化了,而且只会存在一个副本

3. xml配置

    <!--用于起别名-->
    <alias name="student" alias="studenetDTO"/>

    <!--用于合并多个配置文件-->
    <import resource="bean.xml"/>

4. 依赖注入

  1. 构造器注入
  2. set注入
    依赖:bean对象的创建依赖于容器
    注入:bean对象的所有属性,由容器来注入
<bean id="address" class="com.ljh.dao.Address">
        <property name="address" value="成都武侯区"/>
    </bean>

    <bean id="student" class="com.ljh.dao.Student">
        <!--普通值的注入,基于value-->
        <property name="name" value="赖金寒"/>
        <!--bean注入,使用ref-->
        <property name="address" ref="address"></property>

        <!--数组注入,ref-->
        <property name="books" >
            <array>
                <value>红楼梦</value>
                <value>三国</value>
                <value>水浒</value>
            </array>
        </property>
        <!--list注入-->
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>打篮球</value>
            </list>
        </property>
        <!--map注入-->
        <property name="card">
            <map>
                <entry key="ID" value="1212121111"/>
                <entry key="银行卡" value="1212121111"/>
            </map>
        </property>

        <!--set注入-->
        <property name="games">
            <set>
                <value> lol</value>
                <value> coc</value>
            </set>
        </property>

        <!--null值注入-->
        <property name="wife">
            <null/>
        </property>

        <!--properties注入-->
        <property name="info">
            <props>
                <prop key="学号">20019</prop>
                <prop key="姓名">赖金寒</prop>
            </props>
        </property>
    </bean>

获取bean | ref | idref | list | set | map | props | value | null 的注入方法
重点 推荐方式

  1. 扩展方式 注入
    3.1 P-namespace注入
xmlns:p="http://www.springframework.org/schema/p" <!-导入约束--->

<bean id="user" class="com.ljh.dao.User" p:name="赖金寒"/>

p相当于属性的意思,对应于set的属性注入

@Test
    public  void testP_namespace(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = context.getBean("user", User.class);
        System.out.println("测试p-注入");
        System.out.println(user.toString());
    }

测试p-注入
User(name=赖金寒, age=0)

3.2 C-namespace注入

xmlns:c="http://www.springframework.org/schema/c" <!--导入cnamespace-->
<bean id="user1" class="com.ljh.dao.User" c:age="12"/>

cnamespace注入相当于构造器注入
需要在实体类中写构造器方法。

@Test
    public  void testC_namespace(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = context.getBean("user1", User.class);
        System.out.println("测试c-注入");
        System.out.println(user.toString());
    }

测试c-注入
User(name=null, age=12)

5. Bean的生命周期

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中有效。

singleton

single

prototype

pro
每次从容器中get的时候,都会产生一个新的对象

<bean id="student" class="com.ljh.DTO.StudenetDTO" scope="prototype">
        <constructor-arg name="name" value="王五"/>
    </bean>
@Test
    public void  test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        
        StudenetDTO student = (StudenetDTO)context.getBean("student");

        StudenetDTO student1 = (StudenetDTO)context.getBean("student");
        System.out.println("两个对象是否相等");
        System.out.println(student==student1);
    }

两个对象是否相等
false
如果scope="singleton" 两个对象就是相等的。

6. Bean的自动装配

自动装配是spring满足bean依赖的一种方式。spring会在上下午中自动寻找,并自动给bean装配属性

1. 在xml中显示的配置

2. 在java中显示的配置

@Resource

3. 隐示的自动装配

<bean id="cat" class="com.ljh.dto.Cat"/>
    <bean id="dog" class="com.ljh.dto.Dog"/>
    <!--
    byname 会自动在容器上下文中查找和自己set方法 后面的值对应的beand
    bytype 会自动在容器上下文中查找和自己对象属性类型相同的beand
    -->

    <bean id="people" class="com.ljh.dto.People" autowire="byName">
        <!--<property name="cat" ref="cat"/>-->
        <!--<property name="dog" ref="dog"/>-->
        <property name="name" value="张三"/>
    </bean>

byname 时需要bean的id唯一,而且需要和自动注入的属性的set方法的参数一致
bytype 不需要bean的id,需要bean的class唯一 ,需要和自动注入的属性类型相同

4. 使用注解来实现自动装配

  • 导入约束:context约束
  • 配置注解的支持:context:annotation-config/
    Autowired 如果显示定义来Autowired的required的属性为false,说明这个对象可以为null,否则不允许为null
<bean id="cat" class="com.ljh.dto.Cat"/>
<bean id="dog" class="com.ljh.dto.Dog"/>
<bean id="people" class="com.ljh.dto.People"/>
<!--开启注解的支持-->
<context:annotation-config/>
@Data
public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}

直接在属性上写@Autowired即可,还可以在set方法上使用
必须在IOC容器中存在,而且符合名字。在bean容器中按类型来匹配

如果Autowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成的时候,可以使用@Qualifier(value="...")去配置@Autowired的使用,指定唯一一个bean对象注入。 eg:

<bean id="cat" class="com.ljh.dto.Cat"/>
<bean id="dog11" class="com.ljh.dto.Dog"/>
<bean id="dog12" class="com.ljh.dto.Dog"/>
<bean id="people" class="com.ljh.dto.People"/>
@Data
public class People {
    @Autowired
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog11")
    private Dog dog;
    private String name;
}

Autowired 默认是通过byType的方式来实现,加上Qualifier 后就按name来实现。

7. 注解开发

<?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/>
    <!--指定要扫描的包,这个包下注解就会生效-->
    <context:component-scan base-package="com.ljh.dto"/>
</beans>

DTO类

/**
 * @Author laijinhan
 * @date 2020/9/24 10:16 下午
 */

// Component 等价于 <bean id="user" class="com.ljh.dto.User"/>
@Component
public class User {
    // 相当于在bean里面 注入属性值。<property name="name" value="赖金寒">
    @Value("赖金寒")
    public String name;
}

@Componet: 组件,放在类上面,表明这个类被spring管理类,就是Bean
@Repository: 跟Componet一样,只是用于dto层
@Service: 跟Componet一样,只是用于service层
@Controller: 跟Componet一样,只是用于controller层

Spring MVC 模式;这四个注解都是一样的,都是把某一个类注册到spring容器中,装配Bean
使用xml配置更加万能,适用于任何场所;使用注解配置相对简单,但是不是自己类使用不了,维护相对复杂
最佳配合:xml用来配置bean,注解用来配置属性。

8. Java-based Container Configuration

dto类

/**
 * @Author laijinhan
 * @date 2020/9/24 10:42 下午
 */
@Component //表明这个类被spring管理类,注册到类容器里面
@Data
public class User {
    @Value("赖金寒") //属性注入
    public   String name;
}

config 类

/**
 * @Author laijinhan
 * @date 2020/9/24 10:58 下午
 */

// 这个会被spring容器管理,因为本身就是一个@Component
@Configuration //表示这是一个配置类,类似于之前的beans.xml
@ComponentScan("com.ljh.dto")
@Import(MyConfig2.class) //表示导入另外一个配置类
public class MyConfig {

    // 注册一个Bean,相当于之前的xml配置文件里面bean标签
    // 方法名就是 bean标签中的id
    // 返回值就是 bean标签中的class属性
    @Bean
    public User getName(){
        return new User(); //返回要注入到bean的对象
    }
}

测试

public class myTest {
    @Test
    public void  test(){
        // 完全使用配置的方式去做,只能通过AnnotationConfig上下文来获取bean容器,通过配置的class对象加载
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User user = context.getBean("getName", User.class);
        System.out.println(user.getName());

    }
}

这种纯java的配置在springboot中很常见,需要掌握

参考内容

github代码
官方文档


目录