Spring IOC的应用

目录

一、IOC基础

[1、maven导入spring的 jar包 和 单测包](#1、maven导入spring的 jar包 和 单测包)

2、bean的配置

[2.1 纯xml模式](#2.1 纯xml模式)

[2.1.1 xml文件头](#2.1.1 xml文件头)

[2.1.2 实例化Bean的三种方式](#2.1.2 实例化Bean的三种方式)

[2.1.3 Bean的生命周期](#2.1.3 Bean的生命周期)

[2.1.4 Bean标签属性](#2.1.4 Bean标签属性)

[2.1.5 DI依赖注入的xml配置](#2.1.5 DI依赖注入的xml配置)

[2.1.5.1 构造函数注入](#2.1.5.1 构造函数注入)

[2.1.5.2 set方法注入](#2.1.5.2 set方法注入)

[2.1.5.3 复杂数据类型注入](#2.1.5.3 复杂数据类型注入)

[2.2 xml与注解相结合模式(最常用)](#2.2 xml与注解相结合模式(最常用))

[2.2.1 xml中标签与注解的对应关系](#2.2.1 xml中标签与注解的对应关系)

[2.2.2 DI依赖注入的注解实现方式](#2.2.2 DI依赖注入的注解实现方式)

[2.3 纯注解模式](#2.3 纯注解模式)

3、启动IOC的方式

[3.1 Java环境下启动IOC容器](#3.1 Java环境下启动IOC容器)

[3.2 Web环境下启动IOC容器](#3.2 Web环境下启动IOC容器)

[3.2.1 从xml启动容器](#3.2.1 从xml启动容器)

[3.2.2 从配置类启动容器](#3.2.2 从配置类启动容器)

二、IOC高级特性

1、lazy-Init延迟加载

2、自定义Bean的创建过程:FactoryBean


IOC的实现,主要靠 xml/注解 + 工厂模式 + 反射 实现的。通过xml或注解配置需要管理的bean,通过工厂模式+反射获取bean的实例。
spring中文文档

一、IOC基础

1、maven导入spring的 jar包 和 单测包

基础依赖context里面包含context、aop、beans、core、expression这些jar包

XML 复制代码
<!-- spring 基础依赖 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.19</version>
</dependency>

<!--junit 单元测试依赖 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

2、bean的配置

bean的配置主要通过三种模式:纯xml模式、xml+注解模式、纯注解模式

2.1 纯xml模式

2.1.1 xml文件头
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">
2.1.2 实例化Bean的三种方式

方式一:使用无参构造函数
在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建失败。

XML 复制代码
<!--配置service对象-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
</bean>

方式二:使⽤静态⽅法创建
在实际开发中,我们使用的对象有时候并不是通过构造函数直接创建的,在创建过程中可能做很多额外操作,这时提供创建对象的方法恰好是个静态static方法,比如获取数据库连接DriverManager.getConnection()。在历史代码没有用spring框架,但是使用了工厂模式解耦,在接入spring后,可以通过这类对象就可以如下配置:

XML 复制代码
<!--使⽤静态⽅法创建对象的配置⽅式-->
<bean id="userService" class="com.lagou.factory.BeanFactory" 
    factory-method="getTransferService"></bean>

方式三:使用实例化方法创建
同使用静态方法创建类似,区别是用于获取对象的方法不再是static修饰的,而是普通方法

XML 复制代码
<!--使⽤实例⽅法创建对象的配置⽅式-->
<bean id="beanFactory"
    class="com.lagou.factory.instancemethod.BeanFactory"></bean>
<bean id="transferService" factory-bean="beanFactory" 
    factory-method="getTransferService"></bean>
2.1.3 Bean的生命周期

spring支持6种作用范围,不同的作用范围生命周期不同。spring默认bean对象都是单例的。

实际开发中,用到做多的作⽤范围就是singleton(单例模式)和prototype(原型模式,也叫多例模式)。配置⽅式参考下⾯的代码:

XML 复制代码
<!--配置service对象-->
<bean id="transferService"
    class="com.lagou.service.impl.TransferServiceImpl" scope="singleton">
</bean>

不同作⽤范围的⽣命周期:

  • 单例模式:singleton

对象出⽣:当创建容器时,对象就被创建了。
对象活着:只要容器在,对象⼀直活着。
对象死亡:当销毁容器时,对象就被销毁了。
⼀句话总结:单例模式的bean对象⽣命周期与容器相同。

  • 多例模式:prototype

对象出⽣:当使⽤对象时,创建新的对象实例。
对象活着:只要对象在使⽤中,就⼀直活着。
对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。
⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。

2.1.4 Bean标签属性
  • id属性: ⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀。
  • class属性:⽤于指定创建Bean对象的全限定类名。
  • name属性:⽤于给bean提供⼀个或多个名称。多个名称⽤空格分隔。
  • factory-bean属性:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后,class属性失效。
  • factory-method属性:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤,则class属性失效。如配合class属性使⽤,则⽅法必须是static的。
  • scope属性:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时,可以配置为prototype。
  • init-method属性:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是⼀个⽆参⽅法。
  • destory-method属性:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只能为scope是singleton时起作⽤。
2.1.5 DI依赖注入的xml配置

依赖注⼊分类
按照注⼊的⽅式分类
构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。
set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)
按照注⼊的数据类型分类
基本类型和String:注⼊的数据类型是基本类型或者是字符串类型的数据。
其他Bean类型:注⼊的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。那么针对当前Bean来说,就是其他Bean了。
复杂类型(集合类型):注⼊的数据类型是Aarry,List,Set,Map,Properties中的⼀种类型

2.1.5.1 构造函数注入

使⽤要求是,类中提供的构造函数参数个数必须和配置的参数个数⼀致,且数据类型匹配。
同时需要注意的是,当没有⽆参构造时,则必须提供构造函数参数的注⼊,否则Spring框架会报错。

XML 复制代码
<bean id="userDao" class="com.test.UserDao">
    <constructor-arg name="name" value="zhangsan"/>
    <constructor-arg name="age" value="18"/>
    <constructor-arg name="friend" ref="user"/>
</bean>

在使⽤构造函数注⼊时,涉及的标签是 constructor-arg ,该标签有如下属性:
name :⽤于给构造函数中指定名称的参数赋值。
index :⽤于给构造函数中指定索引位置的参数赋值。
value :⽤于指定基本类型或者 String 类型的数据。
ref :⽤于指定其他 Bean 类型的数据。写的是其他 bean 的唯⼀标识。

2.1.5.2 set方法注入

利⽤字段的set⽅法实现赋值的注⼊⽅式,需要有无参构造方法。此种⽅式在实际开发中是使⽤最多的注⼊⽅式。

XML 复制代码
<bean id="userDao" class="com.test.UserDao">
    <property name="name" value="zhangsan"/>
    <property name="age" value="18"/>
    <property name="friend" ref="user"/>
</bean>

在使⽤ set ⽅法注⼊时,需要使⽤ property 标签,该标签属性如下:
name :指定注⼊时调⽤的 set ⽅法名称。(注:不包含 set 这三个字⺟ ,druid 连接池指定属性名称)
value :指定注⼊的数据。它⽀持基本类型和 String 类型。
ref :指定注⼊的数据。它⽀持其他 bean 类型。写的是其他 bean 的唯⼀标识。

2.1.5.3 复杂数据类型注入

复杂类型数据指的是集合类型数据。集合分为两类,⼀类是List结构(数组结构),⼀类是Map接⼝(键值对)
以set方法注入为例:类型分别是String[]、Map、List、Properties

XML 复制代码
<bean id="userDao" class="com.test.UserDao">
    <property name="myArray">
        <array>
            <value>array1</value>
            <value>array2</value>
            <value>array3</value>
        </array>
    </property>
    <property name="myMap">
        <map>
            <entry key="key1" value="value1"/>
            <entry key="key2" value="value2"/>
        </map>
    </property>
    <property name="mySet">
        <set>
            <value>set1</value>
            <value>set2</value>
        </set>
    </property>
    <property name="myProperties">
        <props>
            <prop key="prop1">value1</prop>
            <prop key="prop2">value2</prop>
        </props>
    </property>
</bean>

在 List 结构的集合数据注⼊时, array , list , set 这三个标签通⽤,另外注值的 value 标签内部 可以直接写值,也可以使⽤ bean 标签配置⼀个对象,或者⽤ ref 标签引⽤⼀个已经配合的 bean 的唯⼀标识。
在 Map 结构的集合数据注⼊时, map 标签使⽤ entry ⼦标签实现数据注⼊, entry 标签可以使 ⽤ keyvalue 属性指定存⼊ map 中的数据。使⽤ value-ref 属性指定已经配置好的 bean 的引⽤。
同时 entry 标签中也可以使⽤ ref 标签,但是不能使⽤ bean 标签。⽽ property 标签中不能使 ⽤ ref 或者 bean 标签引⽤对象

2.2 xml与注解相结合模式(最常用)

用xml的bean:第三⽅jar中的bean,⽐如德鲁伊数据库连接池
用注解的bean:⾃⼰开发的bean定义
xml+注解结合模式,xml⽂件依然存在,所以,spring IOC容器的启动仍然从加载xml开始

2.2.1 xml中标签与注解的对应关系

|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| xml形式 | 对应的注解形式 |
| 标签 | @Component("accountDao"),注解加在类上 bean的id属性内容直接配置在注解后面如果不配置,默认定义个这个bean的id为类的类名首字母小写; 另外,针对分层代码开发提供了@Componenet的三种别名@Controller、@Service、@Repository分别用于控制层类、服务层类、dao层类的bean定义,这四个注解的用法完全一样,只是为了更清晰的区分而已 |
| 标签的scope属性 | @Scope("prototype"),默认单例,注解加在类上 |
| 标签的init-method属性 | @PostConstruct,注解加在方法上,该方法就是初始化后调用的方法 |
| 标签的destory-method属性 | @PreDestory,注解加在方法上,该方法就是销毁前调用的方法 |

2.2.2 DI依赖注入的注解实现方式

方法一:@Autowired(推荐使⽤)
@Autowired为Spring提供的注解,需要导⼊包org.springframework.beans.factory.annotation.Autowired。
@Autowired采取的策略为按照类型注⼊。

java 复制代码
@Autowired
private AccountDao accountDao;

这样会去spring容器中找到类型为AccountDao的类,然后将其注⼊进来。这样会产⽣⼀个问题,当⼀个类型有多个bean值的时候,会造成⽆法选择具体注⼊哪⼀个的情况,这个时候我们需要配合着@Qualifier使⽤。
@Qualifier告诉Spring具体去装配哪个对象

java 复制代码
@Autowired
@Qualifier(name="jdbcAccountDaoImpl")
private AccountDao accountDao;

方法二:@Resource
@Resource 注解由 J2EE 提供,需要导⼊包 javax.annotation.Resource。
@Resource 默认按照 ByName ⾃动注⼊。

java 复制代码
@Resource
private AccountDao accountDao;
@Resource(name="studentDao")
private StudentDao studentDao;
@Resource(type="TeacherDao")
private TeacherDao teacherDao;
@Resource(name="manDao",type="ManDao")
private ManDao manDao;

如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不到则抛出异常。
如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异常。
如果指定了 type,则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个,都会抛出异常。
如果既没有指定name,⼜没有指定type,则⾃动按照byName⽅式进⾏装配;
注意:
@Resource 在 Jdk 11中已经移除,如果要使⽤,需要单独引⼊jar包

XML 复制代码
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

2.3 纯注解模式

改造xml+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动
对应注解
@Configuration 注解,表名当前类是⼀个配置类
@ComponentScan 注解,替代 context:component-scan
@PropertySource,引⼊外部属性配置⽂件
@Import 引⼊其他配置类
@Value 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
@Bean 将⽅法返回对象加⼊ SpringIOC 容器

3、启动IOC的方式

3.1 Java环境下启动IOC容器

  • ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使⽤)
  • FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件
  • AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
java 复制代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserDao userDao = (UserDao)context.getBean("userDao");

3.2 Web环境下启动IOC容器

3.2.1 从xml启动容器
XML 复制代码
<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!--配置Spring ioc容器的配置⽂件-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <!--使⽤监听器启动Spring的IOC容器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>
3.2.2 从配置类启动容器
XML 复制代码
<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!--告诉ContextloaderListener知道我们使⽤注解的⽅式启动ioc容器-->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </context-param>
    <!--配置启动类的全限定类名-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.lagou.edu.SpringConfig</param-value>
    </context-param>
    <!--使⽤监听器启动Spring的IOC容器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

二、IOC高级特性

1、lazy-Init延迟加载

ApplicationContext 容器的默认⾏为是在启动服务器时将所有 singleton bean 提前进⾏实例化。提前实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton bean。
默认:lazy-init="false",⽴即加载,表示在spring启动时,⽴刻进⾏实例化
设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,⽽是第⼀次向容器通过 getBean 索取 bean 时实例化的。

XML 复制代码
<bean id="testBean" calss="com.test.LazyBean" lazy-init="true" />

如果⼀个设置了⽴即加载的 bean1,引⽤了⼀个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例化,⽽ bean2 由于被 bean1 引⽤,所以也被实例化,这种情况也符合延时加载的 bean 在第⼀次调⽤时才被实例化的规则。
也可以在容器层次中通过在 元素上使⽤ "default-lazy-init" 属性来控制延时初始化。如下⾯配置:

XML 复制代码
<beans default-lazy-init="true">
    <!-- no beans will be eagerly pre-instantiated... -->
</beans>

如果⼀个 bean 的 scope 属性为 scope="pototype" 时,即使设置了 lazy-init="false",容器启动时也不会实例化bean,⽽是调⽤ getBean ⽅法实例化的。

2、自定义Bean的创建过程:FactoryBean

FactoryBean可以生成某一个类型的Bean实例,spring框架一些组件中会使用,还有其他框架和spring框架整合时会使用

实现类

java 复制代码
public class UserFactoryBean implements FactoryBean<User> {
    private String userInfo;//名字,年龄

    public void setUserInfo(String userInfo) {
        this.userInfo = userInfo;
    }

    //自定义bean的创建过程
    @Override
    public User getObject() throws Exception {
        User user = new User();
        String[] strings = userInfo.split(",");
        user.setName(strings[0]);
        user.setAge(Integer.parseInt(strings[1]));
        return user;
    }

    //返回bean类型
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
    //返回作用域是否单例,默认单例
    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}

xml配置

XML 复制代码
<bean id="userFactoryBean" class="com.test.UserFactoryBean">
    <property name="userInfo" value="张三,19"/>
</bean>

测试

java 复制代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User userDao = (User)context.getBean("userFactoryBean");
System.out.println(userDao);
相关推荐
编程点滴几秒前
go单测报错 monkey undefined jmpToFunctionValue
开发语言·后端·golang
视觉小鸟10 分钟前
【java面试每日五题之基础篇一】(仅个人理解)
java·笔记·面试
为祖国添砖爪哇1 小时前
【Spring】面试题:Spring,Spring Boot, Spring MVC 的关系以及区别(5)
spring boot·spring·mvc
我是一颗小小的螺丝钉1 小时前
idea插件推荐之Cool Request
java·ide·intellij-idea
Kerwin要坚持日更4 小时前
Java小白一文讲清Java中集合相关的知识点(九)
java·开发语言
_Power_Y6 小时前
JavaSE:11、内部类
java·开发语言
小张同学(恩师白云)7 小时前
SpringDataJPA基础增删改查
java·数据库
尘浮生7 小时前
Java项目实战II基于Spring Boot的宠物商城网站设计与实现
java·开发语言·spring boot·后端·spring·maven·intellij-idea
Grey Zeng8 小时前
Java SE 23 新增特性
java·jdk·jdk新特性·jdk23
勤奋的小王同学~8 小时前
怎么修改mvn的java版本
java·开发语言