Spring简介
Spring框架由Rod Johnson开发,2004年发布了Spring框架的第一版。Spring是一个从实际开发中抽取出来的框架,因此它完成了大量开发中的通用步骤,留给开发者的仅仅是与特定应用相关的部分,从而大大提高了企业应用的开发效率。
Spring总结起来优点如下:
- 低侵入式设计,代码的污染极低。
- 独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺。
- Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦。
- Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用。
- Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问。
- Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部。
Spring框架的组成结构图如下所示:
Spring的核心机制
管理Bean
程序主要是通过Spring容器来访问容器中的Bean,ApplicationContext是Spring容器最常用的接口,该接口有如下两个实现类:
- ClassPathXmlApplicationContext: 从类加载路径下搜索配置文件,并根据配置文件来创建Spring容器。
- FileSystemXmlApplicationContext: 从文件系统的相对路径或绝对路径下去搜索配置文件,并根据配置文件来创建Spring容器。
public class BeanTest{ public static void main(String args[]) throws Exception{ ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); Person p = ctx.getBean("person", Person.class); p.say(); } }
Eclipse使用Spring
在Eclipse等IDE工具中,用户可以自建User Library
,然后把Spring的Jar包都放入其中,当然也可以将Jar包直接放在项目的/WEB-INF/lib
目录下,但是如果使用User
,在项目发布时,需要将用户库所引用的Jar文件随应用一起发布,就是将User Library所使用的Jar复制到
Library/WEB-INF/lib
目录下,这是因为对于一个Web应用,Eclipse部署Web应用时不会将用户库的Jar文件复制到/WEB-INF/lib
下,需要手动复制。
依赖注入
Spring框架的核心功能有两个:
- Spring容器作为超级大工厂,负责创建、管理所有的Java对象,这些Java对象被称为Bean。
- Spring容器管理容器中Bean之间的依赖关系,Spring使用一种被称为"依赖注入"的方式来管理Bean之间的依赖关系。
使用依赖注入,不仅可以为Bean注入普通的属性值,还可以注入其他Bean的引用。依赖注入是一种优秀的解耦方式,其可以让Bean以配置文件组织在一起,而不是以硬编码的方式耦合在一起。
理解依赖注入
Rod Johnson是第一个高度重视以配置文件来管理Java实例的协作关系的人,他给这种方式起了一个名字:控制反转(Inverse of Control,IoC)。后来Martine Fowler为这种方式起了另一个名称:依赖注入(Dependency Injection),因此不管是依赖注入,还是控制反转,其含义完全相同。当某个Java对象(调用者)需要调用另一个Java对象(被依赖对象)的方法时,在传统模式下通常有两种做法:
- 原始做法: 调用者主动创建被依赖对象,然后再调用被依赖对象的方法。
- 简单工厂模式: 调用者先找到被依赖对象的工厂,然后主动通过工厂去获取被依赖对象,最后再调用被依赖对象的方法。
注意上面的主动二字,这必然会导致调用者与被依赖对象实现类的硬编码耦合,非常不利于项目升级的维护。使用Spring框架之后,调用者无需主动获取被依赖对象,调用者只要被动接受Spring容器为调用者的成员变量赋值即可,由此可见,使用Spring后,调用者获取被依赖对象的方式由原来的主动获取,变成了被动接受——所以Rod
Johnson称之为控制反转。
另外从Spring容器的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量——相当于为调用者注入它依赖的实例,因此Martine Fowler称之为依赖注入。
设值注入
设值注入是指IoC容器通过成员变量的setter方法来注入被依赖对象。这种注入方式简单、直观,因而在Spring的依赖注入里大量使用。
构造注入
利用构造器来设置依赖关系的方式,被称为构造注入。通俗来说,就是驱动Spring在底层以反射方式执行带指定参数的构造器,当执行带参数的构造器时,就可利用构造器参数对成员变量执行初始化——这就是构造注入的本质。
两种注入方式的对比
设值注入有如下优点:
- 与传统的JavaBean的写法更相似,程序开发人员更容易理解、接受。通过setter方法设定依赖关系显得更加直观、自然。
- 对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在创建Bean实例时,需要同时实例化其依赖的全部实例,因而导致性能下降。而使用设值注入,则能避免这些问题。
- 尤其在某些成员变量可选的情况下,多参数的构造器更加笨重。
构造注入优势如下:
- 构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入。
- 对于依赖关系无需变化的Bean,构造注入更有用处。因为没有setter方法,所有的依赖关系全部在构造器内设定,无须担心后续的代码对依赖关系产生破坏。
- 依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系,对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则。
注意:
建议采用设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他依赖关系的注入,则考虑采用设值注入。
Spring容器中的Bean
对于开发者来说,开发者使用Spring框架主要是做两件事:①开发Bean;②配置Bean。对于Spring框架来说,它要做的就是根据配置文件来创建Bean实例,并调用Bean实例的方法完成"依赖注入"——这就是所谓IoC的本质。
容器中Bean的作用域
当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下五种作用域:
- singleton: 单例模式,在整个Spring IoC容器中,singleton作用域的Bean将只生成一个实例。
- prototype: 每次通过容器的getBean()方法获取prototype作用域的Bean时,都将产生一个新的Bean实例。
- request: 对于一次HTTP请求,request作用域的Bean将只生成一个实例,这意味着,在同一次HTTP请求内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
- 对于一次HTTP会话,session作用域的Bean将只生成一个实例,这意味着,在同一次HTTP会话内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
- global session: 每个全局的HTTP Session对应一个Bean实例。在典型的情况下,仅在使用portlet context的时候有效,同样只在Web应用中有效。
如果不指定Bean的作用域,Spring默认使用singleton作用域。prototype作用域的Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成果,就可以重复使用。因此,应该尽量避免将Bean设置成prototype作用域。
使用自动装配注入合作者Bean
Spring能自动装配Bean与Bean之间的依赖关系,即无须使用ref显式指定依赖Bean,而是由Spring容器检查XML配置文件内容,根据某种规则,为调用者Bean注入被依赖的Bean。
Spring自动装配可通过<beans/>
元素的default-autowire
属性指定,该属性对配置文件中所有的Bean起作用;也可通过对<bean/>
元素的autowire
属性指定,该属性只对该Bean起作用。
autowire
和default-autowire
可以接受如下值:
no
: 不使用自动装配。Bean依赖必须通过ref元素定义。这是默认配置,在较大的部署环境中不鼓励改变这个配置,显式配置合作者能够得到更清晰的依赖关系。byName
: 根据setter方法名进行自动装配。Spring容器查找容器中全部Bean,找出其id与setter方法名去掉set前缀,并小写首字母后同名的Bean来完成注入。如果没有找到匹配的Bean实例,则Spring不会进行任何注入。byType
: 根据setter方法的形参类型来自动装配。Spring容器查找容器中的全部Bean,如果正好有一个Bean类型与setter方法的形参类型匹配,就自动注入这个Bean;如果找到多个这样的Bean,就抛出一个异常;如果没有找到这样的Bean,则什么都不会发生,setter方法不会被调用。constructor
: 与byType类似,区别是用于自动匹配构造器的参数。如果容器不能恰好找到一个与构造器参数类型匹配的Bean,则会抛出一个异常。autodetect
: Spring容器根据Bean内部结构,自行决定使用constructor或byType策略。如果找到一个默认的构造函数,那么就会应用byType策略。
当一个Bean既使用自动装配依赖,又使用ref显式指定依赖时,则显式指定的依赖覆盖自动装配依赖;对于大型的应用,不鼓励使用自动装配。虽然使用自动装配可减少配置文件的工作量,但大大将死了依赖关系的清晰性和透明性。依赖关系的装配依赖于源文件的属性名和属性类型,导致Bean与Bean之间的耦合降低到代码层次,不利于高层次解耦。
<!--通过设置可以将Bean排除在自动装配之外--> <bean id="" autowire-candidate="false"/> <!--除此之外,还可以在beans元素中指定,支持模式字符串,如下所有以abc结尾的Bean都被排除在自动装配之外--> <beans default-autowire-candidates="*abc"/>
创建Bean的3种方式
使用构造器创建Bean实例
使用构造器来创建Bean实例是最常见的情况,如果不采用构造注入,Spring底层会调用Bean类的无参数构造器来创建实例,因此要求该Bean类提供无参数的构造器。
采用默认的构造器创建Bean实例,Spring对Bean实例的所有属性执行默认初始化,即所有的基本类型的值初始化为0或false;所有的引用类型的值初始化为null。
使用静态工厂方法创建Bean
使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类,Spring通过该属性知道由哪个工厂类来创建Bean实例。
除此之外,还需要使用factory-method属性来指定静态工厂方法,Spring将调用静态工厂方法返回一个Bean实例,一旦获得了指定Bean实例,Spring后面的处理步骤与采用普通方法创建Bean实例完全一样。如果静态工厂方法需要参数,则使用<constructor-arg.../>
元素指定静态工厂方法的参数。
调用实例工厂方法创建Bean
实例工厂方法与静态工厂方法只有一个不同:调用静态工厂方法只需使用工厂类即可,而调用实例工厂方法则需要工厂实例。使用实例工厂方法时,配置Bean实例的<bean.../>
元素无须class属性,配置实例工厂方法使用factory-bean
指定工厂实例。
采用实例工厂方法创建Bean的<bean.../>
元素时需要指定如下两个属性:
- factory-bean: 该属性的值为工厂Bean的id。
- factory-method: 该属性指定实例工厂的工厂方法。
若调用实例工厂方法时需要传入参数,则使用<constructor-arg.../>
元素确定参数值。
协调作用域不同步的Bean
当singleton作用域的Bean依赖于prototype作用域的Bean时,会产生不同步的现象,原因是因为当Spring容器初始化时,容器会预初始化容器中所有的singleton Bean
,由于singleton Bean
依赖于prototype
,因此Spring在初始化
Beansingleton Bean
之前,会先创建prototypeBean
——然后才创建singleton Bean
,接下里将prototype
注入
Beansingleton Bean
。
解决不同步的方法有两种:
- 放弃依赖注入: singleton作用域的Bean每次需要prototype作用域的Bean时,主动向容器请求新的Bean实例,即可保证每次注入的
prototype Bean
实例都是最新的实例。 - 利用方法注入: 方法注入通常使用lookup方法注入,使用lookup方法注入可以让Spring容器重写容器中Bean的抽象或具体方法,返回查找容器中其他Bean的结果,被查找的Bean通常是一个
non-singleton Bean
。Spring通过使用JDK动态代理或cglib库修改客户端的二进制码,从而实现上述要求。
建议采用第二种方法,使用方法注入。为了使用lookup方法注入,大致需要如下两步:
- 将调用者Bean的实现类定义为抽象类,并定义一个抽象方法来获取被依赖的Bean。
- 在
<bean.../>
元素中添加<lookup-method.../>
子元素让Spring为调用者Bean的实现类实现指定的抽象方法。
注意:
Spring会采用运行时动态增强的方式来实现
<lookup-method.../>
元素所指定的抽象方法,如果目标抽象类实现过接口,Spring会采用JDK动态代理来实现该抽象类,并为之实现抽象方法;如果目标抽象类没有实现过接口,Spring会采用cglib实现该抽象类,并为之实现抽象方法。Spring4.0的spring-core-xxx.jar包中已经集成了cglib类库。
两种后处理器
Spring提供了两种常用的后处理器:
- Bean后处理器: 这种后处理器会对容器中Bean进行后处理,对Bean进行额外加强。
- 容器后处理器: 这种后处理器会对IoC容器进行后处理,用于增强容器功能。
Bean后处理器
Bean后处理器是一种特殊的Bean,这种特殊的Bean并不对外提供服务,它甚至可以无须id属性,它主要负责对容器中的其他Bean执行后处理,例如为容器中的目标Bean生成代理等,这种Bean称为Bean后处理器。Bean后处理器会在Bean实例创建成功之后,对Bean实例进行进一步的增强处理。Bean后处理器必须实现BeanPostProcessor
接口,同时必须实现该接口的两个方法。
Object postProcessBeforeInitialization(Object bean, String name) throws BeansException
: 该方法的第一个参数是系统即将进行后处理的Bean实例,第二个参数是该Bean的配置idObject postProcessAfterinitialization(Object bean, String name) throws BeansException
: 该方法的第一个参数是系统即将进行后处理的Bean实例,第二个参数是该Bean的配置id
容器中一旦注册了Bean后处理器,Bean后处理器就会自动启动,在容器中每个Bean创建时自动工作,Bean后处理器两个方法的回调时机如下图:
注意一点,如果使用BeanFactory
作为Spring容器,则必须手动注册Bean后处理器,程序必须获取Bean后处理器实例,然后手动注册。
BeanPostProcessor bp = (BeanPostProcessor)beanFactory.getBean("bp"); beanFactory.addBeanPostProcessor(bp); Person p = (Person)beanFactory.getBean("person");
容器后处理器
Bean后处理器负责处理容器中的所有Bean实例,而容器后处理器则负责处理容器本身。容器后处理器必须实现BeanFactoryPostProcessor
接口,并实现该接口的一个方法postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
实现该方法的方法体就是对Spring容器进行的处理,这种处理可以对Spring容器进行自定义扩展,当然也可以对Spring容器不进行任何处理。
类似于BeanPostProcessor
,ApplicationContext
可自动检测到容器中的容器后处理器,并且自动注册容器后处理器。但若使用BeanFactory
作为Spring容器,则必须手动调用该容器后处理器来处理BeanFactory
容器。
Spring的"零配置"支持
搜索Bean类
Spring提供如下几个Annotation来标注Spring Bean:
@Component
: 标注一个普通的Spring Bean类@Controller
: 标注一个控制器组件类@Service
: 标注一个业务逻辑组件类@Repository
: 标注一个DAO组件类
在Spring配置文件中做如下配置,指定自动扫描的包:
<context:component-scan base-package="edu.shu.spring.domain"/>
使用@Resource配置依赖
@Resource
位于javax.annotation
包下,是来自JavaEE规范的一个Annotation
,Spring直接借鉴了该Annotation
,通过使用该Annotation
为目标Bean指定协作者Bean。使用@Resource
与<property.../>
元素的ref属性有相同的效果。
@Resource
不仅可以修饰setter方法,也可以直接修饰实例变量,如果使用@Resource
修饰实例变量将会更加简单,此时Spring将会直接使用JavaEE规范的Field注入,此时连setter方法都可以不要。
使用@PostConstruct和@PreDestroy定制生命周期行为
@PostConstruct
和@PreDestroy
同样位于javax.annotation包下,也是来自JavaEE规范的两个Annotation,Spring直接借鉴了它们,用于定制Spring容器中Bean的生命周期行为。它们都用于修饰方法,无须任何属性。其中前者修饰的方法时Bean的初始化方法;而后者修饰的方法时Bean销毁之前的方法。
Spring4.0增强的自动装配和精确装配
Spring提供了@Autowired
注解来指定自动装配,@Autowired
可以修饰setter方法、普通方法、实例变量和构造器等。当使用@Autowired
标注setter方法时,默认采用byType自动装配策略。在这种策略下,符合自动装配类型的候选Bean实例常常有多个,这个时候就可能引起异常,为了实现精确的自动装配,Spring提供了@Qualifier
注解,通过使用@Qualifier
,允许根据Bean的id来执行自动装配。
Spring的AOP
为什么需要AOP
AOP(Aspect Orient Programming)也就是面向切面编程,作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。其实AOP问世的时间并不太长,AOP和OOP互为补充,面向切面编程将程序运行过程分解成各个切面。
AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP已经成为一种非常常用的解决方案。
使用AspectJ实现AOP
AspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能,其他很多AOP框架都借鉴或采纳其中的一些思想。其主要包括两个部分:一个部分定义了如何表达、定义AOP编程中的语法规范,通过这套语法规范,可以方便地用AOP来解决Java语言中存在的交叉关注点的问题;另一个部分是工具部分,包括编译、调试工具等。
AOP实现可分为两类:
- 静态AOP实现: AOP框架在编译阶段对程序进行修改,即实现对目标类的增强,生成静态的AOP代理类,以AspectJ为代表。
- 动态AOP实现: AOP框架在运行阶段动态生成AOP代理,以实现对目标对象的增强,以Spring AOP为代表。
一般来说,静态AOP实现具有较好的性能,但需要使用特殊的编译器。动态AOP实现是纯Java实现,因此无须特殊的编译器,但是通常性能略差。
AOP的基本概念
关于面向切面编程的一些术语:
- 切面(Aspect): 切面用于组织多个Advice,Advice放在切面中定义。
- 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用。
- 增强处理(Advice): AOP框架在特定的切入点执行的增强处理。处理有"around"、"before"和"after"等类型
- 切入点(Pointcut): 可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。
Spring的AOP支持
Spring中的AOP代理由Spring的IoC容器负责生成、管理,其依赖关系也由IoC容器负责管理。
为了在应用中使用@AspectJ
支持,Spring需要添加三个库:
aspectjweaver.jar
aspectjrt.jar
aopalliance.jar
并在Spring配置文件中做如下配置:
<!--启动@AspectJ支持--> <aop:aspectj-autoproxy/> <!--指定自动搜索Bean组件、自动搜索切面类--> <context:component-scan base-package="edu.shu.sprint.service"> <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/> </context:component-scan>
69道Spring面试题和答案 Spring 概述 1. 什么是spring? Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。 2. 使用Spring框架的好处是什么? · 轻量:Spring 是轻量的,基本的版本大约2MB。 · 控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。 · 面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。 · 容器:Spring 包含并管理应用中对象的生命周期和配置。 · MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。 · 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。 · 异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。 3. Spring由哪些模块组成? 以下是Spring 框架的基本模块: · Core module · Bean module · Context module · Expression Language module · JDBC module · ORM module · OXM module · Java Messaging Service(JMS) module · Transaction module · Web module · Web-Servlet module · Web-Struts module · Web-Portlet module 4. 核心容器(应用上下文) 模块。 这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。 5. BeanFactory – BeanFactory 实现举例。 Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从正真的应用代码中分离。 最常用的BeanFactory 实现是XmlBeanFactory 类。 6. XMLBeanFactory 最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或应用。 7. 解释AOP模块 AOP模块用于发给我们的Spring应用做面向切面的开发, 很多支持由AOP联盟提供,这样就确保了Spring和其他AOP框架的共通性。这个模块将元数据编程引入Spring。 8. 解释JDBC抽象和DAO模块。 通过使用JDBC抽象和DAO模块,保证数据库代码的简洁,并能避免数据库资源错误关闭导致的问题,它在各种不同的数据库的错误信息之上,提供了一个统一的异常访问层。它还利用Spring的AOP 模块给Spring应用中的对象提供事务管理服务。 9. 解释对象/关系映射集成模块。 Spring 通过提供ORM模块,支持我们在直接JDBC之上使用一个对象/关系映射映射(ORM)工具,Spring 支持集成主流的ORM框架,如Hiberate,JDO和 iBATIS SQL Maps。Spring的事务管理同样支持以上所有ORM框架及JDBC。 10. 解释WEB 模块。 Spring的WEB模块是构建在application context 模块基础之上,提供一个适合web应用的上下文。这个模块也包括支持多种面向web的任务,如透明地处理多个文件上传请求和程序级请求参数的绑定到你的业务对象。它也有对Jakarta Struts的支持。 12. Spring配置文件 Spring配置文件是个XML 文件,这个文件包含了类信息,描述了如何配置它们,以及如何相互调用。 13. 什么是Spring IOC 容器? Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。 14. IOC的优点是什么? IOC 或 依赖注入把应用的代码量降到最低。它使应用容易测试,单元测试不再需要单例和JNDI查找机制。最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持加载服务时的饿汉式初始化和懒加载。 15. ApplicationContext通常的实现是什么? · FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。 · ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。 · WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。 16. Bean 工厂和 Application contexts 有什么区别? Application contexts提供一种方法处理文本消息,一个通常的做法是加载文件资源(比如镜像),它们可以向注册为监听器的bean发布事件。另外,在容器或容器内的对象上执行的那些不得不由bean工厂以程序化方式处理的操作,可以在Application contexts中以声明的方式处理。Application contexts实现了MessageSource接口,该接口的实现以可插拔的方式提供获取本地化消息的方法。 17. 一个Spring的应用看起来象什么? · 一个定义了一些功能的接口。 · 这实现包括属性,它的Setter , getter 方法和函数等。 · Spring AOP。 · Spring 的XML 配置文件。 · 使用以上功能的客户端程序。 依赖注入 18. 什么是Spring的依赖注入? 依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来。 19. 有哪些不同类型的IOC(依赖注入)方式? · 构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。 · Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。 20. 哪种依赖注入方式你建议使用,构造器注入,还是 Setter方法注入? 你两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。 Spring Beans 21.什么是Spring beans? Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管理。这些beans通过容器中配置的元数据创建。比如,以XML文件中<bean/> 的形式定义。 Spring 框架定义的beans都是单件beans。在bean tag中有个属性”singleton”,如果它被赋为TRUE,bean 就是单件,否则就是一个 prototype bean。默认是TRUE,所以所有在Spring框架中的beans 缺省都是单件。 22. 一个 Spring Bean 定义 包含什么? 一个Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖。 23. 如何给Spring 容器提供配置元数据? 这里有三种重要的方法给Spring 容器提供配置元数据。 XML配置文件。 基于注解的配置。 基于java的配置。 24. 你怎样定义类的作用域? 当定义一个<bean> 在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope 属性 必须设为 singleton。 25. 解释Spring支持的几种bean的作用域。 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. 26. Spring框架中的单例bean是线程安全的吗? 不,Spring框架中的单例bean不是线程安全的。 27. 解释Spring框架中bean的生命周期。 · Spring容器 从XML 文件中读取bean的定义,并实例化bean。 · Spring根据bean的定义填充所有的属性。 · 如果bean实现了BeanNameAware 接口,Spring 传递bean 的ID 到 setBeanName方法。 · 如果Bean 实现了 BeanFactoryAware 接口, Spring传递beanfactory 给setBeanFactory 方法。 · 如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。 · 如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。 · 如果有BeanPostProcessors 和bean 关联,这些bean的postProcessAfterInitialization() 方法将被调用。 · 如果bean实现了 DisposableBean,它将调用destroy()方法。 28. 哪些是重要的bean生命周期方法? 你能重载它们吗? 有两个重要的bean 生命周期方法,第一个是setup , 它是在容器加载bean的时候被调用。第二个方法是 teardown 它是在容器卸载类的时候被调用。 The bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。 29. 什么是Spring的内部bean? 当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean,为了定义inner bean,在Spring 的 基于XML的 配置元数据中,可以在 <property/>或 <constructor-arg/> 元素内使用<bean/> 元素,内部bean通常是匿名的,它们的Scope一般是prototype。 30. 在 Spring中如何注入一个java集合? Spring提供以下几种集合的配置元素: · <list>类型用于注入一列值,允许有相同的值。 · <set> 类型用于注入一组值,不允许有相同的值。 · <map> 类型用于注入一组键值对,键和值都可以为任意类型。 · <props>类型用于注入一组键值对,键和值都只能为String类型。 31. 什么是bean装配? 装配,或bean 装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。 32. 什么是bean的自动装配? Spring 容器能够自动装配相互合作的bean,这意味着容器不需要<constructor-arg>和<property>配置,能通过Bean工厂自动处理bean之间的协作。 33. 解释不同方式的自动装配 。 有五种自动装配的方式,可以用来指导Spring容器用自动装配方式来进行依赖注入。 · no:默认的方式是不进行自动装配,通过显式设置ref 属性来进行装配。 · byName:通过参数名 自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。 · byType::通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。 · constructor:这个方式类似于byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。 · autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。 34.自动装配有哪些局限性 ? 自动装配的局限性是: · 重写: 你仍需用 <constructor-arg>和 <property> 配置来定义依赖,意味着总要重写自动装配。 · 基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。 · 模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。 35. 你可以在Spring中注入一个null 和一个空字符串吗? 可以。 Spring注解 36. 什么是基于Java的Spring注解配置? 给一些注解的例子. 基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。 以@Configuration 注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。 37. 什么是基于注解的容器配置? 相对于XML文件,注解型的配置依赖于通过字节码元数据装配组件,而非尖括号的声明。 开发者通过在相应的类,方法或属性上使用注解的方式,直接组件类中进行配置,而不是使用xml表述bean的装配关系。 38. 怎样开启注解装配? 注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置 <context:annotation-config/>元素。 39. @Required 注解 这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException。 40. @Autowired 注解 @Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。 41. @Qualifier 注解 当有多个相同类型的bean却只有一个需要自动装配时,将@Qualifier 注解和@Autowire 注解结合使用以消除这种混淆,指定需要装配的确切的bean。 Spring数据访问 42.在Spring框架中如何更有效地使用JDBC? 使用SpringJDBC 框架,资源管理和错误处理的代价都会被减轻。所以开发者只需写statements 和 queries从数据存取数据,JDBC也可以在Spring框架提供的模板类的帮助下更有效地被使用,这个模板叫JdbcTemplate (例子见这里here) 43. JdbcTemplate JdbcTemplate 类提供了很多便利的方法解决诸如把数据库数据转变成基本数据类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据错误处理。 44. Spring对DAO的支持 Spring对数据访问对象(DAO)的支持旨在简化它和数据访问技术如JDBC,Hibernate or JDO 结合使用。这使我们可以方便切换持久层。编码时也不用担心会捕获每种技术特有的异常。 45. 使用Spring通过什么方式访问Hibernate? 在Spring中有两种方式访问Hibernate: · 控制反转 Hibernate Template和 Callback。 · 继承 HibernateDAOSupport提供一个AOP 拦截器。 46. Spring支持的ORM Spring支持以下ORM: · Hibernate · iBatis · JPA (Java Persistence API) · TopLink · JDO (Java Data Objects) · OJB 47.如何通过HibernateDaoSupport将Spring和Hibernate结合起来? 用Spring的 SessionFactory 调用 LocalSessionFactory。集成过程分三步: · 配置the Hibernate SessionFactory。 · 继承HibernateDaoSupport实现一个DAO。 · 在AOP支持的事务中装配。 48. Spring支持的事务管理类型 Spring支持两种类型的事务管理: · 编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。 · 声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。 49. Spring框架的事务管理有哪些优点? · 它为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。 · 它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如 · 它支持声明式事务管理。 · 它和Spring各种数据访问抽象层很好得集成。 50. 你更倾向用那种事务管理类型? 大多数Spring框架的用户选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。 Spring面向切面编程(AOP) 51. 解释AOP 面向切面的编程,或AOP, 是一种编程技术,允许程序模块化横向切割关注点,或横切典型的责任划分,如日志和事务管理。 52. Aspect 切面 AOP核心就是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组API提供横切功能。比如,一个日志模块可以被称作日志的AOP切面。根据需求的不同,一个应用程序可以有若干切面。在Spring AOP中,切面通过带有@Aspect注解的类实现。 52. 在Spring AOP 中,关注点和横切关注的区别是什么? 关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的一个功能。 横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。 54. 连接点 连接点代表一个应用程序的某个位置,在这个位置我们可以插入一个AOP切面,它实际上是个应用程序执行Spring AOP的位置。 55. 通知 通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码段。 Spring切面可以应用五种类型的通知: · before:前置通知,在一个方法执行前被调用。 · after: 在方法执行之后调用的通知,无论方法执行是否成功。 · after-returning: 仅当方法成功完成后执行的通知。 · after-throwing: 在方法抛出异常退出时执行的通知。 · around: 在方法执行之前和之后调用的通知。 56. 切点 切入点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点。 57. 什么是引入? 引入允许我们在已存在的类中增加新的方法和属性。 58. 什么是目标对象? 被一个或者多个切面所通知的对象。它通常是一个代理对象。也指被通知(advised)对象。 59. 什么是代理? 代理是通知目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是一样的。 60. 有几种不同类型的自动代理? BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator Metadata autoproxying 61. 什么是织入。什么是织入应用的不同点? 织入是将切面和到其他应用类型或对象连接或创建一个被通知对象的过程。 织入可以在编译时,加载时,或运行时完成。 62. 解释基于XML Schema方式的切面实现。 在这种情况下,切面由常规类以及基于XML的配置实现。 63. 解释基于注解的切面实现 在这种情况下(基于@AspectJ的实现),涉及到的切面声明的风格与带有java5标注的普通java类一致。 Spring 的MVC 64. 什么是Spring的MVC框架? Spring 配备构建Web 应用的全功能MVC框架。Spring可以很便捷地和其他MVC框架集成,如Struts,Spring 的MVC框架用控制反转把业务对象和控制逻辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。 65. DispatcherServlet Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。 66. WebApplicationContext WebApplicationContext 继承了ApplicationContext 并增加了一些WEB应用必备的特有功能,它不同于一般的ApplicationContext ,因为它能处理主题,并找到被关联的servlet。 67. 什么是Spring MVC框架的控制器? 控制器提供一个访问应用程序的行为,此行为通常通过服务接口实现。控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。Spring用一个非常抽象的方式实现了一个控制层,允许用户创建多种用途的控制器。 68. @Controller 注解 该注解表明该类扮演控制器的角色,Spring不需要你继承任何其他控制器基类或引用Servlet API。 69. @RequestMapping 注解 该注解是用来映射一个URL到一个类或一个特定的方处理法上。