spring在面试中的常见问题

Spring是什么?

Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。

Spring 的优点?

(1)spring属于低侵入式设计,代码的污染极低;

(2)spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;

(3)Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。

(4)spring对于主流的应用框架提供了集成支持。

Spring的AOP理解

OOP面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。

AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。

AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。

(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

        ①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例,  生成目标类的代理对象。

        ②如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

Spring的IoC理解

(1)IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。

(2)最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。

(3)Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。

IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。

ApplicationContext与BeanFactory

ApplicationContext接口由BeanFactory接口派生而来,因而提供BeanFactory所有的功能

两者都是通过xml配置文件加载bean,ApplicationContext和BeanFacotry相比,提供了更多的扩展功能,但其主要区别在于后者是延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext.

Spring bean的生命周期

Bean容器找到配置文件中 定义的Spring Bean。

Bean容器利用Java反射创建一个Bean的实例。

如果涉及到一些属性值 就用set方法设置一些属性值。

如果实现了其他什么Aware接口,就调用相应的方法。

比如,Bean实现了BeanNameAware接口, 工厂调用 Bean 的 setBeanName() 方法传递 Bean 的 ID 。

比如,Bean实现了BeanClassLoaderAware接口,该接口提供了setApplicationContext()方法,该方法将所拥有的应用程序上下文实例提供给Bean。

不如,Bean实现了BeanFactoryAware接口,工厂调用 setBeanFactory() 方法传入工厂自身,可以用来检测bean的作用范围。

如果 BeanPostProcessor 和 Bean 关联,那么 将调用该接口 的postProcessBeforeInitialzation() 方法 对 bean进行加工操作,这个是用来实现 spring 的 AOP的。针对所有Spring上下文中所有的bean,可以在配置文档applicationContext.xml中配置一个BeanPostProcessor,然后对所有的bean进行一个初始化之前和之后的代理。

如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。可以针对某个具体的bean进行配置

如果Bean在配置文件中的定义包含init-method属性,执行指定的初始化方法。

如果 BeanPostProcessor 和 Bean 关联,初始化bean之后,执行postProcessAfterInitialization()方法。做初始化之前和之后的代理

要销毁Bean的时候,有两种方法:

如果Bean实现了DisposableBean接口,执行destroy()方法。

如果Bean在配置文件中的定义包含destroy-method属性,执行指定定制销毁的方法。

bean的作用域

bean 的作用域是 singleton时,IoC容器中只会存在一个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回bean的同一实例。

bean的作用域是 prototype时,表示一个 bean 定义对应多个对象实例。

request只适用于Web程序,每一次 HTTP 请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。

session只适用于Web程序,session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效。

global session 全局作用域,可以用来实现在不同网站上登陆。

springMVC的工作原理

springMVC是个MVC开源框架,通过把model,view,control分离,把复杂的应用变成逻辑清晰的几部分,可以让开发成员去并行开发,分层工作。springMVC是spring的一个后续产品,在spring的基础上提供的web 的mvc模块。

springMVC里有几个比较重要的组件:

DispatcherServlet前端控制器,HandlerMapping处理器映射器,Handler处理器,HandlerAdapter处理器适配器,视图解析器

(1)用户发送请求至前端控制器DispatcherServlet;

(2) 前端控制器收到请求后,调用处理器映射器,获取请求对应的处理器;

(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给前端控制器;

(4)前端控制器调用处理器适配器;

(5)处理器适配器经过适配之后调用具体处理器(Handler,也叫后端控制器);

(6)处理器执行完成返回ModelAndView;

(7)处理器适配器将Handler执行结果ModelAndView返回给前端控制器;

(8)前端控制器将ModelAndView传给视图解析器进行解析;

(9)解析后返回具体View;

(10)前端控制器对View进行渲染视图(即将模型数据填充至视图中)

(11)DispatcherServlet响应用户。

springMVC的拦截器

拦截器是用来拦截用户请求的,在请求来之前之后做自定义的处理。

比如用拦截器可以对用户的请求做权限的验证。

使用方法:写一个具体的实现HandlerInterceptor接口的类,在里面重写preHandle,postHandle,afterCompletion方法,再再配置文件中对要拦截的控制器做配置。

spring的事务管理

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redolog实现的。

Spring事务的种类:

spring支持编程式事务管理和声明式事务管理两种方式:

①编程式事务管理使用TransactionTemplate。

②声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中。

声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

事务传播行为

事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法时事务如何传播。

用伪代码说明:

 public void methodA(){
    methodB();
    //doSomething
 }
 
 @Transaction(Propagation=XXX)
 public void methodB(){
    //doSomething
 }

代码中methodA()方法嵌套调用了methodB()方法,methodB()的事务传播行为由@Transaction(Propagation=XXX)设置决定。这里需要注意的是methodA()并没有开启事务,某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。

Spring中七种事务传播行为

事务传播行为类型说明
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

spring的循环依赖

循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。这个时候如果没有特殊机制的帮助,ABC的bean都很难生成成功。

bean的初始化:

(1)createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象

(2)populateBean:填充属性,这一步主要是多bean的依赖属性进行填充

(3)initializeBean:调用spring xml中的init 方法。

Spring 的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的 field 或属性是可以延后设置的(但是构造器必须是在获取引用之前)。在实例化之后Spring此时将这个对象提前曝光到 singletonFactories 让大家认识。

Spring 为了解决单例的循环依赖问题,使用了三级缓存:

我们在创建bean的时候,Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,如果获取到了则从三级缓存移动到了二级缓存。

构造函数注入能不能和属性注入一块用?

不能,有优先级问题。

使用构造函数依赖注入时,Spring保证一个对象所有依赖的对象先实例化后,才实例化这个对象。使用 set 方法依赖注入时,Spring首先实例化对象,然后才实例化所有依赖的对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值