Spring面试题

 

Spring由哪些模块组成?

Spring Core:核心类库,提供IOC服务; Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等); Spring AOP:AOP服务; Spring DAO:对JDBC的抽象,简化了数据访问异常的处理; Spring ORM:对现有的ORM框架的支持; Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传; Spring MVC:提供面向Web应用的Model-View-Controller实现。

Spring的优点?

1、spring属于低侵入式设计,代码的污染极低; 2、spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性; 3、Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。 4、spring对于主流的应用框架提供了集成支持。

什么是IOC/DI?

IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。 DI依赖注入,和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。 最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。 比如在程序中为一个接口提供了不同的实现类,必然要写代码“接口 = new 实现类”,这种做法造成了程序的紧密耦合,当程序需要更换实现类时,就要改动已经写好的代码,这就违反了开闭原则。为了解决这个问题,就要用到IOC。

Spring是如何实现IOC的?

主要思路是通过工厂类、反射机制、配置文件实现解耦合。

什么是AOP?

OOP面向对象,传统的纵向代码复用,是在需要执行业务逻辑的地方调用业务方法;允许开发者定义纵向的关系,但并不适用于定义横向的关系,导致了大量代码的重复。 AOP面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。

Spring是如何实现AOP的?

AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。 1、AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。 2、Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。 Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理: 1、JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例,  生成目标类的代理对象。 2、如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。 静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

BeanFactory 接口和 ApplicationContext 接口有什么区别 ?

BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。 1、BeanFactory是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。 2、ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:继承MessageSource,因此支持国际化。统一的资源文件访问方式。提供在监听器中注册bean的事件。同时加载多个配置文件。载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。 3、BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。 4、ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。 5、BeanFactory通常以代码的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。 6、BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

Spring中的五种标准事件(ApplicationEvent)?

1、上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。 2、上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。 3、上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。 4、上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。 5、请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。 如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

spring配置bean实例化有哪些方式?

1、使用类构造器实例化(默认无参数)

2、使用静态工厂方法实例化(简单工厂模式) //下面这段配置的含义:调用Bean2Factory的getBean2方法得到bean2

3、使用实例工厂方法实例化(工厂方法模式) //先创建工厂实例bean3Facory,再通过工厂实例创建目标bean实例

Bean的完整调用过程?

1、bean对象实例化; 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。 2、注入属性; 实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。 3、如果Bean实现BeanNameAware 执行 setBeanName();参数就是Spring配置文件中Bean的id值。 4、如果Bean实现BeanFactoryAware 执行setBeanFactory(),参数是Spring工厂自身;或者 ApplicationContextAware 执行 setApplicationContext(),参数是Spring上下文; 5、如果存在类实现 BeanPostProcessor 执行postProcessBeforeInitialization(),它是BeanPostProcessor接口提供的钩子函数,用来动态扩展修改Bean; 6、如果Bean实现InitializingBean 执行 afterPropertiesSet(); 7、调用 指定的初始化方法; 8、如果存在类实现 BeanPostProcessor ,执行postProcessAfterInitialization(); 9、执行业务处理; 10、如果Bean实现 DisposableBean 执行 destroy(); 11、调用 指定的销毁方法;

Bean的四个生命周期?

1、bean定义:在配置文件里面用来进行定义。 2、bean初始化:有两种方式初始化: A.在配置文件中通过指定init-method属性来完成 B.实现org.springframwork.beans.factory.InitializingBean接口 3、bean调用:有三种方式可以得到bean实例,并进行调用 4、bean销毁:销毁有两种方式: A.使用配置文件指定的destroy-method属性 B.实现org.springframwork.bean.factory.DisposeableBean接口

Bean的五种作用域?

1、singleton:当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。 2、prototype:Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean() 方法)时都会创建一个新的bean实例。根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用 singleton作用域 3、request:在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例, 它们依据某个bean定义创建而成。该作用 域仅在基于web的Spring ApplicationContext情形下有效。 4、session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。 5、global session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。该作用域仅在基于 web的Spring ApplicationContext情形下有效。

