Spring 常见的一些面试题整理

Spring 常见的一些面试题整理

Spring 的不同事务传播行为有哪些,干什么用的?

当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播. 例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行. 事务的传播行为可以由传播属性指定.

Spring 定义了7 种类传播行为.(一二两种最常用)

  1. PROPAGATION_REQUIRED如果存在一个事务,则支持当前事务,如果没有事务则开启
  2. PROPAGATION_REQUIRES_NEW总是开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起
  3. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
  4. PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
  5. PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
  6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
  7. PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行

Spring 中用到了那些设计模式?

Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的:

  • 代理模式—在AOP和remoting中被用的比较多。
  • 单例模式—在spring配置文件中定义的bean默认为单例模式。
  • 模板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
  • 工厂模式—BeanFactory用来创建对象的实例。
  • 适配器–spring aop
  • 装饰器–spring data hashmapper
  • 观察者– spring 时间驱动模型
  • 回调–Spring ResourceLoaderAware回调接口

工厂模式(Factory Method)

Spring容器就是实例化和管理Bean的工厂

工厂模式隐藏了创建类的细节,返回值必定是接口或者抽象类,而不是具体的某个对象,工厂类根据条件生成不同的子类实例。当得到子类的实例后,就可以调用基类中的方法,不必考虑返回的是哪一个子类的实例。

这个很明显,在各种BeanFactory以及ApplicationContext创建中都用到了;

Spring通过配置文件,就可以管理所有的bean,而这些bean就是Spring工厂能产生的实例,因此,首先我们在Spring配置文件中对两个实例进行配置

单态模式【单例模式】(Singleton)

Spring默认将所有的Bean设置成 单例模式,即对所有的相同id的Bean的请求,都将返回同一个共享的Bean实例。这样就可以大大降低Java创建对象和销毁时的系统开销

使用Spring将Bean设置称为单例行为,则无需自己完成单例模式。

可以通过singleton=“truefalse” 或者 scope=“?”来指定

适配器(Adapter)

在Spring的Aop中,使用的Advice(通知)来增强被代理类的功能。Spring实现这一AOP功能的原理就使用代理模式(1、JDK动态代理。2、CGLib字节码生成技术代理。)对类进行方法级别的切面增强,即,生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器重的内容增强了代理方法的功能,实现的面向切面编程

代理(Proxy)

Spring实现了一种能够通过额外的方法调用完成任务的设计模式 - 代理设计模式,比如JdkDynamicAopProxy和Cglib2AopProxy。

代理设计模式的一个很好的例子是org.springframework.aop.framework.ProxyFactoryBean该工厂根据Spring bean构建AOP代理。该类实现了定义getObject()方法的FactoryBean接口。此方法用于将需求Bean的实例返回给bean factory。在这种情况下,它不是返回的实例,而是AOP代理。在执行代理对象的方法之前,可以通过调用补充方法来进一步“修饰”代理对象(其实所谓的静态代理不过是在装饰模式上加了个要不要你来干动作行为而已,而不是装饰模式什么也不做就加了件衣服,其他还得由你来全权完成)。

观察者(Observer)

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。spring中Observer模式常用的地方是listener的实现。如ApplicationListener

Spring MVC 的工作原理?

https://www.cnblogs.com/xiaoxi/p/6164383.html

SpringMVC流程


  1. 用户发送请求至前端控制器DispatcherServlet。
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。
  3. 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
  4. DispatcherServlet调用HandlerAdapter处理器适配器。
  5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
  6. Controller执行完成返回ModelAndView。
  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
  9. ViewReslover解析后返回具体View。
  10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
  11. DispatcherServlet响应用户。
  • 第一步: 用户发起请求到前端控制器(DispatcherServlet)
  • 第二步:前端控制器请求处理器映射器(HandlerMappering)去查找处理器(Handle):通过xml配置或者注解进行查找
  • 第三步:找到以后处理器映射器(HandlerMappering)像前端控制器返回执行链(HandlerExecutionChain)
  • 第四步:前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)
  • 第五步:处理器适配器去执行Handler
  • 第六步:Handler执行完给处理器适配器返回ModelAndView
  • 第七步:处理器适配器向前端控制器返回ModelAndView
  • 第八步:前端控制器请求视图解析器(ViewResolver)去进行视图解析
  • 第九步:视图解析器像前端控制器返回View
  • 第十步:前端控制器对视图进行渲染
  • 第十一步:前端控制器向用户响应结果

看到这些步骤我相信大家很感觉非常的乱,这是正常的,但是这里主要是要大家理解springMVC中的几个组件

前端控制器(DispatcherServlet):接收请求,响应结果,相当于电脑的CPU。

处理器映射器(HandlerMapping):根据URL去查找处理器

处理器(Handler):(需要程序员去写代码处理逻辑的)

