一、Spring概述
Spring创始人: Rod Johnson,Java和J2EE开发领域的专家,Spring框架的创始人,同时也是SpringSource的联合创始人。
Spring官网地址: https://spring.io/projects/spring-framework#overview
Spring下载地址: https://repo.spring.io/ui/native/release/org/springframework/spring
Spring官方文档: https://docs.spring.io/spring-framework/docs/5.2.0.RELEASE/spring-framework-reference/index.html
Spring中文文档: https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/
1️⃣为什么使用Spring
Spring使Java编程对每个人来说更快、更容易、更安全。 Spring对速度、简单性和生产率的关注使它成为世界上最流行的Java框架。 Spring给整个行业带来等了春天,为我们软件的开发带来了极大的便利。
🍀(1)Spring is everywhere
Spring框架的足够灵活受到世界各地开发人员的信任。 无论是流媒体电视、在线购物、还是无数其他创新的解决方案,Spring每天都为数百万终端用户提供愉快的体验。 Spring也有来自所有科技巨头的贡献,包括阿里巴巴、亚马逊、谷歌、微软等。
🍀(2)Spring is flexible
Spring灵活而全面的扩展能力和第三方库让开发人员可以构建几乎任何可以想象到的应用程序。 Spring框架的【控制反转(IoC)】和【依赖注入(DI)】特性为一系列广泛的特性和功能提供了基础。 无论您是在为web构建安全的、响应式的、基于云的微服务,还是为企业构建复杂的流数据流,Spring都有工具可以提供帮助。
🍀(3)Spring is productive
Spring Boot(这是我们以后要学习的框架)改变了您处理Java编程任务的方式,从根本上简化了您的体验。 Spring Boot结合了应用程序上下文和自动配置的嵌入式web服务器等必要条件,使microservice开发变得轻而易举。 为了更快,您可以将Spring Boot与Spring Cloud丰富的支持库、服务器、模式和模板组合在一起,以创纪录的时间将整个基于微服务的架构安全地部署到云中。
🍀(4)Spring is fast
我们的工程师非常关心性能。 在Spring中,默认情况下,您会注意到快速启动、快速关闭和优化执行。 Spring项目也越来越多地支持reactive(nonblocking)编程模型,以获得更高的效率。 开发人员的生产力是Spring的超级力量。 Spring Boot帮助开发人员轻松地构建应用程序,而且比其他竞争范式要轻松得多。
🍀(5)Spring is secure
Spring在处理安全问题方面十分可靠。 Spring代码的贡献者与安全专业人员一起修补和测试任何报告的漏洞。 第三方依赖关系也被密切监控,并定期发布更新,以帮助您的数据和应用程序尽可能安全。 此外,Spring Security使您更容易集成行业标准的安全方案,并交付可靠的默认安全解决方案。
🍀(6)Spring is supportive
Spring社区是一个庞大的、全球性的、多样化的社区,涵盖了所有年龄和能力的人,从完全的初学者到经验丰富的专业人士。 无论你处在人生的哪个阶段,你都能找到帮助你进入下一个阶段的支持和资源。
2️⃣Spring 的特性
Core technologies: dependency injection, events, resources, i18n, validation, data binding, type conversion, SpEL,AOP.(核心技术:包括依赖注入、事件模型、资源处理、国际化、数据绑定和验证、类型转化、spring表达式、面向切面编程。核心技术是一切的关键,后边衍生的多个特性都是依托于核心技术。)
Testing: mock objects, TestContext framework, Spring MVC Test, WebTestClient.
Data Access: transactions, DAO support, JDBC, ORM, Marshalling XML.
Spring MVC and Spring WebFlux web frameworks.
Integration: remoting, JMS, JCA, JMX, email, tasks, scheduling, cache.
3️⃣Spring的组成
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
Spring Core: 核心容器提供 Spring 框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring Context: Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP: 通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO: JDBC DAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。SpringDAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
Spring ORM: Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
Spring Web: Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring Web MVC: MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
二、IOC 容器
在学习IOC 容器之前首先需要了解一些常见的名词:
容器: 可以管理对象的生命周期、对象与对象之间的依赖关系。
POJO: POJO(Plain Old Java Object)这种叫法是Martin Fowler、Rebecca Parsons和Josh MacKenzie在2000年的一次演讲的时候提出来的。按照Martin Fowler的解释是“Plain
Old Java
Object”,从字面上翻译为“纯洁老式的Java对象”,但大家都使用“简单java对象”来称呼它。POJO的内在含义是指:那些没有继承任何类、也没有实现任何接口,更没有被其它框架侵入的java对象。不允许有业务方法,也不能携带connection之类的方法,实际就是普通JavaBeans。
JavaBean: JavaBean是一种JAVA语言写成的可重用组件。JavaBean符合一定规范编写的Java类,不是一种技术,而是一种规范。大家针对这种规范,总结了很多开发技巧、工具函数。符合这种规范的类,可以被其它的程序员或者框架使用。它的方法命名,构造及行为必须符合特定的约定:(1)所有属性为private;(2)这个类必须有一个公共的缺省构造函数。即是提供无参数的构造器;(3)这个类的属性使用getter和setter来访问,其他方法遵从标准命名规范;(4)这个类应是可序列化的。实现serializable接口;(5)因为这些要求主要是靠约定而不是靠实现接口,所以许多开发者把JavaBean看作遵从特定命名约定的POJO。
POJO与Java Bean的区别:
POJO | JAVABean |
除了Java语言强加的限制外,它没有其他特殊限制。 | 这是一个特殊的POJO,它有一些限制。 |
它没有对成员提供太多控制。 | 它提供对成员的完全控制。 |
它可以实现Serializable接口。 | 它应该实现可序列化的接口。 |
可以通过字段名称访问字段。 | 字段只能由getter和setter访问。 |
字段可以具有任何可见性。 | 字段只有私人可见性。 |
可能/可能没有no-arg构造函数。 | 它必须具有无参数构造函数。 |
当您不想限制成员并让用户完全访问您的实体时使用它。 | 当您要向用户提供您的实体,但仅向实体的一部分提供服务时,将使用它。 |
POJO类和Bean均用于定义Java对象,以提高其可读性和可重用性。POJO没有其他限制,而bean是具有某些限制的特殊POJO。
SpringBean: SpringBean是受Spring管理的对象,所有能受Spring容器管理的对象都可以成为SpringBean。Spring中的bean,是通过配置文件、javaconfig等的设置,由Spring自动实例化,用完后自动销毁的对象。
SpringBean和JavaBean的区别: (1)用处不同:传统javabean更多地作为值传递参数,而spring中的bean用处几乎无处不在,任何组件都可以被称为bean;(2)写法不同:传统javabean作为值对象,要求每个属性都提供getter和setter方法;但spring中的bean只需为接受设值注入的属性提供setter方法;(3)生命周期不同:传统javabean作为值对象传递,不接受任何容器管理其生命周期;spring中的bean有spring管理其生命周期行为。
Entity Bean: Entity Bean是域模型对象,用于实现O/R映射,负责将数据库中的表记录映射为内存中的Entity对象,事实上,创建一个Entity
Bean对象相当于新建一条记录,删除一个 Entity Bean会同时从数据库中删除对应记录,修改一个Entity
Bean时,容器会自动将Entity Bean的状态和数据库同步。
1️⃣IOC概述
🍀编写spring代码,我们需要创建一个maven工程,并加入以下依赖:
<!-- Spring的核心组件 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.18.RELEASE</version> </dependency> <!-- SpringIoC(依赖注入)的基础实现 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.18.RELEASE</version> </dependency> <!--Spring提供在基础IoC功能上的扩展服务,此外还提供许多企业级服务的支持,如邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存以及各种视图层框架的封装等 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.18.RELEASE</version> </dependency>
🍀本章将介绍Spring框架实现控制反转(IoC)的原理, IoC也称为依赖注入(DI)
org.springframework.beans和org.springframework.context包是Spring框架的IoC容器的基础。
BeanFactory接口提供了一种高级的配置机制,能够管理任何类型的对象。
ApplicationContext是BeanFactory的子接口。 它对BeanFactory进行了补充:
(1)更容易与Spring的AOP特性集成 。
(2)消息资源处理(用于国际化) ,解析消息的能力,支持国际化。继承自MessageSource接口。
(3)事件发布,向注册侦听器发布事件的能力。继承自ApplicationEventPublisher接口。
(4)应用程序层特定的上下文,如WebApplicationContext用于web应用程序。
(5)以通用方式加载文件资源的能力,继承自org.springframe .core.io.ResourceLoader接口。
🍀beanFactory和ApplicationContext接口展示如下:
public interface BeanFactory {}
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}
简而言之,BeanFactory提供了容器的基本功能,而ApplicationContext添加了更多特定于企业的功能。 ApplicationContext是BeanFactory的一个完整超集,仅在本章描述Spring的IoC容器时使用。
在Spring中,由Spring IoC容器【管理】的构成【应用程序主干的对象】称为【bean】。 bean是由Spring IoC容器实例化、组装和管理的对象。 否则,bean只是应用程序中的众多对象之一。 bean及其之间的依赖关系反映在容器使用的【配置元数据】中。
【applicationcontext】接口表示Spring IoC容器,并负责实例化、配置和组装bean。 容器通过读取配置元数据获得关于要实例化、配置和组装哪些对象的指令。 配置元数据以XML、Java注解或Java代码表示。 它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。
Spring提供了ApplicationContext接口的几个实现。
在独立应用程序中,创建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的实例是很常见的。 虽然XML一直是定义配置元数据的传统格式,但您可以通过提供少量的XML配置以声明方式支持这些额外的元数据格式,指示容器使用Java注解或代码作为元数据格式。
2️⃣配置元数据
构建【Spring IoC容器】可以通过构建配置元数据的方式。 这个【配置元数据】说的是:作为应用程序开发人员,您要告诉Spring容器如何去【实例化、配置和组装】应用程序中的对象。 【元数据】传统上以简单而直观的XML格式提供,本章的大部分内容都使用这种格式来传达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 https://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 ’ 属性是标识单个beanDifination的字符串。
’ class ’ 属性定义bean的类型,并使用完全限定的类名。
3️⃣容器实例化与使用
🍀实例化一个容器
ApplicationContext 的构造函数可以是【xml文件的位置路径】的字符串,他允许容器从各种外部资源(如本地文件系统、Java的 'CLASSPATH ’ 等)加载配置元数据。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
下面的示例显示了服务层对象(services.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"> <!-- services --> <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"> <property name="accountDao" ref="accountDao"/> <property name="itemDao" ref="itemDao"/> <!-- additional collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions for services go here --> </beans>
下面的例子展示了数据访问对象(dao.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="accountDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao"> <!-- additional collaborators and configuration for this bean go here --> </bean> <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao"> <!-- additional collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions for data access objects go here --> </beans>
🍀容器的使用
【ApplicationContext】是一个高级工厂的接口,它维护了一个bean的注册列表,保存了容器产生的所有bean对象。 通过使用方法T getBean(String name, Class<T> requiredType) ,您可以检索bean的实例。
【ApplicationContext】允许你读取和访问bean,如下面的示例所示:
// create and configure beans ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml"); // retrieve configured instance,这里使用bean的标识符活class对象检索bean的事例。 PetStoreService service = context.getBean("petStore", PetStoreService.class); // use configured instance List<String> userList = service.getUsernameList();
4️⃣Bean的概述
Spring IoC容器管理一个或多个bean。 这些bean是使用您提供给容器的配置元数据创建的(例如,以XML< bean/>定义的形式)。
在容器本身中,这些定义好的【bean的元数据(描述bean的数据)】被表示【BeanDefinition】对象,其中包含但不限于以下元数据:
全限定类名:通常是被定义的bean的实际【实现类】。
Bean的行为配置元素:它声明Bean在容器中应该存在哪些行为(作用范围、生命周期回调等等)。
bean所需的其他bean的引用(成员变量):这些引用也称为【协作者】或【依赖项】。
接下来我们对其一一进行讲解:
🍀bean的命名
每个bean都有【一个或多个】标识符。 这些标识符在承载bean的容器(ioc容器)中必须是唯一的。 bean通常只有一个标识符。 但是,如果需要多个,则可以考虑使用别名。
在【基于xml】的配置元数据中,可以使用’ id ‘属性、’ name ‘属性或两者同时使用,来指定bean的标识符。 ’ id ‘属性允许您指定一个id,通常,这些名称是字母数字(‘myBean’, ‘someService’等),但它们也可以包含特殊字符。 如果想为bean引入其他别名(一个或者多个都可以),还可以在’ name ‘属性中指定它们,由逗号(’,’)、分号(’;')或空格分隔。
您甚至不需要为bean提供’ name ‘或’ id ‘。 如果您没有显式地提供’ name ‘或’ id ‘,容器将为该bean生成唯一的名称。 但是,如果您想通过名称引用该bean,则必须通过使用’ ref '元素来提供名称。 xml中默认的名字是【权限定名称#数字】。
【bean命名约定 】: 在命名bean时,bean名称以小写字母开头,并从那里开始采用驼峰式大小写。 这类名称的例子包括’ accountManager ‘、’ accountService ‘、’ userDao ‘、’ loginController '等等;一致地命名bean可以使您的配置更容易阅读和理解。
🍀bean的别名
在bean的定义中,您可以为bean提供多个名称,方法是使用’ id ‘属性指定的最多一个名称和’ name '属性中任意数量的其他名称的组合。 这些名称可以是相同bean的等效别名,在某些情况下很有用,例如允许应用程序中的每个组件使用特定于该组件本身的bean名称来引用公共依赖项。 举一个简单的例子,一个人在家叫【狗蛋】,在公司叫【小刘】。
然而,在实际定义bean的地方指定所有别名并不一定能满足所有需求,有时需要为别处定义的bean(比如引入的jar包)引入别名。 这种情况在大型系统中很常见,其中配置在每个子系统之间被分割,每个子系统都有自己的一组对象定义。 在基于xml的配置元数据中,可以使用< alias/>元素来实现这一点。 下面的例子展示了如何做到这一点:
<alias name="fromName" alias="toName"/>
在这种情况下,一个名为【fromName】的bean被定义了一个新的别名【toName】。
例如,子系统A的配置元数据可以以【subsystemA-dataSource】的名称引用数据源。 子系统B的配置元数据可以以【subsystemB-dataSource 】的名称引用数据源。 当编写使用这两个子系统的主应用程序时,主应用程序以【myApp-dataSource】的名称引用数据源。 要使这三个名称都指向同一个对象,您可以向配置元数据添加以下别名定义:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/> <alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
现在,每个组件和主应用程序都可以通过唯一的名称来引用dataSource,并且保证不会与任何其他定义(有效地创建了一个名称空间)发生冲突,但它们引用的是相同的bean。
🍀实例化bean
beanDifination本质上是描述了一个bean是如何被创建的。 当被请求时,容器会查看指定bean的定义,并使用由该beanDifination封装的配置元数据来创建(或获取)实际对象。
如果使用基于xml配置的元数据,则要在< bean/>元素的【class】属性中指定实例化的对象的类型。 这个’ class ‘属性(在内部是’ BeanDefinition ‘实例上的’ class '属性,一个bean的配置加载到内存会形成一个BeanDefinition事例)通常是强制性的。 你可以通过以下两种方式使用Class属性:
(1)在容器中,如果是通过【反射调用其构造函数】直接创建bean,则要指定bean的类型,这有点类似于使用“new”操作符的Java代码。
(2)这个类同样可以是用于创建对象的“静态”工厂方法的实际类,在这种情况下,容器调用该类上的【静态工厂方法来创建bean】。调用静态工厂方法返回的对象类型可能是同一个类,也可能完全是另一个类,这要看你的工厂方法的具体实现。
(1)使用构造函数实例化
当您通过构造函数方法创建bean时,所有普通类都可以被Spring使用并与Spring兼容。 也就是说,正在开发的类不需要实现任何特定的接口,也不需要以特定的方式编码。 只需指定bean类就足够了。 但是,这种情况下您可能需要一个默认(无参)构造函数。
使用基于xml的配置元数据,您可以使用如下方法,指定您的bean类:
<bean id="exampleBean" class="examples.ExampleBean"/> <bean name="anotherExample" class="examples.ExampleBeanTwo"/>
(2)使用静态工厂方法实例化
下面的beanDifination指定通过调用工厂方法创建bean:
在这个例子中,createInstance()方法必须是一个静态方法,下面的示例演示如何指定工厂方法:
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
下面的示例显示了一个具有静态工厂方法的类:
public class ClientService { private static ClientService clientService = new ClientService(); private ClientService() {} public static ClientService createInstance() { return clientService; } }
(3)使用实例工厂方法实例化
该方法类似于通过(静态工厂方法)实例化所需的bean,容器同样可以使用【实例工厂方法】调用【非静态方法】创建一个新的bean。 要使用这种机制,请将【class】属性保留为空,并在【factory-bean】属性中指定当前容器中包含要调用的实例方法的bean的名称。 使用“factory-method”属性设置工厂方法本身的名称。
下面的示例演示如何配置这样的bean:
<!-- the factory bean, which contains a method called createInstance() --> <bean id="serviceLocator" class="examples.DefaultServiceLocator"> <!-- inject any dependencies required by this locator bean --> </bean> <!-- the bean to be created via the factory bean --> <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
下面的例子显示了相应的类:
public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); public ClientService createClientServiceInstance() { return clientService; } }
一个工厂类也可以包含多个工厂方法,如下例所示:
<bean id="serviceLocator" class="examples.DefaultServiceLocator"> <!-- inject any dependencies required by this locator bean --> </bean> <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/> <bean id="accountService" factory-bean="serviceLocator" factory-method="createAccountServiceInstance"/>
下面的例子显示了相应的类:
public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private static AccountService accountService = new AccountServiceImpl(); public ClientService createClientServiceInstance() { return clientService; } public AccountService createAccountServiceInstance() { return accountService; } }
注意: 其实我们这样明白一点,静态工厂方法可以直接调用,事例工厂方法需要容器先构建好事例再进行调用。