spring通过XML配置实现IOC详解

容器概述

ApplicationContext是Spring IoC容器实现的代表,它负责实例化,配置和组装Bean。容器通过读取配置元数据获取有关实例化、配置和组装哪些对象的说明 。配置元数据可以使用XML、Java注解或Java代码来呈现。它允许你处理应用程序的对象与其他对象之间的互相依赖关系。

配置元数据

使用xml的配置
简单、直观 适合入门

基于注解的配置: @Compont(@serivce @controller @repository) @Autowride
Spring 2.5 支持基于注解的元数据配置. SSM框架开发中的使用

基于Java的配置:
@Confiration @Bean @Import
从 Spring 3.0开始, 由Spring JavaConfig项目提供的功能已经成为Spring核心框架的一部分。因此,你可以使用Java配置来代替XML配置定义外部bean
从spring4.0开始支持springboot1.0 之后 springboot完全采用javaConfig的方式进行开发。

容器的实例化

对象在Spring容器创建完成的时候就已经创建完成,不是需要用的时候才创建(类似设计模式中的饿汉模式)

首先在spring-ioc.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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.jony.beans.User" id="user"></bean>
</beans>
复制代码

其次在代码中加载spring-ioc.xml

// 加载spring容器
//ApplicationContext spring的顶层核心接口
// ClassPathXmlApplicationContext 根据项目路径的xml配置来实例化spring容器
// FileSystemXmlApplicationContext 根据磁盘路径的xml配置来实例化spring容器
// AnnotationConfigApplicationContext 根据javaconfig 来配置实例化spring容器
// 在容器实例化的时候 就会加载所有的bean
ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-ioc.xml");
复制代码

编写测试代码

在测试代码中,我们可以以三种方式使用spring ioc帮我们注入的Bean对象。

package com.jont.ioc;

import com.jony.beans.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IocTest {

    @Test
    public void test01(){
        // 加载spring容器
        //ApplicationContext spring的顶层核心接口
        // ClassPathXmlApplicationContext 根据项目路径的xml配置来实例化spring容器
        // FileSystemXmlApplicationContext 根据磁盘路径的xml配置来实例化spring容器
        // AnnotationConfigApplicationContext 根据javaconfig 来配置实例化spring容器
        // 在容器实例化的时候 就会加载所有的bean
        ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-ioc.xml");
        //通过发射机制拿到User bean
        User bean = ioc.getBean(User.class);
        //通过id获得bean
        User bean1= (User) ioc.getBean("user");
        //通过id+类型获得bean
        User bean2=ioc.getBean("user",User.class);
        System.out.println(bean);
    }
}
复制代码

spring配置文件的配置

按住ctrl进入查看beans

image.png

bean的概述

通过下图可以看到,beans中我们可以使用如下三种标签

image.png

1、bean:为导入一个类,让spring帮我们注入

image.png
同时也可以使用name来定义别名,如下:

<bean class="com.jony.beans.User" id="user" name="user2 user3,user4;user5"></bean>
复制代码

name之间的可以使用空格、逗号,分号来进行分割
2、import:假如有多个xml,可以使用import导入一起加载,如下就可以让spring-ioc2.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 class="com.jony.beans.User" id="user"></bean>
    
    <import resource="spring-ioc2.xml"></import>
</beans>
复制代码

3、alias 别名,我们可以将已经定义好的bean,设置一个别名,如下

image.png
在代码中使用别名同样也可以使用注入的bean,同时原来的名字也可以使用的

image.png

依赖注入

基于setter方法的依赖注入

xml配置
 <!--基于setter方法的依赖注入
 1. 属性必须声明了set方法
 2. name是根据set方法的名字来的 比如方法名字是: setIdxx ‐> name="idxx"
-->
 <bean class="com.jony.beans.User" id="userioc">
     <property name="id" value="1"></property>
     <property name="userName" value="zhangsan"></property>
     <property name="realName" value="张三"></property>
 </bean>
复制代码
java代码测试

image.png

基于构造函数的依赖注入

将会调用自定义构造函数来实例化对象,就不会调用默认的无参构造函数

1、根据属性name注入
xml配置

image.png

java代码测试

image.png

2、根据下标位置注入

image.png
通过上图,我们可以发现,即使不填写name属性,bean的内容也是可以注入的,需要注意的是,顺序需要和构造方法的顺序一样才可以,否则就会匹配错误,如下:

image.png

3、设置index下标纠正错误

此处是根据构造函数的顺序进行注入的,按照顺序既可以成功正确注入
image.png