处理器适配器(HandlerAdapter):会把处理器包装成适配器,这样就可以支持多种类型的处理器,类比笔记本的适配器(适配器模式的应用)

视图解析器(ViewResovler):进行视图解析,多返回的字符串,进行处理,可以解析成对应的页面

Spring如何解决循环依赖?

http://www.importnew.com/17580.html

一、构造器循环依赖:表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。

如在创建CircleA类时,构造器需要CircleB类,那将去创建CircleB,在创建CircleB类时又发现需要CircleC类,则又去创建CircleC,最终在创建CircleC时发现又需要CircleA;从而形成一个环,没办法创建。

Spring容器将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉

1)首先让我们看一下配置文件(chapter3/circleInjectByConstructor.xml):


   
   
  1. <bean id= “circleA” class= “cn.javass.spring.chapter3.bean.CircleA” >
  2. <constructor-arg index= “0” ref= “circleB” />
  3. </bean>
  4. <bean id= “circleB” class= “cn.javass.spring.chapter3.bean.CircleB” >
  5. <constructor-arg index= “0” ref= “circleC” />
  6. </bean>
  7. <bean id= “circleC” class= “cn.javass.spring.chapter3.bean.CircleC” >
  8. <constructor-arg index= “0” ref= “circleA” />
  9. </bean>

2)写段测试代码(cn.javass.spring.chapter3.CircleTest)测试一下吧:


   
   
  1. @Test ( expected = BeanCurrentlyInCreationException . class )
  2. public void testCircleByConstructor () throws Throwable {
  3. try {
  4. new ClassPathXmlApplicationContext ( “chapter3/circleInjectByConstructor.xml” );
  5. }
  6. catch ( Exception e ) {
  7. //因为要在创建circle3时抛出;
  8. Throwable e1 = e . getCause (). getCause (). getCause ();
  9. throw e1 ;
  10. }
  11. }

让我们分析一下吧:

1、Spring容器创建“circleA” Bean,首先去“当前创建Bean池”查找是否当前Bean正在创建,如果没发现,则继续准备其需要的构造器参数“circleB”,并将“circleA” 标识符放到“当前创建Bean池”;

2、Spring容器创建“circleB” Bean,首先去“当前创建Bean池”查找是否当前Bean正在创建,如果没发现,则继续准备其需要的构造器参数“circleC”,并将“circleB” 标识符放到“当前创建Bean池”;

3、Spring容器创建“circleC” Bean,首先去“当前创建Bean池”查找是否当前Bean正在创建,如果没发现,则继续准备其需要的构造器参数“circleA”,并将“circleC” 标识符放到“当前创建Bean池”;

4、到此为止Spring容器要去创建“circleA”Bean,发现该Bean 标识符在“当前创建Bean池”中,因为表示循环依赖,抛出BeanCurrentlyInCreationException。

二、setter循环依赖:表示通过setter注入方式构成的循环依赖。

对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的Bean来完成的,而且只能解决单例作用域的Bean循环依赖。


   
   
  1. addSingletonFactory ( beanName , new ObjectFactory () {
  2. public Object getObject () throws BeansException {
  3. return getEarlyBeanReference ( beanName , mbd , bean );
  4. }
  5. });

具体步骤如下:

1、Spring容器创建单例“circleA” Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory ”用于返回一个提前暴露一个创建中的Bean,并将“circleA” 标识符放到“当前创建Bean池”;然后进行setter注入“circleB”;

2、Spring容器创建单例“circleB” Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory”用于返回一个提前暴露一个创建中的Bean,并将“circleB” 标识符放到“当前创建Bean池”,然后进行setter注入“circleC”;

3、Spring容器创建单例“circleC” Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory ”用于返回一个提前暴露一个创建中的Bean,并将“circleC” 标识符放到“当前创建Bean池”,然后进行setter注入“circleA”;进行注入“circleA”时由于提前暴露了“ObjectFactory”工厂从而使用它返回提前暴露一个创建中的Bean;

4、最后在依赖注入“circleB”和“circleA”,完成setter注入。

Spring 如何保证 Controller 并发的安全?

Spring 多线程请求过来调用的Controller对象都是一个,而不是一个请求过来就创建一个Controller对象。

并发的安全? 原因就在于Controller对象是单例的,那么如果不小心在类中定义了类变量,那么这个类变量是被所有请求共享的,这可能会造成多个请求修改该变量的值,出现与预期结果不符合的异常

那有没有办法让Controller不以单例而以每次请求都重新创建的形式存在呢

答案是当然可以,只需要在类上添加注解@Scope(“prototype”)即可,这样每次请求调用的类都是重新生成的(每次生成会影响效率)

虽然这样可以解决问题,但增加了时间成本,总让人不爽,还有其他方法么?答案是肯定的!

使用ThreadLocal来保存类变量,将类变量保存在线程的变量域中,让不同的请求隔离开来。

搜云库 http://www.ymq.io/2018/03/21/spring/



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值