Spring 常见面试题
Spring 是个 java 企业级应用的开源开发框架。Spring 主要用来开发 Java 应用,但是有些扩展是针对构建 J2EE 平台的 web 应用。Spring 框架目标是简化 Java 企业级应用开发,并通过 POJO 为基础的编程模型促进良好的编程习惯。对于软件来说,控制反转就是某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定。在 Spring 框架中,即将控制权交由 S
文章目录
- Spring 框架
- Spring IoC
- Spring Beans
- 1\. 什么是 Spring beans?
- 2\. 一个 Spring Bean 定义 包含什么?
- 3\. 如何给 Spring 容器提供配置元数据?
- 4\. 如何用基于 XML 配置的方式配置 Spring?
- 5\. 如何用基于 Java 配置的方式配置 Spring?
- 6\. 怎样用注解的方式配置 Spring?
- 4\. 你怎样定义类的作用域?
- 5\. 解释 Spring 支持的几种 bean 的作用域。
- 6\. Spring 框架中的单例 bean 是线程安全的吗?
- 7\. 解释 Spring 框架中 bean 的生命周期。
- 8\. 什么是 Spring inner beans?
- 9\. 请举例说明如何在 Spring 中注入一个 Java Collection?
本文是通过收集网上各种面试指南题目及答案然后经过整理归纳而来,仅仅是为了方便以后回顾,无意冒犯各位原创作者。
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 核心容器:容器是 Spring 框架最核心的部分,它管理着 Spring 应用中 Bean 的创建、配置和管理。
- Spring AOP 模块:在此模块中,Spring 对面向切面编程提供了丰富的支持。
- 数据访问与集成
- Web 与远程调用
- Instrumentation
- 测试
4. BeanFactory 和 ApplicationContext 有什么区别?
BeanFactory 和 ApplicationContext 是 Spring 容器的不同实现,Spring 容器负责创建对象,装配它们,配置它们并管理它们的整个生命周期。 BeanFactory 是最简单的容器,提供基本的 DI 支持;ApplicationContext 基于 BeanFactory 构建,并提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用时间给感兴趣的事件监听者。
5. 常见的 ApplicationContext 实现方式?
- AnnotationConfigApplicationContext:从一个或多个基于 Java 的配置类中加载 Spring 应用上下文。
ApplicationContext context = new AnnotationConfigApplicationContext(com.springinaction.server.config.ServerConfig.class);
- ClassPathXmlApplicationContext:从类路径下的一个或多个 XML 配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
- FileSystemXmlApplicationContext:从文件系统下的一个或多个 XML 配置文件中加载上下文定义。
ApplicationContext context = new FileSystemXmlApplicationContext("c:/bean.xml");
- XmlWebApplicationContext:从 Web 应用下的一个或多个 XML 配置文件中加载上下文定义。
Spring IoC
1. 什么是控制反转 (IoC)?什么是依赖注入?
对于软件来说,控制反转就是某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定。在 Spring 框架中,即将控制权交由 Spring 容器借由 bean 配置来进行控制。
由于 IoC 的定义比较晦涩难懂,提出了用 DI(Dependency Injection,依赖注入)的概念来代替 IoC。
依赖注入:既让调用类对某一接口的实现类的依赖关系由第三方(容器或者协作类)注入,以移除调用类对某一接口实现类的依赖。
2. IoC 的类型
从注入方法看,IoC 主要可以分为 3 钟类型:
- 构造器注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
- 属性注入(Setter 方法注入):Setter 方法注入是容器通过调用无参构造器或无参 static 工厂方法实例化 bean 之后,调用该 bean 的 setter 方法,即实现了基于 setter 的依赖注入。
- 接口注入
Spring 支持构造函数注入和属性注入
你两种依赖方式都可以使用,构造器注入和 Setter 方法注入。最好的解决方案是用构造器参数实现强制依赖,setter 方法实现可选依赖。
IoC 的底层技术支持 ——Java 反射机制
Spring Beans
1. 什么是 Spring beans?
Spring beans 是那些形成 Spring 应用的主干的 java 对象。它们被 Spring IOC 容器初始化,装配,和管理。这些 beans 通过容器中配置的元数据创建。比如,以 XML 文件中 的形式定义。
Spring 框架定义的 beans 都是单例 beans。在 bean tag 中有个属性”singleton”,如果它被赋为 TRUE,bean 就是单件,否则就是一个 prototype bean。默认是 TRUE,所以所有在 Spring 框架中的 beans 缺省都是单件。
2. 一个 Spring Bean 定义 包含什么?
一个 Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个 bean,它的生命周期详情及它的依赖。
3. 如何给 Spring 容器提供配置元数据?
这里有三种重要的方法给 Spring 容器提供配置元数据:
- 基于 XML 配置文件。
- 基于注解的配置。
- 基于 java 的配置。
4. 如何用基于 XML 配置的方式配置 Spring?
在 Spring 框架中,依赖和服务需要在专门的配置文件来实现,我常用的 XML 格式的配置文件。这些配置文件的格式通常用 开头,然后一系列的 bean 定义和专门的应用配置选项组成。
SpringXML 配置的主要目的时候是使所有的 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>
5. 如何用基于 Java 配置的方式配置 Spring?
Spring 对 Java 配置的支持是由 @Configuration 注解和 @Bean 注解来实现的。由 @Bean 注解的方法将会实例化、配置和初始化一个新对象,这个对象将由 Spring 的 IoC 容器来管理。@Bean 声明所起到的作用与 元素类似。被 @Configuration 所注解的类则表示这个类的主要目的是作为 bean 定义的资源。被 @Configuration 声明的类可以通过在同一个类的内部调用 @bean 方法来设置嵌入 bean 的依赖关系。
最简单的 @Configuration 声明类请参考下面的代码:
@Configuration
public class AppConfig
{
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
对于上面的 @Beans 配置文件相同的 XML 配置文件如下:
<beans>
<bean id="myService" class="com.howtodoinjava.services.MyServiceImpl"/>
</beans>
上述配置方式的实例化方式如下:利用 AnnotationConfigApplicationContext 类进行实例化
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.howtodoinjava")
public class AppConfig {
...
}
在上面的例子中,com.acme 包首先会被扫到,然后再容器内查找被 @Component 声明的类,找到后将这些类按照 Sring bean 定义进行注册。
如果你要在你的 web 应用开发中选用上述的配置的方式的话,需要用 AnnotationConfigWebApplicationContext 类来读取配置文件,可以用来配置 Spring 的 Servlet 监听器 ContrextLoaderListener 或者 Spring MVC 的 DispatcherServlet。
<web-app>
<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<!-- Configuration locations must consist of one or more comma- or space-delimited
fully-qualified @Configuration classes. Fully-qualified packages may also be
specified for component-scanning -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.howtodoinjava.AppConfig</param-value>
</context-param>
<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Declare a Spring MVC DispatcherServlet as usual -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- Again, config locations must consist of one or more comma- or space-delimited
and fully-qualified @Configuration classes -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.howtodoinjava.web.MvcConfig</param-value>
</init-param>
</servlet>
<!-- map all requests for /app/* to the dispatcher servlet -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
6. 怎样用注解的方式配置 Spring?
Spring 在 2.5 版本以后开始支持用注解的方式来配置依赖注入。可以用注解的方式来替代 XML 方式的 bean 描述,可以将 bean 描述转移到组件类的内部,只需要在相关类上、方法上或者字段声明上使用注解即可。注解注入将会被容器在 XML 注入之前被处理,所以后者会覆盖掉前者对于同一个属性的处理结果。
注解装配在 Spring 中是默认关闭的。所以需要在 Spring 文件中配置一下才能使用基于注解的装配模式。如果你想要在你的应用程序中使用关于注解的方法的话,请参考如下的配置。
<beans>
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
在 context:annotation-config/ 标签配置完成以后,就可以用注解的方式在 Spring 中向属性、方法和构造方法中自动装配变量。
下面是几种比较重要的注解类型:
- @Required:该注解应用于设值方法。
- @Autowired:该注解应用于有值设值方法、非设值方法、构造方法和变量。
- @Qualifier:该注解和 @Autowired 注解搭配使用,用于消除特定 bean 自动装配的歧义。
- JSR-250 Annotations:Spring 支持基于 JSR-250 注解的以下注解,@Resource、@PostConstruct 和 @PreDestroy。
4. 你怎样定义类的作用域?
当定义一个 在 Spring 里,我们还能给这个 bean 声明一个作用域。它可以通过 bean 定义中的 scope 属性来定义。如,当 Spring 要在需要的时候每次生产一个新的 bean 实例,bean 的 scope 属性被指定为 prototype。另一方面,一个 bean 每次使用的时候必须返回同一个实例,这个 bean 的 scope 属性 必须设为 singleton。
5. 解释 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
6. Spring 框架中的单例 bean 是线程安全的吗?
Spring 框架并没有对单例 bean 进行任何多线程的封装处理。关于单例 bean 的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的 Spring bean 并没有可变的状态 (比如 Serview 类和 DAO 类),所以在某种程度上说 Spring 的单例 bean 是线程安全的。如果你的 bean 有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。
最浅显的解决办法就是将多态 bean 的作用域由 “singleton” 变更为 “prototype”。
7. 解释 Spring 框架中 bean 的生命周期。
- Spring 容器(从 XML 文件中读取 bean 的定义)对 bean 进行实例化
- Spring 将值和 bean 的引用注入到 bean 对应的属性中;
- 如果 bean 实现了 BeanNameAware 接口,Spring 将 bean 的 ID 传递给 setBeanName()方法;
- XXXAware 在 Spring 里表示对 XXX 可感知,通俗点解释就是:如果在某个类里面想要使用 spring 的一些东西,就可以通过实现 XXXAware 接口告诉 Spring,Spring 看到后就会给你送过来,而接收方式是通过实现接口唯一的方法 setXXX。比如,有一个类想要使用当前的 ApplicationContext,那么我们只需要让它实现 ApplicationContextAware 接口,然后实现接口中唯一的方法:
void setApplicationContext(ApplicationContext applicationContext)
就可以了,spring 会自动调用这个方法将 applicationContext 传给我们
- XXXAware 在 Spring 里表示对 XXX 可感知,通俗点解释就是:如果在某个类里面想要使用 spring 的一些东西,就可以通过实现 XXXAware 接口告诉 Spring,Spring 看到后就会给你送过来,而接收方式是通过实现接口唯一的方法 setXXX。比如,有一个类想要使用当前的 ApplicationContext,那么我们只需要让它实现 ApplicationContextAware 接口,然后实现接口中唯一的方法:
- 如果 bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanFactory()方法,将 BeanFactory 容器实例传入;
- 如果 bean 实现了 ApplicationContextAware 接口,Spring 将调用 setApplicationContext()方法,将 bean 所在的应用上下文的引用传入进来;
- 如果 bean 实现了 BeanPostProcessor 接口,Spring 将调用它们的 postProcessBeforeInitialization()方法;
- 如果 bean 实现了 InitializingBean 接口,Spring 将调用它们的 afterPropertiesSet()方法。类似的,如果 bean 使用 init-method 声明了初始化方法,该方法也会被调用;
- 如果 bean 实现了 BeanPostProcessor 接口,Spring 将调用它们的 postProcessAfterInitialization()方法;
- 此时,bean 已准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,知道该应用上下文被销毁;
- 如果 bean 实现了 DisposableBean 接口,Spring 将调用它的 destory()接口方法,同样,如果 bean 使用了 destory-method 声明了销毁方法,该方法也会被调用。
若要真正理解 bean 的生命周期,需要了解上述各接口的含义,可自行查看 Spring 源码。
Spring Bean 的生命周期简单易懂。在一个 bean 实例被初始化时,需要执行一系列的初始化操作以达到可用的状态。同样的,当一个 bean 不在被调用时需要进行相关的析构操作,并从 bean 容器中移除。
Spring BeanFactory 负责管理在 Spring 容器中被创建的 bean 的生命周期。Bean 的生命周期由两组回调(callback)方法组成。
- 初始化之后调用的回调方法。
- 销毁之前调用的回调方法。
Spring 框架提供了以下四种方式来管理 bean 的生命周期事件:
- InitializingBean 和 DisposableBean 回调接口
- This is not a preferrable way to initialize the bean because it tightly couple your bean class with spring container. A better approach is to use “init-method” attribute in bean definition in applicationContext.xml file.
- 针对特殊行为的其他 Aware 接口
- Bean 配置文件中的 Custom init () 方法和 destroy () 方法
- @PostConstruct 和 @PreDestroy 注解方式
使用 customInit () 和 customDestroy () 方法管理 bean 生命周期的代码样例如下:
<beans>
<bean id="demoBean" class="com.howtodoinjava.task.DemoBean"
init-method="customInit" destroy-method="customDestroy"></bean>
</beans>
更多内容请参考:Spring 生命周期 Spring Bean Life Cycle
8. 什么是 Spring inner beans?
当一个 bean 仅被用作另一个 bean 的属性时,它能被声明为一个内部 bean,为了定义 inner bean,在 Spring 的 基于 XML 的 配置元数据中,可以在 或 元素内使用 < bean/> 元素,内部 bean 通常是匿名的,它们的 Scope 一般是 prototype。内部 bean 可以用 setter 注入 “属性” 和构造方法注入 “构造参数” 的方式来实现。
比如,在我们的应用程序中,一个 Customer 类引用了一个 Person 类,我们的要做的是创建一个 Person 的实例,然后在 Customer 内部使用。
public class Customer
{
private Person person;
//Setters and Getters
}
public class Person
{
private String name;
private String address;
private int age;
//Setters and Getters
}
内部 bean 的声明方式如下:
<bean id="CustomerBean" class="com.howtodoinjava.common.Customer">
<property name="person">
<!-- This is inner bean -->
<bean class="com.howtodoinjava.common.Person">
<property name="name" value="lokesh" />
<property name="address" value="India" />
<property name="age" value="34" />
</bean>
</property>
</bean>
9. 请举例说明如何在 Spring 中注入一个 Java Collection?
Spring 提供了以下四种集合类的配置元素:
- : 该标签用来装配可重复的 list 值。
- : 该标签用来装配没有重复的 set 值。
- : 该标签支持注入键和值都是字符串类型的键值对。
下面看一下具体的例子:
<beans>
<!-- Definition for javaCollection -->
<bean id="javaCollection" class="com.howtodoinjava.JavaCollection">
<!-- java.util.List -->
<property name="customList">
<list>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>UK</value>
</list>
</property>
<!-- java.util.Set -->
<property name="customSet">
<set>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>UK</value>
</set>
</property>
<!-- java.util.Map -->
<property name="customMap">
<map>
<entry key="1" value="INDIA"/>
<entry key="2" value="Pakistan"/>
<entry key="3" value="USA"/>
<entry key="4" value="UK"/>
</map>
</property>
<!-- java.util.Properties -->
<property name="customProperies">
<props>
<prop key="admin">admin@nospam.com</prop>
<prop key="support">support@nospam.com</prop>
</props>
</property>
</bean>
</beans>
15、如何向 Spring Bean 中注入一个 Java.util.Properties? 第一种方法是使用如下面代码所示的 标签:
<bean id="adminUser" class="com.howtodoinjava.common.Customer">
<!-- java.util.Properties -->
<property name="emails">
<props>
<prop key="admin">admin@nospam.com</prop>
<prop key="support">support@nospam.com</prop>
</props>
</property>
</bean>
也可用”util:” 命名空间来从 properties 文件中创建出一个 propertiesbean,然后利用 setter 方法注入 bean 的引用。
-
什么是 bean 装配?装配,或 bean 装配是指在 Spring 容器中把 bean 组装到一起,前提是容器需要知道 bean 的依赖关系,如何通过依赖注入来把它们装配到一起。
-
什么是 bean 的自动装配? Spring 容器能够自动装配相互合作的 bean,这意味着容器不需要 和 < property > 配置,能通过 Bean 工厂自动处理 bean 之间的协作。
-
解释不同方式的自动装配 。 有五种自动装配的方式,可以用来指导 Spring 容器用自动装配方式来进行依赖注入。
no:默认的方式是不进行自动装配,通过显式设置 ref 属性来进行装配。 byName:通过参数名 自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成 byname,之后容器试图匹配、装配和该 bean 的属性具有相同名字的 bean。 byType::通过参数类型自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成 byType,之后容器试图匹配、装配和该 bean 的属性具有相同类型的 bean。如果有多个 bean 符合条件,则抛出错误。 constructor:这个方式类似于 byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。 autodetect:首先尝试使用 constructor 来自动装配,如果无法工作,则使用 byType 方式。
34. 自动装配有哪些局限性?自动装配的局限性是:
重写: 你仍需用 和 配置来定义依赖,意味着总要重写自动装配。 基本数据类型:你不能自动装配简单的属性,如基本数据类型,String 字符串,和类。 模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。
35. 你可以在 Spring 中注入一个 null 和一个空字符串吗? 可以。
【这里想说,因为自己也走了很多弯路过来的,所以才下定决心整理,收集过程虽不易,但想到能帮助到一部分自学java 的人,心里也是甜的!有需要的伙伴请点㊦方】↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)