整体介绍

◆Spring快速入门 ◆Spring XML配置 ◆对象实例化配置 ◆依赖注入配置 ◆注解与Java Config ◆Spring单元测试

IoC控制反转

● 初始情况:比如,我们眼前有三种苹果供选择,但是我们想吃甜甜的苹果,为此,我们需要把【红富士】、【青苹果】、【金帅】挨个尝一边,才能知道我们需要的是【红富士】。

● 有了水果摊老板的加入:只需要把我们的需求【想吃甜甜的苹果】,告诉水果摊老板,水果摊老板就会把【红富士】推荐给我们;

上面的案例,消费者把【水果的选择】这个工作,转交给了水果摊老板;这个职责转交的过程,就有点控制反转的意味了。自然,为了实现上面的过程,水果摊老板需要有很多工作去做,比如水果摊老板自己就要把【红富士】、【青苹果】、【金帅】这三种苹果批发过来,并且需要了解这三种苹果的特点。

简单工厂模式虽然和IoC不是一个东西,但多少还是有点类似之处;

IoC控制反转是什么

IoC控制反转的核心理念是:不是最终的消费者去负责创建对象,而是引入一个中间人角色,由这个中间人统一负责对象的创建过程;

◆IoC控制反转,全称Inverse of Control,是一种设计理念 ◆由代理人来创建与管理对象,消费者通过代理人来获取对象 ◆IoC的目的是降低对象之间直接耦合

◆加入IoC容器将对象统一管理,让对象关联变为弱耦合

(1) 何为【反转】:【由,对象的使用方,去创建对象】变成了【由,IoC容器,去创建对象】;即,【直接和对象打交道,创建对象】的这个职责发生了转换;

(2) IoC(控制反转)的好处:降低了对象之间的耦合:方便了后续的扩展和维护

● 没有代理人之前,【对象的使用方】需要了解所有对象的内容后,才能知道我们需要的是哪个对象;而这种情况,【对象的使用方】和【对象】是硬性关联,程序难以维护和扩展;

● 但是,引入代理人之后,【对象的使用方】不用去了解所有的对象,【对象的使用方】直接和代理人【IoC容器】打交道就行了;这就意味着,【对象的使用方】和【对象】之间,通过【IoC容器】这个代理人,实现了解耦;对象之间更加灵活,方便扩展和维护;

DI依赖注入

◆IoC是设计理念,是现代程序设计遵循的标准,是宏观目标 ◆Dl(Dependency Injection)是具体技术实现,是微观实现 ◆Dl在Java中利用反射技术实现对象注入(Injection)

1) IoC是一种设计理念,与语言无关;现代应用程序在进行对象关系设计的时候,或多或少的都要引入IoC理念;

(2) 但是,为了在实际软件中应用IoC这种设计理念,在编程语言中,就是使用DI(依赖注入)这种策略去实现的;DI(依赖注入),主要是在【程序运行时】完成对象的创建和绑定工作;

