Spring及Spring MVC总结

来源:互联网 发布:美工自学多久能自己做 编辑:程序博客网 时间:2024/06/13 21:43

一、Spring概述

  Spring是一个分层的Java SE/EE应用一站式的轻量级开源框架,其从持久层、业务层到表现层都拥有相应的支持,几乎为企业应用提供了所需的一切。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。它的核心是IOC和AOP,如下图所示,整个Spring框架按其所属功能可以划分为五个主要模块,这五个模块几乎为企业应用提供了所需的一切,从持久层、业务层到表现层都拥有相应的支持,这就是为什么称Spring是一站式框架的原因。

Spring体系结构.png-50.8kB

1、核心模块(Core Container)

  Spring的核心模块实现了IoC的功能,它将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述。由IoC容器负责类的创建、管理、获取等。BeanFactory接口是Spring框架的核心接口,实现了容器很多核心的功能。

  Context模块构建于核心模块之上,扩展了BeanFactory的功能,包括国际化,资源加载,邮件服务,任务调度等多项功能。ApplicationContext是Context模块的核心接口。

  表达式语言(Expression Language)是统一表达式语言(EL)的一个扩展,支持设置和获取对象属性,调用对象方法,操作数组、集合等。使用它可以很方便的通过表达式和Spring IoC容器进行交互。

2、AOP模块

  Spring AOP模块提供了满足AOP Alliance规范的实现,还整合了AspectJ这种AOP语言级的框架。通过AOP能降低耦合。AOP模块用于给Spring应用做面向切面的开发, 很多支持由AOP联盟提供,这样就确保了Spring和其他AOP框架的共通性。这个模块将元数据编程引入Spring。

3、数据访问集成模块(Data Access/Integration)

  该模块包括了JDBC、ORM、OXM、JMS和事务管理。

  事务模块:该模块用于Spring管理事务,只要是Spring管理对象都能得到Spring管理事务的好处,无需在代码中进行事务控制了,而且支持编程和声明性的事务管理。

  JDBC模块:提供了一个JBDC的样例模板,使用这些模板能消除传统冗长的JDBC编码还有必须的事务控制,而且能享受到Spring管理事务的好处。

  ORM模块:提供与流行的“对象-关系”映射框架的无缝集成,包括hibernate、JPA、MyBatis等。而且可以使用Spring事务管理,无需额外控制事务。

  OXM模块:提供了一个对Object/XML映射实现,将Java对象映射成XML数据,或者将XML数据映射成java对象,Object/XML映射实现包括JAXB、Castor、XMLBeans和XStream。

  JMS模块:用于JMS(Java Messaging Service),提供一套 “消息生产者、消息消费者”模板用于更加简单的使用JMS,JMS用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

4、Web模块

  该模块建立在ApplicationContext模块之上,提供了Web应用的功能,如文件上传、FreeMarker等。Spring可以整合Struts2等MVC框架。此外,Spring自己提供了MVC框架Spring MVC

5、测试模块

  Spring可以用非容器依赖的编程方式进行几乎所有的测试工作,支持JUnit和TestNG等测试框架。

二、Spring框架问答

1、解释JDBC抽象和DAO模块

  通过使用JDBC抽象和DAO模块,保证数据库代码的简洁,并能避免数据库资源错误关闭导致的问题,它在各种不同的数据库的错误信息之上,提供了一个统一的异常访问层。它还利用Spring的AOP模块给Spring应用中的对象提供事务管理服务。

2、 解释对象/关系映射集成模块

  Spring 通过提供ORM模块,支持我们在直接JDBC之上使用一个对象/关系映射映射(ORM)工具,Spring支持集成主流的ORM框架,如Hiberate、JDO和iBATIS SQL Maps。Spring的事务管理同样支持以上所有ORM框架及JDBC。

3、 解释Web模块

  Spring的Web模块是构建在application context 模块基础之上的,它提供了一个适合web应用的上下文。这个模块也包括支持多种面向web的任务,如透明地处理多个文件上传请求和程序级请求参数的绑定到你的业务对象。它也有对Jakarta Struts的支持。

4、ApplicationContext通常的实现是什么

  FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean配置文件的全路径名必须提供给它的构造函数
  ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置
  WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean

5、Bean工厂和Application contexts 有什么区别

  Application contexts提供一种方法处理文本消息,一个通常的做法是加载文件资源(比如镜像),它们可以向注册为监听器的bean发布事件。另外,在容器或容器内的对象上执行的那些不得不由bean工厂以程序化方式处理的操作,可以在Application contexts中以声明的方式处理。Application contexts实现了MessageSource接口,该接口的实现以可插拔的方式提供获取本地化消息的方法。

