Spring学习( 二 ):IOC控制反转和DI依赖注入

Spring学习( 二 ):IOC控制反转和DI依赖注入

(一)、IOC的概念

一句话:把创建对象的权利交给了工厂,原来通过new 来创建,自己可以控制,如果通过Factory创建就把创建去权交出去了。

(二)、首先我们先介绍下ApplicationContext和它相关的类和接口

0

1、顶级接口

从图中可以看出BeanFactory是Spring容器的顶级接口,主要实现类有三个。

2、实现类

ClassPathXmlApplicationContext(最重要):配置文件的相对路径
FileSystemXmlApplicationContext:绝对路径
AnnotationConfigApplicationContext:基于注解

(三)、BeanFactory和ApplicationContext的区别

1、功能不同

BeanFactory:最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。
ApplicationContext:国际化、访问资源,如URL和文件(ResourceLoader)、AOP(拦截器)。

2、装载Bean的时机不同

BeanFactory是在第一次调用getBean的时候创建bean,ApplicationContext是在加载完配置就读取进去了,也可以在Bean配置lazy-init=true设为懒加载。(2种都是默认单例模式)

(四)、bean的三种创建方式

1、调用构造器创建Bean
2、调用实例工厂方法创建Bean
3、调用静态工厂方法创建Bean

下面逐个介绍

1、调用构造器创建Bean

注意点

