Spring-ioc基础学习(1):ioc概念,xml配置相关学习

定义

依赖倒置原则(DIP)

具体实现依赖抽象,下层依赖上层

IOC控制反转

我们原本创建对象时可以随意安排创建对象的时机,进行对象创建,但是在引入spring-ioc容器后,对象创建的工作转交给了容器进行,创建对象的控制权在容器手中,由容器控制在什么时机,用什么方式去创建,控制权从开发者手中转交由容器进行操作控制,这就是控制反转的基本概念

DI依赖注入

创建对象时原本是使用new自己创建,而使用spring-ioc后,创建对象的工作转交给spring-ioc容器进行操作,对象的创建是需要依赖spring-ioc容器的注入才行,就是依赖注入的基础概念

IOC是DIP的设计原理,DI是IOC的具体实现

使用IOC带来的好处

  • 解耦
  • 将对象的创建进行集中管理
  • 提高了功能的复用
  • 提高了程序整个体系的可维护性,灵活性,扩展性

IOC配置详情

ApplicationContext接口是Spring IoC容器实现的代表,它负责实例化,配置和组装Bean
继承类图


ApplicationContext

ApplicationContext配置元数据的方式有三种

  • 使用xml

最基础的使用方式,相对繁琐,需要有配置文件支持,一般使用ClassPathXMLApplicationContext进行实例化容器,但是内容最为清晰直观.

  • 使用java注释

Spring 2.5 支持基于注解的元数据配置.使用包扫扫描(context:component‐scan)等功能将被注释的类加载到容器中,还是结合xml文件进行配置.

  • 使用java代码

从 Spring 3.0开始, 由Spring JavaConfig项目提供的功能已经成为Spring核心框架的一部分,你可以使用Java配置来代替XML配置定义外部bean,使用AnnotationConfigApplicationContext进行实例化容器

容器的使用

使用ApplicationContext/BeanFactory接口来进行bean的创建,进行容器对象加载可以使用getBean方法

  • 现阶段一般使用ApplicationContext接口进行对象创建,因其内部有更多的方法封装,其包含所有BeanFactory的方法以及
  • BeanFactory是实现IOC的核心接口,其中IOC的功能都是由BeanFactory定义的,核心为getBean系列方法
  • BeanFactory和ApplicationContext最大的不同是,加载方式,单例作用域下,BeanFactory默认为懒加载,而ApplicationContext是饿加载,需要手动设置才会为懒加载

XML配置的IOC相关功能具体示例和说明

(1)基础准备工作

0.引入依赖,pom.xml中引入spring-context即可将关联的依赖jar都引入其中

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
            <scope>compile</scope>
</dependency>

1.创建容器对象

xml下容器对象一般使用ClassPathXMLApplicationContext来进行创建容器

常用容器实现类有

  • ClassPathXMLApplicationContext 根据项目路径的xml来配置实例化spring容器
  • FileSystemXmlApplicationContext 根据磁盘路径的xml来配置实例化spring容器
  • AnnotationConfigApplicationContext 根据JavaConfig来配置实例化Spring容器
ApplicationContext ioc =new ClassPathXmlApplicationContext("spring-ioc.xml")

2.定义spring的xml配置文件

spring-ioc.xml配置文件的操作基本都在beans中进行配置,需要引入必要的依赖约束内容(配置文件是自定义名称的并无强制要求,为了明确基础意义,设定为spring-ioc.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">
</beans>

3.定义所需示例类,主要有Person,Wife,Child,三个,Wife和Child继承Person,以及一个工厂Person的工厂类

package com.learn.beans;

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

/**
 * @author wangxing
 * @version 2020/6/26 16:35 Administrator
 */
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 Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
    public void setIdxxx(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;
    }

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

    public Person() {
        System.out.println(" load 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;
    }

    public Person(String name) {
        this.name = name;
    }
    public static Person createPersonFactory(String type){
        if ("wife".equals(type)) {
            return new Wife();
        } else if ("child".equals(type)) {
            return  new Child();
        }else{
            return new Person();
        }
    }
}
package com.learn.beans;

/**
 * @author wangxing
 * @version 2020/6/26 16:38 Administrator
 */
public class Wife extends Person {
    private Integer id;
    private String name;

    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;
    }

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

    public Wife() {
        System.out.println("load wife"+this);
    }

    public Wife(Integer id, String name) {
        this.id = id;
        this.name = name;
        System.out.println("load wife"+this);
    }
}
package com.learn.beans;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

/**
 * @author wangxing
 * @version 2020/6/26 17:57 Administrator
 */
public class Child extends Person implements InitializingBean, DisposableBean {
    public void initByConfig(){
        System.out.println("通过配置实现的初始化方法");
    }
    public void destroyByConfig(){
        System.out.println("通过配置实现的销毁方法");
    }

    public void destroy() throws Exception {
        System.out.println("通过实现DisposableBean接口实现的销毁方法");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("通过实现InitializingBean接口实现的初始化方法");
    }
}
package com.learn.beans.factory;

