Spring一文完结~

目录

一、概述

二、Spring体系结构

三、Spring环境配置

四、Spring IoC容器

五、Spring BeanFactory容器

六、Spring ApplicationContext容器

七、Spring Bean定义

八、Spring Bean作用域

九、Spring Bean生命周期

十、Spring Bean后置处理器

十一、Spring Bean定义继承

十二、Spring依赖注入

十三、注入内部Beans

十四、注入集合

十五、Beans自动装配

十六、基于注解的配置

十七、Spring基于Java的配置

十八、Spring中的事件处理

十九、Spring中的自定义事件

二十、AOP术语

二十一、Spring中基于AOP的XML架构

二十二、Spring 中基于AOP的@AspectJ

二十三、Spring JDBC框架

二十四、Spring中SQL的存储过程

二十五、Spring事务管理

二十六、Spring Web MVC框架

二十七、Spring MVC Hello World例子

二十八、Spring MVC表单处理例子

二十九、Spring 页面重定向例子

三十、Spring静态页面例子

 

后记:


一、概述

Spring是最受欢迎的企业级Java应用程序开发框架(现在好像不能那么说了,因为现在SpringBoot才是独领风骚啊),用Spring框架来创建性能好、易于测试、可重用的代码。

Spring框架是一个开源的Java平台,它最初是由Rod Johnson编写的,并且于2003年6月首次在Apache2.0许可下发布。

Spring框架的核心特性是可用来开发任何的Java应用程序,但是在Java EE平台上构建web应用程序是需要扩展的。Spring框架的目标是使J2EE开发变得更容易使用,通过启动基于POJO(POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB(企业JavaBean)混淆所创造的简称)编程模型来促进良好的编程实践。

1、使用Spring框架的好处

在有HTML+CSS+JS(jQuery)+Bootstrap+servlet+JSP(EL/JSTL)知识,虽然前端开发的难度降低了,前后端无服务器因为JSP应用动态开发也容易了,但是两者之间的交互和总的结构还是不够清晰。有了前端依旧没办法很快设计好后端的接口,这个是目前我的问题,所以看看Spring框架能否解决呢?

  • Spring 可以使开发人员使用 POJOs 开发企业级的应用程序。只使用 POJOs 的好处是你不需要一个 EJB 容器产品,比如一个应用程序服务器,但是你可以选择使用一个健壮的 servlet 容器,比如 Tomcat 或者一些商业产品。

  • Spring 在一个单元模式中是有组织的。即使包和类的数量非常大,你只要担心你需要的,而其它的就可以忽略了。

  • Spring 不会让你白费力气做重复工作,它真正的利用了一些现有的技术,像ORM 框架、日志框架、JEE、Quartz 和 JDK 计时器,其他视图技术。

  • 测试一个用 Spring 编写的应用程序很容易,因为环境相关的代码被移动到这个框架中。此外,通过使用 JavaBean-style POJOs,它在使用依赖注入注入测试数据时变得更容易。

  • Spring 的 web 框架是一个设计良好的 web MVC 框架,它为比如 Structs 或者其他工程上的或者不怎么受欢迎的 web 框架提供了一个很好的供替代的选择。MVC模式导致应用程序的不同方面(输入逻辑,业务逻辑和UI逻辑)分离,同时提供这些元素之间的松散耦合。

    • 模型(Model)封装了应用程序数据,通常它们将由POJO类组成。

    • 视图(View)负责渲染模型数据,一般来说它生成客户端浏览器可以解释HTML输出。

    • 控制器(Controller)负责处理用户请求并构建适当的模型,并将其传递给视图进行渲染。

  • Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。

  • 轻量级的 IOC (控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。)容器往往是轻量级的,例如,特别是当与 EJB 容器相比的时候。这有利于在内存和 CPU 资源有限的计算机上开发和部署应用程序。

  • Spring提供了一致的事务管理接口,可向下扩展到(使用一个单一的数据库,例如)本地事务并扩展到全局事务(例如,使用 JTA)。

2、依赖注入(DI)

Spring最认同的技术是控制反转的依赖注入(DI)模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体例子。

当编写一个复杂的Java应用程序时,应用程序类应该尽可能的独立于其它的java类来增加这些类可重用可能性,当进行单元测试时,可以使它们独立于其他类进行测试。依赖注释(或者有时被称为配线)有助于将这些类粘合在一起,并且在同一时间让它们保持独立。

到底什么是依赖注入?让我们将这两个词分开来看一看。这里将依赖关系部分转化为两个类之间的关联。例如,类A依赖于类B现在。现在,让我们看一看第二部分,注入。所有这一切都意味着类B通过IoC被注入到类A中。

依赖注入可以以向构造函数传递参数的方式发生,或者通过时候用setter方法post-construction。依赖注入是Spring框架的核心部分。

3、面向切面的程序设计(AOP)

Spring框架的一个关键组件是面向切面的程序设计(AOP)框架。一个程序中跨越多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样常见的很好的相关方面的例子,比如日志记录、声明性事务、安全性,和缓存等等。

在OOP中模块化的关键单元是类,而在AOP中模块的关键单元是切面。AOP帮助你将横切关注点从它们所影响的对象中分离出来,然而依赖注入帮助你的应用程序对象从彼此中分离出来。

Spring框架的AOP模块提供了面向切面的程序设计实现,可以定义诸如方向拦截器和切入点等,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以以用类似于.Net属性的方式合并行为信息到代码中。


二、Spring体系结构

Spring有可能成为所有企业应用程序的一站式服务店,然而,Spring是模块化的,允许你挑选和选择适用于你的模块,不必要把的剩余部分也引入。Spring框架提供约20个模块,可以根据应用程序的要求来使用:

Spring一文完结~_第1张图片

 

 

1、核心容器

核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring表达式语言,Spring Expression Language)模块组成,细节如下:

  • spring-core模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。

  • spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。

  • context模块建立在由corebeans 模块的基础上建立起来的,它以一种类似于JNDI注册的方式访问对象。Context模块继承自Bean模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过Servelet容器)等功能。Context模块也支持Java EE的功能,比如EJB、JMX和远程调用等。ApplicationContext接口是Context模块的焦点。spring-context-support提供了对第三方库集成到Spring上下文的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。

  • spring-expression模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。

它们完整依赖关系如下:

Spring一文完结~_第2张图片

 

2、数据访问/集成

数据访问/集成层包括JDBC,ORM,OXM,JMS和事物处理模块,它们的细节如下:

(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)

  • JDBC 模块提供了JDBC抽象层,它消除了冗长的JDBC编码和对数据库供应商特定错误代码的解析。

  • ORM 模块提供了对流行的对象关系映射API的集成,包括JPA、JDO和Hibernate等。通过此模块可以让这些ORM框架和spring的其它功能整合,比如前面提及的事务管理。

  • OXM 模块提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。

  • JMS 模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了spring-messaging模块。。

  • 事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由spring自动处理,编程式事务粒度更细)

3、Web

Web层由Web,Web-MVC,Web-Socket和Web-Portlet组成:

  • Web 模块提供面向web的基本功能和面向web的应用上下文,比如多部分(multipart)文件上传功能、使用Servlet监听器初始化IoC容器等。它还包括HTTP客户端以及Spring远程调用中与web相关的部分。。

  • Web-MVC 模块为web应用提供了模型视图控制(MVC)和REST Web服务的实现。Spring的MVC框架可以使领域模型代码和web表单完全地分离,且可以与Spring框架的其它所有功能进行集成。

  • Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。

  • Web-Portlet 模块提供了用于Portlet环境的MVC实现,并反映了spring-webmvc模块的功能。

4、其它

还有一些重要的模块,像AOP,Aspects,Instrumentation,Web和测试模块:

  • AOP 模块提供了面向方面的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。

  • Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。

  • Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。

  • Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。

  • 测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。


三、Spring环境配置

这里建议还是配置好java环境之后,在IDEA里面直接创建Spring项目就好,它会自动给你导包。如果是eclipse的话也可以,相关教程也是有的。

这里介绍当你已经配置好环境之后(如果是IDEA就是直接创建Spring项目即可,会自动导包)。

1、创建源文件

首先我们在Spring项目下创建名为com.tutorialspoint的包(src下),在包里面创建HelloWorld.java文件的内容:

package com.tutorialspoint;
public class HelloWorld {
   private String message;
   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
}

然后再创建MainApp.java:

package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");
      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();
   }
}

关于主要程序有以下两个要点需要注意:

  • 第一步是我们使用框架 API ClassPathXmlApplicationContext() 来创建应用程序的上下文。这个 API 加载 beans 的配置文件并最终基于所提供的 API,它处理创建并初始化所有的对象,即在配置文件中提到的 beans。

  • 第二步是使用已创建的上下文的 getBean() 方法来获得所需的 bean。这个方法使用 bean 的 ID 返回一个最终可以转换为实际对象的通用对象。一旦有了对象,你就可以使用这个对象调用任何类的方法。

2、创建bean的配置文件

你需要创建一个Bean的配置文件,该文件时一个XML文件,并且作为粘合bean的粘合剂即类。这个文件需要在src目录下创建,通常开发人员保存该文件名称为Beans.xml文件(或者applicationContext.xml)或者是任何你喜欢的名称。但你必须确保这个文件在CLASSPATH中是可用的,并在主应用程序中使用相同的名称,而在MainApp.java文件中创建应用程序的上下文。

Beans.xml用于给不同的bean分配唯一的ID,并且控制不同值的对象的创建,而不会影响Spring的任何源文件。例如,在下面的文件你可以为message变量传递任何值:


​
   
       
   