(3) 为了实现DI(依赖注入)这种策略,在Java这种语言中,底层是利用反射技术来实现的;(关于反射可以参考反射

(4) 为了实现DI(依赖注入)策略,不同的语言的具体方式不同;比如,Java中使用反射技术,Python和.Net又有其他的技术来实现;

Spring介绍

Spring的含义

◆Spring可从狭义与广义两个角度看待 ◆狭义的Spring是指Spring框架(Spring Fremework) ◆广义的Spring是指Spring生态体系

(1)狭义的Spring框架和Mybatis框架类似,都是通过一种高度的封装和抽象的Java技术,来简化程序的开发;只是Mybatis框架简化的是数据库的增删改查;Spring框架提供了IoC容器,来管理系统中的对象及其依赖;

(2)广义的Spring:最原始的Spring是只有一个IoC容器的Spring Framework,对对象集中管理;在此基础上,Spring不断发展,派生了大量的工程和完善的生态体系,由此形成了广义上的Spring;

狭义的Spring框架

◆Spring框架是企业开发复杂性的一站式解决方案 ◆Spring框架的核心是IoC容器AOP面向切面编程 ◆Spring loC负责创建与管理系统对象,并在此基础上扩展功能

(1)以前在实际开发中,有很多 【“开发体验不友好” 或者 “功能缺失” 的部分】;引入Spring框架后,通过Spring的补充,可以让整个体系更加完整;

(2)IoC(控制反转)容器:用于统一管理系统对象;AOP面向切面编程:IoC容器是所有对象管理的基础,包括AOP在内的很多东西都是建立在IoC基础上的;

(3)Spring基于IoC,扩展了很多功能;还是那句话,在Spring中,IoC是前提和基础;

广义上的Spring生态体系

(1)Spring逐渐发展出了以下七大技术:这七大技术,是各种Spring工程和框架的支撑;

(1) Microservice:分布式微服务:其提供了Spring cloud,可以帮助我们轻松地实现分布式架构;

(2) Reactive:响应式编程:这是一种基于异步的、非阻塞的、全新的开发理念和技术;

(3) Cloud:云端技术:如在云端进行自动扩展和自动连接,就可以使用Cloud这个云端技术;

(4)Web apps:网页应用程序:这是我们最常用的一个模块 ,Spring提供了Spring MVC,让我们摆脱了以前使用Servlet的那种繁琐的开发方式,以更简洁和更优雅的API,来完成web应用程序的开发;

(5) Serverless:无服务器编程:在程序运行过程中,可以让网页以单页形式拜托服务器的束缚,从而让用户获得更好的使用体验;这是最近火起来的一个概念;

(6) Event Driven:事件驱动;

(7) Batch:批处理;

以上,就是Spring能够做到的事情;

(2)在以上七种技术的基础上,派生出来了几十种不同的项目;

进入Spring官网Spring | Home

即,可以看到,经过多年的发展,Spring在【狭义的,Spring Framework框架,IoC容器】的基础上,派生出了如此之多的,针对不同场景的不同的应用;由此出现了Spring派:该派的开发者,在解决问题的时候,会优先考虑在Spring生态中是否提供了对应的解决方案,如果有直接优先去用;

(3)然后,这儿先主要看看最基础的【Spring Framework】;

传统开发方式

(1)传统的开发方式:对象直接引用对象

● 传统开发方式,由于对象之间往往会有 直接的 引用, 而这个引用是通过new关键字来创建的,所以导致程序难以维护;

● 如上图案例:【使用者】需要用到【A对象】,但是【A对象】要实现某个功能,需要再去new一个【B对象】;

●结合以前的开发经验,【A对象】可以看成一个Service,【B对象】可以看成一个Dao;然后,在Servlet中,需要new一个Service对象,然后在Service对象中,又需要new一个Dao对象;

这种开发方式又很大弊端;比如,随着程序的开发,【B对象】已经不适合使用了,而是由其他程序员开发了另一个【C对象】,此时,如果【A对象】要抛弃【B对象】而拥抱【C对象】,那么就需要修改【A对象】的源代码,改new【C对象】;既然,修改了源代码,那么程序就要重新编译、重新测试、重新发版、重新审批、重新上线;而这是一个非常繁琐的流程;

所以,在实际开发中,为了避免如上的问题,不推荐由具体的【使用者】去主动创建【对象】; (自然,案例中,对于【A对象】来说,【使用者】是使用者;而,对于【B对象】来说,【A对象】是使用者)

PS:这儿的描述,大概率存在偏差,以后回头来改; ???????????????????

(2)引入Spring框架:使用其中的Spring IoC容器

● 引入Spring IoC容器,采用被动的方式,由IoC容器来创建和管理这些对象,【使用者】只是单纯的从IoC容器中,提取对象就可以了;

● 还是前面的例子,如果用Spring来做,就如上图所示:

● 这个IoC容器是一个抽象的,其就相当于在Java的运行内存中,开辟了一块空间,这个空间是由Spring管理的;

● 所有的对象,不是由【使用者】或者【A对象】 (PS:相对【B对象】来说,【A对象】也是使用者啦) 来创建的,而都是由Spring IoC容器统一负责创建;

●当IoC容器,把对象都创建好了之后;由于【A对象】是依赖于【B对象】的(即在【A对象】中是需要用到【B对象】),我们自然不是在【A对象】中去new一个【B对象】,而是基于反射技术,将【A对象】的依赖【B对象】注入到【A对象】中;这就是所谓的依赖注入(DI);

● 引入Spring IoC容器来统一管理对象之后,对于【使用者】来说,不需要关注在容器内部到底有几个对象,也不需要关注对象之间的关系是什么样的;【使用者】只需要关注“要从什么地方将需要的对象,提取出来”就行了;即【使用者】不再直接面向具体的对象,而是面向IoC容器,通过IoC容器获取所需要的对象;

(3)Spring IoC容器职责

◆对象的控制权交由第三方统一管理(IoC控制反转) ◆利用Java反射技术实现运行时对象创建与关联(DI依赖注入) ◆基于配置提高应用程序的可维护性与扩展性

● 在Spring中可以通过xml配置、注解或Java Config的方式,来实现IoC容器;

分析传统编码方式的不足

●传统编码方式中,【一些字符串文本信息,是写死的】,【对象也是在代码中new好的、写死的】,【对象之间的关系,也是写死的;对象之间是硬关联】;这些因素的结果是,当需要调整程序的时候,就必须要修改源代码;这会导致程序可扩展性和可维护性很低;

● 由此,引出Spring Framework的必要性;(其实也就是,Spring IoC容器的必要性);Spring IoC容器通过配置的方式,实现了【对象实例化】和【对象与对象之间的依赖关系】;从而,可以解决传统编码方式的不足;

一:需求说明:

那么,该如何编写程序,使得孩子吃到自己喜欢的苹果?

二:传统方式,程序代码实现:

这个地方不是重点,不需要抠代码细节,主要关注其中的对象依赖关系

(1)创建一maven工程

(2)创建演示类

Apple类:

package com.imooc.spring.ioc.entity;
 
 public class Apple {
     private String title; //标签、品种
     private String color; //颜色
     private String origin; //产地
 
     public Apple() {
     }
 
     public Apple(String title, String color, String origin) {
         this.title = title;
         this.color = color;
         this.origin = origin;
     }
 
     public String getTitle() {
         return title;
     }
 
     public void setTitle(String title) {
         this.title = title;
     }
 
     public String getColor() {
         return color;
     }
 
     public void setColor(String color) {
         this.color = color;
     }
 
     public String getOrigin() {
         return origin;
     }
 
     public void setOrigin(String origin) {
         this.origin = origin;
     }
 }

Child类:

package com.imooc.spring.ioc.entity;
public class Child {
private String name; //小孩名字
private Apple apple; //小孩吃到的苹果
 
public Child() {
}
 
public Child(String name, Apple apple) {
this.name = name;
this.apple = apple;
}
 
public String getName() {
return name;
}
 
public void setName(String name) {
this.name = name;
}
 
public Apple getApple() {
return apple;
}
 
public void setApple(Apple apple) {
this.apple = apple;
}
 
public void eat() {
System.out.println(name + "吃到了" + apple.getOrigin() + "种植的" +
 
apple.getTitle());
}
}

Application类:

package com.imooc.spring.ioc;
 
 import com.imooc.spring.ioc.entity.Apple;
 import com.imooc.spring.ioc.entity.Child;
 
 public class Application {
     public static void main(String[] args) {
         Apple apple1 = new Apple("红富士","红色","欧洲");
         Apple apple2 = new Apple("青苹果","绿色","中亚");
         Apple apple3 = new Apple("金帅","黄色","中国");
 
         Child lily = new Child("莉莉",apple1);
         Child andy = new Child("安迪", apple2);
         Child luna = new Child("露娜", apple3);
         lily.eat();
         andy.eat();
         luna.eat();
 
     }
 }

三:弊端分析:重点!!!

(1) 问题一:【字符(串)等文本信息】是写死的;

(2) 问题二:【对象】是写死的,在编译时就已经确定了;

(3) 问题三:【对象之间的关系】是写死的,在编译时就已经确定了;即,对象之间是硬关联;(最重要的一个问题)


为了解决上述问题,Spring应运而生,Spring最根本的目的就是【通过配置的方式,完成对象的实例化,和,对象与对象之间的依赖关系】。。。。这一切的目的,都是提高程序的可维护性和可扩展性;

Spring IoC初体验一

传统编码方式的缺点: 传统编码方式中,【一些字符串文本信息,是写死的】,【对象也是在代码中new好的、写死的】,【对象之间的关系,也是写死的;对象之间是硬关联】;这些因素的结果是,当需要调整程序的时候,就必须要修改源代码;这会导致程序可扩展性和可维护性很低;

Spring IoC容器的解决方案: Spring IoC容器则通过配置的方式,完成了【对象实例化】和【对象与对象之间的依赖关系】;从而,可以解决传统编码方式的不足; (2) 本篇博客的内容,主要是演示IoC容器,完成【对象的实例化】;IoC容器完成【对象与对象之间的依赖关系】会在下篇博客说明;

(3) 篇博客的目的是,通过一个案例,来对 Spring有个感性认知,也就是初体验;至于,Spring IoC的各个配置和属性的详细介绍,在后面会逐一介绍;


目录

1.在pom.xml中引入Spring依赖

2.创建【applicationContext.xml】配置文件

3.实际测试和验证


还是在上文【分析传统编码方式的不足】的代码基础上,将这个项目改为由Spring IoC管理的程序;

1.在pom.xml中引入Spring依赖

和Mybatis一样,Spring也是第三方开发的一个程序,所以需要在pom.xml中引入Spring的依赖;

<?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
 
        <groupId>com.imooc.spring</groupId>
        <artifactId>s01</artifactId>
        <version>1.0-SNAPSHOT</version>
        <repositories>
            <repository>
                <id>aliyun</id>
                <name>aliyun</name>
                <url>https://maven.aliyun.com/repository/public</url>
            </repository>
        </repositories>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.9</version>
            </dependency>
        </dependencies>
 
    </project>

说明:

**(1)**啰嗦一下旧内容,某项目的依赖看可以从【Maven中央仓库的检索网站:https://search.maven.org/】查询】;


(2) 依赖说明

上面还有一点要重复啰嗦一下:Spring整个生态中有很多子工程,不同的子工程可以解决不同的需求;这儿我们选择员工的是【springframework下的spring-context】这个子工程;


(3) 关于,groupId和ArtifactId的理解,可以参考【Maven十一:IDEA操作Maven;设置Maven,创建基于maven的java工程(非web工程),IDEA的maven快捷方式;(两个问题:设置Maven不具有普适性;junit单元测试)】;可以发现,我们在自己创建maven工程的时候,都要设置groupId和ArtifactId;然后,这儿对groupId和ArtifactId加深了理解,参考博客【附加:Maven中的groupId和artifactId;】;


2.创建【applicationContext.xml】配置文件

applicnContext.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="sweetApple" class="com.imooc.spring.ioc.entity.Apple">
            <property name="title" value="红富士"></property>
            <property name="origin" value="欧洲"></property>
            <property name="color" value="红色"></property>
        </bean>
        <bean id="sourApple" class="com.imooc.spring.ioc.entity.Apple">
            <property name="title" value="青苹果"></property>
            <property name="origin" value="中亚"></property>
            <property name="color" value="绿色"></property>
        </bean>
        <bean id="softApple" class="com.imooc.spring.ioc.entity.Apple">
            <property name="title" value="金帅"></property>
            <property name="origin" value="中国"></property>
            <property name="color" value="黄色"></property>
        </bean>
    </beans>

说明:

(1) 【applicationContext.xml】文件自然是有格式要求的,然后Spring官方的约束采用的是【XML Schema】(而不是DTD);其格式约束文件通过以下方式获取;

将上面的内容复制进applicationContext.xml后:


(2) 关于,XML文件约束的内容,可以参考【XML二:XML语义约束DTD简介;DTD示例;XML Schema;】及附近相关文章;


(3) <bean>标签介绍;

在【applicationContext.xml】配置文件中,每写一个,在IoC容器启动的时候,在容器中都会创建与之对应的对象;


(4) bean的id不能重复


(5) summary

3.实际测试和验证

SpringApplication类:

package com.imooc.spring.ioc;
 
 
import com.imooc.spring.ioc.entity.Apple;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Apple apple1 = context.getBean("sweetApple", Apple.class);
System.out.println(apple1.getTitle());
}
}

