Spring----控制反转(IOC)

控制反转就是将对象的创建和对象之间调用的管理,交给Spring。我们在一个对象中需要另一个对象的资源无需去考虑如何去获得,Spring自动帮我们完成对象获取这个过程。

目的

降低耦合度

底层原理

xml解析、工厂模式、反射

关于工厂模式,有兴趣的可以看一下我的另一篇博客:传送门

Bean管理

概述

Spring的Bean管理包含两个操作:对象的创建和属性的注入。

想要实现可以使用配置文件的形式也可以使用注解。

基于xml

基于xml文件的形式实现Bean管理简单来说就是在配置文件中添加bean标签,然后在标签上面添加对应的属性就行。

只有一个对象,没有属性

    <bean id="user" class="com.modevil.bean.User">
    </bean>

在这种情况下,只用写一个bean标签,然后写上两个属性就行了。

  • id:这个对象的唯一标识符,我们可以使用这个东西来获取到这个对象。
  • class:全类名,spring依靠反射来创建对象,就是通过这个来获取到类的。

注入普通的属性

正常情况,我们创建一个对象肯定是有各种各样的属性,然后就可以使用下面的方法来注入对应的属性。

有以下两种注入方法:

  • 使用set方法注入:这种情况就是先使用无参构造创建对象,然后再使用set方法,设置属性值。
  • 使用有参构造注入:直接使用有参的构造方法注入属性值。
使用set方法注入

在这种情况下,无参构造方法和每个属性对应的set方法不能少,不然会报错:

No default constructor found; nested exception is java.lang.NoSuchMethodException: com.modevil.bean.User.<init>()

Invalid property 'address' of bean class [com.modevil.bean.User]: Bean property 'address' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?

正常的就是如下的配置:

    <bean id="user" class="com.modevil.bean.User">
        <property name="id" value="1"/>
        <property name="userName" value="张三"/>
        <property name="email" value="qqq@qqq.com"/>
        <property name="address" value="中国"/>
        <property name="sex" value=""/>
    </bean>
  • name:JavaBean文件的属性名
  • value:要注入的值
使用构造方法注入

在这种情况需要创建一个参数为要注入的各个属性的构造函数,建议多创建一个无参构造。

    <bean id="user2" class="com.modevil.bean.User">
        <constructor-arg name="id" value="1"/>
        <constructor-arg name="userName" value="张三"/>
        <constructor-arg name="email" value="qqq@qqq.com"/>
        <constructor-arg name="address" value="中国"/>
        <constructor-arg name="sex" value=""/>
    </bean>

name和value的意义和上面相同,另外就是constructor-arg还有另外一个属性index,可以替代name的作用,这个东西是指向构造方法中的第几个参数的索引,从0开始。

注入对象属性

引用

很简单,property和constructor-arg有另外一个属性,ref这个就是注入对象所使用的标签,ref的值就是配置文件中其他对象的id。

    <bean id="user" class="com.modevil.bean.User">
        <property name="address" ref="address"/>
    </bean>

    <bean id="user2" class="com.modevil.bean.User">
        <constructor-arg name="address" ref="address"/>
    </bean>

    <bean id="address" class="com.modevil.bean.Address">
    </bean>
内部bean

也可以直接在标签内部再写一个bean标签,然后像其他bean一样写各种属性,但是这样的内部bean无法通过id被其他类引用。

    <bean id="user1" class="com.modevil.bean.User">
        <property name="address">
            <bean class="com.modevil.bean.Address">
            </bean>
        </property>
    </bean>

如果再在配置文件中配置一个新的address对象,获取到的和内部bean是同一个对象(因为spring默认是单例模式),即使注入的值不同(会重新进行赋值,但是对象相同),仍然是同一个对象。

级联赋值

如果我们需要单独对address的属性进行赋值,我们可以使用这种情况,使用方法就是对象名.属性名,但是需要注意的是仍然需要对address对象单独赋值,不然会报空指针异常。

    <bean id="user3" class="com.modevil.bean.User">
        <property name="address" ref="address"/>
        <property name="address.zipCode" value="123"/>
        <property name="address.nation" value="拉拉"/>
        <property name="address.full" value="亚洲中国"/>
    </bean>

注入集合属性

注入数组、list、set、map类型,使用下面的方法:

普通的注入
    <bean id="collections0" class="com.modevil.bean.Collections">
        <property name="integerArray">
            <array>
                <value>1</value>
                <value>2</value>
            </array>
        </property>
        <property name="stringArray">
            <array>
                <value>第一个</value>
                <value>第二个</value>
                <value>第三个</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>你睡了吗?</value>
            </list>
        </property>
        <property name="map">
            <map>
                <entry key="哈哈" value="1"/>
            </map>
        </property>
        <property name="set">
            <set>
                <value>0</value>
            </set>
        </property>
    </bean>