4、设置属性的type

设置type属性也可以进行参数设置,但是如果参数类型一致,就无法精准的匹配了。此处就不做演示了

总结:在注入的时候,最好还是使用name属性进行注入,这样即使顺序错误,也不用担心注入错误。

复杂数据依赖注入

准备基础类

创建含有不同类型的Person类
package com.jony.beans;

import java.util.Date;
import java.util.List;
import java.util.Map;

public class Person {
    private Integer id;
    private String name;
    private String gender;
    private Date birthday;
    private List<String> hobbies;
    private Map<Integer,String> course;
    private Wife wife;

    public Person() {
    }

    public Person(Integer id, String name, String gender, Date birthday, List<String> hobbies, Map<Integer, String> course, Wife wife) {
        this.id = id;
        this.name = name;
        this.gender = gender;
        this.birthday = birthday;
        this.hobbies = hobbies;
        this.course = course;
        this.wife = wife;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", gender='" + gender + ''' +
                ", birthday=" + birthday +
                ", hobbies=" + hobbies +
                ", course=" + course +
                ", wife=" + wife +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public Map<Integer, String> getCourse() {
        return course;
    }

    public void setCourse(Map<Integer, String> course) {
        this.course = course;
    }

    public Wife getWife() {
        return wife;
    }

    public void setWife(Wife wife) {
        this.wife = wife;
    }
}
复制代码
创建Wife类
package com.jony.beans;

public class Wife {
    private Integer age;
    private String name;

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

    public Integer getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public Wife() {
    }

    public Wife(Integer age, String name) {
        this.age = age;
        this.name = name;
    }
}
复制代码

1、引入外部bean

xml配置

在应用类中设置ref,指向被引用类的id

<bean class="com.jony.beans.Person" id="person">
    <property name="id" value="1"></property>
    <!--null值-->
    <property name="name">
        <null></null>
    </property>
    <property name="gender" value=""></property>
    <!--使用ref 引入外部bean-->
    <property name="wife" ref="wife"></property>
</bean>

<bean class="com.jony.beans.Wife" id="wife">
    <property name="name" value="迪丽热巴"></property>
    <property name="age" value="24"></property>
</bean>
复制代码
java代码及执行结果

可以看到Person通过ref引用的外部类可以成功注入
image.png

2、引入内部bean

xml配置

我们直接在,bean中设置wife,然后引用class地址

<bean class="com.jony.beans.Person" id="person">
    <property name="id" value="1"></property>
    <!--null值-->
    <property name="name">
        <null></null>
    </property>
    <property name="gender" value=""></property>
    <!--使用ref 引入外部bean-->
    <property name="wife">
        <bean class="com.jony.beans.Wife">
            <property name="name" value="迪丽热巴"></property>
            <property name="age" value="24"></property>
        </bean>
    </property>
</bean>
复制代码

3、list值

xml配置

给Person设置爱好,下面设置的是String值,当然也可以设置其他类型

<bean class="com.jony.beans.Person" id="person">
    <property name="id" value="1"></property>
    <!--null值-->
    <property name="name">
        <null></null>
    </property>
    <property name="gender" value=""></property>
    <!--使用ref 引入外部bean-->
    <property name="wife" ref="wife"></property>
    <!--如果是基本数据类型,用value
        如果是bean 对象,则用<bean>
        List的泛型是比如:List<Wife> <bean>-->
    <property name="hobbies">
        <list>
            <value>唱歌</value>
            <value>跳舞</value>
        </list>
    </property>
</bean>
复制代码
java测试代码及结果

image.png

4、map值

xml代码
<!--map设置
可以直接设置key 以及value的值,同时也可以设置key-ref / value-ref
如果map value为object,我们就尅设置value-ref 为 wife-->
<property name="course">
    <map>
        <entry key="1" value="Java"></entry>
        <entry key="2" value="Python"></entry>
        <entry key="3" value-ref="wife"></entry>
    </map>
</property>
复制代码
执行结果

image.png

5、使用p命名空间

可以使用p命名空间来简化基于setter属性注入 它不支持集合

xml配置如下

此时我没发现p命令为标红状态,我们只需要在p标签那 Alt+enter即可导入包

image.png
可以看到xml头部引入内如如下

image.png
然后我们把刚刚的课程3 value-ref设置为wife2,如下:

image.png

java 执行结果

image.png

3、使用c命名空间

xml配置

注意,与p命名空间不一样的是,c命名空间必须是构造函数

<!--使用c命名空间简化基于构造函数的xml-->
<bean class="com.jony.beans.Wife" id="wife3" c:age="23" c:name="一个美女"></bean>
复制代码

高级功能

1、depends-on属性

spring-ioc.xml中,默认加载顺序为从上到下进行顺序加载,通过使用depends-on属性,可以设置优先加载的bean,也就是控制bean的加载顺序

xml配置

image.png

java执行结果

可以看到,因为设置了depends-on=”wife”,因此wife先加载,然后再加载person。
image.png

2、懒加载 lazy-init

通过设置lazy-init 可以将bean设置为懒加载,只有使用的时候才会加载。

xml配置
<bean class="com.jony.beans.User" id="user" lazy-init="true">
    <property name="id" value="1"></property>
    <property name="realName" value="张三"></property>
</bean>
复制代码
java测试结果

通过下图,我们可以看到,在执行test方法的时候,spring-ioc配置文件已经加载完毕,因为User 设置了lazy-init,因此并未加载,当我们使用User bean的时候才进行加载。

image.png

3、自动注入

当一个对象中需要引用另外一个对象的时候,在之前的配置中我们都是通过property标签来进行手动配置的,其实在spring中还提供了一个非常强大的功能就是自动装配,可以按照我们指定的规则进行配置,配置的方式有以下几种:

3-1、byType 根据类型自动注入

按照类型进行装配,以属性的类型作为查找依据去容器中找到这个组件,如果有多个类型相同的bean对象,那么会报异常,如果找不到则装配null

xml配置

可以看到我们在Person bean中添加了 autowrite=”byType”这样,Person中的属性就会根据类型进行自动注入

<bean class="com.jony.beans.Person" id="person" depends-on="wife" autowire="byType"></bean>

<bean class="com.jony.beans.Wife" id="wife">
    <property name="age" value="24"></property>
    <property name="name" value="大美女"></property>
</bean>
复制代码
测试结果

我们并没有在xml配置 person的wife,但是通过自动注入,spring就会帮我们自动根据类型进行注入。
image.png
我们再次修改xml如下:

image.png
因为有了两个Wife,Person 的autowrite就会报错,因为同一个类型出现多次,spring不知道我们到底要装配哪个bean。如果有这种情况发生,我们就可以使用byName来进行处理

3-2、 byName 根据set方法的名字自动匹配

比如我们Person 中wife的set方法为 setWife,名字即为wife

xml配置

image.png

测试结果

可以看到spirng在自动注入的时候选择的是name为wife的bean
image.png
如果我们把Person的setWife 改为 setWife2,会怎么样呢?来测试一下
xml如下

image.png

setwife改为setWife2

image.png

执行结果

已经由大美女变为了小美女
image.png

3-3、constructor 安装构造器进行匹配

先按照有参构造器参数的类型进行装配,没有就直接装配null;如果按照类型找到了多个,那么就使用参数名作为id继续匹配,找到就装配,找不到就装配null

xml配置

image.png

修改Person 的构造方法

image.png

测试结果

image.png

我们再次修改Person的构造方法,构造方法传入wife2

image.png

执行测试方法

image.png

总结

通过构造函数进行自动注入的时候,会根据类中的构造函数传入的参数名称去xml中去进行匹配,如上面构造我们先传入wife,再传入wife2,执行的结果就不一样。

注意
1、constructor首先会根据构造函数中的参数名称去进行匹配,如果未匹配到,会再次根据byName进行匹配,如果还未匹配到,会再次根据byType进行匹配,如果还未匹配到,则为null
2、构造函数如果是多个参数也无法匹配到。如我们在Person的构造方法中添加一个参数

image.png
再次运行代码,Person中的wife就为null。这个主要是第二个参数需要在IOC容器中存在才可以进行自动注入,假如我们再次修改构造函数如下:

image.png
3、我们将第二个参数改为User,user bean也在xml文件中进行注入,这样就可以通过构造函数进行注入了,测试如下:

image.png
因此构造函数会根据参数进行完整匹配注入,如果构造函数中的参数,在IOC容器中不存在,则无法完成自动注入。

4、如果构造函数里面的参数,在容器中通过类型匹配到了多个bean,可以通过给bean设置primary=”true”来优先注入(byType也遵循这个原则,上面通过byType,如果匹配到多个类型,通过设置这个参数,也可以解决编译报错),如下:

image.png
给第一个wife bean 设置primart=true

image.png

Bean的作用域

1、Singleton 单例作用域

spring默认在注入bean的时候是单例模式,目的是同一个对象不要在内存中创建多次,节省系统资源。

测试代码如下

通过下图可以看到,我们通过getBean创降了两个User Bean,但是在User中的无参构造方法仅执行了一次,这就代表我们这个User Bean是单例的,只创建了一次。
image.png

2、Prototype(原型/多例)的作用域

xml配置

可以看到我们在bean中添加了scope=”prototype”

<bean class="com.jony.beans.User" id="user" lazy-init="true" scope="prototype">
    <property name="id" value="1"></property>
    <property name="realName" value="张三"></property>
</bean>
复制代码
测试执行结果

可以看到通过设置了原型作用域,我们在创建一个新的对象的时候,就会new 一个新的bean.
image.png

其他作用域

后面这四个作用域,由于需要用到web服务,后面再进行叙述。
image.png
大家如果有兴趣,可以去看一下原文:docs.spring.io/spring-fram…

bean的特性

bean的生命周期

1、使用实现接口的方式来实现什么周期的回调
  • 初始化方法: 实现接口: InitializingBean 重写afterPropertiesSet方法初始化会自动调用的方法

  • 销毁的方法: 实现接口: DisposableBean 重写destroy 方法 销毁的时候自动调用方法

image.png

2、使用指定具体方法的方式实现生命周期的回调:

在对应的bean里面创建对应的两个方法

  • init‐method=”init” destroy‐method=”destroy”

xml配置

image.png

java方法

image.png

为了将接口和xml区分开,将接口实现都设置标记1,配置实现的标记为2

3、bean什么时候销毁

IOC初始化需要使用如下三种方式创建,如果使用顶层核心接口,没有colse方法。

ClassPathXmlApplicationContext 根据项目路径的xml配置来实例化spring容器
FileSystemXmlApplicationContext 根据磁盘路径的xml配置来实例化spring容器
AnnotationConfigApplicationContext 根据javaconfig 来配置实例化spring容器
复制代码

在spring容器关闭的时候 close()

image.png

测试结果

image.png
通过以上,我们可以看到不论创建还是销毁都是接口的方法先执行,这样如果我们需要监控bean创建销毁的时候,就可以通过这两种方式来处理了。

spring创建第三方bean对象

在Spring中,很多对象都是单实例的,在日常的开发中,我们经常需要使用某些外部的单实例对象,例如数据库连接池,下面我们来讲解下如何在spring中创建第三方bean实例。比如我们引入数据库连接至,如下

数据库连接池

1、导入数据库连接池的pom文件
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>
复制代码
2、编写配置文件
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/jony"></property>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"> </property>
</bean>
复制代码
3、测试
@Test
public void test04(){
    DruidDataSource dataSource=ioc.getBean("datasource",DruidDataSource.class);
    System.out.println(dataSource);
    System.out.println(dataSource.getConnectCount());
}
复制代码
执行结果

image.png

spring引入外部文件

在实际项目中,我们会把相关的资源配置信息,全部写到一个资源文件中,这样在有修改的时候,就不必再去java代码中去修改。

1、在resource中添加配置文件

比如我配置数据库连接信息的文件,在resource中添加dbconfig.properties

username=root
password=123456
url=jdbc:mysql://localhost:3306/demo
driverClassName=com.mysql.jdbc.Driver
复制代码

2、让spring在注入的时候读取配置文件

核心是需要使用如下配置导入我们需要读取的配置文件

然后再xml开头导入需要的命名空间

<context:property-placeholder location="classpath:dbconfig.properties"/>
复制代码

image.png

3、执行结果如下

image.png

spring SpEL的使用

SpEL:Spring Expression Language,spring的表达式语言,支持运行时查询操作对象使用#{…}作为语法规则,所有的大括号中的字符都认为是SpEL.

spring 给我们提供了如下可以使用的表达式

image.png

1、xml配置

<bean class="com.jony.beans.User" id="user2">
    <!--支持任何运算符 加减乘除等-->
    <property name="id" value="#{12*2}"></property>
    <!--可以引用其他bean的某个属性值-->
    <property name="userName" value="#{address.province}"></property>
    <!--引用其他bean-->
    <property name="realName" value="#{address}"></property>
    <!--调用静态方法-->
    <property name="hobbies" value="#{T(java.util.UUID).randomUUID().toString().substring(0,4)}"></property>
    <!--调用非静态方法-->
    <property name="gender" value="#{address.getCity()}"></property>
</bean>
复制代码

注意:如果使用静态方法需要使用 T 来代表,如上面的 hobbies引用UUID.

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享