spring-springMVC-总结列表

Spring 的优良特性

  • 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
  • 控制反转:IOC——Inversion of Control,指的是将对象的创建权交给 Spring 去创建。使用 Spring 之前,对象的创建都是由我们自己在代码中new创建。而使用 Spring 之后。对象的创建都是给了 Spring 框架。
  • 依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用 setXX 方法去设置,而是通过配置赋值。
  • 面向切面编程:Aspect Oriented Programming——AOP
  • 容器:Spring 是一个容器,因为它包含并且管理应用对象的生命周期
  • 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
  • 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上 Spring 自身也提供了表现层的 SpringMVC 和持久层的 Spring JDBC)

使用 Spring 框架的好处

下面列出的是使用 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 容器往往是轻量级的,例如,特别是当与 EJB 容器相比的时候。这有利于在内存和 CPU 资源有限的计算机上开发和部署应用程序。
  • Spring 提供了一致的事务管理接口,可向下扩展到(使用一个单一的数据库,例如)本地事务并扩展到全局事务(例如,使用 JTA)。

依赖注入(DI)

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

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

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

依赖注入可以以向构造函数传递参数的方式发生,或者通过使用 setter 方法 post-construction。由于依赖注入是 Spring 框架的核心部分,所以我将在一个单独的章节中利用很好的例子去解释这一概念。

面向切面的程序设计(AOP):

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

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

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

Spring 框架具有以下几个特点:

1)方便解耦,简化开发

Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。

2)方便集成各种优秀框架

Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。

3)降低 Java EE API 的使用难度

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

4)方便程序的测试

Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。

5)AOP 编程的支持

Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。

6)声明式事务的支持

只需要通过配置就可以完成对事务的管理,而无须手动编程。

一、Spring体系结构

核心容器

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

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

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

  • context 模块建立在由 core和 beans 模块的基础上建立起来的,它以一种类似于 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 容器检索对象,还支持列表的投影、选择以及聚合等。

数据访问/集成

数据访问/集成层包括 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 自动处理,编程式事务粒度更细)

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 模块的功能。

其他

还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:

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

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

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

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

二、Spring IoC 容器

BeanFactory

BeanFactory 是 IoC 容器的基本实现,也是 Spring 提供的最简单的 IoC 容器,它提供了 IoC 容器最基本的功能,由 org.springframework.beans.factory.BeanFactory 接口定义。

BeanFactory 采用懒加载(lazy-load)机制,容器在加载配置文件时并不会立刻创建 Java 对象,只有程序中获取(使用)这个对对象时才会创建。

XmlBeanFactory factory = new XmlBeanFactory (new ClassPathResource("Beans.xml"));

HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");

ApplicationContext

ApplicationContext 是 BeanFactory 接口的子接口,是对 BeanFactory 的扩展。ApplicationContext 在 BeanFactory 的基础上增加了许多企业级的功能,例如 AOP(面向切面编程)、国际化、事务支持等。

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

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

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

这两个都称为Spring容器,它们都会去加载配置文件。

一、BeanFactory

BeanFactory采取延迟加载,第一次getBean时才会初始化Bean。

二、ApplicationContext

ApplicationContext是对BeanFactory的扩展,提供了更多功能:

1. 国际化处理

2. 事件传递

3. Bean自动装配

4. 各种不同应用层的context实现 

三、加载区别

BeanFactory采取延迟加载。初始化Bean,指的是调用UserServiceImpl的空参构造方法。构建factory不调用空参构造函数,只有当调用了getBean后才调用空参构造函数。

ApplicationContext采取即时加载。构建context时,就调用空参构造方法。

三、Spring Bean 定义

在 Spring 中,Bean 和 Bean 之间也存在继承关系。我们将被继承的 Bean 称为父 Bean,将继承父 Bean 配置信息的 Bean 称为子 Bean。
Spring Bean 的定义中可以包含很多配置信息,例如构造方法参数、属性值。子 Bean 既可以继承父 Bean 的配置数据,也可以根据需要重写或添加属于自己的配置信息。

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

  • 如何创建一个 bean

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

  • bean 的依赖关系

Spring Bean 作用域

注意,如果你使用 web-aware ApplicationContext 时,其中三个是可用的。    

作用域描述
singleton

在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值

prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
global-session 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境

Spring Bean 生命周期

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

Spring 中 Bean 的生命周期较复杂,大致可以分为以下 5 个阶段:

  1. Bean 的实例化
  2. Bean 属性赋值
  3. Bean 的初始化
  4. Bean 的使用
  5. Bean 的销毁

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

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

BeanPostProcessor ​接口定义回调方法,你可以实现该方法来提供自己的实例化逻辑,依赖解析逻辑等。

该接口中包含了两个方法:

  • postProcessBeforeInitialization() 方法:在 Bean 实例化、属性注入后,初始化前调用。
  • postProcessAfterInitialization() 方法:在 Bean 实例化、属性注入、初始化都完成后调用。

四、Spring 依赖注入

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

 <bean id="textEditor" class="com.tutorialspoint.TextEditor"> <constructor-arg ref="spellChecker"/> </bean>或 <constructor-arg name="grade" ref="grade"></constructor-arg>

<bean id="spellChecker" class="com.tutorialspoint.SpellChecker"> </bean>
Spring 基于设值函数的依赖注入

<bean id="textEditor" class="com.tutorialspoint.TextEditor"> <property name="spellChecker" ref="spellChecker"/> </bean>

<bean id="spellChecker" class="com.tutorialspoint.SpellChecker"></bean>
Spring 注入内部 Beans

<bean id="textEditor" class="com.tutorialspoint.TextEditor"> <property name="spellChecker"> <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"/> </property> </bean>
Spring 注入集合

现在如果你想传递多个值,如 Java Collection 类型 List、Set、Map 和 Properties,应该怎么做呢。为了处理这种情况,Spring 提供了四种类型的集合的配置元素

五、Spring Beans 自动装配

Spring 自动装配 byName

<bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire="byName"> <property name="name" value="Generic Text Editor" /> </bean> <!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.tutorialspoint.SpellChecker"> </bean>
Spring 自动装配 byType

<bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire="byType"> <property name="name" value="Generic Text Editor" /> </bean> <!-- Definition for spellChecker bean --> <bean id="SpellChecker" class="com.tutorialspoint.SpellChecker"> </bean>
Spring 由构造函数自动装配

<bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire="constructor"> <constructor-arg value="Generic Text Editor"/> </bean> <!-- Definition for spellChecker bean --> <bean id="SpellChecker" class="com.tutorialspoint.SpellChecker"> </bean>

六、Spring 基于注解的配置

<!--开启组件扫描功能--><context:component-scan base-package="net.biancheng.c"></context:component-scan>

使用注解定义 Bean

Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。

注解说明
@Component 该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。

使用时只需将该注解标注在相应类上即可。
@Repository 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Service 该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller 该注解通常作用在控制层(如 Struts2 的 Action、SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

4. 基于注解方式实现依赖注入

我们可以通过以下注解将定义好 Bean 装配到其它的 Bean 中。

注解说明
@Autowired 可以应用到 Bean 的属性变量、setter 方法、非 setter 方法及构造函数等,默认按照 Bean 的类型进行装配。

@Autowired 注解默认按照 Bean 的类型进行装配,默认情况下它要求依赖对象必须存在,如果允许 null 值,可以设置它的 required 属性为 false。如果我们想使用按照名称(byName)来装配,可以结合 @Qualifier 注解一起使用
@Resource 作用与 Autowired 相同,区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 的名称进行装配。

@Resource 中有两个重要属性:name 和 type。
  • Spring 将 name 属性解析为 Bean 的实例名称,type 属性解析为 Bean 的实例类型。
  • 如果指定 name 属性,则按实例名称进行装配;
  • 如果指定 type 属性,则按 Bean 类型进行装配;
  • 如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。
@Qualifier 与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。

Spring @Required 注解

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

用在set方法上,一旦用了这个注解,那么容器在初始化bean的时候必须要进行set,也就是说必须对这个值进行依赖注入。

Spring @Autowired 注解

autowire(自动装配),这个Autowired注解和autowire也很相似,就是可以不写依赖注入的配置让容器自己寻找依赖并注入,不过这个Autowired注解只能用类别来寻找依赖并注入,不像

@Autowired 注释,它可以对类成员变量、⽅法及构造函数进⾏标注,完成⾃动装配的⼯作。通过 @Autowired的使⽤来消除 set ,get⽅法

Spring @Qualifier 注解

按类型自动装配可能多个 bean 实例的情况,可以使用 @Qualifier 注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法参数

@Resource

@Resource 的作⽤相当于 @Autowired,只不过 @Autowired 按 byType ⾃动注⼊,@Resource 默认按 byName⾃动注⼊罢了。

@Resource 有两个属性是⽐较重要的,分别是 name和 type,Spring将 @Resource 注释的name属性解析为 Bean的名字,
⽽ type属性则解析为 Bean的类型。所以如果使⽤ name属性,则使⽤ byName的⾃动注⼊策略,⽽使⽤ type属性时则使⽤ byType
⾃动注⼊策略。如果既不指定 name也不指定 type属性,这时将通过反射机制使⽤ byName⾃动注⼊策略。

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

七、Spring 框架的 AOP

Spring AOP 的代理机制

Spring 在运行期会为目标对象生成一个动态代理对象,并在代理对象中实现对目标对象的增强。
Spring AOP 的底层是通过以下 2 种动态代理机制,为目标对象(Target Bean)执行横向织入的。

代理技术描述
JDK 动态代理 Spring AOP 默认的动态代理方式,若目标对象实现了若干接口,Spring 使用 JDK 的 java.lang.reflect.Proxy 类进行代理。
CGLIB 动态代理 若目标对象没有实现任何接口,Spring 则使用 CGLIB 库生成目标对象的子类,以实现对目标对象的代理。

Spring 中基于 AOP 的 XML架构
Spring 中基于 AOP 的 @AspectJ注解

在我们开始使用 AOP 工作之前,让我们熟悉一下 AOP 概念和术语。这些术语并不特定于 Spring,而是与 AOP 有关的。

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

通知的类型

Spring 方面可以使用下面提到的五种通知工作:

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

八、Spring JDBC 框架

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

JdbcTemplate 类

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

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

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

Spring 中 SQL 的存储过程

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

九、Spring 事务管理

编程式 vs. 声明式

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

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

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

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

使用 @Transactional 注解

@Transactional 注解是 Spring 声明式事务编程的核心注解,该注解既可以在类上使用,也可以在方法上使用。

<tx:annotation-driven/>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

 

@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, timeout = 10, readOnly = false)

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)

该方法执行一个给定事务的回滚。

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

序号隔离 & 描述
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

使用默认超时的底层事务系统,或者如果不支持超时则没有。

 

十、Spring SpEL表达式语言

Spring Expression Language(简称 SpEL)是一种功能强大的表达式语言,支持运行时查询和操作对象图 。表达式语言一般是用最简单的形式完成最主要的工作,以此减少工作量。

SpEL 提供了以下接口和类:

  • Expression interface:该接口负责评估表达式字符串
  • ExpressionParser interface:该接口负责解析字符串
  • EvaluationContext interface:该接口负责定义上下文环境

SpEL 支持如下表达式:

1. 基本表达式

字面量表达式、关系、逻辑与算术运算表达式、字符串连接及截取表达式、三目运算表达式、正则表达式、括号优先级表达式;

2. 类相关表达式

类类型表达式、类实例化、instanceof 表达式、变量定义及引用、赋值表达式、自定义函数、对象属性存取及安全导航表达式、对象方法调用、Bean 引用;

3. 集合相关表达式

内联 List、内联数组、集合、字典访问、列表、字典、数组修改、集合投影、集合选择;不支持多维内联数组初始化;不支持内联字典定义;

4. 其他表达式

模板表达式。

SpEL对Bean定义的支持

SpEL 表达式可以与 XML 或基于注解的配置元数据一起使用,SpEL 表达式以#{开头,以}结尾,如#{'Hello'}

1. 基于XML的配置

可以使用以下表达式来设置属性或构造函数的参数值。

  1. <bean id="number" class="net.biancheng.Number">
  2. <property name="randomNumber" value="#{T(java.lang.Math).random() * 100.0}"/>
  3. </bean>

2. 基于注解的配置

@Value 注解可以放在字段、方法、以及构造函数参数上,以指定默认值。

以下是一个设置字段变量默认值的例子。

  1. public static class FieldValueTestBean
  2. @value("#{ systemProperties[ 'user.region'] }")
  3. private String defaultLocale;

十一、Spring 与mybatis四种整合方法

一、采用org.mybatis.spring.mapper.MapperScannerConfigurer
二、采用数据映射器(MapperFactoryBean)的方式,不用写mybatis映射文件,采用注解方式提供相应的sql语句和输入参数
三、采用抽象类org.mybatis.spring.support.SqlSessionDaoSupport提供SqlSession
四、采用接口org.apache.ibatis.session.SqlSession的实现类org.mybatis.spring.SqlSessionTemplate

十二、Spring 事务失效场景

1、访问权限问题

众所周知,java的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。但如果我们在开发过程中,把有某些事务方法,定义了错误

2. 方法用final修饰

有时候,某个方法不想被子类重新,这时可以将该方法定义成final的。普通方法这样定义是没问题的,但如果将事务方法定义成final

3、方法内部调用

有时候我们需要在某个Service类的某个方法中,调用另外一个事务方法

4、未被spring管理

在我们平时开发过程中,有个细节很容易被忽略。即使用spring事务的前提是:对象要被spring管理,需要创建bean实例。通常情况下,我们通过@Controller、@Service、

@Component、@Repository等注解,可以自动实现bean实例化和依赖注入的功能。

5、多线程调用

在实际项目开发中,多线程的使用场景还是挺多的。如果spring事务用在多线程场景中,会有问题吗?

6、表不支持事务

周所周知,在mysql5之前,默认的数据库引擎是 myisam。它的好处就不用多说了:索引文件和数据文件是分开存储的,对于查多写少的单表操作,性能比innodb更好。

7、错误的传播特性

其实,我们在使用 @Transactional注解时,是可以指定 propagation参数的。该参数的作用是指定事务的传播特性,spring目前支持7种传播特性

8、自己吞了异常

事务不会回滚,最常见的问题是:开发者在代码中手动try...catch了异常。

9、手动抛了别的异常

即使开发者没有手动捕获异常,但如果抛的异常不正确,spring事务也不会回滚。

10、自定义了回滚异常

在使用@Transactional注解声明事务时,有时我们想自定义回滚的异常,spring也是支持的。可以通过设置rollbackFor参数来完成这个功能。

11、大事务问题

在使用spring事务时,有个让人非常头疼的问题,就是大事务问题。通常情况下,我们会在方法上 @Transactional注解,填加事务功能,

12、编程式事务

上面聊的这些内容都是基于 @Transactional注解的,主要说的是它的事务问题,我们把这种事务叫做:声明式事务。

其实,spring还提供了另外一种创建事务的方式,即通过手动编写代码实现的事务,我们把这种事务叫做:编程式事务。 

十三、Spring 事务配置的5种方式

spring的配置文件中关于事务配置总是由三个部分组成:分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。

    DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问 时,DataSource实际为SessionFactory,TransactionManager的实现为 HibernateTransactionManager。

    具体如下图:


第一种方式:每个Bean都有一个代理

第二种方式:所有Bean共享一个代理基类

第三种方式:使用拦截器

第四种方式:使用tx标签配置的拦截器

第五种方式:全注解

 

十四、Spring 数据源配置

Spring中提供了4种不同形式的数据源配置方式:

1、Spring自带的数据源(DriverMangerDataSource);

2、DBCP数据源;

3、C3P0数据源;

4、JNDI数据源。

Spring 多数据源配置

同一个项目有时会涉及到多个数据库,也就是多数据源。多数据源又可以分为两种情况:

1)两个或多个数据库没有相关性,各自独立,其实这种可以作为两个项目来开发。比如在游戏开发中一个数据库是平台数据库,其它还有平台下的游戏对应的数据库;

2)两个或多个数据库是master-slave的关系,比如有mysql搭建一个 master-master,其后又带有多个slave;或者采用MHA搭建的master-slave复制;

通过 TheadLocal 来保存每个线程选择哪个数据源的标志(key):

定义 ThreadLocalRountingDataSource,继承AbstractRoutingDataSource:

6)AbstractRoutingDataSource原理剖析

ThreadLocalRountingDataSource继承了AbstractRoutingDataSource,实现其抽象方法protected abstract Object determineCurrentLookupKey(); 从而实现对不同数据源的路由功能。我们从源码入手分析下其中原理:

AbstractRoutingDataSource 实现了 InitializingBean 那么spring在初始化该bean时,会调用InitializingBean的接口
void afterPropertiesSet() throws Exception; 我们看下AbstractRoutingDataSource是如何实现这个接口的:

7)扩展 ThreadLocalRountingDataSource

 

 

 ================================ ================================ ================================ ================================

  SpringMVC总结

 ================================ ================================ ================================ ================================

一、Spring MVC优点

结构如下:

    • 视图层(View):负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、界面设计等功能。
    • 控制层(Controller):负责接收并转发请求,对请求进行处理后,指定视图并将响应结果发送给客户端。
    • 数据模型层(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务规则)的处理和实现数据操作(即在数据库中存取数据)。
  • 清晰地角色划分,Spring MVC 在 Model、View 和 Controller 方面提供了一个非常清晰的角色划分,这 3 个方面真正是各司其职,各负其责。
  • 灵活的配置功能,可以把类当作 Bean 通过 XML 进行配置。
  • 提供了大量的控制器接口和实现类,开发者可以使用 Spring 提供的控制器实现类,也可以自己实现控制器接口。
  • 真正做到与 View 层的实现无关。它不会强制开发者使用 JSP,可以根据项目需求使用 Velocity、FreeMarker 等技术。
  • 国际化支持
  • 面向接口编程
  • 与 Spring 框架无缝集成

二、Spring MVC的五大组件

DispatcherServlet是整个 Spring MVC 框架的核心,主要负责截获请求并将其分派给相应的处理器处理。跟所有 Servlet 一样,用户必须在 web.xml 中进行配置。

前端控制器 (DispatcherServlet) 映射处理器(HandlerMapping) 处理器(Controller) 模型和视图(ModelAndView) 视图解析器(ViewResolver) 附: Spring MVC 的运行原理  

文字解析: 客户端请求提交到DispatcherServlet 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller DispatcherServlet将请求提交到Controller Controller调用业务逻辑处理后,返回ModelAndView DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图 视图负责将结果显示到客户

Spring MVC 执行流程

1)DispatcherServlet

DispatcherServlet 是前端控制器,从图 1 可以看出,Spring MVC 的所有请求都要经过 DispatcherServlet 来统一分发。DispatcherServlet 相当于一个转发器或中央处理器,控制整个流程的执行,对各个组件进行统一调度,以降低组件之间的耦合性,有利于组件之间的拓展。

2)HandlerMapping

HandlerMapping 是处理器映射器,其作用是根据请求的 URL 路径,通过注解或者 XML 配置,寻找匹配的处理器(Handler)信息。

3)HandlerAdapter

HandlerAdapter 是处理器适配器,其作用是根据映射器找到的处理器(Handler)信息,按照特定规则执行相关的处理器(Handler)。

4)Handler

Handler 是处理器,和 Java Servlet 扮演的角色一致。其作用是执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装至 ModelAndView 对象中。

5)View Resolver

View Resolver 是视图解析器,其作用是进行解析操作,通过 ModelAndView 对象中的 View 信息将逻辑视图名解析成真正的视图 View(如通过一个 JSP 路径返回一个真正的 JSP 页面)。

6)View

View 是视图,其本身是一个接口,实现类支持不同的 View 类型(JSP、FreeMarker、Excel 等)。 

三、前端控制器 (DispatcherServlet)

 

四、映射处理器(HandlerMapping) 

1.类型一:RequestMappingUrlHandlerMapping

该类型用于匹配我们最常用的controller类。

原理是,发起请求后,RequestMappingUrlHandlerMapping匹配到RequestMapping注解中实际的url路径,从自身维护的map中根据url取出对应的方法,包装成HanderMethod对象,然后交给RequestMappinigHandlerAdapter,用于处理controller中实际的方法。

2.类型二:BeanNameUrlHandlerMapping

该类型用于匹配继承了AbstractController的类。
发起请求后,BeanNameUrlMapping匹配到url,从父类AbstractUrlHandlerMapping维护的map中根据url取出实际的类(如下面的BeanNameController ),然后交给SimpleControllerHandlerAdapter,调用handleRequestInternal方法。

3.类型三:SimpleUrlHandlerMapping

该类型的用法最灵活,既可以用于匹配静态资源、又可以简化controller等等。
我们在如下类(AppConfig是传统mvc项目的配置类)中,看看它的几种不同作用场景。

SpringMVC 内部是怎么通过 HandlerMapping 映射器将各自对应映射的资源在容器初始的时候装到自身的缓存,在请求过来时又是怎么找到对应的资源返回最终对应的 handler 对象已经描述完了。

现在开发我们基本都不用 AbstractUrlHandlerMapping 这种类型的映射器了,但是 SpringMVC 内部还有用到的地方,例如直接 <mvc:view-controller path="" view-name=""/> 标签配置资源不经过视图控制器直接跳转就用到了 SimpleUrlHandlerMapping 这种映射器。AbstractUrlHandlerMapping 匹对解析对应请求最终返回的 handler 是 Controller 对象。
现在我们习惯直接用 @Controller 和 @RequestMapping 这样注解来描述视图控制器的逻辑,这种资源映射用的是 AbstractHandlerMethodMapping 抽象类的子类 RequestMappingHandlerMapping 映射器,匹对解析对应的请求返回HandlerMethod 对象。