import com.learn.beans.Child;
import com.learn.beans.Person;
import com.learn.beans.Wife;

/**
 * @author wangxing
 * @version 2020/6/26 17:59 Administrator
 */
public class PersonFactory {
    public Person createPersonFactory(){
        return new Person();
    }
    public Person createPersonFactory(String type){
        if ("wife".equals(type)) {
            return new Wife();
        } else if ("child".equals(type)) {
            return  new Child();
        }else{
            return new Person();
        }
    }
}

(2)学习spring-ioc的基础功能

0.<bean>标签定义类基础信息

<!--    可以在bean的name中使用,空格,逗号,分号来设置多个别名-->
    <bean class="com.learn.beans.Pesron" id="user" name="user3 user4,user5;user6">
        <description>用来描述一个类是用来做什么的</description>
    </bean>
<!--    别名-->
    <alias name="user" alias="user2"></alias>

bean标签在spring-beans.xsd中的定义

<xsd:element name="bean">
        <xsd:annotation>
            <xsd:documentation source="java:org.springframework.beans.factory.config.BeanDefinition">
<![CDATA[
    Defines a single (usually named) bean.

    A bean definition may contain nested tags for constructor arguments,
    property values, lookup methods, and replaced methods. Mixing constructor
    injection and setter injection on the same bean is explicitly supported.
            ]]>
</xsd:documentation>
        </xsd:annotation>
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="identifiedType">
                    <xsd:group ref="beanElements"/>
                    <xsd:attributeGroup ref="beanAttributes"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>

bean标签的属性
bean标签的属性定义源码

1.在方法中通过IOC容器进行类加载的方式

/*
1.通过类来获取bean
当配置文件中有多个相同类的配置时会报错,一般使用方法二和三
*/
Pesron user = (User)context.getBean(Pesron.class);
/*
 2.通过xml配置文件中名称来获取bean
需要进行强制转型
*/
Pesron user = (Pesron)context.getBean("user");
/*
3.通过名字+类来获取bean
通过名称和类型两个参数进行确认,保证不会加载错误
*/
Pesronuser = context.getBean("user",Pesron.class);

2.进行类信息的注入
主要有两种注入方式

  • setter方法注入

基于setter方法的依赖注入

  1. 属性必须声明了set方法
  2. name是根据set方法的名字来的 比如方法名字是: setIdxx ‐> name="idxx"
 <bean class="com.learn.beans.Person" id="person">
        <property name="id" value="1"></property>
        <property name="idxxx" value="2"></property>
</bean>

可以使用p:标签来进行注入setter方法,但是不能注入集合

使用p标签需要引入对应标签
xmlns:p="http://www.springframework.org/schema/p"

<bean class="com.learn.beans.Person" id="person" p:id="1" p:idxxx="2"/>
  • 构造方法注入

基于构造函数的依赖注入

  1. 将会调用自定义构造函数来实例化对象,就不会调用默认的无参构造函数
  2. name是根据构造函数的参数名来的, 比如:User(String idxx) ‐> name="idxx"
  3. name属性可以省略 但是要注意参数的位置
  4. 如果非要把位置错开 可以使用 name 或者 index 或者 type
  5. index 是下标 从0开始
  6. type 在位置错开情况下只能在类型不一样的时候指定才有明显效果
<bean class="com.learn.beans.Wife" id="wife">
        <constructor-arg name="id" value="123"></constructor-arg>
        <constructor-arg name="name" value="vxk"></constructor-arg>
    </bean>

可以使用c:标签来进行构造方法的参数注入

使用c:标签构造方法进行参数注入需要引入对应标签
xmlns:c="http://www.springframework.org/schema/c"

<bean class="com.learn.beans.Wife" id="wife" c:id="123" c:name="bxl"/>

3.各种类型数据的配置方式

<property>标签进行属性配置
<list>标签配置list对象
<set>标签配置set对象
<map>标签配置map对象
使用ref属性进行引用类的配置

 <bean class="com.learn.beans.Person" id="person" >
        <property name="id" value="1"></property>
        <property name="name"> <null></null></property>
        <property name="gender" value=""></property>
        <property name="wife" ref="wife"></property>
        <property name="hobbies">
            <list>
                <value>阅读</value>
                <value>运动</value>
            </list>
        </property>
        <property name="course">
            <map>
                <entry key="1" value="java"></entry>
                <entry key="2" value="Spring"></entry>
                <entry key="3" value="SpringBoot"></entry>
            </map>
        </property>
    </bean>
<bean class="com.learn.beans.Wife" id="wife" c:id="123" c:name="bxl"/>

4.控制依赖加载顺序使用bean:depends-on属性来控制加载依赖于某个类

    <bean class="com.learn.beans.Person" id="person2" depends-on="wife2" c:name="wx" c:wife-ref="wife2" lazy-init="true"/>
    <bean class="com.learn.beans.Wife" id="wife2" c:name="bxl2" c:id="2"/>