当Spring应用程序被加载到内存中时,框架利用了上面的配置文件来创建所有已经定义的beans,并且按照标签定义为它们分配一个唯一的ID。你可以使用标签来传递在创建对象时不同变量的值。

3、运行程序

一旦你完成了创建源代码和bean的配置文件后,就可以准备编译和运行你的程序了。


四、Spring IoC容器

Spring容器是Spring框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理它们的整个生命周期从创建到销毁。Spring容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为Spring Beans。

通过阅读配置元数据提供的指令,容器知道哪些对象进行实例化、配置和组装。配置元数据可以通过XML,Java注释或Java代码来表示。下图是Spring如何工作的高级视图。Spring IoC容器利用Java的POJO类和配置元数据来生成完全配置和可执行的系统或应用程序。

Spring一文完结~_第3张图片

 

IOC容器具有依赖注释功能的容器,它可以创建对象,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而“控制反转”是指new实例工作不由程序员来做,而是交给Spring容器来做。在Spring中BeanFactory是IOC容器的实际代表者。

Spring提供了以下两种不同类型的容器:

序号 容器 & 描述
1 Spring BeanFactory 容器它是最简单的容器,给 DI 提供了基本的支持,它用 org.springframework.beans.factory.BeanFactory 接口来定义。BeanFactory 或者相关的接口,如 BeanFactoryAware,InitializingBean,DisposableBean,在 Spring 中仍然存在具有大量的与 Spring 整合的第三方框架的反向兼容性的目的。
2 Spring ApplicationContext 容器该容器添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由 org.springframework.context.ApplicationContext 接口定义。

ApplicationContext容器包括BeanFactory容器的所有功能,所以通常认为包含BeanFactory。BeanFactory仍然可以用于轻量级的应用程序,如移动设备或基于applet的应用程序,其中它的数据量和速度是显著的。


五、Spring BeanFactory容器

这是一个最简单的容器,它的主要功能是为依赖注入(DI)提供支持,这个容器接口在org.springframewordk.beans.factory.BeanFactor中被定义。

BeanFactory和相关的接口,比如BeanFactoryAware、DisposableBean、InitializingBean,仍旧保留在Spring中,主要目的是向后者兼容已经存在的和那些Spring整合在一起的第三方框架。

在Spring中,有大量对BeanFactory接口的实现。其中,最常被使用的是XmlBeanFactory类。这个容器从一个XML文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。

在资源宝贵的移动设备或者基于applet的应用当中,BeanFactory会被优先选择。否则,一般使用的是ApplicationgContext,除非你有更好的理由选择BeanFactory。


六、Spring ApplicationContext容器

Application Context是BeanFactory的子接口,也被称为Spring上下文。

Application Context是spring中较高级的容器。和BeanFactory类似,它可以加载配置文件中定义的bean,将所有的bean集中在一起,当有请求的时候分配bean。另外,它增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。这个容器在org.springframework.context.ApplicationContext interface接口中定义。

ApplicationContext包含BeanFactory所有的功能,一般情况下,相对于BeanFactory,ApplicationContext会更加优秀。当然,BeanFactory仍然可以在轻量级应用中使用。

最常被使用的ApplicationContext接口实现:

  • FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。

  • ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。

  • WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。


七、Spring Bean定义

Bean对象是构成应用程序的支柱也是由Spring IoC容器管理的。bean是一个被实例化,组装,并通过Spring IoC容器所管理的对象。这些bean是由容器提供的配置元数据创建的。

bean定义包含成为配置元数据的信息,下述容器也需要知道配置元数据:

  • 如何创建一个 bean

  • bean 的生命周期的详细信息

  • bean 的依赖关系

上述所有的配置元数据转换成一组构成每个bean定义的下列属性:

属性 描述
class 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。
name 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
scope 这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。
constructor-arg 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
properties 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
autowiring mode 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
lazy-initialization mode 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。
initialization 方法 在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的生命周期章节中进行讨论。
destruction 方法 当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章节中进行讨论。

1、Bean与Spring容器的关系:

Spring一文完结~_第4张图片

 

2、Spring配置元数据

Spring IoC容器完全由实际编写的配置元数据的格式解耦,有三个重要的方法把配置元数据提供给Spring容器:

  • 基于 XML 的配置文件

  • 基于注解的配置

  • 基于 Java 的配置

对于基于XML的配置,Spring2.0以后使用Schema的格式,使得不同类型的配置拥有了自己的命名空间,使配置文件更具扩展性。

 

你已经看到了如何把基于XML的配置元素提供给容器,但是我们要看另一个例子,这个配置文件中不同的bean定义,包括延迟初始化,初始化方法和销毁方法的:


​
   
   
       
   
​
   
   
       
   
​
   
   
       
   
​
   
   
       
   
​
   

在上述示例中:

1)xmlns="http://www.springframework.org/schema/beans",默认命名空间:它没有命名空间,用于Spring Bean的定义;

2)xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance",xsi命名空间:这个命名空间用于为每个文档中命名空间指定相应的Schema样式文件,是标准组织定义的标准命名空间。


八、Spring Bean作用域

当Spring中定义一个bean时,你必须声明该bean的作用域的选项。例如,为了强制Spring在每次需要时都产生一个新的bean实例,你应该声明bean的作用域属性为prototype。同理,如果你想让Spring在每次需要时都返回同一个bean实例,你应该声明bean的作用域的属性为singleton。

Spring框架支持以下五个作用域,分别为singleton、prototype、request、session和global session:

作用域 描述
singleton 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值
prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
global-session 一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境

1、singleton作用域

singleton是默认的作用域,也就是说,当定义Bean时,如果没有指定作用域配置,则Bean的作用域被默认为singleton。

当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且对所有bean的请求,只要id与该bean定义相匹配,则只会返回bean的统一实例。

也就是说,当将一个bean定义设置为singleton作用域的时候,Spring IoC容器只会创建该bean定义的唯一实例。

Singleton是单例类型,就是在创建起容器时就自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。



    

2、prototype作用域

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。


九、Spring Bean生命周期

理解Spring bean的生命周期很容易。当一个bean被实例化时,它可能需要执行一些初始化使它转换为可用状态。同样,当bean不需要,并且从容器中移除时,可能需要做一些清除工作。

为了定义安装和拆卸一个bean,我们要声明init-method和destroy-method参数。init-method属性指定一个方法,在实例化bean时立即调用该方法。同样,destroy-method指定一个方法,只有从容器中移除bean之后,才能调用该方法。

Bean的生命周期可以表达为:Bean的定义——-Bean的初始化——Bean的使用——Bean的销毁

1、初始化调用

org.springframework..beans.factory.InitializingBean接口指定一个单一的方法:

void afterPropertiesSet() throws Exception;

因此,你可以简单的实现上述接口和初始化工作可以在afterPropertiesSet()方法中执行,如下所示:

public class ExampleBean implements InitializingBean {
   public void afterPropertiesSet() {
      // do some initialization work
   }
}

在基于XML的配置元数据的情况下,你可以使用init-method属性来指定带有void无参方法的名称。

下面是类的定义:

public class ExampleBean {
   public void init() {
      // do some initialization work
   }
}

2、销毁回调

org.springframework.beans.factory.DisposableBean接口指定一个单一的方法:

void destroy() throws Exception;

因此你可以简单的实现上述接口并且结束工作可以在destroy()方法中执行,如下:

public class ExampleBean implements DisposableBean {
   public void destroy() {
      // do some destruction work
   }
}

在基于XML的配置元数据的情况下,可以使用destroy-method属性来指定带有void无参数方法的名称。例如:

下面是类的定义:

public class ExampleBean {
   public void destroy() {
      // do some destruction work
   }
}

如果你在非web应用程序环境中使用Spring的IoC容器;例如在丰富的客户端桌面环境中;那么在JVM中你要注册关闭hook。这样做可以确保正常关闭,为了让所有资源都被释放,可以在单个beans上调用destroy方法。

建议你不要十四用InitializingBean或者DisposableBean的回调方法,因为XML匹配值在命名方法上提供了极大的灵活性。

 

3、例子:

步骤 描述
1 创建一个名称为 SpringExample 的项目,并且在创建项目的 src 文件夹中创建一个包 com.tutorialspoint
2 使用 Add External JARs 选项,添加所需的 Spring 库,解释见 Spring Hello World Example 章节。
3 com.tutorialspoint 包中创建 Java 类 HelloWorldMainApp
4 src 文件夹中创建 Beans 配置文件 Beans.xml
5 最后一步是创建的所有 Java 文件和 Bean 配置文件的内容,并运行应用程序,解释如下所示。

上面是eclipse中的步骤,如果是IDEA,直接创建类之后配置Beans.xml即可。

1)HelloWorld.java:

package com.tutorialspoint;
​
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.");
   }
}

2)MainApp.java

package com.tutorialspoint;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
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();
   }
}

3)配置方法


​
   
       
   

4、默认的初始化和销毁方法

如果你有太多具有相同名称的初始化或销毁方法的Bean,那么你不需要在每一个bean上声明初始化方法和销毁方法。框架使用元素中的default-init-method和default-destroy-method属性提供了灵活的配置这种情况。


​
   
       
   

十、Spring Bean后置处理器

Bean后置处理器允许在调用初始化方法前后对Bean进行额外的处理。

BeanPostProcessor接口定义回调方法,你可以实现该方法来提供自己的实例化逻辑,依赖解析逻辑等。你也可以在Spring容器通过插入一个或多个BeanPostProcessor的实现来完成实例化,配置和初始化一个bean之后实现一些自定义逻辑回调方法。

你可以配置多个BeanPostProcessor接口,通过设置BeanPostProcessor实现的Ordered接口提供的order属性来控制这些BeanPostProcessor接口的执行顺序。