默认是无参构造函数创建Bean,如果Bean中存在有参构造函数,没写无参构造且使用默认xml配置,那么程序会报错。(没没找到默认的无参构造方法

要想不出错,直接加个无参构造,但是如果Bean是再JAR包中,我们无法修改代码,或者说我们想把Jar包中每个方法返回值(类)加载到Spring容器中,这时候就要用下面2中方法来获取Bean了。
在这里插入图片描述

xml配置

<bean id="userService" class="com.service.impl.UserServiceImpl" />

Bean对象

public class UserServiceImpl implements UserService {
    @Override
    public String getUser(String name) {
        return "我是:" + name;
    }
}

2、调用实例工厂方法创建Bean

应用场景:我们想把Jar包中每个方法返回值(类)加载到Spring容器中

xml配置

    <bean id="instanceFactory" class="com.factory.InstanceFactory"></bean>
    <bean id="userService" factory-bean="instanceFactory" factory-method="getUserservice" />

工厂Bean对象

/**
 * @author wangquan
 * @date 2020/12/11
 * 模拟一个工厂类,我们想把Jar包中每个方法返回值(类)加载到Spring容器中
 */
public class InstanceFactory {
    public UserServiceImpl getUserservice(){
        return new UserServiceImpl();
    }
}

3、调用静态工厂方法创建Bean

xml配置

    <bean id="userService" class="com.factory.StaticFactory" factory-method="getUserservice" />

静态工厂Bean对象

public class StaticFactory {
    public static UserServiceImpl getUserservice(){
        return new UserServiceImpl();
    }
}

(五)、bean的作用域

目前Bean的作用域有以下6种

作用域描述
singleton在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。
prototype每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。
request每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。
session同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。
application限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。
global-session全局session,集群下的session,如果不是集群环境,和session没去别。

详情看这篇文章

Spring Bean的作用域

(六)、bean的生命周期

总的说下面8步

1、实例化Bean(通过构造方法或工厂创建)
2、注入属性(依赖注入)
3、执行实现Aware接口的方法,如实现了BeanNameAware接口,调用setBeanName()方法传递Bean的ID。
4、将Bean传递给Bean的前置处理器postProcessBeforeInitialization
5、调用Bean的初始化方法。
6、将Bean传递给Bean的后置处理器postProcessAfterInitialization
7、使用Bean
8、容器关闭前调用destroy方法。(如果作用域为prototype,不会调用,而是由垃圾回收器销毁)

代码演示

1、Student类实现了BeanNameAware接口,重写setBeanName方法

public class Student implements BeanNameAware {
    private String name;

    public Student(){
        System.out.println("1、调用构造方法初始化Bena");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("3、BeanNameAware设置BeanName:" + name);
    }

    public void setName(String name) {
        System.out.println("2、设置对象属性setName()..");
        this.name = name;
    }

    /**
     * Bean的使用
     */
    public void study() {
        System.out.println("7、Student这个Bean:使用,调用study()方法,此时我的名字:" + this.name);
    }

    public void initMethod(){
        System.out.println("5、设置Bean的初始化方法..");
    }

    public void destroyMethod(){
        System.out.println("8、设置Bean的销毁方法..");
    }

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

2、MyBeanPostProcessor实现BeanPostProcessor,重写前置和后置方法

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("4、对初始化之前的Bean进行处理,此时我的名字:" + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Student stu = null;
        String xmlBeanName = "student";
        System.out.println("6、对初始化之后的Bean进行处理,将Bean的成员变量的值修改了");
        if(xmlBeanName.equals(beanName) || bean instanceof Student) {
            stu = (Student) bean;
            stu.setName("李四");
        }
        return stu;
    }
}

3、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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="student" class="com.bean.Student" init-method="initMethod" destroy-method="destroyMethod">
        <property name="name" value="张三"/>
    </bean>
    <!-- 配置bean的后置处理器,不需要id,IoC容器自动识别是一个BeanPostProcessor -->
    <bean class="com.MyBeanPostProcessor"></bean>
</beans>

4、测试类

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Student student = (Student) context.getBean("student");
student.study();
context.close();

5、打印结果

在这里插入图片描述

(七)、依赖注入

1、什么是依赖注入

一个类要用到另一个类,我们只需要到spring配置文件配置,让spring为我们提供,如MySQL的配置注入到连接类里面,我们可以直接拿到类,而不需要额外配置。

2、依赖注入类型和方式

能注入的类型

  • 基本类型和String
  • 其他bean类型(配置文件配置的bean或注解配置过的)
  • 复杂类型 / 集合类型

注入的方式

  • 构造方法注入
  • set注入
  • 注解注入

3、注入方式

1、构造方法注入

Bean:Student

public class Student {

    private String name;
    private Integer age;
    private Date birthday;

    public Student(String name, Integer age, Date birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public void getStudent(){
        System.out.println(this.name+" : " + age + " : " + birthday);
    }
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="student" class="com.bean.Student">
        <constructor-arg name="name" value="12"/>
        <constructor-arg name="age" value="12"/>
        <constructor-arg name="birthday" ref="date"/>
    </bean>
    <bean id="date" class="java.util.Date"></bean>
</beans>

测试类

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Student student = (Student) context.getBean("student");
student.getStudent();

在这里插入图片描述

优势
在获取Bean,注入操作必须的,否则无法创建Bean
劣势
改变了Bean的实例化方式,再创建对象的时候,如果用不到这些数据,也必须提供。

2、get方法注入

Bean:Student

public class Student {

    private String name;
    private Integer age;
    private Date birthday;
    // 省略set方法。。。
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="student" class="com.bean.Student">
        <property name="name" value="张三" />
        <property name="age" value="33" />
        <property name="birthday" ref="date" />
    </bean>
    <bean id="date" class="java.util.Date"></bean>
</beans>

测试类
和上面一样
在这里插入图片描述
复杂数据类型的注入
Bean

public class Student {

    private String[] array;
    private List<String> lists;
    private Set<String> sets;
    private Map<String,String> map;
    private Properties properties;
  	 // 省略set方法。。。
  	@Override
    public String toString() {
        return "Student{" +
                "array=" + Arrays.toString(array) +
                ", lists=" + lists +
                ", sets=" + sets +
                ", map=" + map +
                ", properties=" + properties +
                '}';
    }
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="student" class="com.bean.Student">
        <property name="array" >
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>

        <property name="strings">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>

        <property name="sets">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>

        <property name="map">
            <map>
                <entry key="aaa" value="bbb"/>
                <entry key="ccc" value="ddd"/>
                <entry key="eee" value="fff"/>
            </map>
        </property>

        <property name="properties">
            <props>
                <prop key="aaa">bbb</prop>
                <prop key="ccc">ccc</prop>
                <prop key="eee">ddd</prop>
            </props>
        </property>
    </bean>
</beans>

测试结果
只要记住一个list一个map即可
在这里插入图片描述
发现互用也可以。
在这里插入图片描述

3、注解注入

1、找到对应的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"
       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">
    <!--找到对应的XML头,打开包扫描-->
    <context:component-scan base-package="com"/>
</beans>

2、把要注入到spring容器的类加上注解

@Repository
public class UserDapImpl implements UserDao {

@Service("userService")
public class UserServiceImpl implements UserService {
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值