二、HandlerAdapter

1 RequestMappingHandlerAdapter

2 HttpRequestHandlerAdapter

3 SimpleControllerHandlerAdapter

4 SimpleServletHandlerAdapter 

五、处理器(Controller) 

Spring MVC 使用扫描机制找到应用中所有基于注解的控制器类,所以,为了让控制器类被 Spring MVC 框架扫描到,需要在配置文件中声明 spring-context,并使用 <context:component-scan/> 元素指定控制器类的基本包(请确保所有控制器类都在基本包及其子包下)。

RequestMapping注解

使用 @RequestMapping 来完成映射,具体包括 4 个方面的信息项:请求 URL、请求参数、请求方法和请求头。

1. value 属性

value 属性是 @RequestMapping 注解的默认属性,因此如果只有 value 属性时,可以省略该属性名,如果有其它属性,则必须写上 value 属性名称。如下。
  • @RequestMapping(value="toUser")
  • @RequestMapping("toUser")
value 属性支持通配符匹配,如 @RequestMapping(value="toUser/*") 表示 http://localhost:8080/toUser/1 或 http://localhost:8080/toUser/hahaha 都能够正常访问。

2. path属性

path 属性和 value 属性都用来作为映射使用。即 @RequestMapping(value="toUser") 和 @RequestMapping(path="toUser") 都能访问 toUser() 方法。

path 属性支持通配符匹配,如 @RequestMapping(path="toUser/*") 表示 http://localhost:8080/toUser/1 或 http://localhost:8080/toUser/hahaha 都能够正常访问。

3. name属性

name属性相当于方法的注释,使方法更易理解。如 @RequestMapping(value = "toUser",name = "获取用户信息")。

4. method属性

method 属性用于表示该方法支持哪些 HTTP 请求。如果省略 method 属性,则说明该方法支持全部的 HTTP 请求。

@RequestMapping(value = "toUser",method = RequestMethod.GET) 表示该方法只支持 GET 请求。也可指定多个 HTTP 请求,如 @RequestMapping(value = "toUser",method = {RequestMethod.GET,RequestMethod.POST}),说明该方法同时支持 GET 和 POST 请求。

5. params属性

params 属性用于指定请求中规定的参数,代码如下。
  • @RequestMapping(value = "toUser",params = "type")
  • public String toUser() {
  • return "showUser";
  • }
以上代码表示请求中必须包含 type 参数时才能执行该请求。即 http://localhost:8080/toUser?type=xxx 能够正常访问 toUser() 方法,而 http://localhost:8080/toUser 则不能正常访问 toUser() 方法。
  • @RequestMapping(value = "toUser",params = "type=1")
  • public String toUser() {
  • return "showUser";
  • }
以上代码表示请求中必须包含 type 参数,且 type 参数为 1 时才能够执行该请求。即 http://localhost:8080/toUser?type=1 能够正常访问 toUser() 方法,而 http://localhost:8080/toUser?type=2 则不能正常访问 toUser() 方法。

6. header属性

header 属性表示请求中必须包含某些指定的 header 值。

@RequestMapping(value = "toUser",headers = "Referer=http://www.xxx.com") 表示请求的 header 中必须包含了指定的“Referer”请求头,以及值为“http://www.xxx.com”时,才能执行该请求。

7. consumers属性

consumers 属性用于指定处理请求的提交内容类型(Content-Type),例如:application/json、text/html。如 
@RequestMapping(value = "toUser",consumes = "application/json")。

8. produces属性

produces 属性用于指定返回的内容类型,返回的内容类型必须是 request 请求头(Accept)中所包含的类型。如 @RequestMapping(value = "toUser",produces = "application/json")。 

六、模型和视图(ModelAndView)

七、视图解析器(ViewResolver)

URLBasedViewResolver

UrlBasedViewResolver 是对 ViewResolver 的一种简单实现,主要提供了一种拼接 URL 的方式来解析视图。

InternalResourceViewResolver

InternalResourceViewResolver 为“内部资源视图解析器”。它是 URLBasedViewResolver 的子类,拥有 URLBasedViewResolver 的一切特性。无需再单独指定 viewClass 属性。

FreeMarkerViewResolver

FreeMarkerViewResolver 是 UrlBasedViewResolver 的子类,可以通过 prefix 属性指定前缀,通过 suffix 属性指定后缀。

八、Spring MVC传递参数

  • 通过实体 Bean 接收请求参数
  • 通过处理方法的形参接收请求参数
  • 通过 HttpServletRequest 接收请求参数
  • 通过 @PathVariable 接收 URL 中的请求参数
  • 通过 @RequestParam 接收请求参数
  • 通过 @ModelAttribute 接收请求参数

Spring MVC 请求方式分为转发、重定向 2 种,分别使用 forward 和 redirect 关键字在 controller 层进行处理。

//转发到一个请求方法(同一个控制器类可以省略/index/)
return "forward:/index/isLogin";
//重定向到一个请求方法
return "redirect:/index/isRegister";

九、Spring MVC @ModelAttribute注解

用来将请求参数绑定到 Model 对象。在 Controller 中使用 @ModelAttribute 时,有以下几种应用情况。

需要注意的是,因为模型对象要先于 controller 方法之前创建,所以被 @ModelAttribute 注解的方法会在 Controller 每个方法执行之前都执行。因此一个 Controller 映射多个 URL 时,要谨慎使用。

  • 应用在方法上
  • 应用在方法的参数上
  • 应用在方法上,并且方法也使用了 @RequestMapping

