前言:前短时间看了一下狂神的Spring部分的知识,感觉还是有点混乱,并且对于框架部分的学习,还是要靠自己去学习,这样既可以提高自己的自学的能力,还可以帮助自己对Spring有一个更深的记忆,下面主要是通过官方文档,和网上相关资料进行学习总结。
官网文档:Spring Framework中文版
狂神Spring Framework视频地址:【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
@[TOC]
一.依赖注入和控制反转
问题1:什么是依赖注入和控制反转
对象依赖:有A类,B类,在A类中B类以形参的方式作为A类中的某个方法的参数,这个时候我们就可以认为A类B类是相互依赖的,即A类依赖B类,当然在Java,C++中体现为:局部变量、方法/函数的参数或者是对静态方法的调用。1.Java应用程序,通过各个对象协助组成,对象彼此之间相互
依赖
,这种方式缺乏将基本构建块组织成一个连贯的整体的方法(图1)。
相关学习链接:
Java语言中的关联与依赖关系
java对象间的关系(依赖,关联,组合,聚合)
Spring如何解决循环依赖问题
2.Spring 框架控制反转(IoC)组件通过提供一种形式化
的方法来将不同的组件
组成一个可以正常使用的应用程序,从而解决了这一问题(图2)。
3.Ioc
也称为依赖注入(DI)
,这是一个过程,在此过程中,对象通过构造函数参数,工厂方法的参数或在对象实例从工厂方法构造或返回后设置的属性来定义其依赖关系即与它们一起使用的其他对象。然后,容器
在创建Bean时注入那些依赖项。
回答上面的问题什么是控制反转和依赖注入:
下面的内容来源于:【第二章】 IoC 之 2.1 IoC基础 ——跟我学Spring3
4.控制反转
Ioc—Inversion of Control,即“控制反转
”,不是什么技术,而是一种设计思想
。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
对于控制反转你需要搞懂以下三个问题:
4.1 谁控制谁
传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象,这时候就是程序控制对象
。
而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建,这时候就是Ioc容器控制对象
。
4.2 控制什么
主要控制了外部资源获取
(不只是对象包括比如文件等)。
4.3 正转和反转:
正转
:传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,对象主动接受依赖对象,也就是正转
反转
:由容器来帮忙创建及注入依赖对象,对象被动的接受依赖对象,所以是反转
5.依赖注入
DI—Dependency Injection,即“
依赖注入
”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
对于依赖注入你需要搞懂以下四个问题:
5.1 谁依赖于谁
谁依赖于谁:当然是应用程序依赖于IoC容器;
5.2 为什么需要依赖
应用程序需要IoC容器来提供对象需要的外部资源;
5.3 谁注入谁
很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
5.4 注入了什么
就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)
最后对于控制反转和依赖注入我觉得狂神的理解方式挺不错的:
关于本部分的代码实例,你可以通过这篇博文进一步加深印象:Spring-IOC本质
二 Spring框架的模块
对于这部分本人觉得,你需要大致了解Spring框架包含哪一些组件,每一个组件的功能,相关技术。
2.1 Spring 框架
Spring 框架包含组织为约 20 个模块的功能。这些模块分为核心容器
,数据访问/集成
,Web
,AOP(面向方面的编程)
,检测
,消息传递
和测试
,如下图所示。
2.2.1 核心容器
2.2.2 AOP 和检测
2.2.3 Messaging
2.2.4 数据访问/集成
2.2.5 Web
2.2.6 Test
三:核心技术
该部分对应官方文档III.核心技术部分
3.1 IOC容器
org.springframework.beans
和org.springframework.context
软件包是 Spring Framework
的 IoC 容器
的基础。BeanFactory
提供了配置框架和基本功能,而ApplicationContext
添加了更多企业特定的功能。BeanFactory
接口提供了一种高级配置机制,能够 Management 任何类型的对象。 ApplicationContext
是BeanFactory
的子接口。Spring IoC容器
Management 的对象称为 beans
。 Bean
是由 Spring IoC 容器
实例化,组装和以其他方式 Management 的对象。
3.2 容器概述
接口org.springframework.context.ApplicationContext
代表 Spring IoC 容器
,并负责实例化,配置和组装上述 bean
。容器通过读取配置元数据来获取有关要实例化,配置和组装哪些对象的指令。配置元数据以 XML
,Java 注解
或 Java 代码
表示。Spring
提供了ApplicationContext
接口的几种实现方式。在独立应用程序中,通常创建ClassPathXmlApplicationContext
或FileSystemXmlApplicationContext
的实例。
通过上面的学习,大致的整体框架如图:
从上面的整个框架我们可以知道,要使用IOC容器
就得先获取ApplicationContext
接口,在独立应用程序中,通常创建ClassPathXmlApplicationContext
或FileSystemXmlApplicationContext
的实例,实例的参数就是元数据(XML
,Java 注解
或 Java 代码
),IOC容器
通过对应的元数据来获取对应的beans
(在beans
中存放我们需要交给IOC容器
管理的对象),进而来获取有关要实例化,配置和组装哪些对象的指令。
3.2.1 配置元数据
此配置元数据表示您作为应用程序开发人员如何告诉 Spring
容器实例化,配置和组装应用程序中的对象。
Spring 配置由容器必须 Management 的至少一个(通常是一个以上)bean 定义组成。基于 XML 的配置元数据显示了在<beans/>顶级元素中配置为<bean/>元素的这些 bean。 Java 配置通常在@Configuration类中使用@BeanComments 的方法。
以下示例显示了基于 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 id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
id属性是用于标识单个 bean 定义的字符串。 class属性定义 Bean 的类型,并使用完全限定的类名。 id 属性的值是指协作对象。在此示例中未显示用于引用协作对象的 XML。有关更多信息,请参见Dependencies。
3.2.2 实例化容器
使用import可以导入多个元数据配置:
在resources中有二个xml的元数据配置,如果beans.xml要使用bean2.xml里的一个id名为hello2的bean
bean2.xml:
这时候只需要在beans.xml中通过import指向该bean2.xml即可
然后直接通过bean的id直接获取该bean即可:
3.2.3 使用容器
ApplicationContext
是高级工厂的界面,该工厂能够维护不同 bean
及其依赖关系的注册表。使用方法T getBean(String name, Class<T> requiredType)
,您可以检索 bean
的实例。
3.3 hellospring应用
下面通过创建一个hellospring的应用程序,程来感受整个过程。
- 该程序通过Maven获取相关jar包
- 元数据通过xml方式进行获取
在项目的pom.xml中导入springframework的jar包
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
</dependencies>
1.程序框架:
2.Hello.class
3.beans.xml
4.HelloTest.class
运行HelloTest.class:
相关参数的解释:
beans.xml中的bean
beans中的bean,一个bean对应一个对象,这个对象就是我们交给IOC容器进行管理的对象。
<bean id="hello" class="com.zhu.pojo.Hello">
<property name="str" value="Spring"></property>
</bean>
- id:对应bean的标识名,当要获取该bean的时候就是通过该标识名进行获取(我们通过IOC容器获取bean,IOC容器通过 getBean(String var1)方法进行获取,这里的var1指的就是该bean的标识名)
- class:对应类的完整路径
<property name="str" value="Spring"></property>
相关的类解释:
1.ApplicationContext
接口org.springframework.context.ApplicationContext代表 Spring IoC 容器,并负责实例化,配置和组装上述 bean。
2.ClassPathXmlApplicationContext
ClassPathXmlApplicationContext 通过xml方式实例化ApplicationContext接口对象
3.FileSystemXmlApplicationContext
FileSystemXmlApplicationContext通过文件方式实例化ApplicationContext接口对象
总结:从上面的方式1和方式2我们会发现一个巨大的变化就是没有New对象,其实这就是IOC的作用,IOC通过依赖注入实现,项目中的beans.xml是配置信息,通过这一个配置信息,可以让IOC容器给我们自动分配一个对象,从而对象由spring来创建,管理,装配,这样我们只需要关心具体业务即可。
3.4 Bean 概述
这部分主要包括了对bean的概述,命名,实例化的一些基本的操作,相关的内容。
本节需要使用一个新的项目:
1.项目结构:
2.maven导包:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
</dependencies>
3.User
4.beans.xml
<bean id="user" class="com.zhu.pojo.User">
<property name="name" value="张三"></property>
</bean>
5.测试类
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
User user= (User) context.getBean("user");
System.out.println(user.getName());
运行结果:
3.4.1 概述
3.4.2 命名 bean
1. <alias/>
2 . <name/>
<name/>
等同于前面的别名不同的是name可以取多个
3.4.3 实例化 bean
3.4.3.1 有参构造函数
在前面的例子中,输出结果先是无参构造
,然后再是user.getName()
返回的结果,这个其实就是通过无参构造进行类初始化,再调用类的getName()
,那有参构造进行类的初始化是怎么做的呢?
对于类的初始化使用<constructor-arg>
进行初始化,初始化有以下几种:
1. 使用下标进行类初始化
这里的index值的就是构造函数的下标,注意下标从0开始
<!-- 【1】使用下标进行类初始化-->
<bean id="user" class="com.zhu.pojo.User">
<constructor-arg index="0" value="李四"></constructor-arg>
</bean>
运行结果:
分析一下,用new对象的方式来想想它是怎么执行的,下面对的所有操作的测试类均不变
1.实例化对象,这里通过有参构造进行实例化
对于这里的name,就是通过beans.xml配置文件进行赋值的
上面的操作和下面代码可以看作等价
2.使用类型进行类初始化
这里的type就是有参构造对应参数的数据类型
运行结果:
type里面对应的值就是这里name的数据类型
同样的上面的操作和下面代码可以看作等价
3. 使用参数名进行类初始化
运行结果:
3.4.3.2 构造函数加载问题
如果在beans.xml中创建二个不同对象,其中只调用一个对象的方法,另一个对象会被实例化吗?
在实体对象中创建一个UserT对象
然后在beans.xml中创建一个UserT的bean
测试类:
执行结果:
上面的操作说明,当beans.xml进行加载的时候,beans.xml的bean(对象)就已经被创建了。
3.4.2 Dependencies(依赖)
以前对象之间是依赖关系直接进行协作操作,而容器中的多个Bean是独立的,又是如何实现对象的协作?
3.4.2.1 依赖注入
基于构造函数的依赖项注入
在前面我们通过有参构造函数实例化,其实就是在进行依赖注入!
基于 Setter 的依赖项注入
常见的数据类型,如何进行依赖注入
- 普通注入name="参数" value="初始化的值"。
- ref="address" 可以指向一个bean对象
- 数组,通过
<array>
表示数组,value表示数组的值 - list集合,通过
<list>
表示集合,value表示集合的值 - map,通过
<map>
表示map,<entry>
表示一个键值对,key表示键,value表示值 - set,通过
<set>
表示set,value表示值 - null,通过
</null>
表示null - Properties通过<>
C命名和P命名空间注入(上面二种注入的快捷方式)
C命名是通过Set进行注入(相当于property),而P命名空间通过有参构造方法进行注入(相当于construct-args)
注意:C命名和P命名不能直接使用,需要导入XML约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
并且C和P还可以使用ref属性
例子:
3.4.3 Bean 作用域
- 单例作用域(默认作用域,scope="singleton"):创建的多个bean都共用一个对象
例子:
- 原型作用域(scope="prototype"):一个bean对应一个对象
例子:
- 其他作用域(Request ,Session Application等)对应Web中相应的作用域
3.4.4 自动装配
Cat.java
Dog.java
People.java
beans.xml配置文件代码
测试端代码:
运行效果:
在一个bean中引用另一个bean其实可以通过自动装配完成
再次运行:
如果把dog的id改了在运行:
这是因为byName通过判断id是否和X相等(setx,setCat=>x=cat,setDog,x=dog),(查找id
)
将上面的autowire改成byType
再次运行:
这是因为byType会自动去容器上下文中查找,和自己对象属性类型相同的bean(查找class
)
小结:
使用注解实现自动装配
在7.9这里(下文)提到了开发的时候可以使用Comments(注解)进行开发。
注解注入是在Xml之前执行的,所以如果同一个操作同时使用注解和Xml,Xml就会把注解的进行覆盖
注解开发的环境搭建(jdk1.5,spring2.5及以上支持注解开发)
- 导入context约束和相关约束支持:
context约束:xmlns:context="http://www.springframework.org/schema/context"
- 开启注解
<context:annotation-config/>
完整的开启注解代码如下:
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
@Autowired注解:(先通过使用byType找到相应属性,然后通过byName找相应属性)
- @Autowired应用于构造函数
- @Autowired应用于“传统”设置方法
- @Autowired应用于具有任意名称和/或多个参数的方法
- @Autowired应用于字段,甚至将其与构造函数混合使用
补充:Autowired(required=false),说明这个对象可以为null(属性使用@Nullable这个注解表示这个属性可以为null)
@Qualifier(value)可以指定唯一的bean对象进行注入
@Resource注解(先通过byName如果没有找到,再通过bytype),里面的name用来指定唯一的bean
小结(@Autowired()是先通过byType然后是byName,而@Resource()是先通过byName然后再是byName):
3.5 使用注解开发
<context:component-scan base-package="包名">
用于扫描具体包中的注解
- @Component组件,放在类上说明这个类被spring管理了,相当于前面的
<bean>
根据三层架构衍生了另外三个组件
- @Value()对参数进行初始化,相当于
<property>
- @Configuration(),配置标签,表示这个类是一个配置类相当于bean.xml
另一个配置类
pojo类
测试类中: