Spring经典高频面试题,原来是长这个样子

Spring经典高频面试题,原来是长这个样子

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处连接和本声明。
本文连接: http://www.javashuo.com/article/p-qhmifvnc-dx.html

本文选自《Spring 5核心原理与30个类手写实战》一书,文末参与互动有机会赢取本书。本文题目目录:javascript

1  什么是Spring框架,Spring框架有哪些主要模块css

2  使用Spring框架能带来哪些好处html

3  什么是控制反转(IoC),什么是依赖注入前端

4  在Java中依赖注入有哪些方式java

5  BeanFactory和ApplicationContext有什么区别python

6  Spring提供几种配置方式来设置元数据web

7  如何使用XML配置方式配置Spring面试

8  Spring提供哪些配置形式算法

9  怎样用注解的方式配置Springspring

10  请解释Spring Bean的生命周期

11  更多经典高频面试题

1  什么是Spring框架,Spring框架有哪些主要模块

Spring框架是一个为Java应用程序开发提供综合、普遍的基础性支持的Java平台。Spring帮助开发者解决了开发中基础性的问题,使得开发人员能够专一于应用程序的开发。Spring框架自己也是按照设计模式精心打造的,这使得咱们能够在开发环境中安心地集成Spring框架,没必要担忧Spring是如何在后台工做的。

 

2  使用Spring框架能带来哪些好处

下面列举了一些使用Spring框架带来的主要好处。

(1)Dependency Injection(DI)使得构造器和JavaBean properties文件中的依赖关系一目了然。

(2)与EJB容器相比较,IoC容器更加趋向于轻量级。这样一来使用IoC容器在有限的内存和CPU资源的状况下进行应用程序的开发和发布就变得十分有利。

(3)Spring并无闭门造车,Spring利用了已有的技术,好比ORM框架、logging框架、J2EE、Quartz和JDK Timer,以及其余视图技术。

(4)Spring框架是按照模块的形式来组织的。由包和类的编号就能够看出其所属的模块,开发者只需选用须要的模块便可。

(5)要测试一个用Spring开发的应用程序十分简单,由于测试相关的环境代码都已经囊括在框架中了。更加简单的是,利用JavaBean形式的POJO类,能够很方便地利用依赖注入来写入测试数据。

(6)Spring的Web框架也是一个精心设计的Web MVC框架,为开发者在Web框架的选择上提供了一个除主流框架(好比Struts)和过分设计的、不流行Web框架之外的选择。

(7)Spring提供了一个便捷的事务管理接口,适用于小型的本地事务处理(好比在单DB的环境下)和复杂的共同事务处理(好比利用JTA的复杂DB环境)。

 

3  什么是控制反转(IoC),什么是依赖注入

(1)控制反转是应用于软件工程领域的,在运行时被装配器对象用来绑定耦合对象的一种编程技巧,对象之间的耦合关系在编译时一般是未知的。在传统的编程方式中,业务逻辑的流程是由应用程序中早已被设定好关联关系的对象来决定的。在使用控制反转的状况下,业务逻辑的流程是由对象关系图来决定的,该对象关系图由装配器负责实例化,这种实现方式还能够将对象之间的关联关系的定义抽象化。绑定的过程是经过“依赖注入”实现的。

(2)控制反转是一种以给予应用程序中目标组件更多控制为目的设计范式,并在实际工做中起到了有效的做用。

(3)依赖注入是在编译阶段还没有知所需的功能是来自哪一个的类的状况下,将其余对象所依赖的功能对象实例化的模式。这就须要一种机制来激活相应的组件以提供特定的功能,因此依赖注入是控制反转的基础。不然若是在组件不受框架控制的状况下,框架又怎么知道要建立哪一个组件呢?

 

4  在Java中依赖注入有哪些方式

(1)构造器注入。

(2)Setter方法注入。

(3)接口注入。

 

5  BeanFactory和ApplicationContext有什么区别

BeanFactory 能够理解为含有Bean集合的工厂类。BeanFactory 包含了Bean的定义,以便在接收到客户端请求时将对应的Bean实例化。

BeanFactory还能在实例化对象时生成协做类之间的关系。此举将Bean自身从Bean客户端的配置中解放出来。BeanFactory还包含Bean生命周期的控制,调用客户端的初始化方法(Initialization Method)和销毁方法(Destruction Method)。

从表面上看,ApplicationContext如同BeanFactory同样具备Bean定义、Bean关联关系的设置及根据请求分发Bean的功能。但ApplicationContext在此基础上还提供了其余功能。

(1)提供了支持国际化的文本消息。

(2)统一的资源文件读取方式。

(3)已在监听器中注册的Bean的事件。

如下是三种较常见的 ApplicationContext 实现方式。

(1) ClassPathXmlApplicationContext: 从ClassPath的XML配置文件中读取上下文,并生成上下文定义。应用程序上下文从程序环境变量中取得。 

  •  
ApplicationContext context = new ClassPathXmlApplicationContext(“application.xml”);

(2)FileSystemXmlApplicationContext :由文件系统中的XML配置文件读取上下文。 

  •  
ApplicationContext context = new FileSystemXmlApplicationContext(“application.xml”);

(3)XmlWebApplicationContext:由Web应用的XML文件读取上下文。

 

6  Spring提供几种配置方式来设置元数据

Spring提供如下三种配置方式来设置元数据:

(1)基于XML的配置。

(2)基于注解的配置。

(3)基于Java的配置。

 

7  如何使用XML配置方式配置Spring

在Spring框架中,依赖和服务须要专门的配置文件实现,通常用XML格式的配置文件。这些配置文件的格式采用公共的模板,由一系列的Bean定义和专门的应用配置选项组成。 

Spring XML配置的主要目的是使全部的Spring组件均可以用XML文件的形式来进行配置。这意味着不会出现其余的Spring配置类型(好比声明配置方式或基于Java Class的配置方式)。

Spring的XML配置方式是使用被Spring命名空间所支持的一系列的XML标签来实现的。Spring主要的命名空间有context、beans、jdbc、tx、aop、mvc和aso。例如:

<beans><!-- JSON Support --><bean name="viewResolver"class="org.springframework.web.servlet.view.BeanNameViewResolver"/><bean name="jsonTemplate"class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/><bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/></beans>

下面这个web.xml仅配置了DispatcherServlet,最简单的配置便能知足应用程序配置运行时组件的需求。​​​​​​​

<web-app><display-name>Archetype Created Web Application</display-name><servlet><servlet-name>spring</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>spring</servlet-name><url-pattern>/</url-pattern></servlet-mapping></web-app>

8  Spring提供哪些配置形式

Spring对Java配置的支持是由@Configuration注解和@Bean注解来实现的。由@Bean注解的方法将会实例化、配置和初始化一个新对象,这个对象将由Spring的IoC容器来管理。@Bean声明所起到的做用与元素相似。被@Configuration所注解的类则表示这个类的主要目的是做为Bean定义的资源。被@Configuration声明的类能够经过在同一个类内部调用@bean方法来设置嵌入Bean的依赖关系。

最简单的@Configuration 声明类请参考下面的代码:​​​​​​​

@Configurationpublic class AppConfig{@Beanpublic MyService myService() {return new MyServiceImpl();}}

与上面的@Beans配置文件相同的XML配置文件以下:​​​​​​​

<beans><bean id="myService" class="com.gupaoedu.services.MyServiceImpl"/></beans>

上述配置方式的实例化方式以下:​​​​​​​

public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);MyService myService = ctx.getBean(MyService.class);myService.doStuff();}

要使用组件扫描,仅需用@Configuration进行注解便可:​​​​​​​

@Configuration@ComponentScan(basePackages = "com.gupaoedu")public class AppConfig {}

在上面的例子中,com.gupaoedu包首先会被扫描到,而后在容器内查找被@Component 声明的类,找到后将这些类按照Spring Bean定义进行注册。 

若是你要在Web应用开发中选用上述配置方式,须要用AnnotationConfigWebApplicationContext类来读取配置文件,能够用来配置Spring的Servlet监听器ContrextLoaderListener或者Spring MVC的DispatcherServlet。

例如:​​​​​​​

<web-app><context-param><param-name>contextClass</param-name><param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value></context-param><context-param><param-name>contextConfigLocation</param-name><param-value>com.gupaoedu.AppConfig</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextClass</param-name><param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value></init-param><init-param><param-name>contextConfigLocation</param-name><param-value>com.gupaoedu.web.MVCConfig</param-value></init-param></servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/web/*</url-pattern></servlet-mapping></web-app> 

9  怎样用注解的方式配置Spring

Spring在2.5版本之后开始支持用注解的方式配置依赖注入。能够用注解的方式来替代XML方式的Bean描述,能够将Bean描述转移到组件类的内部,只须要在相关类上、方法上或者字段声明上使用注解便可。注解注入将会被容器在XML注入以前处理,因此后者会覆盖前者对于同一个属性的处理结果。

注解装配在Spring中是默认关闭的,须要在Spring文件中进行配置才能使用基于注解的装配模式。若是你想要在应用程序中使用注解的方式,请参考以下配置:​​​​​​​

<beans><context:annotation-config/></beans>

配置完成之后,就能够用注解的方式在Spring中向属性、方法和构造方法中自动装配变量。

下面是几种比较重要的注解类型。

(1)@Required:该注解应用于设值方法。 

(2)@Autowired:该注解应用于设值方法、非设值方法、构造方法和变量。 

(3)@Qualifier:该注解和@Autowired注解搭配使用,用于消除特定Bean自动装配的歧义。 

(4)JSR-250 Annotations:Spring支持基于JSR-250 注解的注解,即@Resource、@PostConstruct和@PreDestroy。

10  请解释Spring Bean的生命周期

Spring Bean的生命周期简单易懂。在一个Bean实例被初始化时,须要执行一系列初始化操做以使其达到可用的状态。一样,当一个Bean再也不被调用时须要进行相关的析构操做,并从Bean容器中移除。

Spring Bean Factory 负责管理在Spring容器中被建立的Bean的生命周期。Bean的生命周期由两组回调方法组成。 

(1)初始化以后调用的回调方法。 

(2)销毁以前调用的回调方法。 

Spring提供了如下4种方式来管理Bean的生命周期事件:

(1)InitializingBean和DisposableBean回调接口。

(2)针对特殊行为的其余Aware接口。

(3)Bean配置文件中的 customInit() 方法和 customDestroy() 方法。

(4)@PostConstruct和@PreDestroy注解方式。

使用customInit()和 customDestroy()方法管理Bean生命周期的代码样例以下:​​​​​​​

<beans><bean id="demoBean" class="com.gupaoedu.task.DemoBean"init-Method="customInit" destroy-Method="customDestroy"></bean></beans>

更多经典高频面试题

11  Spring Bean做用域的区别是什么

12  什么是Spring Inner Bean

13  Spring中的单例Bean是线程安全的吗

14  请举例说明如何在Spring中注入一个Java集合

15  如何向Spring Bean中注入java.util.Properties

16  请解释Spring Bean的自动装配

17  自动装配有哪些局限性

18  请解释各类自动装配模式的区别

19  请举例解释@Required注解

20  请举例说明@Qualifier注解

21  构造方法注入和设值注入有什么区别

22  Spring中有哪些不一样类型的事件

23  FileSystemResource和ClassPathResource有什么区别

24  Spring中用到了哪些设计模式

25  在Spring中如何更有效地使用JDBC

26  请解释Spring中的IoC容器

27  在Spring中能够注入null或空字符串吗

 

 

Java框架经典面试题之Spring(内附答案) 

Spring 框架是一个开源的Java平台,它为容易而快速的开发出耐用的Java应用程序提供了全面的基础设施。今天千锋小编着重给你们总结了一些关于Spring框架的经典面试题,但愿能助你们的面试一臂之力。

一、Spring框架中有哪些不一样类型的事件?

Spring 提供了如下5中标准的事件:

  • 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。也能够在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
  • 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/从新开始容器时触发该事件。
  • 上下文中止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法中止容器时触发该事件。
  • 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的全部单例Bean都被销毁。
  • 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

二、Spring 框架中都用到了哪些设计模型?

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

  • 代理模式—在AOP和remoting中被用的比较多。
  • 单例模式—在spring配置文件中定义的bean默认为单例模式。
  • 模板方法—用来解决代码重复的问题。
  • 前端控制器—Srping提供了DispatcherServlet来对请求进行分发。
  • 视图帮助(View Helper )—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里。
  • 依赖注入—贯穿于BeanFactory / ApplicationContext接口的核心理念。
  • 工厂模式—BeanFactory用来建立对象的实例。

三、使用Spring框架的好处是什么?

  1. 轻量:Spring 是轻量的,基本的版本大约2MB。
  2. 控制反转:Spring经过控制反转实现了松散耦合,对象们给出它们的依赖,而不是建立或查找依赖的对象们。
  3. 面向切面的编程(AOP):Spring支持面向切面的编程,而且把应用业务逻辑和系统服务分开。
  4. 容器:Spring 包含并管理应用中对象的生命周期和配置。
  5. MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
  6. 事务管理:Spring 提供一个持续的事务管理接口,能够扩展到上至本地事务下至全局事务(JTA)。
  7. 异常处理:Spring 提供方便的API把具体技术相关的异常(好比由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。

四、解释Spring支持的几种bean的做用域?

Spring框架支持如下五种bean的做用域:

  1. singleton : bean在每一个Spring ioc 容器中只有一个实例。
  2. prototype:一个bean的定义能够有多个实例。
  3. request:每次http请求都会建立一个bean,该做用域仅在基于web的Spring ApplicationContext情形下有效。
  4. session:在一个HTTP Session中,一个bean定义对应一个实例。该做用域仅在基于web的Spring ApplicationContext情形下有效。
  5. global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该做用域仅在基于web的Spring ApplicationContext情形下有效。

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

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

 

  • <list>类型用于注入一列值,容许有相同的值。
  • <set> 类型用于注入一组值,不容许有相同的值。
  • <map> 类型用于注入一组键值对,键和值均可觉得任意类型。
  • <props>类型用于注入一组键值对,键和值都只能为String类型。

 

6.聊一聊 Spring 中的线程安全性

ImportNew. 2018-10-23 12:00
 
来源:SylvanasSun ,
juejin.im/post/5a0045ef5188254de169968e
Spring与线程安全
Spring做为一个IOC/DI容器,帮助咱们管理了许许多多的“bean”。但其实,Spring并无保证这些对象的线程安全,须要由开发者本身编写解决线程安全问题的代码。
Spring对每一个bean提供了一个scope属性来表示该bean的做用域。它是bean的生命周期。例如,一个scope为singleton的bean,在第一次被注入时,会建立为一个单例对象,该对象会一直被复用到应用结束。
咱们交由Spring管理的大多数对象其实都是一些无状态的对象,这种不会由于多线程而致使状态被破坏的对象很适合Spring的默认scope,每一个单例的无状态对象都是线程安全的(也能够说只要是无状态的对象,无论单例多例都是线程安全的,不过单例毕竟节省了不断建立对象与GC的开销)。
无状态的对象便是自身没有状态的对象,天然也就不会由于多个线程的交替调度而破坏自身状态致使线程安全问题。无状态对象包括咱们常用的DO、DTO、VO这些只做为数据的实体模型的贫血对象,还有Service、DAO和Controller,这些对象并无本身的状态,它们只是用来执行某些操做的。例如,每一个DAO提供的函数都只是对数据库的CRUD,并且每一个数据库Connection都做为函数的局部变量(局部变量是在用户栈中的,并且用户栈自己就是线程私有的内存区域,因此不存在线程安全问题),用完即关(或交还给链接池)。
有人可能会认为,我使用request做用域不就能够避免每一个请求之间的安全问题了吗?这是彻底错误的,由于Controller默认是单例的,一个controller对象是会被多个线程共享的,这就又回到了线程的安全问题。固然,你也能够把Controller的scope改为prototype,实际上Struts2就是这么作的,但有一点要注意,Spring MVC对请求的拦截粒度是基于每一个方法的,而Struts2是基于每一个类的,因此把Controller设为多例将会频繁的建立与回收对象,严重影响到了性能。
经过阅读上文其实已经说的很清楚了,Spring根本就没有对bean的多线程安全问题作出任何保证与措施。对于每一个bean的线程安全问题,根本缘由是每一个bean自身的设计。不要在bean中声明任何有状态的实例变量或类变量,若是必须如此,那么就使用ThreadLocal把变量变为线程私有的,若是bean的实例变量或类变量须要在多个线程之间共享,那么就只能使用synchronized、lock、CAS等这些实现线程同步的方法了。
下面将经过解析ThreadLocal的源码来了解它的实现与做用,ThreadLocal是一个很好用的工具类,它在某些状况下解决了线程安全问题(在变量不须要被多个线程共享时)。
ThreadLocal
ThreadLocal是一个为线程提供线程局部变量的工具类。它的思想也十分简单,就是为线程提供一个线程私有的变量副本,这样多个线程均可以随意更改本身线程局部的变量,不会影响到其余线程。不过须要注意的是,ThreadLocal提供的只是一个浅拷贝,若是变量是一个引用类型,那么就要考虑它内部的状态是否会被改变,想要解决这个问题能够经过重写ThreadLocal的initialValue()函数来本身实现深拷贝,建议在使用ThreadLocal时一开始就重写该函数。
ThreadLocal与像synchronized这样的锁机制是不一样的。首先,它们的应用场景与实现思路就不同,锁更强调的是如何同步多个线程去正确地共享一个变量,ThreadLocal则是为了解决同一个变量如何不被多个线程共享。从性能开销的角度上来说,若是锁机制是用时间换空间的话,那么ThreadLocal就是用空间换时间。
ThreadLocal中含有一个叫作ThreadLocalMap的内部类,该类为一个采用线性探测法实现的HashMap。它的key为ThreadLocal对象并且还使用了WeakReference,ThreadLocalMap正是用来存储变量副本的。
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
....
ThreadLocal中只含有三个成员变量,这三个变量都是与ThreadLocalMap的hash策略相关的。
/**
* ThreadLocals rely on per-thread linear-probe hash maps attached
* to each thread (Thread.threadLocals and
* inheritableThreadLocals). The ThreadLocal objects act as keys,
* searched via threadLocalHashCode. This is a custom hash code
* (useful only within ThreadLocalMaps) that eliminates collisions
* in the common case where consecutively constructed ThreadLocals
* are used by the same threads, while remaining well-behaved in
* less common cases.
*/
private final int threadLocalHashCode = nextHashCode();
/**
* The next hash code to be given out. Updated atomically. Starts at
* zero.
*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/**
* The difference between successively generated hash codes - turns
* implicit sequential thread-local IDs into near-optimally spread
* multiplicative hash values for power-of-two-sized tables.
*/
private static final int HASH_INCREMENT = 0x61c88647;
/**
* Returns the next hash code.
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
惟一的实例变量threadLocalHashCode是用来进行寻址的hashcode,它由函数nextHashCode()生成,该函数简单地经过一个增量HASH_INCREMENT来生成hashcode。至于为何这个增量为0x61c88647,主要是由于ThreadLocalMap的初始大小为16,每次扩容都会为原来的2倍,这样它的容量永远为2的n次方,该增量选为0x61c88647也是为了尽量均匀地分布,减小碰撞冲突。
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
要得到当前线程私有的变量副本须要调用get()函数。首先,它会调用getMap()函数去得到当前线程的ThreadLocalMap,这个函数须要接收当前线程的实例做为参数。若是获得的ThreadLocalMap为null,那么就去调用setInitialValue()函数来进行初始化,若是不为null,就经过map来得到变量副本并返回。
setInitialValue()函数会去先调用initialValue()函数来生成初始值,该函数默认返回null,咱们能够经过重写这个函数来返回咱们想要在ThreadLocal中维护的变量。以后,去调用getMap()函数得到ThreadLocalMap,若是该map已经存在,那么就用新得到value去覆盖旧值,不然就调用createMap()函数来建立新的map。
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
return setInitialValue();
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
protected T initialValue() {
return null;
ThreadLocal的set()与remove()函数要比get()的实现还要简单,都只是经过getMap()来得到ThreadLocalMap而后对其进行操做。
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
getMap()函数与createMap()函数的实现也十分简单,可是经过观察这两个函数能够发现一个秘密:ThreadLocalMap是存放在Thread中的。
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
// Thread中的源码
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
仔细想一想其实就可以理解这种设计的思想。有一种广泛的方法是经过一个全局的线程安全的Map来存储各个线程的变量副本,可是这种作法已经彻底违背了ThreadLocal的本意,设计ThreadLocal的初衷就是为了不多个线程去并发访问同一个对象,尽管它是线程安全的。而在每一个Thread中存放与它关联的ThreadLocalMap是彻底符合ThreadLocal的思想的,当想要对线程局部变量进行操做时,只须要把Thread做为key来得到Thread中的ThreadLocalMap便可。这种设计相比采用一个全局Map的方法会多占用不少内存空间,但也所以不须要额外的采起锁等线程同步方法而节省了时间上的消耗。
ThreadLocal中的内存泄漏
咱们要考虑一种会发生内存泄漏的状况,若是ThreadLocal被设置为null后,并且没有任何强引用指向它,根据垃圾回收的可达性分析算法,ThreadLocal将会被回收。这样一来,ThreadLocalMap中就会含有key为null的Entry,并且ThreadLocalMap是在Thread中的,只要线程迟迟不结束,这些没法访问到的value会造成内存泄漏。为了解决这个问题,ThreadLocalMap中的getEntry()、set()和remove()函数都会清理key为null的Entry,如下面的getEntry()函数的源码为例。
/**
* Get the entry associated with key. This method
* itself handles only the fast path: a direct hit of existing
* key. It otherwise relays to getEntryAfterMiss. This is
* designed to maximize performance for direct hits, in part
* by making this method readily inlinable.
* @param key the thread local object
* @return the entry associated with key, or null if no such
*/
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
/**
* Version of getEntry method for use when key is not found in
* its direct hash slot.
* @param key the thread local object
* @param i the table index for key's hash code
* @param e the entry at table[i]
* @return the entry associated with key, or null if no such
*/
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
// 清理key为null的Entry
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
return null;
在上文中咱们发现了ThreadLocalMap的key是一个弱引用,那么为何使用弱引用呢?使用强引用key与弱引用key的差异以下:
但要注意的是,ThreadLocalMap仅仅含有这些被动措施来补救内存泄漏问题。若是你在以后没有调用ThreadLocalMap的set()、getEntry()和remove()函数的话,那么仍然会存在内存泄漏问题。
在使用线程池的状况下,若是不及时进行清理,内存泄漏问题事小,甚至还会产生程序逻辑上的问题。因此,为了安全地使用ThreadLocal,必需要像每次使用完锁就解锁同样,在每次使用完ThreadLocal后都要调用remove()来清理无用的Entry
 

spring和springMVC的面试问题总结

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处连接和本声明。
本文连接: http://www.javashuo.com/article/p-vizmbpfj-dy.html

1.Spring中AOP的应用场景、Aop原理、好处?

答:AOP--Aspect Oriented Programming面向切面编程;用来封装横切关注点,具体能够在下面的场景中使用:

Authentication 权限、Caching 缓存、Context passing 内容传递、Error handling 错误处理Lazy loading懒加载、Debugging调试、logging, tracing, profiling and monitoring 记录跟踪优化 校准、Performance optimization 性能优化、Persistence 持久化、Resource pooling 资源池、Synchronization 同步、Transactions 事务

原理:AOP是面向切面编程,是经过动态代理的方式为程序添加统一功能,集中解决一些公共问题。

优势:1.各个步骤之间的良好隔离性耦合性大大下降 
           2.源代码无关性,再扩展功能的同时不对源码进行修改操做 

2.Spring中IOC的做用与原理?对象建立的过程。

答:IOC--Inversion of Control控制反转。当某个角色须要另一个角色协助的时候,在传统的程序设计过程当中,一般由调用者来建立被调用者的实例对象。但在spring中建立被调用者的工做再也不由调用者来完成,所以称为控制反转。建立被调用者的工做由spring来完成,而后注入调用者 直接使用。

3.介绍spring框架

   它是一个一站式(full-stack全栈式)框架,提供了从表现层-springMVC到业务层-spring再到持久层-springdata的一套完整的解决方案。咱们在项目中能够只使用spring一个框架,它就能够提供表现层的mvc框架,持久层的Dao框架。它的两大核心IoC和AOP更是为咱们程序解耦和代码简洁易维护提供了支持。

4.Spring常见建立对象的注解?

答:@Component@Controller@ Service@ Repository

5.Spring中用到的设计模式

答:简单工厂、工厂方法、单例模式、适配器、包装器、代理、观察者、策略、模板方法

详细介绍:请参考本微博的:开发经常使用设计模式

6.Spring的优势?

答:1.下降了组件之间的耦合性 ,实现了软件各层之间的解耦 
2.可使用容易提供的众多服务,如事务管理,消息服务等 
3.容器提供单例模式支持 
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能 
5.容器提供了众多的辅助类,能加快应用的开发 
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 
7.spring属于低侵入式设计,代码的污染极低 
8.独立于各类应用服务器 
9.spring的DI机制下降了业务对象替换的复杂性 
10.Spring的高度开放性,并不强制应用彻底依赖于Spring,开发者能够自由选择spring 的部分或所有 

7.Spring Bean的做用域之间有什么区别?

Spring容器中的bean能够分为5个范围。全部范围的名称都是自说明的,可是为了不混淆,仍是让咱们来解释一下:

singleton:这种bean范围是默认的,这种范围确保无论接受到多少个请求,每一个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。

prototype:原形范围与单例范围相反,为每个bean请求提供一个实例。

request:在请求bean范围内会每个来自客户端的网络请求建立一个实例,在请求完成之后,bean会失效并被垃圾回收器回收。

Session:与请求范围相似,确保每一个session中有一个bean的实例,在session过时后,bean会随之失效。

global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工做时,它包含不少portlet。若是你想要声明让全部的portlet共用全局的存储变量的话,那么这全局变量须要存储在global-session中。

全局做用域与Servlet中的session做用域效果相同。

8.Spring管理事务有几种方式?

答:有两种方式:

一、编程式事务,在代码中硬编码。(不推荐使用)

二、声明式事务,在配置文件中配置(推荐使用)

声明式事务又分为两种:

a、基于XML的声明式事务

b、基于注解的声明式事务

9.spring中自动装配的方式有哪些?

答:一、 No:即不启用自动装配。

二、 byName:经过属性的名字的方式查找JavaBean依赖的对象并为其注入。好比说类Computer有个属性printer,指定其autowire属性为byName后,Spring IoC容器会在配置文件中查找id/name属性为printer的bean,而后使用Seter方法为其注入。

三、 byType:经过属性的类型查找JavaBean依赖的对象并为其注入。好比类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用Seter方法为其注入。

四、 constructor:通byType同样,也是经过类型查找依赖对象。与byType的区别在于它不是使用Seter方法注入,而是使用构造子注入。

五、 autodetect:在byType和constructor之间自动的选择注入方式。

六、 default:由上级标签<beans>的default-autowire属性肯定。

10.spring中的核心类有那些,各有什么做用?

答:BeanFactory:产生一个新的实例,能够实现单例模式

BeanWrapper:提供统一的get及set方法

ApplicationContext:提供框架的实现,包括BeanFactory的全部功能

11.Bean的调用方式有哪些?

答:有三种方式能够获得Bean并进行调用:
一、使用BeanWrapper
HelloWorld hw=new HelloWorld();
BeanWrapper bw=new BeanWrapperImpl(hw);
bw.setPropertyvalue(”msg”,”HelloWorld”);
system.out.println(bw.getPropertyCalue(”msg”));
二、使用BeanFactory
InputStream is=new FileInputStream(”config.xml”);
XmlBeanFactory factory=new XmlBeanFactory(is);
HelloWorld hw=(HelloWorld) factory.getBean(”HelloWorld”);
system.out.println(hw.getMsg());
三、使用ApplicationConttext
ApplicationContext actx=new FleSystemXmlApplicationContext(”config.xml”);
HelloWorld hw=(HelloWorld) actx.getBean(”HelloWorld”);
System.out.println(hw.getMsg());

