Spring 是模块化的,所以可以只使用其中需要的部分。可以在任何 web 框架上使用控制反转(IoC),也可以只使用 Hibernate 集成代码或 JDBC 抽象层。它支持声明式事务管理、通过RMI 或 web 服务实现远程访问,并可以使用多种方式持久化数据。它提供了功能全面的 MVC 框架,可以透明地集成 AOP 到软件中。
Spring 被设计为非侵入式的,这意味着你的域逻辑代码通常不会依赖于框架本身。在集成层(比如数据访问层),会存在一些依赖同时依赖于数据访问技术和 Spring,但是这些依赖可以很容易地从代码库中分离出来。Spring 框架是基于 Java 平台的,它为开发 Java 应用提供了全方位的基础设施支持,并且它很好地处理了这些基础设施,所以你只需要关注你的应用本身即可。Spring 可以使用 POJO(普通的 Java 对象,plain old java objects)创建应用,并且可以将企业服务非侵入式地应用到 POJO。这项功能适用于 Java SE 编程模型以及全部或部分的 Java EE。
Spring 体系结构
Core Container 核心容器
Spring 的核心容器是其他模块的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言四个部分组成。Beans 模块:提供了 BeanFactory,用于创建和管理对象,Spring 将产生出来的对象称为 Bean。
Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
Context 上下文模块:建立在 Core 核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
Data Access 模块
Data Access/Integration包括 JDBC、ORM、OXM、JMS 和 Transactions 五个模块,具体介绍如下。JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
ORM 模块:对流行的 ORM框架管理的 API ,也是我们常用的组件之一,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
Web 模块
Spring 的 Web 层包括 WebSocket、Servlet、Web 和 Portlet 组件,具体介绍如下。‘WebSocket 模块:提供了客户端和服务器之间的一个持久链接,管理之间数据的发送。
Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
Portlet 模块:提供了在 Portlet 环境中使用 MVC 的实现,类似 Web-Servlet 模块的功能。
其他模块
Spring的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下:
AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架的测试。
Spring 特点
方便解耦,简化开发
通过 Spring 提供的 IoC 容器,我们可以将对象之间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。有了 Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
AOP 编程的支持
通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
声明事物的支持
在 Spring 中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在 Spring 里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring 对 Junit4 支持,可以通过注解方便的测试 Spring 程序。
方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,相反,Spring 可以降低各种框架的使用难度,Spring 提供了对各种优秀框架(如 Struts,Hibernate、Hessian、Quartz)等的直接支持。
降低 Java EE API 的使用难度
Spring 对很多难用的 Java EE API(如 JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过 Spring 的简易封装,这些 Java EE API 的使用难度大为降低。
Java 源码是经典学习范例
Spring 的源码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对 Java 技术的高深造诣。Spring 框架源码无疑是 Java 技术的最佳实践范例。如果想在短时间内迅速提高自己的 Java 技术水平和应用开发水平,学习和研究 Spring 源码将会使你收到意想不到的效果。
Spring IOC 概述
控制反转 IoC(Inversion of Control):是一种设计思想,DI (依赖注入)是实现 IoC 的一种方法,也有人认为 DI 只是 IoC 的另一种说法。没有 IoC 的程序中我们使用面向对象编程对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。
IoC 是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控。控制权由应用代码中转到了外部容器,控制权的转移就是所谓反转。对于 Spring 而言,就是由 Spring 来控制对象的生命周期和对象之间的依赖关系。IoC 还有另外一个名字——“依赖注入(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,即由容器动态地将某种依赖关系注入到组件之中。
在 Spring 的工作方式中,所有的类都会在 spring 容器中登记,告诉 spring 这是个什么东西,你需要什么东西,然后 spring 会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring 来控制,也就是说控制对象生存周期的不再是引用它的对象,而是 spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 spring 控制,所以这叫控制反转。
在系统运行中,动态的向某个对象提供它所需要的其他对象。
依赖注入的思想是通过反射机制实现的,在实例化一个类时,它通过反射调用类中 set 方法将事先保存在 HashMap 中的类属性注入到类中。 总而言之,在传统的对象创建方式中,通常由调用者来创建被调用者的实例,而在 Spring 中创建被调用者的工作由 Spring 来完成,然后注入调用者,即所谓的依赖注入 or 控制反转。IoC 的优点:降低了组件之间的耦合,降低了业务对象之间替换的复杂性,使之能够灵活的管理对象。
Spring IOC 容器
面向对象最基本的就是对象,我们对于对象的使用都是从 new 开始的。
传统方式
我们需要用到那个类的对象,只需要 new 它就可以产生该类的对象了,然在在通过该对象调用它的属性和方法。例如:
IoC 方式
所有对象的产生都交给了容器来实现,当你需要对象时,在通过容器获取它就行。
在启动 Spring 时读取 applicationContext.xml 文件,将文件中需要产生的对象都产生出来并存放到 Spring IOC 容器中,当需要使用这个对象时,从容器中将它获取出来就行。
IoC 容器的规范
IoC 容器有两种规范:BeanFactory 和 ApplicationContext。
BeanFactory:BeanFactory 为 IOC 容器提供了基础功能,Spring 文档中提到,当前该类仅仅是为了向后兼容老的版本,除非你有更好的原因否则就应该使用第二种容器。
ApplicationContext:通过 API 文档可以知道,ApplicationContext 是BeanFactory 的子接口,并且从文档中也可以看到 ApplicaionContext 除了包含有 BeanFactory 的所有功能还支持了更多的功能。
BeanFactory 和 ApplicationContext 多是接口,都是对 IoC 容器提出了规范,对于它们而言是通过对应的实现类去实现,分别有四种实现方式。
FileSystemXmlApplicationContext 实现
ApplicationContext bean = new FileSystemXmlApplicationContext("ioc/src/main/resources/applicationContext.xml");
ClassPathXmlApplicationContext 实现
加载配置文件的时候根据 ClassPath 位置。maven 项目的标准规范,以 resources 文件夹为相对路径起始点。
ApplicationContext bean = new ClassPathXmlApplicationContext("applicationContext.xml");
XmlWebApplicationContext 实现
在 Web 环境下初始化监听器的时候会加载。很少使用,在 web 项目中启动 tomcat 时加载配置文件,但我们实际上有其他方式来完成,具体内容在Spring MVC 中讲解。
AnnotationConfigApplicationContext 实现
注解方式,零 xml 配置。根据注解的方式启动 Spring 容器。
ApplicationContext bean = new AnnotationConfigApplicationContext("com.dailyblue.java.spring");
Spring IOC 实现方式
1. 引入 ioc 模块的依赖
<!-- 引入了SpringIOC的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
2. 在 resources 文件夹下创建 applicationContext.xml 配置文件
3. 书写需要使用的类
DemoA.java
@Data
public class DemoA {
private Integer id;
private String name;
private Character gender;
}
DemoB.java
@Data
public class DemoB {
private String[] loves;
private List<String> cities;
private Set<String> providers;
private Map<String,String> maps;
}
DemoC.java
@Data
public class DemoC {
private DemoA da;
private DemoB db;
}
application.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" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<!-- 产生DemoA类的对象-->
<!-- import com.dalyblue.java.spring.DemoA;
DemoA da = new DemoA();
id:产生完整的对象名
class:类的完整性
-->
<!-- 产生了一个对象da-->
<bean id="da" class="DemoA"></bean>
<!--产生对象的同时 给属性赋值(setter)-->
<bean id="da1" class="DemoA">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="gender" value="男"></property>
</bean>
<!-- 又创建了一个DemoA对象,此时如果用b()中的 byType方式 会报错-->
<bean id="ba2" class="DemoA">
<property name="id">
<value>1</value>
</property>
<property name="name" value="张三"/>
<property name="gender" value="男"/>
</bean>
<!-- 产生DemoB类 -->
<bean id="db" class="DemoB">
<property name="loves">
<array>
<value>足球</value>
<value>篮球</value>
<value>排球</value>
</array>
</property>
<property name="cities">
<list>
<value>西安</value>
<value>宝鸡</value>
<value>渭南</value>
<value>延安</value>
</list>
</property>
<property name="providers">
<set>
<value>可口可乐</value>
<value>雪碧</value>
</set>
</property>
<property name="maps">
<map>
<entry key="A" value="aa"/>
<entry key="B" value="bb"/>
<entry key="C" value="cc"/>
</map>
</property>
</bean>
<!-- 产生DemoC类 通过指引方式-->
<bean id="dc" class="DemoC">
<property name="da" ref="da"></property>
<property name="db">
<ref bean="db"></ref>
</property>
</bean>
</beans>
DemoD.java
@Data
public class DemoD {
private Integer id;
private String name;
private String[] loves;
private DemoB db;
public DemoD() {
}
public DemoD(Integer id, String name, String[] loves, DemoB db) {
this.id = id;
this.name = name;
this.loves = loves;
this.db = db;
}
}
DemoE.java
public class DemoE {
static {
System.out.println("我是DemoE,在加载时被执行");
}
public DemoE() {
System.out.println("DemoE对象产生了,在对象被创建时执行");
}
}
application.xml
<!-- 产生DemoD类 -->
<bean id="dd" class="DemoD">
<!--
constructor 构造器赋值
index 索引号,代表给第几个参数赋值
name 参数名,代表哪一个参数赋值
ref 按引用赋值
value 按值赋值
type 属性的数据类型 一般不写
-->
<constructor-arg index="1" value="张三"></constructor-arg>
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg index="3" ref="db"></constructor-arg>
<constructor-arg name="loves">
<array>
<value>11</value>
<value>22</value>
<value>33</value>
</array>
</constructor-arg>
</bean>
<!-- DemoE类 lazy-init="true 默认为勤加载
-->
<bean id="de" class="DemoE" lazy-init="true"></bean>
</beans>
Main.java(运行的)
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Main {
private static void a() {
// 加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
// 获取到对象
Object object = context.getBean("da");
System.out.println(object);
Object obj2 = context.getBean("da1");
System.out.println(obj2);
}
private static void b() {
// 加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
// 返回的结果是object类型,需停药强转
DemoA da = (DemoA) context.getBean("da");
// byType方式 容器中只能为一个该类型的对象 如果多创建会报错
// DemoA da2 = context.getBean(DemoA.class);
// byName+byType
DemoA da3 = context.getBean("da1", DemoA.class);
System.out.println(da);
// System.out.println(da2);
System.out.println(da3);
}
private static void c() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
DemoB bean = context.getBean(DemoB.class);
System.out.println(bean);
}
private static void d() {
ApplicationContext context
= new ClassPathXmlApplicationContext("application.xml");
DemoC c = context.getBean(DemoC.class);
System.out.println(c);
}
private static void e() {
// ClassPathXmlApplicationContext maven环境下用,在properties下开始用
/*ApplicationContext context
= new ClassPathXmlApplicationContext("application.xml");*/
// 从项目SPringleBasic下开始寻找
ApplicationContext context = new FileSystemXmlApplicationContext("D:\\jiazhongxuexi\\SpringBasic\\spring-ioc\\src\\main\\resources\\application.xml");
DemoD d = context.getBean(DemoD.class);
System.out.println(d);
}
private static void f() {
ApplicationContext context
= new ClassPathXmlApplicationContext("application.xml");
DemoE d = context.getBean(DemoE.class);
System.out.println(d);
}
public static void main(String[] args) {
f();
}
}
常见标签 API
<bean> 标签:每一个标签 bean 对应的就一个类对象,通过 id(或者Name)以及 Class 来定位一个类,这边 Class 要求是完全限定类名。
<property> 标签:属性标签,负责给对象属性赋值。
<constructor-arg> 标签:构造器标签,负责给对象属性赋值。
注解方式
注解不需要 applicationContext.xml 配置文件,以@注解的方式注册和管理 bean。
@Component
public class GuanWei{
@Resource //byName+byType 首先按照名称查找,如果找到赋值 如果没有找到,按照类型查询
private ZhangSan zhang;
public void print(){
System.out.println("我是关为!");
zhang.print();
}
}
注解 API
一些常用的注解,产生对象,引入依赖,设置配置等。
Spring IOC 中 bean 的生命周期
Bean 的生命周期包括 Bean 的定义,Bean 的初始化,Bean 的使用,Bean 的销毁。
Bean 的定义:一般 Bean 使用 XML 文件的方式进行定义,定义的时候将 Bean 之间的依赖关系和属性的赋值都进行了定义。
Bean 的初始化:其实 Bean 的初始化包括 Bean 的创建和初始化两个方法,Bean 的创建和初始化一般是同步进行的,Bean 在完成创建后直接就会进行初始化操作,创建的时机与 Bean 的 lazy-init 属性的设置有关。
Bean 的使用:在 web 程序运行期间,发生对某一个 Bean 的调用时,就会使用这个 Bean实例,如果使用编码的方式来获取 Bean 同样也是 Bean 的使用,Bean 的编码使用方式有三种,第一种是使用 BeanWarpper,第二种是使用 BeanFactory,第三种就是使用 ApplicationContext。Bean 的销毁:Bean 实例在程序退出的时候会进行销毁,而在销毁之前会自动调用 Bean的 destory-method 属性指定名称的方法。
加载 xml 文件后 bean 的加载方式
默认情况下,是勤加载模式,加载 xml 后加载类文件并产生对象,我们也可以设置当前 bean 为懒加载模式,当设置为懒加载后,xml 加载后不会加载类文件,更不会产生对象。
设置懒加载方式
默认情况下,bean 是单例的,你可以设置当前 bean 为多例。
<bean id="da" class="com.dailyblue.java.spring.bean.DemoA" scope="prototype"/>
bean 的单例和多例模式
默认情况下,bean 是单例的,你可以设置当前 bean 为多例。
// da1 是单例模式的,每一次 getBean 时获取的都是同一个对象
DemoA a1 = beans.getBean("da1", DemoA.class); // 从容器中获取到DemoA类型的对象
DemoA a2 = beans.getBean("da1", DemoA.class);
System.out.println(a1 == a2);
// da2 是多例效果的,每一次 getBean 时都是产生对象
DemoA a3 = beans.getBean("da2", DemoA.class);
DemoA a4 = beans.getBean("da2", DemoA.class);
System.out.println(a3 == a4);
getBean 获取对象的方式
从容器中获取对象,我们主要由三种方式
- byName:根据名字获取
- byType:根据类型获取
- byName+byType:根据名字和类型获取
// byName 根据名称获取对象,缺点是返回结果是Object类型的,需要自行强转
Object obj1 = beans.getBean("db");
// byType 根据类型获取对象,缺点是容器中只能有一个DemoB类型的对象
DemoB obj2 = beans.getBean(DemoB.class);
// byName+byType:根据名称和类型获取,缺点是写得多
DemoB obj3 = beans.getBean("db", DemoB.class);
@Controller(value = "ec")
public class EmpController {
// 先byName 再byType
@Resource(name = "empService")
private EmpService service;
public void find() {
System.out.println("EmpController");
service.find();
}
}