说明:

(1) 首先,加载配置文件,获得ApplicationContext对象,也就是初始化IoC容器;

这一点说明一下:只有当程序中执行到了【ApplicationContext context = new ClassPathXmlApplicationContext(“classpath:applicationContext.xml”);】 这句话时,才会去加载applicationContext.xml文件、才会去初始化IoC容器、才会去创建这些对象;即这些创建对象的过程,不是在程序编译时执行的,而是在程序运行时才执行的;而在java中,这背后的支撑就是反射


(2) 然后,从IoC容器中,获取想要的对象;


(3) 运行结果:


(4) 可以感受到:

● Spring IoC容器,就是通过配置的方式,让我们在不需要new关键字的情况下创建对象;而对象的创建的规则,就是按照applicationContext.xml配置文件中的内容来进行的;

● 对于每一创建的对象,都默认放在了Spring IoC容器中,由IoC统一管理;并给每一个对象贴上标签,这个标签就是bean ID;

● 通过Spring管理对象,最直观的好处是:把原本【写死的代码】变成了【可配置的文本】;

●然后,有个启发:在以后工作中,使用Spring开发时,绝大多数的静态信息都不必写在程序中,而是采用配置的方式放到xml文本文件中;一旦,要更改某些信息,直接修改xml这个文本文件就行了,不需要修改源代码;