BeanPostProcessor可以对bean(或对象)实例进行操作,这意味着Spring IoC容器实例化一个bean实例,然后BeanPostProcessor接口进行它们的工作。

ApplicationContext会自动检测由BeanPostProcessor接口的实现定义的bean,注册这些bean为后置处理器,然后通过在容器中创建bean,在适当的时候调用它。

例子:

1)HelloWorld.java:

package com.tutorialspoint;
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.");
   }
}

2)InitHelloWorld.java:

package com.tutorialspoint;
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
   }
}

3)MainApp.java:

package com.tutorialspoint;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
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();
   }
}

4)配置Beans.xml





   
       
   

   


十一、Spring Bean定义继承

bean定义可以包含很多的配置信息,包括构造函数的参数,属性值,容器的具体信息例如初始化信息,静态工厂方法名,等等。

Spring Bean定义的继承与java类的继承无关,但是继承的概念是一样的。你可以定义一个父bean的定义作为模板和其它子bean就可以从父bean中继承所需的配置。

当你使用基于XML的配置元数据时,通过使用父属性,指定父bean作为该属性的值来表明子bean的定义。

例子:

我们在适当的位置使用 Eclipse IDE,然后按照下面的步骤来创建一个 Spring 应用程序:

步骤 描述
1 创建一个名称为 SpringExample 的项目,并且在创建项目的 src 文件夹中创建一个包 com.tutorialspoint
2 使用 Add External JARs 选项,添加所需的 Spring 库,解释见 Spring Hello World Example 章节。
3 com.tutorialspoint 包中创建 Java 类 HelloWorldHelloIndiaMainApp
4 src 文件夹中创建 Beans 配置文件 Beans.xml
5 最后一步是创建的所有 Java 文件和 Bean 配置文件的内容,并运行应用程序,解释如下所示。

下面是配置文件 Beans.xml,在该配置文件中我们定义有两个属性 message1message2 的 “helloWorld” bean。然后,使用 parent 属性把 “helloIndia” bean 定义为 “helloWorld” bean 的孩子。这个子 bean 继承 message2 的属性,重写 message1 的属性,并且引入一个属性 message3





   
      
      
   

   
      
      
   

HelloWorld.java:

package com.tutorialspoint;
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

package com.tutorialspoint;

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.java:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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();
   }
}

 

1、Bean定义模板

你可以创建一个Bean定义模板,不需要话太多功夫它就可以被其它子bean定义使用。在定义一个Bean定义模板时,你不应该使用类的属性,而应该指定带true值的抽象属性:





   
      
      
      
   

   
      
      
   

父bean自身不能被实例化,因为它是不完整的,而且它被明确的标记为抽象的。当一个定义是抽象的,它仅仅作为一个纯粹的模板bean定义来使用,充当子定义的父定义使用。


十二、Spring依赖注入

Spring框架的核心能力之一就是通过哦依赖注入的方式来管理Bean之间的依赖关系。

1、依赖注入

买个基于应用程序的java都有几个对象,这些对象一起工作来呈现出终端用户所看到的工作的应用程序。当编写一个复杂的java应用程序时,应用程序类应该尽可能独立于其他的java类来增加可重用的可能性,并且在做单元测试时,测试独立于其他类的独立性。依赖注入(或有时成为布线)有助于把这些类粘合在一起,同时保持他们独立。

假设你有一个包含文本编辑器组件的应用程序,并且你想要提供拼写检查。标准代码看起来是这样的:

public class TextEditor {
   private SpellChecker spellChecker;  
   public TextEditor() {
      spellChecker = new SpellChecker();
   }
}

在这里我们所做的就是创建一个TextEditor和SpellChecker之间的依赖关系。在控制反转的场景中,我们反而会做这样的事情:

public class TextEditor {
   private SpellChecker spellChecker;
   public TextEditor(SpellChecker spellChecker) {
      this.spellChecker = spellChecker;
   }
}

 

在这里,TextEditor 不应该担心 SpellChecker 的实现。SpellChecker 将会独立实现,并且在 TextEditor 实例化的时候将提供给 TextEditor,整个过程是由 Spring 框架的控制。

在这里,我们已经从 TextEditor 中删除了全面控制,并且把它保存到其他地方(即 XML 配置文件),且依赖关系(即 SpellChecker 类)通过类构造函数被注入到 TextEditor 类中。因此,控制流通过依赖注入(DI)已经“反转”,因为你已经有效地委托依赖关系到一些外部系统。

依赖注入的第二种方法是通过 TextEditor 类的 Setter 方法,我们将创建 SpellChecker 实例,该实例将被用于调用 setter 方法来初始化 TextEditor 的属性。

序号 依赖注入类型 & 描述
1 Constructor-based dependency injection当容器调用带有多个参数的构造函数类时,实现基于构造函数的 DI,每个代表在其他类中的一个依赖关系。
2 Setter-based dependency injection基于 setter 方法的 DI 是通过在调用无参数的构造函数或无参数的静态工厂方法实例化 bean 之后容器调用 beans 的 setter 方法来实现的。

1、Spring基于构造函数的依赖注入

当容器调用有一组参数的类构造函数时,基于构造函数的DI就完成了,其中每个参数代表一个对其他类的依赖。

例子:

TextEditor.java:

package com.tutorialspoint;
public class TextEditor {
   private SpellChecker spellChecker;
   public TextEditor(SpellChecker spellChecker) {
      System.out.println("Inside TextEditor constructor." );
      this.spellChecker = spellChecker;
   }
   public void spellCheck() {
      spellChecker.checkSpelling();
   }
}

SpllChecker.java

package com.tutorialspoint;
public class SpellChecker {
   public SpellChecker(){
      System.out.println("Inside SpellChecker constructor." );
   }
   public void checkSpelling() {
      System.out.println("Inside checkSpelling." );
   } 
}

MainApp.java:

package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");
      TextEditor te = (TextEditor) context.getBean("textEditor");
      te.spellCheck();
   }
}

Beans.xml:





   
   
      
   

   
   
   

 

2、构造函数参数解析

如果存在不止一个参数时,把参数传递给构造函数时,可能会存在歧义。要解决这个问题,那么构造函数的参数在bean定义殊勋就是把这些参数提供给适当的构造函数的顺序就可以了。

 

3、基于设值函数的依赖注入

当容器调用一个无参的构造函数或一个无参的静态factory方法来初始化你的bean后,通过容器在你的bean上设值函数,基于设值函数的DI就完成了。


十三、注入内部Beans

正如你所知道的Java内部类是在其他类的范围内被定义的,同理,inner beans也是在其它bean的范围内定义的bean。





   
      
         
      
   


十四、注入集合

你已经看到了如何使用value属性来配置基本数据类型,也使用了property标签的ref属性来配置对象引用。这两种情况下处理一个值传递给一个bean。

如果你想传递多个值,如Java Collection类型List、Set、Map和Properites,那么有Spring提供了四种类型的集合的配置元素:

元素 描述
它有助于连线,如注入一列值,允许重复。
它有助于连线一组值,但不能重复。
它可以用来注入名称-值对的集合,其中名称和值可以是任何类型。
它可以用来注入名称-值对的集合,其中名称和值都是字符串类型。




   
   

      
      
         
            INDIA
            Pakistan
            USA
            USA
         
      

      
      
         
            INDIA
            Pakistan
            USA
            USA
        
      

      
      
         
            
            
            
            
         
      

      
      
         
            INDIA
            Pakistan
            USA
            USA
         
      

   


十五、Beans自动装配

你已经知道使用bean元素来声明bean和通过使用XML配置文件中的constructor-arg和property元素来注入。

Spring容器可以在不适用constructor-arg和property元素的情况下自动装配互相协助的bean之间的关系,这有助于减少编写一个大的基于Spring的应用程序的XML配置的数量。

1、自动装配模式:

下列自动装配模式,它们可以用于指示Spring容器为来使用自动装配进行依赖注入,你可以使用bean元素的autowire属性为一个bean定义自动装配模式。

模式 描述
no 这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。在依赖注入章节你已经看到这个了。
byName 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。
byType 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。
constructor 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。
autodetect Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。

2、自动装配的局限性

当自动装配始终在同一个项目中使用时,它的效果最好。如果通常不适用自动装配,它可能会使开发人员混淆的使用它来连接只有一个或两个bean定义。不过,自动装配可以显著减少需要指定的属性或构造器属性,但你应该在使用它们之前考虑到自动装配的局限性和缺点。

限制 描述
重写的可能性 你可以使用总是重写自动装配的 设置来指定依赖关系。
原始数据类型 你不能自动装配所谓的简单类型包括基本类型,字符串和类。
混乱的本质 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。

十六、基于注解的配置

从Spring2.5开始就可以使用注解来配置依赖注入。而不是采用XML来描述一个bean连线。你可以使用相关类,方法或字段声明的注解,将bean配置移动到组件类本身。

在XML注入之前进行注解注入,因此后者的配置将通过两种方式的属性连线被前者重写。

注解连线在默认情况下在Spring容器中打不开。因此,在可以使用基于注解的连线之前,我们将需要我们在Spring配置文件中启用它。所以如果你想要在Spring应用程序中使用任何注解,可以考虑下面的配置文件:





   
   

一旦被配置后,你就可以开始注解你的代码,表明Spring应该自动链接值到属性、方法和构造函数。让我们来看看几个重要的注解:

序号 注解 & 描述
1 @Required@Required 注解应用于 bean 属性的 setter 方法。
2 @Autowired@Autowired 注解可以应用到 bean 属性的 setter 方法,非 setter 方法,构造函数和属性。
3 @Qualifier通过指定确切的将被连线的 bean,@Autowired 和 @Qualifier 注解可以用来删除混乱。
4 JSR-250 AnnotationsSpring 支持 JSR-250 的基础的注解,其中包括了 @Resource,@PostConstruct 和 @PreDestroy 注解。

1)Spring @Required注释

@Required 注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。

示例:

让我们使 Eclipse IDE 处于工作状态,请按照下列步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为 SpringExample 的项目,并且在所创建项目的 src 文件夹下创建一个名为 com.tutorialspoint 的包。
2 使用 Add External JARs 选项添加所需的 Spring 库文件,就如在 Spring Hello World Example 章节中解释的那样。
3 com.tutorialspoint 包下创建 Java 类 StudentMainApp
4 src 文件夹下创建 Beans 配置文件 Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,并且按如下解释的那样运行应用程序。

下面是 Student.java 文件的内容:

package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Required;
public class Student {
   private Integer age;
   private String name;
   @Required
   public void setAge(Integer age) {
      this.age = age;
   }
   public Integer getAge() {
      return age;
   }
   @Required
   public void setName(String name) {
      this.name = name;
   }
   public String getName() {
      return name;
   }
}

下面是 MainApp.java 文件的内容:

package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      Student student = (Student) context.getBean("student");
      System.out.println("Name : " + student.getName() );
      System.out.println("Age : " + student.getAge() );
   }
}

下面是配置文件 Beans.xml: 文件的内容:





   

   
   
      

      
      
   

一旦你已经完成的创建了源文件和 bean 配置文件,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将引起 BeanInitializationException 异常,并且会输出一下错误信息和其他日志消息:

Property 'age' is required for bean 'student'

下一步,在你按照如下所示从 “age” 属性中删除了注释,你可以尝试运行上面的示例:





   

   
   
      
      
   

现在上面的示例将产生如下结果:

Name : Zara
Age : 11

2)Spring @Autowired注释

@Autowired 注释对在哪里和如何完成自动连接提供了更多的细微的控制。

@Autowired 注释可以在 setter 方法中被用于自动连接 bean,就像 @Autowired 注释,容器,一个属性或者任意命名的可能带有多个参数的方法。

Setter 方法中的 @Autowired

你可以在 XML 文件中的 setter 方法中使用 @Autowired 注释来除去 元素。当 Spring遇到一个在 setter 方法中使用的 @Autowired 注释,它会在方法中视图执行 byType 自动连接。

示例

让我们使 Eclipse IDE 处于工作状态,然后按照如下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为 SpringExample 的项目,并且在所创建项目的 src 文件夹下创建一个名为 com.tutorialspoint 的包。
2 使用 Add External JARs 选项添加所需的 Spring 库文件,就如在 Spring Hello World Example 章节中解释的那样。
3 com.tutorialspoint 包下创建 Java 类 TextEditor, SpellCheckerMainApp
4 src 文件夹下创建 Beans 配置文件 Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,并且按如下解释的那样运行应用程序。

这里是 TextEditor.java 文件的内容:

package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
   private SpellChecker spellChecker;
   @Autowired
   public void setSpellChecker( SpellChecker spellChecker ){
      this.spellChecker = spellChecker;
   }
   public SpellChecker getSpellChecker( ) {
      return spellChecker;
   }
   public void spellCheck() {
      spellChecker.checkSpelling();
   }
}

下面是另一个依赖的类文件 SpellChecker.java 的内容:

package com.tutorialspoint;
public class SpellChecker {
   public SpellChecker(){
      System.out.println("Inside SpellChecker constructor." );
   }
   public void checkSpelling(){
      System.out.println("Inside checkSpelling." );
   }  
}

下面是 MainApp.java 文件的内容:

package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      TextEditor te = (TextEditor) context.getBean("textEditor");
      te.spellCheck();
   }
}

下面是配置文件 Beans.xml





   

   
   
   

   
   
   

一旦你已经完成的创建了源文件和 bean 配置文件,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将会输出以下消息:

Inside SpellChecker constructor.
Inside checkSpelling.

属性中的 @Autowired

你可以在属性中使用 @Autowired 注释来除去 setter 方法。当时使用 为自动连接属性传递的时候,Spring 会将这些传递过来的值或者引用自动分配给那些属性。所以利用在属性中 @Autowired 的用法,你的 TextEditor.java 文件将变成如下所示:

package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
   @Autowired
   private SpellChecker spellChecker;
   public TextEditor() {
      System.out.println("Inside TextEditor constructor." );
   }  
   public SpellChecker getSpellChecker( ){
      return spellChecker;
   }  
   public void spellCheck(){
      spellChecker.checkSpelling();
   }
}

下面是配置文件 Beans.xml





   

   
   
   

   
   
   

一旦你在源文件和 bean 配置文件中完成了上面两处改变,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将会输出以下消息:

Inside TextEditor constructor.
Inside SpellChecker constructor.
Inside checkSpelling.

构造函数中的 @Autowired

你也可以在构造函数中使用 @Autowired。一个构造函数 @Autowired 说明当创建 bean 时,即使在 XML 文件中没有使用 元素配置 bean ,构造函数也会被自动连接。让我们检查一下下面的示例。

这里是 TextEditor.java 文件的内容:

package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
   private SpellChecker spellChecker;
   @Autowired
   public TextEditor(SpellChecker spellChecker){
      System.out.println("Inside TextEditor constructor." );
      this.spellChecker = spellChecker;
   }
   public void spellCheck(){
      spellChecker.checkSpelling();
   }
}

下面是配置文件 Beans.xml





   

   
   
   

   
   
   

一旦你在源文件和 bean 配置文件中完成了上面两处改变,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将会输出以下消息:

Inside SpellChecker constructor.Inside TextEditor constructor.
Inside checkSpelling.

@Autowired 的(required=false)选项

默认情况下,@Autowired 注释意味着依赖是必须的,它类似于 @Required 注释,然而,你可以使用 @Autowired 的 (required=false) 选项关闭默认行为。

即使你不为 age 属性传递任何参数,下面的示例也会成功运行,但是对于 name 属性则需要一个参数。你可以自己尝试一下这个示例,因为除了只有 Student.java 文件被修改以外,它和 @Required 注释示例是相似的。

package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class Student {
   private Integer age;
   private String name;
   @Autowired(required=false)
   public void setAge(Integer age) {
      this.age = age;
   }  
   public Integer getAge() {
      return age;
   }
   @Autowired
   public void setName(String name) {
      this.name = name;
   }   
   public String getName() {
      return name;
   }
}

3)Spring@Quqlifier注释

可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装配来消除混乱。

4)Spring JSR-250注释

Spring还使用基于 JSR-250 注释,它包括 @PostConstruct, @PreDestroy 和 @Resource 注释。因为你已经有了其他的选择,尽管这些注释并不是真正所需要的,但是关于它们仍然让我给出一个简短的介绍。

@PostConstruct 和 @PreDestroy 注释:

为了定义一个 bean 的安装和卸载,我们使用 init-method 和/或 destroy-method 参数简单的声明一下 。init-method 属性指定了一个方法,该方法在 bean 的实例化阶段会立即被调用。同样地,destroy-method 指定了一个方法,该方法只在一个 bean 从容器中删除之前被调用。

你可以使用 @PostConstruct 注释作为初始化回调函数的一个替代,@PreDestroy 注释作为销毁回调函数的一个替代。


十七、Spring基于Java的配置

到现在,你已经会使用XML配置文件来配置Spring bean。如果你熟悉使用XML配置,那么不需要在学习任何进行基于Java的配置。

基于 Java 的配置选项,可以使你在不用配置 XML 的情况下编写大多数的 Spring,但是一些有帮助的基于 Java 的注解,解释如下:

@Configuration 和 @Bean 注解

带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。最简单可行的 @Configuration 类如下所示:

package com.tutorialspoint;
import org.springframework.context.annotation.*;
@Configuration
public class HelloWorldConfig {
   @Bean 
   public HelloWorld helloWorld(){
      return new HelloWorld();
   }
}

等同于XML配置:


   

十八、Spring中的事件处理

Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期。当加载 beans 时,ApplicationContext 发布某些类型的事件。例如,当上下文启动时,ContextStartedEvent 发布,当上下文停止时,ContextStoppedEvent 发布。

通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。

Spring 提供了以下的标准事件:

序号 Spring 内置事件 & 描述
1 ContextRefreshedEventApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。
2 ContextStartedEvent当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
3 ContextStoppedEvent当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。
4 ContextClosedEvent当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。
5 RequestHandledEvent这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。

十九、Spring中的自定义事件

步骤:

步骤 描述
1 创建一个名称为 SpringExample 的项目,并且在创建项目的 src 文件夹中创建一个包 com.tutorialspoint。
2 使用 Add External JARs 选项,添加所需的 Spring 库,解释见 Spring Hello World Example 章节。
3 通过扩展 ApplicationEvent,创建一个事件类 CustomEvent。这个类必须定义一个默认的构造函数,它应该从 ApplicationEvent 类中继承的构造函数。
4 一旦定义事件类,你可以从任何类中发布它,假定 EventClassPublisher 实现了 ApplicationEventPublisherAware。你还需要在 XML 配置文件中声明这个类作为一个 bean,之所以容器可以识别 bean 作为事件发布者,是因为它实现了 ApplicationEventPublisherAware 接口。
5 发布的事件可以在一个类中被处理,假定 EventClassHandler 实现了 ApplicationListener 接口,而且实现了自定义事件的 onApplicationEvent 方法。
6 src 文件夹中创建 bean 的配置文件 Beans.xml 和 MainApp 类,它可以作为一个 Spring 应用程序来运行。
7 最后一步是创建的所有 Java 文件和 Bean 配置文件的内容,并运行应用程序,解释如下所示。