如果是上面几种集合中的元素是对象,可以使用ref这个属性,直接指向引用的对象,也可以直接创建。

使用引用

也可以使用引用的方式进行属性注入,如果是这样的话需要添加一个依赖
在这里插入图片描述

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

然后下面是几种实现方法

    <bean id="collections1" class="com.modevil.bean.Collections">
        <property name="integerArray">
            <array>
                <value>1</value>
                <value>2</value>
            </array>
        </property>
        <property name="stringArray">
            <array>
                <value>第一个</value>
                <value>第二个</value>
                <value>第三个</value>
            </array>
        </property>
        <property name="list" ref="list"/>
        <property name="map" ref="map"/>
        <property name="set" ref="set"/>
    </bean>

    <util:list id="list">
        <value>这是我的list</value>
    </util:list>
    <util:set id="set">
        <value>123</value>
    </util:set>
    <util:map id="map">
        <entry key="天上地下" value="123"/>
    </util:map>

自动装配

自动装配就是Spring根据属性类型,或者是属性名自动的进行属性的注入操作。

具体的只需要加上一个autowire属性就行了

    <bean id="user" class="com.modevil.bean.User" autowire="byType">

常用的属性有以下两个:

  • byType:依据属性类型进行注入
  • byName:依据属性名进行注入

基于注解

有空再说

作用域和生命周期

作用域

默认Spring创建的对象都是单例,如果想要设置成多例或者是其他的情况,需要进行以下设置:

    <bean id="user" class="com.modevil.bean.User" scope="prototype">
    </bean>

关键点就是scope这个属性,常用的有以下两个值:

  • prototype:多例模式,每次获取都会创建一个新的对象。
  • singleton:对于一个类只会创建一个对象,不管配置了多少次;如果多次配置,会根据配置的属性再次进行注入。

生命周期

对Spring的bean对象有以下的生命周期:

  1. 通过无参构造创建对象
  2. 调用set方法对属性进行赋值
  3. 调用配置中的初始化方法
  4. bean正式“创建”成功,可以被其他对象拿到使用
  5. 当容器销毁的时候,调用销毁的方法

对于上面的过程,有以下的说明

  • 如果是使用构造参数进行属性注入,1、2过程会换成调用对应的构造方法
  • 只有scope设置为singleton的对象的销毁,spring才会调用相应的方方法;如果是prototype这样创建的方法,spring并不负责销毁时调用方法,所以不存在调用对应的方法。

以下是对这些配置的一个小案例

    <bean id="people" class="com.modevil.bean.People" init-method="init" destroy-method="destroy">
        <property name="name" value="王五"/>
    </bean>
  • init-method:指定初始化方法
  • destroy-method:指定销毁的方法
package com.modevil.bean;

/**
 * @author modev
 * @date 2020/10/13  20:00
 */
public class People {
    private String name;

    public People() {
        System.out.println("这里是无参构造!!!");
    }

    public void init() {
        System.out.println("初始化方法!!!");
    }

    public void destroy() {
        System.out.println("销毁方法!!!!");
    }

    public People(String name) {
        this.name = name;
        System.out.println("这里是有参构造!!!");
    }

    public String getName() {
        System.out.println("这里是取值方法!!!");

        return name;
    }

    public void setName(String name) {
        System.out.println("这里是赋值方法!!!");

        this.name = name;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                '}';
    }
}
后置处理器

Spring还提供了一个后置处理器,带上后置处理器后,完整的生命周期如下:

  1. 通过无参构造创建对象
  2. 调用set方法对属性进行赋值
  3. 调用后置处理器的方法postProcessBeforeInitialization
  4. 调用配置中的初始化方法
  5. 调用后置处理器的方法postProcessAfterInitialization
  6. bean正式“创建”成功,可以被其他对象拿到使用
  7. 当容器销毁的时候,调用销毁的方法

可以看到是在初始化的前后进行调用的。

想要实现后置处理器,只需要实现一个接口:BeanPostProcessor,重写方法对应的方法。最后在配置文件中进行注册即可。

package com.modevil.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @author modev
 * @date 2020/10/13  20:44
 */
public class MyBeanPost implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("前置方法" + beanName);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置方法" + beanName);

        return bean;
    }
}

    <bean id="myBeanPost" class="com.modevil.bean.MyBeanPost"/>

需要注意的是后置处理器对于所有的bean创建的时候都会生效,并且可以在后置处理器中获得对应的bean以及beanName。

参考

  • 尚硅谷的spring5课程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值