6、Spring IoC容器依赖的含义

  Spring IoC容器的依赖有两层含义:Bean依赖容器和容器注入Bean的依赖资源
  Bean依赖容器:也就是说Bean要依赖于容器,这里的依赖是指容器负责创建Bean并管理Bean的生命周期,正是由于由容器来控制创建Bean并注入依赖,也就是控制权被反转了,这也正是IoC名字的由来,此处的有依赖是指Bean和容器之间的依赖关系。
  容器注入Bean的依赖资源:容器负责注入Bean的依赖资源,依赖资源可以是Bean、外部文件、常量数据等,在Java中都反映为对象,并且由容器负责组装Bean之间的依赖关系,此处的依赖是指Bean之间的依赖关系,可以认为是传统类与类之间的“关联”、“聚合”、“组合”关系。

7、有哪些不同类型的依赖注入方式

  构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。

  Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。

8、哪种依赖注入方式更建议使用,构造器注入,还是 Setter方法注入

  两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。

9、什么是Spring beans

  Spring beans是那些形成Spring应用的主干的Java对象。它们被Spring IOC容器初始化、装配和管理。这些beans通过容器中配置的元数据创建。比如,以XML文件中<bean/> 的形式定义。

  Spring框架定义的beans都是单例beans。在bean tag中有个属性”singleton”,如果它被赋为TRUE,bean 就是单例,否则就是一个 prototype bean。默认是TRUE,所以所有在Spring框架中的beans 缺省都是单例。

10、如何给Spring 容器提供配置元数据

  这里有三种重要的方法给Spring 容器提供配置元数据:
  1、XML配置文件
  2、基于注解的配置
  3、基于java的配置

11、怎样定义类的作用域

  当使用<bean> 标签在Spring里定义一个bean时,我们还能给这个bean声明一个作用域。它可以通过bean定义中的scope属性来定义。如,当Spring要在需要的时候每次都生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,如果一个bean每次使用的时候必须返回同一个实例,这个bean的scope属性必须设为 singleton。

12、 解释Spring支持的几种bean的作用域

  singleton : bean在每个Spring ioc 容器中只有一个实例。

  prototype:一个bean的定义可以有多个实例。

  request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。

  session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

  global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

  缺省的Spring bean的作用域是Singleton。

13、 重要的bean生命周期方法有哪些,它们能重载吗?

  有两个重要的bean 生命周期方法,第一个是setup , 它是在容器加载bean的时候被调用。第二个方法是teardown 它是在容器卸载类的时候被调用。

  <bean>标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化方法和注销方法,它们也有相应的注解(@PostConstruct和@PreDestroy)。

14、 什么是Spring的内部bean

  当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean,为了定义inner bean,在Spring 的基于XML的 配置元数据中,可以在<property/>或<constructor-arg/> 元素内使用<bean/> 元素,内部bean通常是匿名的,它们的Scope一般是prototype。

15、 在 Spring中如何注入一个java集合

  Spring提供以下几种集合的配置元素:

  <list>类型用于注入一列值,允许有相同的值。

  <set> 类型用于注入一组值,不允许有相同的值。

  <map> 类型用于注入一组键值对,键和值都可以为任意类型。

  <props>类型用于注入一组键值对,键和值都只能为String类型。

16、 什么是bean装配和自动装配

  装配或bean装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。

  Spring容器能够自动装配相互合作的bean,这意味着容器不需要<constructor-arg>和<property>配置,能通过Bean工厂自动处理bean之间的协作。

17、解释不同方式的自动装配

  有五种自动装配的方式,可以用来指导Spring容器用自动装配方式来进行依赖注入。

  no:默认的方式是不进行自动装配,通过显式设置ref属性来进行装配。

  byName:通过参数名自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。

  byType:通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。

  constructor:这个方式类似于byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。

  autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。

18、自动装配有哪些局限性

  自动装配的局限性是:

  重写: 你仍需用<constructor-arg>和 <property> 配置来定义依赖,意味着总要重写自动装配。

  基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。

  模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。

19、什么是基于Java的Spring注解配置

  基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。

  以@Configuration 注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。

20、怎样开启注解装配

  注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置<context:annotation-config/>元素。

21、@Required注解

  这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException。

22、@Autowired 注解

  @Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。