二十、AOP术语

让我们熟悉一下AOP概念和术语:

描述
Aspect 一个模块具有一组提供横切需求的 APIs。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面,这取决于需求。
Join point 在你的应用程序中它代表一个点,你可以在插件 AOP 方面。你也能说,它是在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。
Advice 这是实际行动之前或之后执行的方法。这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。
Pointcut 这是一组一个或多个连接点,通知应该被执行。你可以使用表达式或模式指定切入点正如我们将在 AOP 的例子中看到的。
Introduction 引用允许你添加新方法或属性到现有的类中。
Target object 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。
Weaving Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。

1、通知类型:

通知 描述
前置通知 在一个方法执行之前,执行通知。
后置通知 在一个方法执行之后,不考虑其结果,执行通知。
返回后通知 在一个方法执行之后,只有在方法成功完成时,才能执行通知。
抛出异常后通知 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。
环绕通知 在建议方法调用之前和之后,执行通知。

2、实现自定义切面

Spring 支持 @AspectJ annotation style 的方法和基于模式的方法来实现自定义方面。这两种方法已经在下面两个子节进行了详细解释。

方法 描述
XML Schema based 方面是使用常规类以及基于配置的 XML 来实现的。
@AspectJ based @AspectJ 引用一种声明方面的风格作为带有 Java 5 注释的常规 Java 类注释。

二十一、Spring中基于AOP的XML架构

使用aop命名空间标签,需要导入spring-aop j框架,如:




   

1、声明一个aspect

一个 aspect 是使用 元素声明的,支持的 bean 是使用 ref 属性引用的,如下所示:


   
   ...
   


...

2、声明一个切入点

一个切入点有助于确定使用不同建议执行的感兴趣的连接点(即方法)。在处理基于配置的 XML 架构时,切入点将会按照如下所示定义:


   
   
   ...
   


...

二十二、Spring 中基于AOP的@AspectJ

@AspectJ 作为通过 Java 5 注释注释的普通的 Java 类,它指的是声明 aspects 的一种风格。通过在你的基于架构的 XML 配置文件中包含以下元素,@AspectJ 支持是可用的。

1、声明一个aspect

Aspects 类和其他任何正常的 bean 一样,除了它们将会用 @AspectJ 注释之外,它和其他类一样可能有方法和字段,如下所示:

package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AspectModule {
}

二十三、Spring JDBC框架

1、JDBC框架概述

在使用普通的 JDBC 数据库时,就会很麻烦的写不必要的代码来处理异常,打开和关闭数据库连接等。但 Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常,处理事务,到最后关闭连接。

所以当从数据库中获取数据时,你所做的是定义连接参数,指定要执行的 SQL 语句,每次迭代完成所需的工作。

Spring JDBC 提供几种方法和数据库中相应的不同的类与接口。我将给出使用 JdbcTemplate 类框架的经典和最受欢迎的方法。这是管理所有数据库通信和异常处理的中央框架类。

2、JdbcTemplate类

JdbcTemplate 类执行 SQL 查询、更新语句和存储过程调用,执行迭代结果集和提取返回参数值。它也捕获 JDBC 异常并转换它们到 org.springframework.dao 包中定义的通用类、更多的信息、异常层次结构。

JdbcTemplate* 类的实例是线程安全配置的。所以你可以配置 JdbcTemplate 的单个实例,然后将这个共享的引用安全地注入到多个 DAOs 中。

使用 JdbcTemplate 类时常见的做法是在你的 Spring 配置文件中配置数据源,然后共享数据源 bean 依赖注入到 DAO 类中,并在数据源的设值函数中创建了 JdbcTemplate。

3、配置数据源

我们在数据库 TEST 中创建一个数据库表 Student。假设你正在使用 MySQL 数据库,如果你使用其他数据库,那么你可以改变你的 DDL 和相应的 SQL 查询。

CREATE TABLE Student(
   ID   INT NOT NULL AUTO_INCREMENT,
   NAME VARCHAR(20) NOT NULL,
   AGE  INT NOT NULL,
   PRIMARY KEY (ID)
);

现在,我们需要提供一个数据源到 JdbcTemplate 中,所以它可以配置本身来获得数据库访问。你可以在 XML 文件中配置数据源,其中一段代码如下所示:


   
   
   
   

4、执行SQL语句

我们看看如何使用 SQL 和 jdbcTemplate 对象在数据库表中执行 CRUD(创建、读取、更新和删除)操作。

查询一个整数类型:

String SQL = "select count(*) from Student";
int rowCount = jdbcTemplateObject.queryForInt( SQL );

查询一个 long 类型:

String SQL = "select count(*) from Student";
long rowCount = jdbcTemplateObject.queryForLong( SQL );

一个使用绑定变量的简单查询:

String SQL = "select age from Student where id = ?";
int age = jdbcTemplateObject.queryForInt(SQL, new Object[]{10});

查询字符串:

String SQL = "select name from Student where id = ?";
String name = jdbcTemplateObject.queryForObject(SQL, new Object[]{10}, String.class);

查询并返回一个对象:

String SQL = "select * from Student where id = ?";
Student student = jdbcTemplateObject.queryForObject(SQL, 
                  new Object[]{10}, new StudentMapper());
public class StudentMapper implements RowMapper {
   public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
      Student student = new Student();
      student.setID(rs.getInt("id"));
      student.setName(rs.getString("name"));
      student.setAge(rs.getInt("age"));
      return student;
   }
}

查询并返回多个对象:

String SQL = "select * from Student";
List students = jdbcTemplateObject.query(SQL,
                         new StudentMapper());
public class StudentMapper implements RowMapper {
   public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
      Student student = new Student();
      student.setID(rs.getInt("id"));
      student.setName(rs.getString("name"));
      student.setAge(rs.getInt("age"));
      return student;
   }
}

在表中插入一行:

String SQL = "insert into Student (name, age) values (?, ?)";
jdbcTemplateObject.update( SQL, new Object[]{"Zara", 11} );

更新表中的一行:

String SQL = "update Student set name = ? where id = ?";
jdbcTemplateObject.update( SQL, new Object[]{"Zara", 10} );

从表中删除一行:

String SQL = "delete Student where id = ?";
jdbcTemplateObject.update( SQL, new Object[]{20} );

5、执行DDL语句

你可以使用 jdbcTemplate 中的 execute(..) 方法来执行任何 SQL 语句或 DDL 语句。下面是一个使用 CREATE 语句创建一个表的示例:

String SQL = "CREATE TABLE Student( " +
   "ID   INT NOT NULL AUTO_INCREMENT, " +
   "NAME VARCHAR(20) NOT NULL, " +
   "AGE  INT NOT NULL, " +
   "PRIMARY KEY (ID));"
jdbcTemplateObject.execute( SQL );

6、Spring JDBC框架例子:

步骤 描述
1 创建一个名为 SpringExample 的项目,并在创建的项目中的 src 文件夹下创建包 com.tutorialspoint
2 使用 Add External JARs 选项添加必需的 Spring 库,解释见 Spring Hello World Example 章节。
3 在项目中添加 Spring JDBC 指定的最新的库 mysql-connector-java.jarorg.springframework.jdbc.jarorg.springframework.transaction.jar。如果这些库不存在,你可以下载它们。
4 创建 DAO 接口 StudentDAO 并列出所有必需的方法。尽管这一步不是必需的而且你可以直接编写 StudentJDBCTemplate 类,但是作为一个好的实践,我们最好还是做这一步。
5 com.tutorialspoint 包下创建其他的必需的 Java 类 StudentStudentMapperStudentJDBCTemplateMainApp
6 确保你已经在 TEST 数据库中创建了 Student 表。并确保你的 MySQL 服务器运行正常,且你可以使用给出的用户名和密码读/写访问数据库。
7 src 文件夹下创建 Beans 配置文件 Beans.xml
8 最后一步是创建所有的 Java 文件和 Bean 配置文件的内容并按照如下所示的方法运行应用程序。

1)数据库接口文件StudentDAO.java

package com.tutorialspoint;
import java.util.List;
import javax.sql.DataSource;
public interface StudentDAO {
   /** 
    * This is the method to be used to initialize
    * database resources ie. connection.
    */
   public void setDataSource(DataSource ds);
   /** 
    * This is the method to be used to create
    * a record in the Student table.
    */
   public void create(String name, Integer age);
   /** 
    * This is the method to be used to list down
    * a record from the Student table corresponding
    * to a passed student id.
    */
   public Student getStudent(Integer id);
   /** 
    * This is the method to be used to list down
    * all the records from the Student table.
    */
   public List listStudents();
   /** 
    * This is the method to be used to delete
    * a record from the Student table corresponding
    * to a passed student id.
    */
   public void delete(Integer id);
   /** 
    * This is the method to be used to update
    * a record into the Student table.
    */
   public void update(Integer id, Integer age);
}

Stuent.java:

package com.tutorialspoint;
public class Student {
   private Integer age;
   private String name;
   private Integer id;
   public void setAge(Integer age) {
      this.age = age;
   }
   public Integer getAge() {
      return age;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getName() {
      return name;
   }
   public void setId(Integer id) {
      this.id = id;
   }
   public Integer getId() {
      return id;
   }
}

StudentMapper.java

package com.tutorialspoint;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class StudentMapper implements RowMapper {
   public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
      Student student = new Student();
      student.setId(rs.getInt("id"));
      student.setName(rs.getString("name"));
      student.setAge(rs.getInt("age"));
      return student;
   }
}