(5) 然后,这个例子只是演示了【Spring IoC容器中的,对象实例化】;下一篇博客会演示【Spring IoC容器中的,对象与对象之间的依赖关系】;

Spring IoC初体验二

说明:

●传统编码方式的缺点:传统编码方式中,【一些字符串文本信息,是写死的】,【对象也是在代码中new好的、写死的】,【对象之间的关系,也是写死的;对象之间是硬关联】;这些因素的结果是,当需要调整程序的时候,就必须要修改源代码;这会导致程序可扩展性和可维护性很低;

● Spring IoC容器的解决方案:Spring IoC容器则通过配置的方式,完成了【对象实例化】和【对象与对象之间的依赖关系】;从而,可以解决传统编码方式的不足;

(2) 上文初体验一中演示了IoC容器,完成【对象的实例化】; 本篇博客就来演示,IoC容器完成【对象与对象之间的依赖关系】;

(3) 篇博客的目的是,通过一个案例,来对 Spring有个感性认知,也就是初体验;至于,Spring IoC的各个配置和属性的详细介绍,在后面会逐一介绍;


一:在【applicationContext.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="sweetApple" class="com.imooc.spring.ioc.entity.Apple">
            <property name="title" value="红富士"></property>
            <property name="origin" value="欧洲"></property>
            <property name="color" value="红色"></property>
        </bean>
        <bean id="sourApple" class="com.imooc.spring.ioc.entity.Apple">
            <property name="title" value="青苹果"></property>
            <property name="origin" value="中亚"></property>
            <property name="color" value="绿色"></property>
        </bean>
        <bean id="softApple" class="com.imooc.spring.ioc.entity.Apple">
            <property name="title" value="金帅"></property>
            <property name="origin" value="中国"></property>
            <property name="color" value="黄色"></property>
        </bean>
        <bean id="lily" class="com.imooc.spring.ioc.entity.Child">
            <property name="name" value="莉莉"></property>
            <property name="apple" ref="sweetApple"></property>
        </bean>
        <bean id="andy" class="com.imooc.spring.ioc.entity.Child">
            <property name="name" value="安迪"></property>
            <property name="apple" ref="sourApple"></property>
        </bean>
        <bean id="luna" class="com.imooc.spring.ioc.entity.Child">
            <property name="name" value="露娜"></property>
            <property name="apple" ref="softApple"></property>
        </bean>
    </beans>