@ModelAttribute 和 @RequestMapping 注解同时应用在方法上时,有以下作用:

  1. 方法的返回值会存入到 Model 对象中,key 为 ModelAttribute 的 value 属性值。
  2. 方法的返回值不再是方法的访问路径,访问路径会变为 @RequestMapping 的 value 值,例如:@RequestMapping(value = "/index") 跳转的页面是 index.jsp 页面。

总而言之,@ModelAttribute 注解的使用方法有很多种,非常灵活,可以根据业务需求选择使用。

Model和ModelView的区别

Model:每次请求中都存在的默认参数,利用其 addAttribute() 方法即可将服务器的值传递到客户端页面中。
ModelAndView:包含 model 和 view 两部分,使用时需要自己实例化,利用 ModelMap 来传值,也可以设置 view 的名称。

十、Spring MVC类型转换器(Converter)

Spring MVC 框架的 Converter<S,T> 是一个可以将一种数据类型转换成另一种数据类型的接口,这里 S 表示源类型,T 表示目标类型。开发者在实际应用中使用框架内置的类型转换器基本上就够了,但有时需要编写具有特定功能的类型转换器。

1)标量转换器

StringToBooleanConverter String 到 boolean 类型转换
ObjectToStringConverter Object 到 String 转换,调用 toString 方法转换
StringToNumberConverterFactory String 到数字转换(例如 Integer、Long 等)

2)集合、数组相关转换器

ArrayToCollectionConverter 任意数组到任意集合(List、Set)转换
CollectionToArrayConverter 任意集合到任意数组转换
ArrayToArrayConverter 任意数组到任意数组转换

类型转换是在视图与控制器相互传递数据时发生的。Spring MVC 框架对于基本类型(例如 int、long、float、double、boolean 以及 char 等)已经做好了基本类型转换。

自定义类型转换器

@Component
public class UserConverter implements Converter<String, User> 

配置信息

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="net.biancheng.converter.UserConverter" />
</list>
</property>
</bean>

内置的格式化转换器

Spring MVC 提供了几个内置的格式化转换器,具体如下。

  • NumberFormatter:实现 Number 与 String 之间的解析与格式化。
  • CurrencyFormatter:实现 Number 与 String 之间的解析与格式化(带货币符号)。
  • PercentFormatter:实现 Number 与 String 之间的解析与格式化(带百分数符号)。
  • DateFormatter:实现 Date 与 String 之间的解析与格式化。

十一、Spring MVC表单标签库

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

以上标签基本都拥有以下属性。

  • path:属性路径,表示表单对象属性,如 userName、userCode 等。
  • cssClass:表单组件对应的 CSS 样式类名。
  • cssErrorClass:当提交表单后报错(服务端错误),采用的 CSS 样式类。
  • cssStyle:表单组件对应的 CSS 样式。
  • htmlEscape:绑定的表单属性值是否要对 HTML 特殊字符进行转换,默认为 true。

常用的 Spring 表单标签如下表所示。

名称作用
form 渲染表单元素
input 输入框组件标签,渲染 <input type="text"/> 元素
password 密码框组件标签,渲染 <input type="password"/> 元素
hidden 隐藏框组件标签,渲染 <input type="hidden"/> 元素
textarea 多行输入框组件标签,渲染 textarea 元素
checkbox 复选框组件标签,渲染一个 <input type="checkbox"/> 元素
checkboxes 渲染多个 <input type="checkbox"/> 元素
radiobutton 单选框组件标签,渲染一个 <input type="radio"/> 元素
radiobuttons 渲染多个 <input type="radio"/> 元素
select 下拉列表组件标签,渲染一个选择元素
option 渲染一个选项元素
options 渲染多个选项元素
errors 显示表单数据校验所对应的错误信息

 

十二、Spring MVC JSON数据交互

Spring MVC 提供了 MappingJackson2HttpMessageConverter 实现类默认处理 JSON 格式请求响应。

在使用注解开发时需要用到两个重要的 JSON 格式转换注解,分别是 @RequestBody 和 @ResponseBody。

    • @RequestBody:用于将请求体中的数据绑定到方法的形参中,该注解应用在方法的形参上。
    • @ResponseBody:用于直接返回 return 对象,该注解应用在方法上。

各个JSON技术比较

1)json-lib  2)开源的Jackson 3)Google的Gson   4)阿里巴巴的FastJson

/**
* 接收页面请求的JSON参数,并返回JSON格式的结果
*/
@RequestMapping("/testJson")
@ResponseBody
public User testJson(@RequestBody User user) {
// 打印接收的 JSON数据
System.out.println("name=" + user.getName() + ",password=" + user.getPassword() + ",age=" + user.getAge());
// 返回JSON格式的响应
return user;
}

十三、Spring MVC拦截器(Interceptor)

在 Spring MVC 框架中定义一个拦截器需要对拦截器进行定义和配置,主要有以下 2 种方式。

  1. 通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类(例如 HandlerInterceptorAdapter)来定义;
  2. 通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义。

public class TestInterceptor implements HandlerInterceptor {

上述拦截器的定义中实现了 HandlerInterceptor 接口,并实现了接口中的 3 个方法,说明如下。

    • preHandle( ):该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
    • postHandle( ):该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
    • afterCompletion( ):该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。

<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 配置一个全局拦截器,拦截所有请求 -->
<bean class="net.biancheng.interceptor.TestInterceptor" />
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**" />
<!-- 配置不需要拦截作用的路径 -->
<mvc:exclude-mapping path="" />
<!-- 定义<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
<bean class="net.biancheng.interceptor.Interceptor1" />
</mvc:interceptor>
</mvc:interceptors>

十四、Spring MVC数据校验

Spring MVC 有以下两种方法可以验证输入:

  • 利用 Spring 自带的验证框架
  • 利用 JSR 303 实现

数据验证分为客户端验证和服务器端验证,客户端验证主要是过滤正常用户的误操作,通过 JavaScript 代码完成。服务器端验证是整个应用阻止非法数据的最后防线,通过在应用中编程实现。
本节使用 JSR 303 实现服务器端的数据验证。

JSR 303 不需要编写验证器,它定义了一套可标注在成员变量、属性方法上的校验注解,如下表所示。

名称说明
@Null 被标注的元素必须为 null
@NotNull 被标注的元素必须不为 null
@AssertTrue 被标注的元素必须为 true
@AssertFalse 被标注的元素必须为 false
@Min(value) 被标注的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被标注的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMax(value) 被标注的元素必须是一个数字,其值必须大于等于指定的最大值
@DecimalMin(value) 被标注的元素必须是一个数字,其值必须小于等于指定的最小值
@size 被标注的元素的大小必须在指定的范围内
@Digits(integer,fraction) 被标注的元素必须是一个数字,其值必须在可接受的范围内;integer 指定整数精度,fraction 指定小数精度
@Past 被标注的元素必须是一个过去的日期
@Future 被标注的元素必须是一个将来的日期
@Pattern(value) 被标注的元素必须符合指定的正则表达式

十五、 Spring MVC异常处理---Spring MVC REST风格

 Spring MVC 有以下 3 种处理异常的方式:

  1. 使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver。
  2. 实现 Spring 的异常处理接口 HandlerExceptionResolver,自定义自己的异常处理器。
  3. 使用 @ExceptionHandler 注解实现异常处理

Spring MVC REST风格

Spring REST 风格可以简单理解为:使用 URL 表示资源时,每个资源都用一个独一无二的 URL 来表示,并使用 HTTP 方法表示操作,即准确描述服务器对资源的处理动作(GET、POST、PUT、DELETE),实现资源的增删改查。

    • GET:表示获取资源
    • POST:表示新建资源
    • PUT:表示更新资源
    • DELETE:表示删除资源

十六、Spring MVC文件上传|下载

Spring MVC 框架的文件上传基于 commons-fileupload 组件,并在该组件上做了进一步的封装,简化了文件上传的代码实现,取消了不同上传组件上的编程差异。

MultpartiResolver 接口有以下两个实现类:

    • StandardServletMultipartResolver:使用了 Servlet 3.0 标准的上传方式。
    • CommonsMultipartResolver:使用了 Apache 的 commons-fileupload 来完成具体的上传操作。

1. 导入 jar 文件

文件上传使用 Apache Commons FileUpload 组件,需要导入 commons-io-2.4.jar 和 commons-fileupload-1.2.2.jar 两个 jar 文件(可在 Apache 官网下载)。

2. 配置 MultipartResolver

使用 CommonsMultipartReslover 配置 MultipartResolver 解析器,在 springmvc-servlet.xml 中添加代码如下。

<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

3. 编写文件上传表单页面

负责文件上传表单的编码类型必须是“multipart/form-data”类型。

表单的 enctype 属性指定的是表单数据的编码方式,该属性有以下 3 个值。

  • application/x-www-form-urlencoded:这是默认的编码方式,它只处理表单域里的 value 属性值。
  • multipart/form-data:该编码方式以二进制流的方式来处理表单数据,并将文件域指定文件的内容封装到请求参数里。
  • text/plain:该编码方式只有当表单的 action 属性为“mailto:”URL 的形式时才使用,主要适用于直接通过表单发送邮件的方式。

文件下载的实现方法

文件下载有以下两种实现方法:

  • 通过超链接实现下载:实现简单,但暴露了下载文件的真实位置,并且只能下载 Web 应用程序所在目录下的文件,WEB-INF 目录除外。
  • 利用程序编码实现下载:增强安全访问控制,可以下载除 Web 应用程序所在目录以外的文件,也可以将文件保存到数据库中。

利用程序编码实现下载需要设置以下两个报头:

  1. Web 服务器需要告诉浏览器其所输出内容的类型不是普通文本文件或 HTML 文件,而是一个要保存到本地的下载文件,这需要设置 Content-Type 的值为 application/x-msdownload。
  2. Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。

该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。
设置报头的示例如下:

response.setHeader("Content-Type", "application/x-msdownload");
response.setHeader("Content-Disposition", "attachment;filename="+filename);

十七、Spring MVC用拦截器+token防止重复提交

Token,可以翻译成标记!最大的特点就是随机性,不可预测,一般黑客或软件无法猜测出来。

Token一般用在两个地方:

  • 1: 防止表单重复提交
  • 2: anti csrf攻击(Cross-site request forgery 跨站点请求伪造)

两者在原理上都是通过session token来实现的。当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session当中,然后将Token发给客户端(一般通过构造hidden表单)。
下次客户端提交请求时,Token会随着表单一起提交到服务器端。

1、应用于“anti csrf攻击”:

服务器端会对Token值进行验证,判断是否和session中的Token值相等,若相等,则可以证明请求有效,不是伪造的。

2、应用于“防止表单重复提交”:

服务器端第一次验证相同过后,会将session中的Token值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的Token没变,但服务器端session中Token已经改变了。

2.1. 单机

第一种方法:判断session中保存的token

第二种方法(判断请求url和数据是否和上一次相同)

2.2. 分布式

使用分布式锁 Redisson 来尝试获取一把锁
如果成功获取锁,再获取判断 Redis 中的 key 值是否存在(key 值自定义)
如果 key 值不存在,则证明不是重复请求,再给 Redis 中存入数据(使用 UUID 不重复);反之,则证明是重复请求进行提交
最后,释放分布式锁
 

十八、Spring MVC有哪些绑定数据的注解