下面定义的DAO接口StudentDAO的实现类:

StudentJDBCTemplate.java:

package com.tutorialspoint;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class StudentJDBCTemplate implements StudentDAO {
   private DataSource dataSource;
   private JdbcTemplate jdbcTemplateObject; 
   public void setDataSource(DataSource dataSource) {
      this.dataSource = dataSource;
      this.jdbcTemplateObject = new JdbcTemplate(dataSource);
   }
   public void create(String name, Integer age) {
      String SQL = "insert into Student (name, age) values (?, ?)";     
      jdbcTemplateObject.update( SQL, name, age);
      System.out.println("Created Record Name = " + name + " Age = " + age);
      return;
   }
   public Student getStudent(Integer id) {
      String SQL = "select * from Student where id = ?";
      Student student = jdbcTemplateObject.queryForObject(SQL, 
                        new Object[]{id}, new StudentMapper());
      return student;
   }
   public List listStudents() {
      String SQL = "select * from Student";
      List  students = jdbcTemplateObject.query(SQL, 
                                new StudentMapper());
      return students;
   }
   public void delete(Integer id){
      String SQL = "delete from Student where id = ?";
      jdbcTemplateObject.update(SQL, id);
      System.out.println("Deleted Record with ID = " + id );
      return;
   }
   public void update(Integer id, Integer age){
      String SQL = "update Student set age = ? where id = ?";
      jdbcTemplateObject.update(SQL, age, id);
      System.out.println("Updated Record with ID = " + id );
      return;
   }
}

MainApp.java:

package com.tutorialspoint;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.tutorialspoint.StudentJDBCTemplate;
public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");
      StudentJDBCTemplate studentJDBCTemplate = 
      (StudentJDBCTemplate)context.getBean("studentJDBCTemplate");    
      System.out.println("------Records Creation--------" );
      studentJDBCTemplate.create("Zara", 11);
      studentJDBCTemplate.create("Nuha", 2);
      studentJDBCTemplate.create("Ayan", 15);
      System.out.println("------Listing Multiple Records--------" );
      List students = studentJDBCTemplate.listStudents();
      for (Student record : students) {
         System.out.print("ID : " + record.getId() );
         System.out.print(", Name : " + record.getName() );
         System.out.println(", Age : " + record.getAge());
      }
      System.out.println("----Updating Record with ID = 2 -----" );
      studentJDBCTemplate.update(2, 20);
      System.out.println("----Listing Record with ID = 2 -----" );
      Student student = studentJDBCTemplate.getStudent(2);
      System.out.print("ID : " + student.getId() );
      System.out.print(", Name : " + student.getName() );
      System.out.println(", Age : " + student.getAge());      
   }
}

beans.xml配置:




   
   
      
      
      
      
   

   
   
          
   


二十四、Spring中SQL的存储过程

SimpleJdbcCall 类可以被用于调用一个包含 IN 和 OUT 参数的存储过程。你可以在处理任何一个 RDBMS 时使用这个方法,就像 Apache Derby, DB2, MySQL, Microsoft SQL Server, Oracle,和 Sybase。


二十五、Spring事务管理

事务管理

一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性说成是 ACID

  • 原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。

  • 一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。

  • 隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。

  • 持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。

一个真正的 RDBMS 数据库系统将为每个事务保证所有的四个属性。使用 SQL 发布到数据库中的事务的简单视图如下:

  • 使用 begin transaction 命令开始事务。

  • 使用 SQL 查询语句执行各种删除、更新或插入操作。

  • 如果所有的操作都成功,则执行提交操作,否则回滚所有操作。

Spring 框架在不同的底层事务管理 APIs 的顶部提供了一个抽象层。Spring 的事务支持旨在通过添加事务能力到 POJOs 来提供给 EJB 事务一个选择方案。Spring 支持编程式和声明式事务管理。EJBs 需要一个应用程序服务器,但 Spring 事务管理可以在不需要应用程序服务器的情况下实现。

局部事物 vs. 全局事务

局部事务是特定于一个单一的事务资源,如一个 JDBC 连接,而全局事务可以跨多个事务资源事务,如在一个分布式系统中的事务。

局部事务管理在一个集中的计算环境中是有用的,该计算环境中应用程序组件和资源位于一个单位点,而事务管理只涉及到一个运行在一个单一机器中的本地数据管理器。局部事务更容易实现。

全局事务管理需要在分布式计算环境中,所有的资源都分布在多个系统中。在这种情况下事务管理需要同时在局部和全局范围内进行。分布式或全局事务跨多个系统执行,它的执行需要全局事务管理系统和所有相关系统的局部数据管理人员之间的协调。

编程式 vs. 声明式

Spring 支持两种类型的事务管理:

  • 编程式事务管理 :这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。

  • 声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。

声明式事务管理比编程式事务管理更可取,尽管它不如编程式事务管理灵活,但它允许你通过代码控制事务。但作为一种横切关注点,声明式事务管理可以使用 AOP 方法进行模块化。Spring 支持使用 Spring AOP 框架的声明式事务管理。

Spring 事务抽象

Spring 事务抽象的关键是由 org.springframework.transaction.PlatformTransactionManager 接口定义,如下所示:

 

public interface PlatformTransactionManager {
   TransactionStatus getTransaction(TransactionDefinition definition);
   throws TransactionException;
   void commit(TransactionStatus status) throws TransactionException;
   void rollback(TransactionStatus status) throws TransactionException;
}
序号 方法 & 描述
1 TransactionStatus getTransaction(TransactionDefinition definition)根据指定的传播行为,该方法返回当前活动事务或创建一个新的事务。
2 void commit(TransactionStatus status)该方法提交给定的事务和关于它的状态。
3 void rollback(TransactionStatus status)该方法执行一个给定事务的回滚。

TransactionDefinition 是在 Spring 中事务支持的核心接口,它的定义如下:

 

public interface TransactionDefinition {
   int getPropagationBehavior();
   int getIsolationLevel();
   String getName();
   int getTimeout();
   boolean isReadOnly();
}
序号 方法 & 描述
1 int getPropagationBehavior()该方法返回传播行为。Spring 提供了与 EJB CMT 类似的所有的事务传播选项。
2 int getIsolationLevel()该方法返回该事务独立于其他事务的工作的程度。
3 String getName()该方法返回该事务的名称。
4 int getTimeout()该方法返回以秒为单位的时间间隔,事务必须在该时间间隔内完成。
5 boolean isReadOnly()该方法返回该事务是否是只读的。

下面是隔离级别的可能值:

序号 隔离 & 描述
1 TransactionDefinition.ISOLATION_DEFAULT这是默认的隔离级别。
2 TransactionDefinition.ISOLATION_READ_COMMITTED表明能够阻止误读;可以发生不可重复读和虚读。
3 TransactionDefinition.ISOLATION_READ_UNCOMMITTED表明可以发生误读、不可重复读和虚读。
4 TransactionDefinition.ISOLATION_REPEATABLE_READ表明能够阻止误读和不可重复读;可以发生虚读。
5 TransactionDefinition.ISOLATION_SERIALIZABLE表明能够阻止误读、不可重复读和虚读。

下面是传播类型的可能值:

序号 传播 & 描述
1 TransactionDefinition.PROPAGATION_MANDATORY支持当前事务;如果不存在当前事务,则抛出一个异常。
2 TransactionDefinition.PROPAGATION_NESTED如果存在当前事务,则在一个嵌套的事务中执行。
3 TransactionDefinition.PROPAGATION_NEVER不支持当前事务;如果存在当前事务,则抛出一个异常。
4 TransactionDefinition.PROPAGATION_NOT_SUPPORTED不支持当前事务;而总是执行非事务性。
5 TransactionDefinition.PROPAGATION_REQUIRED支持当前事务;如果不存在事务,则创建一个新的事务。
6 TransactionDefinition.PROPAGATION_REQUIRES_NEW创建一个新事务,如果存在一个事务,则把当前事务挂起。
7 TransactionDefinition.PROPAGATION_SUPPORTS支持当前事务;如果不存在,则执行非事务性。
8 TransactionDefinition.TIMEOUT_DEFAULT使用默认超时的底层事务系统,或者如果不支持超时则没有。

TransactionStatus 接口为事务代码提供了一个简单的方法来控制事务的执行和查询事务状态。

 

public interface TransactionStatus extends SavepointManager {
   boolean isNewTransaction();
   boolean hasSavepoint();
   void setRollbackOnly();
   boolean isRollbackOnly();
   boolean isCompleted();
}
序号 方法 & 描述
1 boolean hasSavepoint()该方法返回该事务内部是否有一个保存点,也就是说,基于一个保存点已经创建了嵌套事务。
2 boolean isCompleted()该方法返回该事务是否完成,也就是说,它是否已经提交或回滚。
3 boolean isNewTransaction()在当前事务时新的情况下,该方法返回 true。
4 boolean isRollbackOnly()该方法返回该事务是否已标记为 rollback-only。
5 void setRollbackOnly()该方法设置该事务为 rollback-only 标记。

二十六、Spring Web MVC框架

MVC 框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的 web 应用程序的组件。MVC 模式导致了应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)的分离,同时提供了在这些元素之间的松散耦合。

  • 模型封装了应用程序数据,并且通常它们由 POJO 组成。

  • 视图主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的 HTML 输出。

  • 控制器主要用于处理用户请求,并且构建合适的模型并将其传递到视图呈现。

DispatcherServlet

Spring Web 模型-视图-控制(MVC)框架是围绕 DispatcherServlet 设计的,DispatcherServlet 用来处理所有的 HTTP 请求和响应。Spring Web MVC DispatcherServlet 的请求处理的工作流程如下图所示:

Spring一文完结~_第5张图片

 

下面是对应于 DispatcherServlet 传入 HTTP 请求的事件序列:

  • 收到一个 HTTP 请求后,DispatcherServlet 根据 HandlerMapping 来选择并且调用适当的控制器

  • 控制器接受请求,并基于使用的 GET 或 POST 方法来调用适当的 service 方法。Service 方法将设置基于定义的业务逻辑的模型数据,并返回视图名称到 DispatcherServlet 中。

  • DispatcherServlet 会从 ViewResolver 获取帮助,为请求检取定义视图。

  • 一旦确定视图,DispatcherServlet 将把模型数据传递给视图,最后呈现在浏览器中。

上面所提到的所有组件,即 HandlerMapping、Controller 和 ViewResolver 是 WebApplicationContext 的一部分,而 WebApplicationContext 是带有一些对 web 应用程序必要的额外特性的 ApplicationContext 的扩展。

需求的配置

你需要映射你想让 DispatcherServlet 处理的请求,通过使用在 web.xml 文件中的一个 URL 映射。下面是一个显示声明和映射 HelloWeb DispatcherServlet 的示例:

 

web.xml 文件将被保留在你的应用程序的 WebContent/WEB-INF 目录下。好的,在初始化 HelloWeb DispatcherServlet 时,该框架将尝试加载位于该应用程序的 WebContent/WEB-INF 目录中文件名为 [servlet-name]-servlet.xml 的应用程序内容。在这种情况下,我们的文件将是 HelloWeb-servlet.xml

接下来,`` 标签表明哪些 URLs 将被 DispatcherServlet 处理。这里所有以 .jsp 结束的 HTTP 请求将由 HelloWeb DispatcherServle t处理。

如果你不想使用默认文件名 [servlet-name]-servlet.xml 和默认位置 WebContent/WEB-INF,你可以通过在 web.xml 文件中添加 servlet 监听器 ContextLoaderListener 自定义该文件的名称和位置,如下所示:



....

   contextConfigLocation
   /WEB-INF/HelloWeb-servlet.xml


   
      org.springframework.web.context.ContextLoaderListener
   

现在,检查 HelloWeb-servlet.xml 文件的请求配置,该文件位于 web 应用程序的 WebContent/WEB-INF 目录下:

 



   

   
      
      
   

以下是关于 HelloWeb-servlet.xml 文件的一些要点:

  • [servlet-name]-servlet.xml 文件将用于创建 bean 定义,重新定义在全局范围内具有相同名称的任何已定义的 bean。

  • `` 标签将用于激活 Spring MVC 注释扫描功能,该功能允许使用注释,如 @Controller 和 @RequestMapping 等等。

  • InternalResourceViewResolver 将使用定义的规则来解决视图名称。按照上述定义的规则,一个名称为 hello 的逻辑视图将发送给位于 /WEB-INF/jsp/hello.jsp 中实现的视图。

下一节将向你展示如何创建实际的组件,例如控制器,模式和视图。

定义控制器

DispatcherServlet 发送请求到控制器中执行特定的功能。@Controller 注释表明一个特定类是一个控制器的作用。@RequestMapping 注释用于映射 URL 到整个类或一个特定的处理方法。

 

@Controller
@RequestMapping("/hello")
public class HelloController{
   @RequestMapping(method = RequestMethod.GET)
   public String printHello(ModelMap model) {
      model.addAttribute("message", "Hello Spring MVC Framework!");
      return "hello";
   }
}

@Controller 注释定义该类作为一个 Spring MVC 控制器。在这里,第一次使用的 @RequestMapping 表明在该控制器中处理的所有方法都是相对于 /hello 路径的。下一个注释 @RequestMapping(method = RequestMethod.GET) 用于声明 printHello() 方法作为控制器的默认 service 方法来处理 HTTP GET 请求。你可以在相同的 URL 中定义其他方法来处理任何 POST 请求。

你可以用另一种形式来编写上面的控制器,你可以在 @RequestMapping 中添加额外的属性,如下所示:

 

@Controller
public class HelloController{
   @RequestMapping(value = "/hello", method = RequestMethod.GET)
   public String printHello(ModelMap model) {
      model.addAttribute("message", "Hello Spring MVC Framework!");
      return "hello";
   }
}

属性表明 URL 映射到哪个处理方法,方法属性定义了 service 方法来处理 HTTP GET 请求。关于上面定义的控制器,这里有以下几个要注意的要点:

  • 你将在一个 service 方法中定义需要的业务逻辑。你可以根据每次需求在这个方法中调用其他方法。

  • 基于定义的业务逻辑,你将在这个方法中创建一个模型。你可以设置不同的模型属性,这些属性将被视图访问并显示最终的结果。这个示例创建了一个带有属性 “message” 的模型。

  • 一个定义的 service 方法可以返回一个包含视图名称的字符串用于呈现该模型。这个示例返回 “hello” 作为逻辑视图的名称。

创建 JSP 视图

对于不同的表示技术,Spring MVC 支持许多类型的视图。这些包括 JSP、HTML、PDF、Excel 工作表、XML、Velocity 模板、XSLT、JSON、Atom 和 RSS 提要、JasperReports 等等。但我们最常使用利用 JSTL 编写的 JSP 模板。所以让我们在 /WEB-INF/hello/hello.jsp 中编写一个简单的 hello 视图:


   
   Hello Spring MVC
   
   
   

${message}

其中,${message} 是我们在控制器内部设置的属性。你可以在你的视图中有多个属性显示。

Spring Web MVC 框架例子

基于上述概念,让我们看看一些重要的例子来帮助你建立 Spring Web 应用程序:

序号 例子 & 描述
1 Spring MVC Hello World Example这个例子将解释如何编写一个简单的 Spring Web Hello World 应用程序。
2 Spring MVC Form Handling Example这个例子将解释如何编写一个 Spring Web 应用程序,它使用 HTML 表单提交数据到控制器,并且显示处理结果。
3 Spring Page Redirection Example学习在 Spring MVC 框架中如何使用页面重定向功能。
4 Spring Static Pages Example学习在 Spring MVC 框架中如何访问静态页面和动态页面。
5 Spring Exception Handling Example学习在 Spring MVC 框架中如何处理异常。

二十七、Spring MVC Hello World例子

1、HelloController.java文件

这里是 HelloController.java 文件的内容:

package com.tutorialspoint;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.ui.ModelMap;
@Controller
@RequestMapping("/hello")
public class HelloController{ 
   @RequestMapping(method = RequestMethod.GET)
   public String printHello(ModelMap model) {
      model.addAttribute("message", "Hello Spring MVC Framework!");
      return "hello";
   }
}

下面是 Spring Web 配置文件 web.xml 的内容



   Spring MVC Application

   
      HelloWeb
      
         org.springframework.web.servlet.DispatcherServlet
      
      1
   

   
      HelloWeb
      /
   

下面是另一个 Spring Web 配置文件 HelloWeb-servlet.xml 的内容



   

   
      
      
   

下面是 Spring 视图文件 hello.jsp 的内容

<%@ page contentType="text/html; charset=UTF-8" %>


Hello World


   

${message}


二十八、Spring MVC表单处理例子

Spring MVC 表单处理例子

下面的例子说明了如何编写一个简单的基于 web 的应用程序,它利用了使用 Spring 的 Web MVC 框架的 HTML 表单。为了开始使用它,让我们在恰当的位置使用 Eclipse IDE,然后按照下面的步骤使用 Spring 的 Web 框架来开发一个动态的基于表单的 Web 应用程序:

步骤 描述
1 创建一个名称为 HelloWeb动态 Web 项目,并且在已创建的项目的 src 文件夹中创建一个包 com.tutorialspoint
2 将上面提到的 Spring 和其他库拖拽到文件夹 WebContent/WEB-INF/lib 中。
3 com.tutorialspoint 包下创建一个 Java 类 StudentStudentController
4 WebContent/WEB-INF 文件夹下创建 Spring 的配置文件 Web.xmlHelloWeb-servlet.xml
5 WebContent/WEB-INF 文件夹下创建名称为 jsp 的子文件夹。在这个子文件夹下创建视图文件 student.jspresult.jsp
6 最后一步是创建所有的源代码和配置文件的内容,并导出该应用程序,正如下面解释的一样。

这里是 Student.java 文件的内容:

package com.tutorialspoint;
public class Student {
   private Integer age;
   private String name;
   private Integer id;
   public void setAge(Integer age) {
      this.age = age;
   }
   public Integer getAge() {
      return age;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getName() {
      return name;
   }
   public void setId(Integer id) {
      this.id = id;
   }
   public Integer getId() {
      return id;
   }
}

下面是 StudentController.java 文件的内容:

package com.tutorialspoint;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.ui.ModelMap;
@Controller
public class StudentController {
   @RequestMapping(value = "/student", method = RequestMethod.GET)
   public ModelAndView student() {
      return new ModelAndView("student", "command", new Student());
   }   
   @RequestMapping(value = "/addStudent", method = RequestMethod.POST)
   public String addStudent(@ModelAttribute("SpringWeb")Student student, 
   ModelMap model) {
      model.addAttribute("name", student.getName());
      model.addAttribute("age", student.getAge());
      model.addAttribute("id", student.getId());      
      return "result";
   }
}

在这里,第一个 service 方法 student(),我们已经在名称为 “command” 的 ModelAndView 对象中传递一个空的 Student 对象,因为 spring 框架需要一个名称的 “command” 的对象,如果你在 JSP 文件中使用 form:form 标签。所以,当 student() 方法被调用时,它返回 student.jsp 视图。

第二个 service 方法 addStudent() 将调用 HelloWeb/addStudent URL 中的 POST 方法。你将根据提交的信息准备好你的模型对象。最后一个 “result” 视图会从 service 方法中返回,它将导致呈现 result.jsp。

下面是 Spring Web 配置文件 web.xml 的内容