说明:

(1) 通过以下的方式,完成对象之间的依赖关系

二:实际验证和测试

SpringApplication类:

package com.imooc.spring.ioc;
 
    import com.imooc.spring.ioc.entity.Apple;
    import com.imooc.spring.ioc.entity.Child;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
 
    public class SpringApplication {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
            Apple apple1 = context.getBean("sweetApple", Apple.class);
            System.out.println(apple1.getTitle());
 
            Child childLily = context.getBean("lily",Child.class);
            childLily.eat();
            Child childAndy = context.getBean("andy",Child.class);
            childAndy.eat();
            Child childLuna = context.getBean("luna",Child.class);
            childLuna.eat();
 
        }
    }

说明:

(1) 程序执行:从IoC容器中,获取想要的对象;

(2) 运行结果

三:【Spring IoC初体验】部分Summary

1.用传统的代码方式不好吗?为什么要用这种比较复杂的配置文件来实现【对象之间的依赖】?

使用配置方式来实现【对象之间的依赖】目的是:提高程序上线后的可维护性;

●比如,当莉莉不喜欢吃红富士后,如果采用传统的在代码中new对象的方式,就需要去修改源代码;但如果采用上面的配置方式,我们只需要修改配置文件就行了,不需要修改源代码;

●又比如,莉莉不喜欢吃红富士了,而是喜欢吃除了红富士、青苹果、金帅之外的的另一种苹果【美国的蛇果】,而这个蛇果在配置文件是没有的;那么我们也不需要修改源代码,只需要在配置文件中,新创建一个对象,然后将其和莉莉设置上就可以了;

所以,Spring IoC容器根本的用途就是【让对象和对象之间有效的解耦】;

比如这个案例

案例: 莉莉不喜欢吃红富士了,而是喜欢吃除了红富士、青苹果、金帅之外的的另一种苹果【美国的蛇果】

完全不需要修改源代码,因为IoC中的对象是通过配置得来的,所以这儿我们只需在applicationContext.xml配置文件中,增加蛇果这个Apple对象的配置就行了,不用修改任何源代码;

2.Summary:

引入Spring IoC容器后,编程上带来了一个巨大变化;以前,对象关系是写在代码中的,即对象关系是通过代码来维护的;现在,对象关系是通过配置的方式来维护;

这给软件开发理念带来了颠覆式的变化;

变化一:对于程序员的直观感受来说,【对象的创建】和【对象之间的依赖关系】实现方式变化了;

以前传统的编码方式: 所有的对象都是通过new关键字得来的,在编译时就决定了对象之间的依赖关系;可以认为,【对象的创建】和【对象之间的依赖关系】都是由程序主动发起的;

引入Spring IoC容器后: 所有对象的创建都是在Spring IoC容器初始化的时候,自动实例化的;虽然其本质,在程序最底层也是通过反射机制去创建并实例化对象,然后再给这个对象贴上一个标签,但是这个工作交给了Spring IoC来做;作为程序员的我们,不需要关心底层的依赖关系,只需要知道【从哪个标签中,可以获取到我们需要的对象】,而这个对象的底层到底依赖其他哪个对象,大部分情况下我们不需要知道,因为这个对象的关联工作都是IoC容器帮我们做好的;在运行时,IoC容器会主动的进行对象的注入,而我们在获取对象的时候,从IoC容器中进行提取,将现成的对象直接拿出来用就行了;上面这是一个被动的过程,因为对象已经由IoC容器创建好了,对象创建的主体也从【程序代码】转移到了【Spring IoC容器中】,这也是IoC容器的【控制反转】的意涵所在;

上面的过程其实可以这样理解:

● 现在有两个工作【在配置文件中,完成Spring IoC容器的配置工作】和【在程序代码中,直接去获取IoC容器中的对象】;