12.什么是IOC,什么又是DI,他们有什么区别?

答:依赖注入DI是一个程序设计模式和架构模型, 一些时候也称做控制反转,尽管在技术上来说,依赖注入是一个IOC的特殊实现,依赖注入是指一个对象应用另一个对象来提供一个特殊的能力,例如:把一个 数据库链接已参数的形式传到一个对象的结构方法里面而不是在那个对象内部自行建立一个链接。控制反转和依赖注入的基本思想就是把类的依赖从类内部转化到外 部以减小依赖

应用控制反转,对象在被建立的时候,由一个调控系统内全部对象的外界实体,将其所依赖的对象的引用,传递给它。也能够说,依赖被注入到对象中。所 以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。

13.spring有两种代理方式:

答: 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。

      优势:由于有接口,因此使系统更加松耦合

      缺点:为每个目标类建立接口

若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。

      优势:由于代理类与目标类是继承关系,因此不须要有接口的存在。

      缺点:由于没有使用接口,因此系统的耦合性没有使用JDK的动态代理好。

14.springMVC的流程?

答:1.用户发送请求至前端控制器DispatcherServlet

2.DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3.处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(若是有则生成)一并返回给DispatcherServlet。

4.DispatcherServlet经过HandlerAdapter处理器适配器调用处理器

5.执行处理器(Controller,也叫后端控制器)。

6.Controller执行完成返回ModelAndView

7.HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet

8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器

9.ViewReslover解析后返回具体View

10.DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。

11.DispatcherServlet响应用户

15.Springmvc的优势

答:1.它是基于组件技术的.所有的应用对象,不管控制器和视图,仍是业务对象之类的都是 java组件.而且和Spring提供的其余基础结构紧密集成.

2.不依赖于Servlet API(目标虽是如此,可是在实现的时候确实是依赖于Servlet的)

3. 能够任意使用各类视图技术,而不只仅局限于JSP

4 . 支持各类请求资源的映射策略

5 .它应是易于扩展的

相关文章
相关标签/搜索