5.将对象设置为懒加载,使用bean:lazy‐init属性,设置为true时类的加载变更为懒加载,只在调用时进行加载

   <bean class="com.learn.beans.Person" id="person" c:name="wx" c:wife-ref="wife" lazy-init="true"/>

6.自动注入使用bean:autowire属性来进行控制
autowire的值包括四种

  • default/no:不自动装配
  • byType 按照类型进行装配,以属性的类型作为查找依据去容器中找到这个组件,如果有多个类型相同的bean对象,那么会报异常,如果找不到则装配null
  • byName 按照名字进行装配,以属性名作为id去容器中查找组件,进行赋值,如果找不到则装配null
  • constructor:按照构造器进行装配,先按照有参构造器参数的类型进行装配,没有就直接装配null;如果按照类型找到了多个,那么就使用参数名作为id继续匹配,找到就装配,找不到就装配null
<bean class="com.learn.beans.Person" autowire="byName"></bean>
//通过将autowire-candidate 属性设置为false,避免对bean定义进行自动装配
//通过将其<bean/> 元素的primary属性设置为 true,将单个bean定义指定为主要候选项

7.bean的作用域设置使用bean:scope属性进行配置

| singleton | (默认) 每一Spring IOC容器都拥有唯一的实例对象。
| prototype | 一个Bean定义可以创建任意多个实例对象.
| request | 将单个bean定义范围限定为单个HTTP请求的生命周期。 也就是说,每个HTTP请求都有自己的bean实例,它是在单个bean定义的后面创建的。 只有基于Web的Spring ApplicationContext的才可用。
| session | 将单个bean定义范围限定为HTTP Session的生命周期。 只有基于Web的Spring ApplicationContext的才可用。
| application | 将单个bean定义范围限定为ServletContext的生命周期。 只有基于Web的Spring ApplicationContext的才可用。
| websocket | 将单个bean定义范围限定为 WebSocket的生命周期。 只有基于Web的Spring ApplicationContext的才可用。

注意

多线程下单例模式可能会产生线程安全问题,单例模式的对象中控制尽量不要有数据属性,只保留操作方法,如果一定要有属性,那么记得加锁

<bean class="com.learn.beans.Wife" id="wife3" scope="prototype" c:id="2" c:name="123"></bean>

8.bean的生命周期控制

在对象的加载前和销毁后执行我们定义的方法
实现方式有两种

  • 1使用接口实现的方式来实现生命周期的回调
    初始化方法: 实现接口: InitializingBean 重写afterPropertiesSet方法初始化会自动调用的方法
    销毁的方法: 实现接口: DisposableBean 重写destroy 方法 销毁的时候自动调用方法
    -2 使用指定具体方法的方式实现生命周期的回调,在对应的bean里面创建对应的两个方法init‐method="init" destroy‐method="destroy"

注意:

销毁是在spring容器关闭的时候

package com.learn.beans;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

/**
 * @author wangxing
 * @version 2020/6/26 17:57 Administrator
 */
public class Child extends Person implements InitializingBean, DisposableBean {
    public void initByConfig(){
        System.out.println("通过配置实现的初始化方法");
    }
    public void destroyByConfig(){
        System.out.println("通过配置实现的销毁方法");
    }

    public void destroy() throws Exception {
        System.out.println("通过实现DisposableBean接口实现的销毁方法");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("通过实现InitializingBean接口实现的初始化方法");
    }
}

<bean class="com.learn.beans.Child" init-method="initByConfig" destroy-method="destroyByConfig" id="child" p:id="1" p:name="wyr" lazy-init="true"></bean>

9.向配置文件中引入配置文件使用context:property-placeholder标签

使用context:property-placeholder标签需要引入对应引用
xmlns:context="http://www.springframework.org/schema/context"

定义配置文件

mysql.username=xxx
mysql.password=xxx
mysql.url=jdbc:mysql://192.168.1.150:3306/mall
mysql.driver=com.mysql.jdbc.Driver

xml用引入配置文件,使用第三方类

<context:property-placeholder location="db.properties"></context:property-placeholder>

 <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource" lazy-init="true">
         <property name="username" value="${mysql.username}"></property>
         <property name="password" value="${mysql.password}"></property>
         <property name="url"  value="${mysql.url}"></property>
         <property name="driverClassName" value="${mysql.driver}"></property>
     </bean>

总结

定义

DIP/IOC/DI

IOC的好处

IOC配置

ApplicationContext的三种配置方式

XML配置方式的学习整理

spring-ioc的xml配置基础标签

1. <bean>
2. bean的注入方式
3. 各类数据类型的装配方式
4. 对类注入进行依赖控制depends-on
5. 将类注入设置为懒加载lazy‐init
6. 自动注入bean:autowire
7. bean的作用域bean:scope
8. bean生命周期控制init‐method="init" destroy‐method="destroy"
9. 引入外部配置文件context:property-placeholder
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335