● 对于自己来说,这两个工作都需要自己去完成;

● 只是,当自己完成了【在配置文件中,完成Spring IoC容器的配置工作】后,再去做【在程序代码中,直接去获取IoC容器中的对象】的时候,就可以暂时不用关心对象的创建和对象底层依赖的内容,因为这些内容在我们做【在配置文件中,完成Spring IoC容器的配置工作】的时候就已经弄好了。

变化二:(【对象的创建】和【对象之间的依赖关系】)从【编译时就定死】到【运行时动态进行】的变化;

以前传统的编码方式: 通过程序代码进行对象的主动创建和设置对象之间关系时,程序在编译时,就已经完全固定了;

**引入Spring IoC容器后:**所有对象的创建和对象之间的关系,都是被IoC容器来做的;而这个过程是在程序运行时,通过反射技术来进行动态的注入;所以,这种方式更加灵活;

正是由于引入了Spring IoC,大大提高了程序的可维护性和可扩展性;

默认构造方法初始化Ioc容器

(1) Spring IoC部分主要分为以下几个模块:

即Spring IoC部分主要内容是【 使用XML的方式,实现Spring IoC 】,【Bean对象的作用域和生命周期 】,【使用注解方式,实现Spring IoC 】,【 使用Java Config方式,实现Spring IoC 】这四部分。 而本篇博客主要介绍,如上图的红色部分:无参构造(也就是默认构造方法)实例化对象;

说明: (1) 其实Spring IoC的核心就是管理对象,而Spring IoC容器中管理的对象是Java Bean对象(以后,可以使用Bean来指代IoC容器中的对象;),所以Spring IoC部分的主要内容也可以认为是【如何配置Bean】;

这三种配置方式,其本质是一样的,都是告诉Spring IoC容器如何实例化Bean和如何管理Bean;只是,这三种配置方式外在表现形式不一样而已;


1.内容简介

(1.1)在xml配置文件中,配置Bean(就是对象啦);

(1.2)如何去加载xml配置文件?


2.演示:基于无参的默认构造方法实例化对象

在pom文件中,引入Spring依赖;

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
 
        <groupId>com.imooc</groupId>
        <artifactId>s2</artifactId>
        <version>1.0-SNAPSHOT</version>
 
        <repositories>
            <repository>
                <id>aliyun</id>
                <name>aliyun</name>
                <url>https://maven.aliyun.com/repository/public</url>
            </repository>
        </repositories>
 
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.9</version>
            </dependency>
        </dependencies>
 
    </project>

说明:

(1) 这儿可以参考下【Spring IoC容器与Bean管理4:使用XML方式实现Spring IoC预一:Spring IoC初体验一:IoC容器完成【对象的实例化】;】;

(2) 对于Spring IoC来说,在pom.xml中我们只需要手动引入一个模块:SpringFramework框架中的【spring-context】模块即可;这是因为,当我们引入【spring-context】模块后,其他需要的、所需要依赖的其他模块,maven会自动帮我们引入;

(3) 然后,经过一会,在工程扩展的类库的地方,就会看到【由maven引入的,spring-context模块和其他需要依赖的组件和模块】:

如上图所示,引入了【aop】,【beans】,【context】,【core】,【expression】,【jcl】这几个模块; 这几个模块是构成Spring运行的 最基础的支撑模块;其中,【context】模块就是我们在pom.xml中手动引入的模块;【aop】,【beans】,【core】,【expression】,【jcl】这几个模块,是maven根据【context】的依赖,自动帮我们引入的;

(4) 狭义SpringFramework框架的自带的其他模块介绍:

创建【包】和【两个演示用的实体类】

为了能够利用IoC对Apple类的对象进行实例化,就需要在xml配置文件了;

创建配置文件applicationContext.xml

applicationContext.xml:这个文件可以起其他名字,只是我们约定俗称起做applicationContext而已;

<?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="apple1" class="com.imooc.spring.ioc.entity.Apple">
        </bean>
    </beans>

说明:

(1) 这儿可以参考下【Spring IoC容器与Bean管理4:使用XML方式实现Spring IoC预一:Spring IoC初体验一:IoC容器完成【对象的实例化】;】 ;

(2) 为了,能看到,【其在实例化对象的时候,确实是调用无参构造来实例化的】,所以在Apple类的无参构造中,我们打印了一点东西:

实际测试:创建测试类:SpringApplication

SpringApplication:

package com.imooc.spring.ioc;
 
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
 
    public class SpringApplication {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        }
    }

说明:

(1) ApplicationContext接口;

(2) 可以看到,使用基于【默认构造方法】实例化对象,仅仅是创建了这个对象,这个对象的属性并没有初始化;

(3) 这儿可以参考下【Spring IoC容器与Bean管理4:使用XML方式实现Spring IoC预一:Spring IoC初体验一:IoC容器完成【对象的实例化】;】;