    Spring MVC Form Handling

    
        HelloWeb
        
           org.springframework.web.servlet.DispatcherServlet
        
        1
    

    
        HelloWeb
        /
    

下面是另一个 Spring Web 配置文件 HelloWeb-servlet.xml 的内容



   

   
      
      
   

下面是 Spring 视图文件 student.jsp 的内容

<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>


    Spring MVC Form Handling



Student Information

Name
Age
id

下面是 Spring 视图文件 result.jsp 的内容

<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>


    Spring MVC Form Handling



Submitted Student Information

Name ${name}
Age ${age}
ID ${id}

最后,下面是包含在你的 web 应用程序中的 Spring 和其他库的列表。你仅仅需要将这些文件拖拽到 WebContent/WEB-INF/lib 文件夹中。

  • commons-logging-x.y.z.jar

  • org.springframework.asm-x.y.z.jar

  • org.springframework.beans-x.y.z.jar

  • org.springframework.context-x.y.z.jar

  • org.springframework.core-x.y.z.jar

  • org.springframework.expression-x.y.z.jar

  • org.springframework.web.servlet-x.y.z.jar

  • org.springframework.web-x.y.z.jar

  • spring-web.jar

一旦你完成了创建源代码和配置文件后,导出你的应用程序。右键单击你的应用程序,并且使用 Export > WAR File 选项,并且在 Tomcat 的 webapps 文件夹中保存你的 HelloWeb.war 文件。

现在启动你的 Tomcat 服务器,并且确保你能够使用标准的浏览器访问 webapps 文件夹中的其他 web 页面。现在尝试访问该 URL http://localhost:8080/HelloWeb/student。如果你的 Spring Web 应用程序一切都正常,你应该看到下面的结果。


二十九、Spring 页面重定向例子

下面的例子说明了如何编写一个简单的基于 web 的应用程序,它利用重定向来传送一个 http 请求到另一个页面中。为了开始使用它,让我们在恰当的位置使用 Eclipse IDE,然后按照下面的步骤使用 Spring 的 Web 框架来开发一个动态的基于表单的 Web 应用程序:

步骤 描述
1 创建一个名称为 HelloWeb动态 Web 项目,并且在已创建的项目的 src 文件夹中创建一个包 com.tutorialspoint
2 将上面提到的 Spring 和其他库拖拽到文件夹 WebContent/WEB-INF/lib 中。
3 com.tutorialspoint 包下创建一个 Java 类 WebController
4 WebContent/WEB-INF 文件夹下创建 Spring 的配置文件 Web.xmlHelloWeb-servlet.xml
5 WebContent/WEB-INF 文件夹下创建名称为 jsp 的子文件夹。在这个子文件夹下创建视图文件 index.jspfinal.jsp
6 最后一步是创建所有的源代码和配置文件的内容,并导出该应用程序,正如下面解释的一样。

这里是 WebController.java 文件的内容:

package com.tutorialspoint;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class WebController {
   @RequestMapping(value = "/index", method = RequestMethod.GET)
   public String index() {
       return "index";
   }   
   @RequestMapping(value = "/redirect", method = RequestMethod.GET)
   public String redirect() {     
      return "redirect:finalPage";
   }   
   @RequestMapping(value = "/finalPage", method = RequestMethod.GET)
   public String finalPage() {     
      return "final";
   }
}

下面是 Spring Web 配置文件 web.xml 的内容



    Spring Page Redirection

    
        HelloWeb
        
           org.springframework.web.servlet.DispatcherServlet
        
        1
    

    
        HelloWeb
        /
    

下面是另一个 Spring Web 配置文件 HelloWeb-servlet.xml 的内容




    

    
    
    
    

下面是 Spring 视图文件 index.jsp 文件的内容。这将是一个登陆页面,这个页面将发送一个请求来访问重定向 service 方法,该方法将把这个请求重定向到另一个 service 方法中,最后将显示 final.jsp 页面。

<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>


    Spring Page Redirection


Spring Page Redirection

Click below button to redirect the result to new page

下面是 Spring 视图文件 final.jsp 的内容。这是最终的重定向页面。

<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>


    Spring Page Redirection



Redirected Page

最后,下面是包含在你的 web 应用程序中的 Spring 和其他库的列表。你仅仅需要将这些文件拖拽到 WebContent/WEB-INF/lib 文件夹中。

  • commons-logging-x.y.z.jar

  • org.springframework.asm-x.y.z.jar

  • org.springframework.beans-x.y.z.jar

  • org.springframework.context-x.y.z.jar

  • org.springframework.core-x.y.z.jar

  • org.springframework.expression-x.y.z.jar

  • org.springframework.web.servlet-x.y.z.jar

  • org.springframework.web-x.y.z.jar

  • spring-web.jar

一旦你完成了创建源代码和配置文件后,导出你的应用程序。右键单击你的应用程序,并且使用 Export > WAR File 选项,并且在 Tomcat 的 webapps 文件夹中保存你的 HelloWeb.war 文件。

现在启动你的 Tomcat 服务器,并且确保你能够使用标准的浏览器访问 webapps 文件夹中的其他 web 页面。现在尝试访问该 URL http://localhost:8080/HelloWeb/index。如果你的 Spring Web 应用程序一切都正常,你应该看到下面的结果


三十、Spring静态页面例子

 

下面的例子说明了如何使用 Spring MVC 框架来编写一个简单的基于 web 的应用程序,它可以在 mvc:resources 标签的帮助下访问静态页面和动态页面。为了开始使用它,让我们在恰当的位置使用 Eclipse IDE,然后按照下面的步骤使用 Spring 的 Web 框架来开发一个动态的基于表单的 Web 应用程序:

步骤 描述
1 创建一个名称为 HelloWeb动态 Web 项目,并且在已创建的项目的 src 文件夹中创建一个包 com.tutorialspoint
2 将上面提到的 Spring 和其他库拖拽到文件夹 WebContent/WEB-INF/lib 中。
3 com.tutorialspoint 包下创建一个 Java 类 WebController
4 WebContent/WEB-INF 文件夹下创建 Spring 的配置文件 Web.xmlHelloWeb-servlet.xml
5 WebContent/WEB-INF 文件夹下创建名称为 jsp 的子文件夹。在这个子文件夹下创建一个视图文件 index.jsp
6 WebContent/WEB-INF 文件夹下创建名称为 pages 的子文件夹。在这个子文件夹下创建一个静态文件 final.htm
7 最后一步是创建所有的源代码和配置文件的内容,并导出该应用程序,正如下面解释的一样。

这里是 WebController.java 文件的内容:

package com.tutorialspoint;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class WebController {
   @RequestMapping(value = "/index", method = RequestMethod.GET)
   public String index() {
       return "index";
   }   
   @RequestMapping(value = "/staticPage", method = RequestMethod.GET)
   public String redirect() {     
      return "redirect:/pages/final.htm";
   }
}

下面是 Spring Web 配置文件 web.xml 的内容



    Spring Page Redirection

    
        HelloWeb
        
           org.springframework.web.servlet.DispatcherServlet
        
        1
    

    
        HelloWeb
        /
    

下面是另一个 Spring Web 配置文件 HelloWeb-servlet.xml 的内容




    

    
    
    
    

    
    

在这里,mvc:resources..../ 标签被用来映射静态页面。 mapping 属性必须是一个指定一个 http 请求的 URL 模式的 Ant 模式。 location 属性必须指定一个或者多个具有包含图片,样式表,JavaScript 和其他静态内容的静态页面的资源目录位置。多个资源位置可以使用逗号分隔这些值的列表来被指定。

下面是 Spring 视图文件 WEB-INF/jsp/index.jsp 的内容。这将是一个登陆页面,这个页面将发送一个请求来访问 staticPage 的 service 方法,它将重定向这个请求到 WEB-INF/pages 文件夹中的一个可用的静态页面。

<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>


    Spring Landing Page


Spring Landing Pag

Click below button to get a simple HTML page

下面是 Spring 视图文件 WEB-INF/pages/final.htm 的内容。



    Spring Static Page



A simple HTML page

最后,下面是包含在你的 web 应用程序中的 Spring 和其他库的列表。你仅仅需要将这些文件拖拽到 WebContent/WEB-INF/lib 文件夹中。

  • commons-logging-x.y.z.jar

  • org.springframework.asm-x.y.z.jar

  • org.springframework.beans-x.y.z.jar

  • org.springframework.context-x.y.z.jar

  • org.springframework.core-x.y.z.jar

  • org.springframework.expression-x.y.z.jar

  • org.springframework.web.servlet-x.y.z.jar

  • org.springframework.web-x.y.z.jar

  • spring-web.jar

一旦你完成了创建源代码和配置文件后,导出你的应用程序。右键单击你的应用程序,并且使用 Export > WAR File 选项,并且在 Tomcat 的 webapps 文件夹中保存你的 HelloWeb.war 文件。

现在启动你的 Tomcat 服务器,并且确保你能够使用标准的浏览器访问 webapps 文件夹中的其他 web 页面。现在尝试访问该 URL http://localhost:8080/HelloWeb/index。 如果你的 Spring Web 应用程序一切都正常,你应该看到下面的结果


后记:

     站在巨人的肩膀上,希望有一天我也可以为其添加高度。 

你可能感兴趣的:(javaEE,Spring,Servlet,JSP,javaee)