Spring Node
概述
- 目的: 解决企业应用开发的复杂性
- 功能: 使用基本的JavaBean代替EJB(Enterprise Service Bus,), 并提供更多的企业应用功能
- ESB提供了连接企业内部及跨企业间新的和现有软件应用程序的功能,以一组丰富的功能启用管理和监控应用程序之间的交互。
- 范围: 任何范围的Java应用
特性
- 非嵌入式: 基于Spring开发的应用中的对象可以不依赖Spring的API
- 控制反转(Ioc): 对象由Spring去创建, 不需要程序员去new
- 依赖注入(DI): 依赖的对象不需要手动调用setXXX()方法去设置, 而是通过配置赋值
- 面向切面(AOP)
- Spring是一个容器, 其包含并且管理应用对象的生命周期
- 组件化: Spring可以实现使用简单的组件配置组合成衣蛾复杂的应用
- 一站式: 在IoC和AOP的基础上可以整合各种企业应用的开源框架和第三方类库
- 总结: Spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架
三层架构
- 表现层 --> web层 (MVC是表现层的一个设计模型)
- 业务层 --> service层
- 持久层 --> dao层
体系结构
核心容器
核心容器由 spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring 表达式语言,Spring Expression Language)等模块组成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMDKhMAC-1623500113933)(https://i.loli.net/2021/03/20/z2keoLWXZ9r1iIx.png)]
- Spring-core模块提供了框架的基本组成部分, 包括IoC和依赖注入
- spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦
- context 模块建立在由 core和 beans 模块的基础上建立起来的,它以一种类似于 JNDI 注册的方式访问对象。
- spring-expression 模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是 JSP2.1 规范中定义的统一表达式语言的扩展,支持 set 和 get 属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从 Spring IoC 容器检索对象,还支持列表的投影、选择以及聚合等。
其完整的依赖关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KPmzazgm-1623500113948)(https://i.loli.net/2021/03/28/UsuicNKYH29k3Me.png)]
拓展:
-
Spring Boot:
- 一个快速开发的脚手架
- 基于SpringBoot可以快速开发单个微服务
-
Spring Cloud
- SpringCloud 是基于SpringBoot实现的微服务开发框架
依赖:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version>
</dependency>
IoC 容器
容器将创建对象, 把它们连接在一起, 配置它们, 并管理它们的在整个生命周期.
通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML,Java 注释或 Java 代码来表示。
IOC理论推导
不使用Spring框架实现一个业务的准备工作:
- UserDao 接口
- UserDaoImpl 实现类
- UserService 业务接口
- UserServiceImpl 业务实现
弊端: 用户的需求可能会影响原来的代码, 如果直接根据用户需求去修改原来的代码, 在大型项目中, 这种修改成本非常昂贵
IoC的本质
控制反转IoC(Inversion of Control)是一种设计思想, DI(依赖注入)是实现IoC的一种方法, 没有IoC的程序中, 我们使用面向对象编程, 对象的创建与对象间的依赖完全写在程序中, 对象的创建由程序自己控制, 控制反转后将对象的创建转移给第三方(所以获得依赖对象的方式反转了)
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式. 在spring中实现控制反转的是Ioc container, 其实现方法是注入依赖(Dependency Injection, DI)
控制: 谁来控制对象的创建, 传统应用程序的对象是由程序本身控制创建的, 使用Spring后,对象由Spring来创建
反转: 程序本身不创建对象, 而变成被动的接收对象
依赖注入: 就是通过setXXX()方法来进行注入的
所以, 对IoC的理解: 对象的来创建, 管理, 装配由Spring来实现
Spring Bean
Bean 是一些被实例化, 组装, 并通过Spring IoC所管理的对象, 这些Bean是由容器提供的配置元数据创建的
Bean 定义常用的属性:
id: Bean 对象的唯一标识符
class: bean 对象所对应的全限定名
name: 别名, 可以同时区多个别名
scope: 指定特定的Bean定义创建对象的作用域
Spring 定义元数据
Spring IoC容器完全由实际编写的配置元数据的格式解耦
把配置元数据提供给Spring容器:
-
基于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-3.0.xsd"> <!-- A simple bean definition --> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <!-- A bean definition with lazy init set on --> <bean id="..." class="..." lazy-init="true"> <!-- collaborators and configuration for this bean go here --> </bean>
-
基于注解的配置
-
基于Java的配置
Spring Bean 的作用域
作用域 | 描述 |
---|---|
singleton(default) | 在Spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
global-session | 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境 |
Singleton 是单例类型,就是在创建起容器时就同时自动创建了一个 bean 的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton 作用域是 Spring 中的缺省作用域
Prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象
Spring Bean 生命周期
初始化回调
<bean id="exampleBean"
class="examples.ExampleBean" init-method="init"/>
<!-- init是ExampleBean中的一个方法 -->
销毁回调
<bean id="exampleBean"
class="examples.ExampleBean" destroy-method="destroy"/>
<!-- destroy是ExampleBean中的一个方法 -->
Spring Bean 定义继承
Spring Bean定义继承于java的继承无关, 但概念是一样的. 可以通过定义一个父Bean的定义作为模板和其他子Bean 就可以从bean中继承所需的配置.
使用基于XML的配置元数据时, 指定父Bean作为该属性的值来表明子Bean的定义
<bean class="Parent" id="parent" name="parent">
<property name="message1" value="parent message1"/>
<property name="message2" value="parent message2"/>
</bean>
<!-- 使用parent属性建立Bean之间的继承关系 -->
<bean class="Child" id="child" name="child" parent="parent">
<property name="message2" value="child message2"/>
<property name="message3" value="child message3"/>
</bean>
</beans>
public class TestMain {
public static void main(String[] args) {
ApplicationContext acx = new ClassPathXmlApplicationContext("Bean.xml");
Parent parent = (Parent) acx.getBean("parent");
Child child = (Child) acx.getBean("child");
System.out.println(parent.toString());
System.out.println(child.toString());
// Parent{message1='parent message1', message2='parent message2'}
// Child{message1='parent message1', message2='child message2', message3='child message3'}
}
}
Spring的核心机制
管理bean
程序主要是通过Spring容器来访问容器中的Bean, ApplicationContext是Spring容器最常用的接口,该interface有以下两个实现类:
ClassPathXmlApplicationContext
: 从类加载路径下搜索配置文件, 并根据配置文件来创建Spring容器.FileSystemXmlApplicationContext
: 从文件系统的相对路径或绝对路径下去搜索配置文件, 并根据配置文件来创建Spring容器
public class TestMain {
public static void main(String[] args) {
// 创建Spring上下文(加载bean.xml)得到Spring容器
ApplicationContext acx = new ClassPathXmlApplicationContext("spring-config.xml");
// 在Spring容器中获取Student实例
Student student = acx.getBean("student", Student.class);
// 调用方法
System.out.println(student.toString());
}
}
依赖注入
Spring框架的核心功能:
- Spring容器作为超级工厂, 负责创建,管理所有的Java对象, 这些对象也就是容器中的Bean
- Spring容器管理容器中Bean之间的依赖关系, Spring使用"依赖注入"来管理Bean之间的依赖关系
依赖注入:
- 依赖: Bean对象的创建依赖于容器
- 注入: Bean对象中的所有属性值, 由容器注入
使用依赖注入, 不仅可以为Bean注入普通的属性值, 还可以注入其他Bean之间的依赖关系
依赖注入是一种优秀的解耦方式, 其可以让Bean以配置文件组织在一起, 而不是以硬编码的方式耦合在一起
依赖注入的方式:
类型 | 描述 |
---|---|
Constructor-based dependency injection | 当容器调用带有多个参数的构造函数类时,实现基于构造函数的 DI,每个代表在其他类中的一个依赖关系 |
Setter-based dependency injection | 基于 setter 方法的 DI 是通过在调用无参数的构造函数或无参数的静态工厂方法实例化 bean 之后容器调用 beans 的 setter 方法来实现的 |
Spring 基于构造函数的依赖注入
-
使用无参构造器创建
-
使用有参构造
<!-- 通过无参构造创建(必须声明类的无参构造函数) --> <bean class="com.example.Human" id="human" name="human"/> <!-- 通过参数类型传递参数 --> <bean class="com.example.Human" id="human1" name="human1"> <constructor-arg type="String" value="张三"/> <constructor-arg type="int" value="18"/> </bean> <bean class="com.example.Student" id="student" name="student"> <constructor-arg ref="human" /> <constructor-arg name="age" value="1"/> </bean> <!-- 通过索引传入参数值(注意从零开始) --> <bean class="com.example.Human" id="human2" name="human2"> <constructor-arg index="0" value="张三"/> <constructor-arg index="1" value="18"/> </bean> <!-- 通过参数名称传入参数值 --> <bean class="com.example.Human" id="human3" name="human3"> <constructor-arg name="name" value="张三"/> <constructor-arg name="age" value="18"/> </bean>
使用这种方式, 使用的类必须要定义构造函数
Spring 基于设值函数的依赖注入
<bean class="example.Human" id="human1">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
<bean class="example.Student" id="student1">
<property name="human" ref="human1" />
<property name="age" value="18" />
</bean>
<!-- 简写(注意 P 需要在beans 里面定义) -->
<!-- xmlns:p="http://www.springframework.org/schema/p" -->
<bean class="example.Student" id="student2"
p:human-ref="human1"
p:age="20" />
使用这种方式注入依赖, 使用的类必须定义setter方法
两种依赖注入方式的对比
唯一的区别就是在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素
Spring 注入集合
<!-- Definition for javaCollection -->
<bean id="javaCollection" class="com.tutorialspoint.JavaCollection">
<!-- results in a setAddressList(java.util.List) call -->
<property name="addressList">
<list>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>USA</value>
</list>
</property>
<!-- results in a setAddressSet(java.util.Set) call -->
<property name="addressSet">
<set>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>USA</value>
</set>
</property>
<!-- results in a setAddressMap(java.util.Map) call -->
<property name="addressMap">
<map>
<entry key="1" value="INDIA"/>
<entry key="2" value="Pakistan"/>
<entry key="3" value="USA"/>
<entry key="4" value="USA"/>
</map>
</property>
<!-- results in a setAddressProp(java.util.Properties) call -->
<property name="addressProp">
<props>
<prop key="one">INDIA</prop>
<prop key="two">Pakistan</prop>
<prop key="three">USA</prop>
<prop key="four">USA</prop>
</props>
</property>
</bean>
Spring Bean 自动装配
-
使用
autowire="byName"
自动装配: 这种模式由属性名称指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 auto-wire 属性设置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。<bean class="example.Human" id="human1"> <property name="name" value="张三"/> <property name="age" value="18"/> </bean> <bean class="example.Student" id="student1"> <property name="human" ref="human1" /> <property name="age" value="18" /> </bean> <!-- 使用byName自动装配 --> <bean class="example.Human" id="human2" name="human"> <property name="name" value="张三"/> <property name="age" value="18"/> </bean> <bean class="example.Student" id="student2" autowire="byName"> <property name="age" value="18" /> </bean>
-
使用
autowire="byType"
自动转配: 这种模式由属性类型指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。<!-- 使用byType自动装配 --> <bean class="example.Human" id="human"> <property name="name" value="张三"/> <property name="age" value="18"/> </bean> <bean class="example.Student" id="student3" autowire="byType"> <property name="age" value="18" /> </bean>
Spring 基于注解的配置
使用注解来配置依赖注入, 可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身。
别名
<alias>
Bean的配置
import
用于导入多个xml文件, 将各个文件中的bean合并