Bean注入属性的几种方式?

代码三种: 1、接口注入: 2、构造器注入: 3、set注入: 标签两种: 1、构造器注入,通过 元素完成注入 2、setter方法注入, 通过 元素完成注入【开发中常用方式】

Spring里面applicationContext.xml文件能不能改成其他文件名?

ContextLoaderListener是一个ServletContextListener, 它在你的web应用启动的时候初始化。缺省情况下, 它会在WEB-INF/applicationContext.xml文件找Spring的配置。 你可以通过定义一个元素名字为”contextConfigLocation”来改变Spring配置文件的 位置。示例如下:

<listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener
        <context-param> 
            <param-name>contextConfigLocation</param-name> 
            <param-value>/WEB-INF/xyz.xml</param-value> 
        </context-param>   
    </listener-class> 
</listener> 

Spring框架中的单例Beans是线程安全的么?

Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。

Spring如何处理线程并发问题?

Spring使用ThreadLocal解决线程安全问题: 一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。 ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。 在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。 而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。 由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用。 概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

Spring的自动装配?

在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象。 xml配置中共有5种自动装配: 1、no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。 2、byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。  3、byType:通过参数的数据类型进行自动装配。 4、constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。 5、autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。 基于注解的方式: 使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean: 1、如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据; 2、如果查询的结果不止一个,那么@Autowired会根据名称来查找; 3、如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。 @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。 @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

Spring 框架中都用到了哪些设计模式?

1、工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例; 2、单例模式:Bean默认为单例模式。 3、代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术; 4、模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。 5、观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。

Spring事物传播行为?

spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。 PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。 PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。 PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。 PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。 PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

Spring中的隔离级别?

ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。 ISOLATION_READ_UNCOMMITTED:允许另外一个事务可以看到这个事务未提交的数据。 ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。 ISOLATION_REPEATABLE_READ:保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。 ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。

介绍一下Spring的事物管理

事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。这样可以防止出现脏数据,防止数据库数据出现问题。 spring支持编程式事务管理和声明式事务管理两种方式: 编程式使用TransactionMananger进行管 理,可以通过Spring的注入来完成此功能。spring提供了几个关于事务处理的类:TransactionDefinition //事务属性定义;TranscationStatus //代表了当前的事务,可以提交,回滚;PlatformTransactionManager这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类 AbstractPlatformTransactionManager,我们使用的事务管理类例如 DataSourceTransactionManager等都是这个类的子类。 一般事务定义步骤:

TransactionDefinition td =newTransactionDefinition();
TransactionStatus ts = transactionManager.getTransaction(td);
try{ 
    //do sth
    transactionManager.commit(ts);
}catch(Exception e){
    transactionManager.rollback(ts);
}

编程式主要使用transactionTemplate。省略了部分的提交,回滚,一系列的事务对象定义,需注入事务管理对象.

void add(){
    transactionTemplate.execute(newTransactionCallback(){
        pulic Object doInTransaction(TransactionStatus ts){
            //do sth
        }
    }
}

声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。 声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中。 声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

解释一下Spring AOP里面的几个名词

切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。

连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的执行。 通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。

通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链。

切入点(Pointcut):匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。

引入(Introduction):(也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。

目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。

AOP代理(AOP Proxy): AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)风格和@AspectJ注解风格的切面声明,对于使用这些风格的用户来说,代理的创建是透明的。

织入(Weaving):把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。

通知有哪些类型?

前置通知(Before advice):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。 返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。 抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。 后通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。 环绕通知(Around Advice):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。 同一个aspect,不同advice的执行顺序: 1、没有异常情况下的执行顺序:around before advice;before advice;target method 执行;around after advice;after advice;afterReturning。 2、有异常情况下的执行顺序:around before advice;before advice;target method 执行;around after advice;after advice;afterThrowing:异常发生;java.lang.RuntimeException: 异常发生