(4) 这儿可以想起我们以前为什们学习DOM4j操作xml文件,Spring的框架其实底层的发展建构离不开以前前人的探索基础,这里使用ApplicationContext可以预料到如何读取对象,其实就是Dom4j操作Xml,没什么两样


本篇博客是【通过默认构造方法,也就是无参构造啦】来实例化对象,但是默认构造方法的能力是有限的;很多情况下,我们需要带参构造方法去创建对象;这也就是下一篇博客的内容;

带参构造实例化Ioc容器

实例化Bean的二种方式 ◆基于构造方法对象实例化 ◆基于静态工厂实例化 ◆基于工厂实例方法实例化

本篇博客主要内容:

(1) 已知Spring IoC部分主要内容是【使用XML的方式,实现Spring IoC】,【Bean对象的作用域和生命周期】,【使用注解方式,实现Spring IoC】,【使用Java Config方式,实现Spring IoC】这四部分。 而本篇博客主要介绍,如下图的红色部分: 有参构造实例化对象

说明: (1) 在实际中,百分至九十的情况都是【基于构造方法实例化对象】;后面的【基于静态工厂实例化对象】和【就要工厂实例方法实例化对象】了解一下即可;


一:【基于有参构造方法实例化对象】简介

方式一:通过<constructor-arg>标签的name属性,来给构造方法指定的参数,设置对应的值

推荐使用这种方式;


方式二:通过<constructor-arg>标签的index属性,利用构造方法的参数的位置类设置

不推荐使用这种方式,万一以后一不小心更改了Apple类的带参构造的参数顺序;而在applicationContext.xml中却没做相应的调整,程序就会出现逻辑上的错误;


2.演示:基于有参构造方法实例化对象

在ApplicationContext.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
            https://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="apple1" class="com.imooc.spring.ioc.entity.Apple"></bean>
        <bean id="apple2" class="com.imooc.spring.ioc.entity.Apple">
            <constructor-arg name="title" value="蛇果"></constructor-arg>
            <constructor-arg name="color" value="红色"></constructor-arg>
            <constructor-arg name="origin" value="美国"></constructor-arg>
        </bean>
    </beans>

说明:

(1) 这个传值的过程:【applicationContext.xml中的<constructor-arg标签中的name值】➡【Apple类的有参构造方法的参数】➡【Apple类对象apple2对象的属性值】

(2) 可以印证(1)中的说法;

(3) 为了,能看到,【其在实例化对象的时候,确实是调用了带参构造来实例化的】,所以在Apple类的带参构造中,我们也打印了一点东西:

(4) 运行结果


附加一:如果构造方法中的参数是数字类型,那么applicationContext.xml文件中的<bean标签的<constructor-arg>中的value属性值可以是字符串

比如:

(1)首先,在Apple中增加一个数字类型的属性;然后,增加一个包含该属性的构造方法

(2)然后,在applicationContext.xml中,增加【可以调用,那个包含price属性的构造方法】的<bean>

说明:

(1) Spring会根据参数个数,去判断在创建对象的时候,去调用哪个构造方法

(2) Spring会自动的进行类型转换

(3)运行,观察效果


附加二:applicationContext.xml中的<bean>在类中找不到符合要求的构造方法:几种错误情况

正常情况:

但是,如果像下面这样: 错误情况一:

对于这种情况,如果硬要运行的话:

具体的错误信息摘抄部分如下:其意思是【调用的构造方法的参数数量不对,并没有找到两个参数的构造方法】

org.springframework.beans.factory.UnsatisfiedDependencyException: Error
creating bean with name 'apple3' defined in class path resource
[applicationContext.xml]: Unsatisfied dependency expressed through
constructor parameter 2: Ambiguous argument values for parameter of type
[java.lang.String] - did you specify the correct bean references as arguments?

又如下面这样: 错误情况二:

对于这种情况,如果硬要运行的话:

具体的错误信息摘抄部分如下:可以瞅两眼

org.springframework.beans.factory.UnsatisfiedDependencyException: Error
creating bean with name 'apple4' defined in class path resource
[applicationContext.xml]: Unsatisfied dependency expressed through
constructor parameter 2: Ambiguous argument values for parameter of type
[java.lang.String] - did you specify the correct bean references as
arguments?

又如下面这样: 错误情况三:

(PS:可能是我的IDEA版本低的问题,在其他版本的IDEA中,bean的id重复时,IDEA其也是会报错的)

但是,运行后会:

错误信息内容部分摘抄如下:其提示apple2这个bean已经被使用了;

Exception in thread "main"
org.springframework.beans.factory.parsing.BeanDefinitionParsingException:
Configuration problem: Bean name 'apple2' is already used in this <beans>
element

基于工厂实例化对象

(1)本篇博客,就介绍另一种实例化对象的方法:利用工厂实例化对象;即下面红色部分:【基于静态工厂实例化对象】和【基于工厂实例实例化对象】

