该篇随笔,主要用于记录Spring Framework 基础知识。由于笔者是初学者,见识与能力有限,难免出现错误,如果发现错误,望不吝赐教。
Spring好处
以下列出了使用Spring Framework的一些巨大好处
-
Spring使开发人员能够使用POJO开发企业级应用程序。仅使用POJO的好处是您不需要EJB容器产品(如应用程序服务器),但您可以选择仅使用强大的servlet容器(如Tomcat)或某些商业产品。
-
Spring采用模块化方式组织。即使包和类的数量很大,你也只需要担心你需要的那些而忽略其余的。
-
Spring并没有重新发明轮子,而是真正利用了一些现有技术,如几个ORM框架,日志框架,JEE,Quartz和JDK计时器以及其他视图技术。
-
测试用Spring编写的应用程序很简单,因为依赖于环境的代码被移动到这个框架中。此外,通过使用JavaBeanstyle POJO,使用依赖注入来注入测试数据变得更加容易。
-
Spring的Web框架是一个设计良好的Web MVC框架,它提供了一个很好的替代Web框架,如Struts或其他过度设计或不太流行的Web框架。
-
Spring提供了一个方便的API,用于将特定于技术的异常(例如,JDBC,Hibernate或JDO抛出)转换为一致的,未经检查的异常。
-
轻量级IoC容器往往是轻量级的,尤其是与EJB容器相比时。这有利于在具有有限内存和CPU资源的计算机上开发和部署应用程序。
-
Spring提供了一致的事务管理接口,可以缩小到本地事务(例如,使用单个数据库)并扩展到全局事务(例如,使用JTA)。
依赖注入
- Inversion of Control
- 编写java程序,尽可能独立于其他java类,增加重用这些类的可能性
- 含义:A依赖于B类,意味着,B类将由IoC注入A类
面向面编程(AOP)
- 关键组件
- 跨领域问题:日志记录,声明式事务,安全性,缓存等
- AOP中,模块化单元是面
- AOP可帮助您将交叉问题与它们所影响的对象分离
- 允许定义方法拦截器和切入点,以便解耦。
Spring Framework
Core Container
- Beans
- Core,提供了框架的基本部分,包括IOC和依赖注入特征
- Bean提供了BeanFactory,复杂的实现工厂模式
- Context由core和bean提供,它是访问任何定义和配置的对象的媒介。ApplicationContext接口是上下文模块的焦点。
- SpEL模块提供了一种强大的表达式语言,用于在运行时查询和操作对象图。
Data Access/Integration
- JDBC
- ORM 模块为流行的对象关系映射api提供集成层,包括JPA、JDO、Hibernate和iBatis
- OXM 模块提供了一个抽象层,支持JAXB、Castor、XMLBeans、JiBX和XStream的对象/XML映射实现。
- JMS 包含用于生成和消费消息的特性。
- Transaction 模块为实现特殊接口的类和所有pojo支持编程和声明式事务管理
Web
- Web Web模块提供了基本的面向Web的集成特性,例如多部分文件上传功能,以及使用servlet侦听器和面向Web的应用程序上下文初始化IoC容器。
- Web-MVC 模块包含Spring用于web应用程序的模型-视图-控制器(MVC)实现。
- Web-Socket 模块在web应用程序中支持基于websocket的客户机和服务器之间的双向通信。
- Web-Portlet 模块提供了在portlet环境中使用的MVC实现,并反映了web servlet模块的功能。
Miscellaneous
- AOP模块提供了面向方面的编程实现,允许您将方法拦截器和切入点定义为实现应该分离的功能的干净解耦代码。
- Aspects模块提供了与AspectJ的集成,这又是一个强大而成熟的AOP框架。
- Instrumentation 模块提供了类插装支持和类装入器实现,用于某些应用服务器。
- Messaging模块支持STOMP作为应用程序中使用的WebSocket子协议。它还支持一个注释编程模型,用于路由和处理来自WebSocket客户端的STOMP消息。
- Test模块支持使用JUnit或TestNG框架测试Spring组件
编写第一个程序
1.使用IDEA创建Spring项目
2.创建HelloWorld文件
package top.ninwoo.learn;
public class HelloWorld {
private String message;
public void getMessage() {
System.out.println("Your message " + message);
}
public void setMessage(String message) {
this.message = message;
}
}
3.创建Main函数
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloworld");
obj.getMessage();
}
}
使用ClassPathXmlApplicationContext读取bean配置文件
4.配置文件
<?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="helloworld" class="top.ninwoo.learn.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>
</beans>
property 传入了HelloWorld的参数
IoC容器
- Spring Framework的核心
- 容器船舰对象,并连接彼此,并管理其生命周期
- DI来管理组成应用程序的组件
- 对象称为Spring Beans
Spring IoC容器利用Java POJO类和配置元数据来生成完全配置和可执行的系统或应用程序,配置元数据可以由:
- XML
- Java注释或Java代码表示
容器类型:
- Spring BeanFactory: 最简单容器,目的向后兼容与Spring集成的大量第三方框架
- Spring ApplicationContext: 添加了更多企业特有的功能,比如能够解析来自属性文件的文本消息,
以及能够向感兴趣的事件侦听器发布应用程序事件。
ApplicationContext包含全部的BeanFactory,BeanFactory在小型化设备上仍可以用。
Spring Bean
构成应用程序主干并由Spring IoC容器管理的对象称为bean.
Bean的定义:
- 如何创建Bean
- Bean的生命周期细节
- Bean的依赖关系
参数:
- class :必须,指定创建的Bean类
- name : 唯一指定Bean标识符,XML中可以使用id/name来指定
- scope : 作用域
- construtor-arg : 用于注入依赖项
- properties : 用于注入依赖项
- autowiring mode : 用于注入依赖项
- lazy-initialization mode : 延迟初始化的bean告诉IoC容器在第一次请求时创建bean实例,而不是在启动时创建
- initialization method : 在容器设置了bean之后所有必要属性之后调用的回调。它将在bean生命周期章节中讨论
- destruction method : 当Bean容器被销毁时使用的回调。
Spring配置元数据
方法:
- XML
- 注解
- Java的配置
<?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>
<!-- A bean definition with initialization method -->
<bean id = "..." class = "..." init-method = "...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- A bean definition with destruction method -->
<bean id = "..." class = "..." destroy-method = "...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
Spring - Bean Scopes
当定义一个prototype
可以强制Spring每次都产生一个新的Bean实例,singleton
每次返回的都是同一个bean。
Spring Framework 支持以下5种作用域,如果使用的是web-aware Application只有三种可用。
- singleton : 将bean定义范围限定为每个Spring IoC容器的单个实例(默认)
- prototype : 将bean定义范围限定为具有任意数量的对象实例
- request : 将bean定义范围限定为HTTP请求。仅在Web感知Spring ApplicationContext的上下文中有效
- session : 将bean定义范围限定为HTTP会话。仅在Web感知Spring ApplicationContext的上下文中有效。
- global-session : 将bean定义范围限定为全局HTTP会话。仅在Web感知Spring ApplicationContext的上下文中有效。
singleton
只创建该Bean定义的对象的一个实例。
<!-- A bean definition with singleton scope -->
<bean id = "..." class = "..." scope = "singleton">
<!-- collaborators and configuration for this bean go here -->
</bean>
配置完成之后,如下的代码,有以下的效果。
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
objA.setMessage("I'm object A");
objA.getMessage();
HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
objB.getMessage();
该程序打印:
I'm object A
I'm object A
这证明,实际只创建了一个bean对象,虽然调用了两次getBean,但返回的都是同一个对象。
prototype
每次调用bean,都会创建一个新的方法。
将上述的bean设置为prototype,执行同样的代码将会出现不同的效果。
该程序打印:
I'm object A
null
Spring-Bean Life Cycle
主要指的是,当bean被实例化时,可能需要执行一些初始化以使其进入可用状态。类似地,当不再需要bean并从容器中移除bean时,可能需要进行一些清理。
中间也存在一些活动,本章重点讨论以下两个声明周期回调方法。
- initmethod
- destroy-method
初始化回调
org.springframework.beans.factory.InitializingBean已经为我们提供了开发接口。
void afterPropertiesSet() throws Exception;
这样我们就可以通过实现上面的接口实现初始化工作
public class ExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
另外,对于XML的配置元数据,可以通过init-method指定具有void无参数的方法
<bean id = "exampleBean" class = "examples.ExampleBean" init-method = "init"/>
销毁回调
org.springframework.beans.factory.DisposableBean也已经提供好了接口
void destroy() throws Exception;
这样,我们可以通过实现该接口实现销毁的工作
public class ExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work
}
}
注意:如果想要使该函数起作用,需要注册registerShutdownHook()方法。
同样,基于XML配置元数据,可以使用destroy-method指定具有void的无参方法
<bean id = "exampleBean" class = "examples.ExampleBean" destroy-method = "destroy"/>
建议还是使用XML的方式,更加灵活。
默认的初始化和销毁方法
如果很多bean都需要同名的初始化和销毁方法,可以在
default-destroy-method。
<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"
default-init-method = "init"
default-destroy-method = "destroy">
<bean id = "..." class = "...">
<!-- collaborators and configuration for this bean go here -->
</bean>
</beans>
Spring - Bean Post Processors
BeanPostProcessor接口定义了可以实现的回调方法,以提供您自己的实例化逻辑、依赖性解析逻辑等等。您还可以在Spring容器通过插入一个或多个BeanPostProcessor实现完成bean的实例化、配置和初始化之后实现一些定制逻辑。
可以配置多个BeanPostProcessor接口,还可以通过设置BeanPostProcessor实现有序接口所提供的order属性来控制这些BeanPostProcessor接口的执行顺序。
BeanPostProcessor对bean(或对象)实例进行操作,这意味着Spring IoC容器实例化一个bean实例,然后BeanPostProcessor接口执行它们的工作。
ApplicationContext自动检测任何通过BeanPostProcessor接口的实现定义的bean,并将这些bean注册为postprocessor,然后在bean创建时由容器适当地调用。
Example
HelloWorld.java
public class HelloWorld {
private String message;
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
public void init(){
System.out.println("Bean is going through init.");
}
public void destroy(){
System.out.println("Bean will destroy now.");
}
}
InitHelloWorld.java——实现BeanPostProcessor接口
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
public class InitHelloWorld implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("BeforeInitialization : " + beanName);
return bean; // you can return any other object as well
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("AfterInitialization : " + beanName);
return bean; // you can return any other object as well
}
}
MainAPP.java
public class MainApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
context.registerShutdownHook();
}
}
Beans.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">
<bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld"
init-method = "init" destroy-method = "destroy">
<property name = "message" value = "Hello World!"/>
</bean>
<bean class = "com.tutorialspoint.InitHelloWorld" />
</beans>
输出结果:
BeforeInitialization : helloWorld
Bean is going through init.
AfterInitialization : helloWorld
Your Message : Hello World!
Bean will destroy now.
Spring - Bean Definition Inheritance(Bean的定义继承)
Spring Bean的继承和Java类的继承没有关系,但是概念相同。可以将父Bean定义为模板,其他子bean从父bean继承所需配置。
Example
Beans.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">
<bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld">
<property name = "message1" value = "Hello World!"/>
<property name = "message2" value = "Hello Second World!"/>
</bean>
<bean id ="helloIndia" class = "com.tutorialspoint.HelloIndia" parent = "helloWorld">
<property name = "message1" value = "Hello India!"/>
<property name = "message3" value = "Namaste India!"/>
</bean>
</beans>
HelloWorld.java
public class HelloWorld {
private String message1;
private String message2;
public void setMessage1(String message){
this.message1 = message;
}
public void setMessage2(String message){
this.message2 = message;
}
public void getMessage1(){
System.out.println("World Message1 : " + message1);
}
public void getMessage2(){
System.out.println("World Message2 : " + message2);
}
}
HelloIndia.java
public class HelloIndia {
private String message1;
private String message2;
private String message3;
public void setMessage1(String message){
this.message1 = message;
}
public void setMessage2(String message){
this.message2 = message;
}
public void setMessage3(String message){
this.message3 = message;
}
public void getMessage1(){
System.out.println("India Message1 : " + message1);
}
public void getMessage2(){
System.out.println("India Message2 : " + message2);
}
public void getMessage3(){
System.out.println("India Message3 : " + message3);
}
}
MainApp
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
objA.getMessage1();
objA.getMessage2();
HelloIndia objB = (HelloIndia) context.getBean("helloIndia");
objB.getMessage1();
objB.getMessage2();
objB.getMessage3();
}
}
最终结果:
World Message1 : Hello World!
World Message2 : Hello Second World!
India Message1 : Hello India!
India Message2 : Hello Second World!
India Message3 : Namaste India!
由此可以看出,虽然
<bean id ="helloIndia" class = "com.tutorialspoint.HelloIndia" parent = "helloWorld">
<property name = "message1" value = "Hello India!"/>
<property name = "message3" value = "Namaste India!"/>
</bean>
没有给message2复制,但是将父bean的message2的值传递给了子bean的message2。
总结来说,子Bean存在则覆盖,没有则继承父类的定义。
注入集合
本小结主要讲述如何使用XML注入集合
可以注入的类型如下:
<list>
<set>
<map>
<props>
你可以使用或
Example
JavaCollection
public class JavaCollection {
List addressList;
Set addressSet;
Map addressMap;
Properties addressProp;
// a setter method to set List
public void setAddressList(List addressList) {
this.addressList = addressList;
}
// prints and returns all the elements of the list.
public List getAddressList() {
System.out.println("List Elements :" + addressList);
return addressList;
}
// a setter method to set Set
public void setAddressSet(Set addressSet) {
this.addressSet = addressSet;
}
// prints and returns all the elements of the Set.
public Set getAddressSet() {
System.out.println("Set Elements :" + addressSet);
return addressSet;
}
// a setter method to set Map
public void setAddressMap(Map addressMap) {
this.addressMap = addressMap;
}
// prints and returns all the elements of the Map.
public Map getAddressMap() {
System.out.println("Map Elements :" + addressMap);
return addressMap;
}
// a setter method to set Property
public void setAddressProp(Properties addressProp) {
this.addressProp = addressProp;
}
// prints and returns all the elements of the Property.
public Properties getAddressProp() {
System.out.println("Property Elements :" + addressProp);
return addressProp;
}
}
MainApp.java
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
JavaCollection jc=(JavaCollection)context.getBean("javaCollection");
jc.getAddressList();
jc.getAddressSet();
jc.getAddressMap();
jc.getAddressProp();
}
}
Beans.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">
<!-- 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 = "one">INDIA</prop>
<prop key = "two">Pakistan</prop>
<prop key = "three">USA</prop>
<prop key = "four">USA</prop>
</props>
</property>
</bean>
</beans>
输出:
List Elements :[INDIA, Pakistan, USA, USA]
Set Elements :[INDIA, Pakistan, USA]
ap Elements :{1 = INDIA, 2 = Pakistan, 3 = USA, 4 = USA}
Property Elements :{two = Pakistan, one = INDIA, three = USA, four = USA}
注入Bean引用
可以直接注入bean引用作为集合的元素之一。
Example
<?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">
<!-- Bean Definition to handle references and values -->
<bean id = "..." class = "...">
<!-- Passing bean reference for java.util.List -->
<property name = "addressList">
<list>
<ref bean = "address1"/>
<ref bean = "address2"/>
<value>Pakistan</value>
</list>
</property>
<!-- Passing bean reference for java.util.Set -->
<property name = "addressSet">
<set>
<ref bean = "address1"/>
<ref bean = "address2"/>
<value>Pakistan</value>
</set>
</property>
<!-- Passing bean reference for java.util.Map -->
<property name = "addressMap">
<map>
<entry key = "one" value = "INDIA"/>
<entry key = "two" value-ref = "address1"/>
<entry key = "three" value-ref = "address2"/>
</map>
</property>
</bean>
</beans>
注入空值和空的字符串
1.传入空的字符串
<bean id = "..." class = "exampleBean">
<property name = "email" value = ""/>
</bean>
2.传入null
<bean id = "..." class = "exampleBean">
<property name = "email"><null/></property>
</bean>
Spring-Beans Auto-Wiring(Bean自动装配)
Spring container可以自动连接协作bean之间的关系,而不需要使用
自动装配模式
您可以使用
- no : 默认设置,不自动装配,需要配置显示bean引用连接
- byName : 通过属性名称自动装配。Spring容器查看在XML配置文件中将autowire属性设置为byName的bean的属性。然后,它尝试将其属性与配置文件中相同名称定义的bean匹配并连接起来。
- byType : 按属性数据类型自动连接。Spring容器查看在XML配置文件中将autowire属性设置为byType的bean的属性。然后,如果其类型与配置文件中的某个bean名称完全匹配,则尝试匹配并连接属性。如果存在多个这样的bean,则抛出一个致命异常。
- constructor : 类似于byType,但类型应用于构造函数参数。如果容器中没有确切的构造函数参数类型的bean,则会引发致命错误。
- autodetect : Spring首先尝试通过使用构造函数进行连接,如果它不工作,Spring将尝试通过byType进行自动连接。
可以使用byType
和constructor
自动装配模式去装配arrays和其他的集合类型。
自动装配的限制
自动装配在项目中一致使用时效果最好。如果通常不使用自动装配的话,对开发者只使用一两个bean定义的情况,会比较困惑。但是,autowiring可以显著减少指定属性或构造函数参数的需要,但是在使用它们之前,应该考虑到autowiring的局限性和缺点。
限制:
- 重写可能性
您仍然可以使用和 settings来指定依赖关系,这些设置总是会覆盖autowiring。
- 原始数据类型
您不能自动连接所谓的简单属性,如原语、字符串和类。
- 混乱
不如显式装配精确,所以如果可能,最好使用显式装配。
基于注释的配置
从Spring 2.5起,支持使用注解进行配置依赖注入。因此,您可以通过在相关的类、方法或字段声明上使用注释将bean配置移动到组件类本身,而不是使用XML来描述bean连接。
注释注入在XML注入之前执行。因此,对于通过两种方法连接的属性,后一种配置将覆盖前一种配置。
默认情况下,在Spring容器中没有打开注释连接。因此,在使用基于注释的连接之前,我们需要在Spring配置文件中启用它。因此,如果您想在Spring应用程序中使用任何注释,请考虑以下配置文件。
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
一旦<context:annotation-config/>
被配置,您可以开始对代码进行注释,以指示Spring应该自动将值连接到属性、方法和构造函数。让我们看看一些重要的注释,了解它们是如何工作的:
- Required : @Required注释应用于bean属性setter方法。
- Autowired : @Autowired注释可以应用于bean属性setter方法、非设置方法、构造函数和属性。
- Qualifier : 与@Autowired一起使用@Qualifier注释可以通过指定连接哪个bean来消除混淆。
- JSR-250 Annotations : Spring支持基于JSR-250的注释,包括@Resource、@PostConstruct和@PreDestroy注释。