     1.@RequestParam,绑定单个请求数据,可以是URL中的数据,表单提交的数据或上传的文件; 
     2.@PathVariable,绑定URL模板变量值; 
     3.@CookieValue,绑定Cookie数据; 
     4.@RequestHeader,绑定请求头数据; 
     5.@ModelAttribute,绑定数据到Model; 
     6.@SessionAttributes,绑定数据到Session; 
     7.@RequestBody,用来处理Content-Type不是application/x-www-form-urlencoded编码的内容,例如application/json, application/xml等; 
     8.@RequestPart,绑定“multipart/data”数据,并可以根据数据类型进项对象转换;

 

十九、Spring MVC对pdf模板添加文本域

依赖     <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.10</version>
        </dependency>

首先介绍利用POI如何生成excel

<dependency>
       <groupId>org.apache.poi</groupId>
       <artifactId>poi-ooxml</artifactId>
          <version>3.9</version>
     </dependency> 

对于生成Excel,POI提供了如下几个基本对象:

     HSSFWorkbook             excel 的文档对象

     HSSFSheet                excel 的表单

     HSSFRow                  excel 的行

     HSSFCell                 excel 的格子单元

     从上面的图片和Excel的组织结构,我们就可以明白创建Excel的步骤。

        1、生成文档对象HSSHWorkbook。

        2、通过HSSFWorkbook生成表单HSSFSheet。

        3、通过HSSFSheet生成行HSSFRow

        4、通过HSSFRow生成单元格HSSFCell。

 

 

十七、Spring MVC表单标签库

十七、Spring MVC表单标签库

十七、Spring MVC表单标签库

 

 

 

 

 

 

 

 

 

问题: SpringMVC的控制器是不是单例模式,如果是,有什么问题,怎么解决?

是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能,解决方案是在控制器里面不能写字段。

 

 

 

 

 

 

 

 

三、Spring MVC和Struts2的区别

1. Spring MVC 基于方法开发,Struts2 基于类开发。
2. Spring MVC 可以进行单例开发,Struts2 无法使用单例
3. Struts2 的处理速度要比 SpringMVC 慢,原因是 Struts2 使用了 Struts 标签,Struts 标签由于设计原因,会出现加载数据慢的情况

1、springmvc入口是一个servlet前端控制器( DispatcherServlet ),struts2入口是一个filter过滤器(StrutsPrepareAndExecuteFilter).
2、struts2通过在action类中定义成员变量接收参数,(属性驱动和模型驱动),它只能使用多例模式管理action.
springmvc通过在controller方法中定义形参接收参数,springmvc可以使用单例模式管理controller.
3、springmvc是基于方法开发的,注解开发中使用requestMapping将url和方法进行 映射,如果根据url找到controller类的方法生成一个handler处理器对象(只包括一个method).
struts2是基于类开发的,每个请求过来创建一个action实例,实例对象中有若干个方法.
4、struts2采用值栈存储请求和相应的数据,通过OGNL存取数据;springmvc通过参数绑定期将request请求内容解析,并给方法形参赋值.
5、struts2和springmvc的速度是相当的,由于struts2的漏洞较多,跟多企业使用springmvc
6、SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱。
7、设计思想上,Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展。
8、拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式,这样导致Struts2的配置文件量还是比SpringMVC大。

1. springmvc的请求调度器是单Servlet多线程的,其controller默认是单例的,因为controller是以方法参数来接收请求参数的,也就是在线程内声明并接收请求参数的。

2. struts2的action是存在实例变量的,即是以action的实例变量来接收请求参数的,所以是线程不安全的,故只能在每个线程内部都创建一个action对象,即action是多实例的。

Servlet默认情况下是单实例多线程的,编程的过程中需要程序员处理可能出现的线程安全问题。

默认情况下,Servlet容器对每个Servlet只实例化一次。Servlet容器维护一个线程池服务请求,当容器收到一个访问Servlet的请求时,就会从线程池中选一个线程,把请求传递给这个线程,由这个线程调度Servlet的方法。

所以在默认情况下,Servlet是多线程的,一个Servlet实例同时在多个线程中执行,并发地处理多个客户端请求。因为Servlet是多线程的,所以开发Servlet时必须注意线程安全。Servlet线程的安全有两个问题,一个是变量的线程安全,局部变量总是线程安全的,实例变量和类变量不是线程安全的。另一个是属性的线程安全,请求对象的属性访问是线程安全的,而session对象和上下文的属性访问不是线程安全的。

有几种方法可以实现Servlet线程安全:
1,实现 SingleThreadModel 接口的servlet是线程安全的
2,用synchronized同步对共享数据的操作
3,避免使用实例变量 

四、Spring MVC传递参数

五、Spring 依赖注入

六、Spring 依赖注入

七、Spring 依赖注入

八、Spring 依赖注入

九、Spring 依赖注入

十、Spring 依赖注入

十一、Spring 依赖注入

十、Spring 依赖注入

十、Spring 依赖注入

十、Spring 依赖注入

十、Spring 依赖注入

 十、Spring 依赖注入

十、Spring 依赖注入

 

 
posted @ 2022-05-08 21:17  hanease  阅读(69)  评论(0编辑  收藏  举报