说明:

(1) 在实际中,构造方法实例化对象占了90%以上。所以,本篇博客的内容,在实际中用的并不多,但也需要知道。

(2) 关于工厂(工厂通常就是指工厂模式)

(3) 工厂的根本用途就是隐藏创建类的细节,通过一个额外的工厂类,来组织和创建我们所需的对象;

(4) 按照工厂的表现形式,又可以分为【静态工厂】和【工厂实例】;


一:基于静态工厂实例化对象

创建一个静态工厂类

AppleStaticFactory类:

package com.imooc.spring.ioc.factory;
 
 import com.imooc.spring.ioc.entity.Apple;
 
 /**
  * 静态工厂:通过静态方法创建对象,隐藏创建的细节
  */
 public class AppleStaticFactory {
     public static Apple createSweetApple() {
         Apple apple = new Apple();
         apple.setTitle("红富士");
         apple.setOrigin("欧洲");
         apple.setColor("红色");
         return apple;
     }
 }

(1) 之所以是静态工厂,就是用于创建对象的方法是静态的;如上面的createSweetApple这个方法是static的;

(2) createSweetApple()方法是static的,所以这个方法是属于AppleStaticFactory这个工厂类本身的;

那么,Spring IoC容器,如何通过这个静态工厂类,去调用createSweetApple方法,获取对应的Apple对象?

在applicationContext.xml配置一个<bean>:去调用AppleStaticFactory这个工厂类的createSweetApple静态方法

(1) 上面配置以后,就可以在程序运行时,通过AppleStaticFactory这个工厂类的createSweetApple静态方法,来完成对象的创建,然后这个创建的对象的id设置为apple4,被Spring IoC容器管理;

(2) 很容易理解,因为createSweetApple()方法是静态的,即这个方法是直接属于工厂类;所以,上面的<bean就有点通过类直接去调用静态方法的赶脚;

测试

如果放开断点,让其彻底执行,AppleStaticFactory静态工厂类中的createSweetApple静态方法所返回的apple对象,就会被放入到Spring IoC容器中;

【基于静态工厂实例化对象】的合理性解释

疑问?

合理性解释:

第一条:还是多少有点【对象之间解耦】的赶脚的:

即,【基于静态工厂实例化对象】的主要目的是在IoC容器之外,通过程序代码的方式来组织对象。这样做的好处是:

第二条:可以在创建对象的同时,附加一些其他的逻辑


二:基于工厂实例方法实例化对象

创建演示【工厂实例方法实例化对象】的类:AppleFactoryInstance

已知,静态工厂中,静态方法是属于工厂类本身,不属于工厂的对象;

AppleFactoryInstance:

package com.imooc.spring.ioc.factory;
 
 import com.imooc.spring.ioc.entity.Apple;
 
 /**
  * 工厂实例方法创建对象:指IoC容器对工厂类进行实例化并调用对应的实例方法创建对象
  */
 public class AppleFactoryInstance {
     /**
      * 这个方法不是static的,即这个方法是属于工厂类对象的,不是属于工厂类本身的;
      * @return
      */
     public Apple createSweetApple() {
         Apple apple = new Apple();
         apple.setTitle("红富士");
         apple.setOrigin("欧洲");
         apple.setColor("红色");
         return apple;
     }
 }

(1) 可以到,createSweetApple()方法不是静态的,即这个方法是隶属于AppleFactoryInstance类的对象的;

在applicationContext.xml中配置

(1) 因为【createSweetApple()方法不是静态的,即这个方法是隶属于AppleFactoryInstance类的对象的】;所以,上面就先需要一个<bean将AppleFactoryInstance类的对象添加到Spring IOC容器中;;;然后再使用另外一个<bean去调用对象中的createSweetApple()方法,获取最终的Apple对象;

运行验证


三:summary:比较重要!

(1)使用【工厂实例化对象】也体现了对象解耦的意味:

如果我们的需求改变了,即有一个苹果比“红富士”更甜,那么我们只需要修改工厂类中的代码就行了,不用修改applicationContext.xml中的内容。虽然也是修改了代码,但是工厂模块在某种程度上可以看成是一个与其他代码相对隔绝的独立模块,也算是一种【对象与对象之间的解耦吧】

(2)使用【工厂实例化对象】,我们可以在创建对象的同时添加一些其他逻辑:

然后,由于【基于静态工厂实例化对象】和【基于工厂实例实例化对象】是通过代码来实例化对象,所以我们可以在实例化对象的同时,比较容易的添加其他的逻辑;这提供了一种灵活性,这也是Spring提供【基于工厂实例化对象】的用意所在;

(3)使用【工厂实例化对象】,用的比较少

在实际中,随着Spring的功能越来越强大,【基于工厂实例化对象】的使用越来越少了;