23、在Spring框架中如何更有效地使用JDBC

  使用Spring JDBC框架,资源管理和错误处理的代价都会被减轻。所以开发者只需写statements和queries从数据库存取数据,JDBC也可以在Spring框架提供的模板类的帮助下更有效地被使用,这个模板叫JdbcTemplate。

  JdbcTemplate类提供了很多便利的方法解决诸如把数据库数据转变成基本数据类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据错误处理。

24、Spring对DAO的支持

  Spring对数据访问对象(DAO)的支持旨在简化它和数据访问技术如JDBC,Hibernate or JDO的结合使用。这使我们可以方便地切换持久层。编码时也不用担心会捕获每种技术特有的异常。

  

25、Spring支持的事务管理类型

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

  编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。

  声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。

26、实践中更倾向用那种事务管理类型

  大多数使用Spring框架的用户选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。

27、什么是Spring的MVC框架

  Spring配备构建Web应用的全功能MVC框架。Spring可以很便捷地和其他MVC框架集成,如Struts,Spring的MVC框架用控制反转把业务对象和控制逻辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。

  Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。

  WebApplicationContext继承了ApplicationContext 并增加了一些WEB应用必备的特有功能,它不同于一般的ApplicationContext ,因为它能处理主题,并找到被关联的servlet。

28、什么是Spring MVC框架的控制器

  控制器提供一个访问应用程序的行为,此行为通常通过服务接口实现。控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。Spring用一个非常抽象的方式实现了一个控制层,允许用户创建多种用途的控制器。

三、Spring与Spring MVC的关系及配置

  在Spring整体框架的核心概念中,容器的核心思想是管理Bean的整个生命周期。但在一个项目中,Spring容器往往不止一个,最常见的场景就是在一个项目中引入Spring和Spring MVC这两个框架,其本质就是两个容器:Spring是根容器,Spring MVC是其子容器。

  Spring和Spring MVC作为Bean管理容器和MVC层的默认框架,已被众多web应用采用。但是在实际应用中,初级开发者常常会因对Spring和Spring MVC的配置失当导致一些奇怪的异常现象,比如Controller的方法无法拦截、Bean被多次加载等问题,这种情况发生的根本原因在于开发者对Spring容器和Spring MVC容器之间的关系了解不够深入。

1. Spring容器、SpringMVC容器与ServletContext之间的关系

  在Web容器中配置Spring时,你可能已经司空见惯于web.xml文件中的以下配置代码,下面我们以该代码片段为基础来了解Spring容器、Spring MVC容器与ServletContext之间的关系。要想理解这三者的关系,需要先熟悉Spring是怎样在web容器(Tomcat和Jetty等)中启动起来的。Spring的启动过程其实就是其Spring IOC容器的启动过程。特别地,对于web程序而言,IOC容器启动过程即是建立上下文的过程。

<web-app>    ...    <!-- 利用Spring提供的ContextLoaderListener监听器去监听ServletContext对象的创建,并初始化WebApplicationContext对象 -->    <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>    <!-- Context Configuration locations for Spring XML files(默认查找/WEB-INF/applicationContext.xml) -->    <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>classpath:applicationContext.xml</param-value>    </context-param>    <!-- 配置Spring MVC的前端控制器:DispatchcerServlet -->    <servlet>        <servlet-name>SpringMVC</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <init-param>            <param-name>contextConfigLocation</param-name>            <param-value>classpath:springmvc.xml</param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>SpringMVC</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping>    ...</web-app>

Spring的启动过程

  (1). 对于一个web应用,其部署在web容器中,web容器提供给它一个全局的上下文环境,这个上下文就是ServletContext,其为后面的Spring IOC容器提供了宿主环境

   (2). 在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用。在这个方法中,Spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext。WebApplicationContext是一个接口,确切的说,其实际的实现类是XmlWebApplicationContext,它就是Spring的IOC容器,其对应的Bean定义的配置由web.xml中的<context-param>标签指定。在这个IoC容器初始化完毕后,Spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;

  (3). ContextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个Servlet可以配置多个,以最常见的DispatcherServlet为例,这个Servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个Servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有Spring MVC相关的bean。特别地,在建立DispatcherServlet自己的IoC上下文前,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,Spring以与Servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个Servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

Spring容器与Spring MVC容器的联系与区别

  在ContextLoaderListener中创建的Spring容器主要用于整个Web应用程序需要共享的一些组件,比如DAO、数据库的ConnectionFactory等;而由DispatcherServlet创建的Spring MVC容器主要用于和该Servlet相关的一些组件,比如Controller、ViewResovler等。它们之间的关系如下:

  (1). 作用范围:子容器(Spring MVC容器)可以访问父容器(Spring容器)的Bean,父容器(Spring容器)不能访问子容器(SpringMVC容器)的Bean。也就是说,当在Spring MVC容器中getBean时,如果在自己的容器中找不到对应的bean,则会去父容器中去找,这也解释了为什么由Spring MVC容器创建的Controller可以获取到Spring容器创建的Service组件的原因

  (2). 具体实现:在Spring的具体实现上,子容器和父容器都是通过ServletContext的setAttribute方法放到ServletContext中的。但是,ContextLoaderListener会先于DispatcherServlet创建ApplicationContext,DispatcherServlet在创建ApplicationContext时会先找到由ContextLoaderListener所创建的ApplicationContext,再将后者的ApplicationContext作为参数传给DispatcherServlet的ApplicationContext的setParent()方法。也就是说,子容器的创建依赖于父容器的创建,父容器先于子容器创建。在Spring源代码中,你可以在FrameServlet.Java中找到如下代码:

wac.setParent(parent);

  其中,wac即为由DisptcherServlet创建的ApplicationContext,而parent则为有ContextLoaderListener创建的ApplicationContext。此后,框架又会调用ServletContext的setAttribute()方法将wac加入到ServletContext中。

2. Spring容器和Spring MVC容器的配置

  在Spring整体框架的核心概念中,容器是核心思想,就是用来管理Bean的整个生命周期的,而在一个项目中,容器不一定只有一个,Spring中可以包括多个容器,而且容器间有上下层关系,目前最常见的一种场景就是在一个项目中引入Spring和Spring MVC这两个框架,其实就是两个容器:Spring是根容器,Spring MVC是其子容器。在上文中,我们提到,Spring MVC容器可以访问Spring容器中的Bean,Spring容器不能访问Spring MVC容器的Bean。但是,若开发者对Spring容器和Spring MVC容器之间的关系了解不够深入,常常会因配置失当而导致同时配置Spring和Spring MVC时出现一些奇怪的异常,比如Controller的方法无法拦截、Bean被多次加载等问题。

  Spring容器先于Spring MVC容器进行创建,并且Spring MVC容器的创建依赖于Spring容器。在实际工程中,一个项目中会包括很多配置,根据不同的业务模块来划分,我们一般思路是各负其责,明确边界,即:Spring根容器负责所有其他非controller的Bean的注册,而Spring MVC容器只负责controller相关的Bean的注册,下面我们演示这种配置方案。

  (1). Spring容器配置:Spring根容器负责所有其他非controller的Bean的注册

<!-- 启用注解扫描,并定义组件查找规则 ,除了@controller,扫描所有的Bean -->    <context:component-scan base-package="cn.edu.tju.rico">        <context:exclude-filter type="annotation"            expression="org.springframework.stereotype.Controller" />    </context:component-scan>

  (2). SpringMVC容器配置:Spring MVC容器只负责controller相关的Bean的注册,其中@ControllerAdvice用于对控制器进行增强,常用于实现全局的异常处理类:

<!-- 启用注解扫描,并定义组件查找规则 ,mvc层只负责扫描@Controller、@ControllerAdvice -->    <!-- base-package 如果多个,用“,”分隔 -->    <context:component-scan base-package="cn.edu.tju.rico"        use-default-filters="false">        <!-- 扫描@Controller -->        <context:include-filter type="annotation"            expression="org.springframework.stereotype.Controller" />        <!--控制器增强,使一个Contoller成为全局的异常处理类,类中用@ExceptionHandler方法注解的方法可以处理所有Controller发生的异常 -->        <context:include-filter type="annotation"            expression="org.springframework.web.bind.annotation.ControllerAdvice" />    </context:component-scan>

  在<context:component-scan>中可以添加use-default-filters,Spring配置中的use-default-filters用来指示是否自动扫描带有@Component、@Repository、@Service和@Controller的类。默认为true,即默认扫描。如果想要过滤其中这四个注解中的一个,比如@Repository,可以添加<context:exclude-filter />子标签,如下:

<context:component-scan base-package="cn.edu.tju.rico" scoped-proxy="targetClass" use-default-filters="true">      <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>  </context:component-scan>  

  而<context:include-filter/>子标签是用来添加扫描注解的:

<context:component-scan base-package="cn.edu.tju.rico" scoped-proxy="targetClass" use-default-filters="false">      <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>  </context:component-scan>  

引用及参考资料

Spring 深入浅出核心技术 (一)
Spring AOP无法拦截Controller中的方法
springmvc-servlet.xml中use-default-filters的作用
Spring与SpringMVC的容器关系分析
深入分析Spring 与 Spring MVC容器

原创粉丝点击