我把面试问烂了的⭐Spring面试题⭐总结了一下(带答案,万字总结,精心打磨,建议收藏)
Posted Java程序鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我把面试问烂了的⭐Spring面试题⭐总结了一下(带答案,万字总结,精心打磨,建议收藏)相关的知识,希望对你有一定的参考价值。
💂 个人主页: Java程序鱼
🤟 整个Java 体系的面试题我都会分享,大家可以持续关注
💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦。
💅 有任何问题欢迎私信,看到会及时回复!
序号 | 内容 | 链接地址 |
---|---|---|
1 | Java基础知识面试题 | https://blog.csdn.net/qq_35620342/article/details/119636436 |
2 | Java集合容器面试题 | 待分享 |
3 | Java并发编程面试题 | 待分享 |
4 | Java异常面试题 | 待分享 |
5 | JVM面试题 | 待分享 |
6 | Java Web面试题 | https://blog.csdn.net/qq_35620342/article/details/119642114 |
7 | Spring面试题 | https://blog.csdn.net/qq_35620342/article/details/119956512 |
8 | Spring MVC面试题 | https://blog.csdn.net/qq_35620342/article/details/119965560 |
9 | Spring Boot面试题 | 待分享 |
10 | MyBatis面试题 | https://blog.csdn.net/qq_35620342/article/details/119956541 |
11 | Spring Cloud面试题 | 待分享 |
12 | Redis面试题 | https://blog.csdn.net/qq_35620342/article/details/119575020 |
13 | mysql数据库面试题 | https://blog.csdn.net/qq_35620342/article/details/119930887 |
14 | RabbitMQ面试题 | 待分享 |
15 | Dubbo面试题 | 待分享 |
16 | Linux面试题 | 待分享 |
17 | Tomcat面试题 | 待分享 |
18 | ZooKeeper面试题 | 待分享 |
19 | Netty面试题 | 待分享 |
20 | 数据结构与算法面试题 | 待分享 |
文章目录
- 一、Spring概述
- 二、Spring控制反转
- 三、Spring Beans
- 1.什么是 Spring beans?
- 2.一个 Spring Bean 定义包含什么?
- 3.如何给Spring 容器提供配置元数据?Spring有几种配置方式
- 4.Spring基于xml注入bean的几种方式
- 5.你怎样定义类的作用域?
- 6.解释Spring支持的几种bean的作用域
- 7.Spring框架中的单例bean是线程安全的吗?
- 8.Spring如何处理线程并发问题?
- 9.解释Spring框架中bean的生命周期
- 10.在 Spring中如何注入一个java集合?
- 12.什么是bean装配?
- 13.什么是bean的自动装配?
- 14.Sring 自动装配 bean 有哪些方式?
- 15.使用@Autowired注解自动装配的过程是怎样的?
- 16.自动装配有哪些局限性?
- 四、Spring注解
- 五、Spring数据访问
- 1.解释对象/关系映射集成模块
- 2.在Spring框架中如何更有效地使用JDBC?
- 3.解释JDBC抽象和DAO模块
- 4.Spring DAO 有什么用?
- 5.Spring JDBC API 中存在哪些类?
- 6.JdbcTemplate是什么
- 7.使用Spring通过什么方式访问Hibernate?使用 Spring 访问 Hibernate 的方法有哪些?
- 8.如何通过HibernateDaoSupport将Spring和Hibernate结合起来?
- 9.Spring支持的事务管理类型, spring 事务实现方式有哪些?
- 10.Spring事务的实现方式和实现原理
- 11.Spring的事务传播行为
- 12.Spring 的事务隔离?
- 13.Spring框架的事务管理有哪些优点?
- 14.你更倾向用那种事务管理类型?
- 六、Spring面向切面编程(AOP)
- 1.什么是AOP?
- 2.Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?
- 3.JDK动态代理和CGLIB动态代理的区别
- 4.如何理解 Spring 中的代理?
- 5.解释一下Spring AOP里面的几个名词
- 6.Spring在运行时通知对象
- 7.Spring只支持方法级别的连接点
- 8.在Spring AOP 中,关注点和横切关注的区别是什么?在 spring aop 中 concern 和 cross-cutting concern 的不同之处
- 9.Spring通知有哪些类型?
- 10.什么是切面 Aspect?
- 11.解释基于XML Schema方式的切面实现
- 12.解释基于注解的切面实现
- 13.有几种不同类型的自动代理?
一、Spring概述
1.什么是spring?
Spring是一个轻量级Java开发框架,最早由Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。
Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。
Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)。
为了降低Java开发的复杂性,Spring采取了以下4种关键策略
- 基于POJO的轻量级和最小侵入性编程;
- 通过依赖注入和面向接口实现松耦合;
- 基于切面和惯例进行声明式编程;
- 通过切面和模板减少样板式代码。
2.Spring框架的设计目标,设计理念,和核心是什么?
Spring设计目标:Spring为开发者提供一个一站式轻量级应用开发平台;
Spring设计理念:在JavaEE开发中,支持POJO和JavaBean开发方式,使应用面向接口开发,充分支持OO(面向对象)设计方法;Spring通过IoC容器实现对象耦合关系的管理,并实现依赖反转,将对象之间的依赖关系交给IoC容器,实现解耦;
Spring框架的核心:IoC容器和AOP模块。通过IoC容器管理POJO对象以及他们之间的耦合关系;通过AOP以动态非侵入的方式增强服务。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
3.使用Spring的好处是什么?
-
轻量:Spring 是轻量的,基本的版本大约 2MB
-
控制反转:Spring 通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们
-
面向切面的编程(AOP):Spring 支持面向切面的编程,并且把应用业务逻辑和系统服务分开
-
容器:Spring 包含并管理应用中对象的生命周期和配置
-
MVC 框架:Spring 的 WEB 框架是个精心设计的框架,是 Web 框架的一个很好的替代品
-
事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)
-
异常处理:Spring 提供方便的 API 把具体技术相关的异常(比如由 JDBC,Hibernate or JDO抛出的)转化为一致的 unchecked 异常
4.Spring由哪些模块组成?
-
核心容器( Core Container)
核心容器提供Spring框架的基本功能。核心容器中的主要组件是BeanFactory类,它是工厂模式的实现,JavaBean的管理就由它来负责。BeanFactory类通过IOC将应用程序的配置以及依赖性规范与实际的应用程序代码相分离。
-
数据访问/集成模块(Data Access/Integration)
ORM模块为主流的对象关系映射(object-relation mapping)API提供了集成层,这些主流的对象关系映射API包括了JPA、JDO、hibernate和IBatis。该模块可以将O/R映射框架与Spring提供的特性进行组合来使用 -
Web模块
Web模块包括Web、Servlet、Protlet这几个模块。
Web模块提供了基本的面向Web的集成功能,如多文件上传、使用servlet监听器初始化IOC容器和面向Web的应用上下文,还包含Spring的远程支持中与Web相关的部分。
Servlet模块提供了Spring的Web应用的模型-视图-控制器(MVC)实现。
Protlet模块提供了一个在protlet环境中使用的MVC实现。 -
AOP模块
AOP模块提供了一个在AOP联盟标准的面向切面编程的实现 -
Messaging
模块包含发布和订阅消息的特性。 -
Test模块
Test模块支持使用JUnit和TestNG对Spring组件进行测试,它提供一致的ApplicationContexts并缓存这下上下文,他还提供了一些mock对象,使得开发者可以独立的测试代码。
5.Spring 框架中都用到了哪些设计模式?
(1)工厂设计模式
Spring使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 bean 对象。
两者对比:
- BeanFactory :延迟注入(使用到某个 bean 的时候才会注入),相比于BeanFactory来说会占用更少的内存,程序启动速度更快。
- ApplicationContext :容器启动的时候,不管你用没用到,一次性创建所有 bean 。BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory ,除了有BeanFactory的功能之外还有额外更多功能,所以一般开发人员使用ApplicationContext会更多。
ApplicationContext的三个实现类:
- ClassPathXmlApplication:把上下文文件当成类路径资源。
- FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。
- XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息。
(2)单例模式
Spring中bean的默认作用域就是singleton(单例)的
(3)代理模式
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP就是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,Spring AOP会使用Cglib,这时候Spring AOP会使用Cglib生成一个被代理对象的子类来作为代理,如下图所示:
当然你也可以使用AspectJ,Spring AOP以及集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。
使用AOP之后我们可以把一些通用的功能抽象出来,在在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。
(4)模板方法
Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。
(5)观察者模式
定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现ApplicationListener。
(6)适配器模式
适配器模式(Adapter Pattern) 将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。
Spring AOP中的适配器模式:
我们知道 Spring AOP 的实现是基于代理模式,但是 Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是AdvisorAdapter 。Advice 常用的类型有:BeforeAdvice(目标方法调用前,前置通知)、AfterAdvice(目标方法调用后,后置通知)、AfterReturningAdvice(目标方法执行结束后,return之前)等等。每个类型Advice(通知)都有对应的拦截器:MethodBeforeAdviceInterceptor、AfterReturningAdviceAdapter、AfterReturningAdviceInterceptor。Spring预定义的通知要通过对应的适配器,适配成 MethodInterceptor接口(方法拦截器)类型的对象(如:MethodBeforeAdviceInterceptor 负责适配 MethodBeforeAdvice)。
Spring MVC中的适配器模式:
在Spring MVC中,DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由HandlerAdapter 适配器处理。HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。
6.详细讲解一下核心容器(spring context应用上下文) 模块
这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。
Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从真正的应用代码中分离。最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或应用。
7.Spring框架中有哪些不同类型的事件
Spring 提供了以下5种标准的事件:
-
上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
-
上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
-
上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
-
上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
-
请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。
8.Spring 应用程序有哪些不同组件?
Spring 应用一般有以下组件:
- 接口 - 定义功能。
- Bean 类 - 它包含属性,setter 和 getter 方法,函数等。
- Bean 配置文件 - 包含类的信息以及如何配置它们。
- Spring 面向切面编程(AOP) - 提供面向切面编程的功能。
- 用户程序 - 它使用接口。
9.使用 Spring 有哪些方式?
使用 Spring 有以下方式:
- 作为一个成熟的 Spring Web 应用程序。
- 作为第三方 Web 框架,使用 Spring Frameworks 中间层。
- 作为企业级 Java Bean,它可以包装现有的 POJO(Plain Old Java Objects)。
- 用于远程使用。
二、Spring控制反转
1.什么是Spring IOC 容器?
IOC 全称为 Inversion of Control,翻译为 “控制反转”。
如何理解“控制反转”好呢?理解好它的关键在于我们需要回答如下四个问题:
- 谁控制谁:在传统的开发模式下,我们都是采用直接 new 一个对象的方式来创建对象,也就是说你依赖的对象直接由你自己控制,但是有了 IOC 容器后,则直接由 IoC 容器来控制。所以“谁控制谁”,当然是 IoC 容器控制对象。
- 控制什么:控制对象。
- 为何是反转:没有 IoC 的时候我们都是在自己对象中主动去创建被依赖的对象,这是正转。但是有了 IoC 后,所依赖的对象直接由 IoC 容器创建后注入到被注入的对象中,依赖的对象由原来的主动获取变成被动接受,所以是反转。
- 哪些方面反转了:所依赖对象的获取被反转了。
2.控制反转(IoC)有什么作用?
-
管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
-
解耦,由容器去维护具体的对象
-
托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的
3.SpringIOC如何降低对象之间的耦合度?
在我们的日常开发中,创建对象的操作随处可见以至于对其十分熟悉的同时又感觉十分繁琐,每次需要对象都需要亲手将其new出来,甚至某些情况下由于坏编程习惯还会造成对象无法被回收,这是相当糟糕的。但更为严重的是,我们一直倡导的松耦合,少入侵原则,这种情况下变得一无是处。于是前辈们开始谋求改变这种编程陋习,考虑如何使用编码更加解耦合,由此而来的解决方案是面向接口的编程,于是便有了如下写法:
public class BookServiceImpl {
private BookDaoImpl bookDaoImpl;
public void oldCode(){
//原来的做法
bookDaoImpl = new bookDaoImpl();
bookDaoImpl.getAllCategories();
}
}
public class BookServiceImpl {
private BookDao bookDao;
public void newCode(){
//变为面向接口编程
bookDao = new bookDaoImpl();
bookDao.getAllCategories();
}
}
BookServiceImpl类中由原来直接与BookDaoImp打交互变为BookDao,即使BookDao最终实现依然是BookDaoImp,这样的做的好处是显而易见的,所有调用都通过接口bookDao来完成,而接口的真正的实现者和最终的执行者就是bookDaoImpl,当替换bookDaoImpl类,也只需修改bookDao指向新的实现类。
虽然上述的代码在很大程度上降低了代码的耦合度,但是代码依旧存在入侵性和一定程度的耦合性,比如在修改bookDao的实现类时,仍然需求修改BookServiceImpl的内部代码,当依赖的类多起来时,查找和修改的过程也会显得相当糟糕,因此我们仍需要寻找一种方式,它可以令开发者在无需触及BookServiceImpl内容代码的情况下实现修改bookDao的实现类,以便达到最低的耦合度和最少入侵的目的。实际上存在一种称为反射的编程技术可以协助解决上述问题,反射是一种根据给出的完整类名(字符串方式)来动态地生成对象,这种编程方式可以让对象在生成时才决定到底是哪一种对象,因此可以这样假设,在某个配置文件,该文件已写好bookDaoImpl类的完全限定名称,通过读取该文件而获取到bookDao的真正实现类完全限定名称,然后通过反射技术在运行时动态生成该类,最终赋值给bookDao接口,也就解决了刚才的存在问题,这里为简单演示,使用properties文件作为配置文件,className.properties如下:
bookDao.name=com.spring.dao.BookDaoImpl
获取该配置文件信息动态为bookDao生成实现类:
public class BookServiceImpl implements BookService
{
//读取配置文件的工具类
PropertiesUtil propertiesUtil=new PropertiesUtil("conf/className.properties");
private BookDao bookDao;
public void DaymicObject() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//获取完全限定名称
String className=propertiesUtil.get("bookDao.name");
//通过反射
Class c=Class.forName(className);
//动态生成实例对象
bookDao= (BookDao) c.newInstance();
}
}
的确如我们所愿生成了bookDao的实例,这样做的好处是在替换bookDao实现类的情况只需修改配置文件的内容而无需触及BookServiceImpl的内部代码,从而把代码修改的过程转到配置文件中,相当于BookServiceImpl及其内部的bookDao通过配置文件与bookDao的实现类进行关联,这样BookServiceImpl与bookDao的实现类间也就实现了解耦合,当然BookServiceImpl类中存在着BookDao对象是无法避免的,毕竟这是协同工作的基础,我们只能最大程度去解耦合。
了解了上述的问题再来理解IOC就显得简单多了。Spring IOC 也是一个java对象,在某些特定的时间被创建后,可以进行对其他对象的控制,包括初始化、创建、销毁等。简单地理解,在上述过程中,我们通过配置文件配置了BookDaoImpl实现类的完全限定名称,然后利用反射在运行时为BookDao创建实际实现类,包括BookServiceImpl的创建,Spring的IOC容器都会帮我们完成,而我们唯一要做的就是把需要创建的类和其他类依赖的类以配置文件的方式告诉IOC容器需要创建那些类和注入哪些类即可。Spring通过这种控制反转(IoC)的设计模式促进了松耦合,这种方式使一个对象依赖其它对象时会通过被动的方式传送进来(如BookServiceImpl被创建时,其依赖的BookDao的实现类也会同时被注入BookServiceImpl中),而不是通过手动创建这些类。我们可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在配置文件(XML)中给出定义的,然后利用Java的反射技术,根据XML中给出的类名生成相应的对象。从某种程度上来说,IoC相当于把在工厂方法里通过硬编码创建对象的代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性,更是达到最低的耦合度,因此我们要明白所谓为的IOC就将对象的创建权,交由Spring完成,从此解放手动创建对象的过程,同时让类与类间的关系到达最低耦合度。
4.IOC的优点是什么?
- IOC 或 依赖注入把应用的代码量降到最低。
- 它使应用容易测试,单元测试不再需要单例和JNDI查找机制。
- 最小的代价和最小的侵入性使松散耦合得以实现。
- IOC容器支持加载服务时的饿汉式初始化和懒加载。
5.Spring 的 IOC支持哪些功能
Spring 的 IoC 设计支持以下功能:
- 依赖注入
- 依赖检查
- 自动装配
- 支持集合
- 指定初始化方法和销毁方法
- 支持回调某些方法(但是需要实现 Spring 接口,略有侵入)
其中,最重要的就是依赖注入,从 XML 的配置上说,即 ref 标签。对应 Spring RuntimeBeanReference 对象。
对于 IoC 来说,最重要的就是容器。容器管理着 Bean 的生命周期,控制着 Bean 的依赖注入。
6.BeanFactory 和 ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
(1)依赖关系
BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
- 继承 MessageSource,提供国际化的标准访问策略。
- 继承 ApplicationEventPublisher ,提供强大的事件机制
- 扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源
- 对 Web 应用的支持
(2)加载方式
ApplicationContext接口继承BeanFactory接口,Spring核心工厂是BeanFactory,BranFactory采用延迟加载,第一次getBean时才会初始化Bean,ApplicationContext是会在加载配置文件时初始化Bean。
(3)创建方式
BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
(4)注册方式
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
7.ApplicationContext通常的实现是什么?
-
FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径
-
ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
-
WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
8.有哪些不同类型的依赖注入实现方式?
依赖注入是时下最流行的IoC实现方式,依赖注入分为接口注入(Interface Injection),Setter方法注入(Setter Injection)和构造器注入(Constructor Injection)三种方式。其中接口注入由于在灵活性和易用性比较差,现在从Spring4开始已被废弃。
构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。
9.构造器依赖注入和 Setter方法注入的区别
构造函数注入 | setter 注入 |
---|---|
没有部分注入 | 有部分注入 |
不会覆盖 setter 属性 | 会覆盖 setter 属性 |
任意修改都会创建一个新实例 | 任意修改不会创建一个新实例 |
适用于设置很多属性 | 适用于设置少量属性 |
两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。
三、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有几种配置方式
这里有三种重要的方法给Spring 容器提供配置元数据。
- XML配置文件。
- 基于注解的配置。
- 基于java的配置。
4.Spring基于xml注入bean的几种方式
-
Set方法注入;
-
构造器注入:①通过index设置参数的位置;②通过type设置参数类型;
-
静态工厂注入;
-
实例工厂;
5.你怎样定义类的作用域?
当定义一个 在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope 属性 必须设为 singleton。
6.解释Spring支持的几种bean的作用域
(1)Singleton作用域
所谓Bean的作用域是指spring容器创建Bean后的生存周期即由创建到销毁的整个过程。之前我们所创建的所有Bean其作用域都是Singleton,这是Spring默认的,在这样的作用域下,每一个Bean的实例只会被创建一次,而且Spring容器在整个应用程序生存期中都可以使用该实例。因此之前的代码中spring容器创建Bean后,通过代码获取的bean,无论多少次,都是同一个Bean的实例。我们可使用<bean>标签的scope属性来指定一个Bean的作用域,如下:
<!-- 默认情况下无需声明Singleton -->
<bean name="accountDao" scope="singleton"
class="com.spring.springIoc.dao.impl.AccountDaoImpl"/>
(2)prototype作用域
除了Singleton外还有另外一种比较常用的作用域,prototype,它代表每次获取Bean实例时都会新创建一个实例对象,类似new操作符。我们来简单测试一下:
<!-- 作用域:prototype -->
<bean name="accountDao" scope="prototype" class="com.spring.springIoc.dao.impl.AccountDaoImpl"/>
测试代码:
@Test
public void test2() {
ApplicationContext applicationContext=new
ClassPathXmlApplicationContext("spring/spring-ioc.xml");
AccountDao accountDao1 = (AccountDao) applicationContext.getBean("accountDao");
AccountDao accountDao2 = (AccountDao) applicationContext.getBean("accountDao");
System.out.println("accountDao1地址:"+accountDao1.toString());
System.out.println("accountDao2地址:"+accountDao2.toString());
}
执行结果:
accountDao1地址:com.spring.springIoc.dao.impl.AccountDaoImpl@579a0931
accountDao2地址:com.spring.springIoc.dao.impl.AccountDaoImpl@49ads019
显然是两个不同的实例对象。当然我们也可通过注解来声明作用域:
@Scope("prototype")
public class AccountDaoImpl {
//......
}
这里还需要说明一种特殊的情景,当一个作用域为Singleton的Bean依赖于一个作用域为prototype的Bean时如下:
<!-- 作用域prototype-->
<bean name="accountDao" scope="prototype"
class="com.spring.springIoc.dao.impl.AccountDaoImpl"/>
<!-- 作用域Singleton -->
<bean name="accountService" class="com.spring.springIoc.service.impl.AccountServiceImpl">
<!-- 注入作用域为prototype的accountDao对象,需要set方法 -->
<property name="accountDao" ref="accountDao"/>
</bean>
在这样的情况下希望的是每次getBean(“accountService”)处理的都是一个新的accountDao实例对象,但是由于accountService的依赖是在Bean被创建时注入的,而且accountService是一个Singleton,整个生存周期中只会创建一次,因此它所依赖的accountDao实例对象也只会被注入一次,此后不会再注入任何新的accountDao实例对象。为了解决这种困境,只能放弃使用依赖注入的功能,使用代码实现,如下:通过实现ApplicationContextAware接口,重写setApplicationContext,这样的话Spring容器在创建AccountServiceImpl实例时会自动注入ApplicationContext对象,此时便可以通过ApplicationContext获取accountDao实例了,这样可以保证每次获取的accountDao实例都是新的(这里的代码只是演示该过程,实际开发中一般不会要求accountDao每次都是新实例,因为accountDao无需记录状态信息,即无状态bean,一般默认singleton即可)
<bean name="accountDao" scope="prototype" class="com.zejian.spring.springIoc.dao.impl.AccountDaoImpl"/>
<!-- accountDao通过代码注入 -->
<bean name="accountService" class="com.zejian.spring.springIoc.service.impl.AccountServiceImpl" />
代码注入示范:
public class AccountServiceImpl implements AccountService , ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void doSomething() {
System.out.println("AccountServiceImpl#doSomething......");
System.out.println("getAccountDao....."+ getAccountDao().toString());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
private AccountDao getAccountDao(){
return applicationContext.getBean(AccountDao.class);
}
}
测试代码:
//加载配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring/spring-ioc.xml");
//测试获取不同实例的AccountDao
AccountService accountService= (AccountService) applicationContext.getBean("accountService");
accountService.doSomething();
AccountService accountService1= (AccountService) applicationContext.getBean("accountService");
accountService1.doSomething();
运行结果:
AccountServiceImpl#doSomething......
getAccountDao.....com.spring.springIoc.dao.impl.AccountDaoImpl@ab321564
AccountServiceImpl#doSomething......
getAccountDao.....com.spring.springIoc.dao.impl.AccountDaoImpl@2aga1580
显然这样的方式,每次获取的daoAccount都不一样,也就解决上述的问题,另外的一种情况是当一个作用域为propotype的Bean依赖于一个Singleton作用域的Bean时,解决方案跟上述是相同的。请注意,当一个Bean被设置为prototype 后Spring就不会对一个bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。因此我们需要慎用它,一般情况下,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。所谓有状态就是该bean有保存信息的能力,不能共享,否则会造成线程安全问题,而无状态则不保存信息,是线程安全的,可以共享,spring中大部分bean都是Singleton,整个生命周期过程只会存在一个。
(3)request与session作用域
在spring2.5中专门针对Web应该程序引进了request和session这两种作用域。关于request作用域,对于每次HTTP请求到达应用程序,Spring容器会创建一个全新的Request作用域的bean实例,且该bean实例仅在当前HTTP request内有效,整个请求过程也只会使用相同的bean实例,因此我们可以根据需要放心的更改所建实例的内部状态,而其他请求HTTP请求则创建新bean的实例互不干扰,当处理请求结束,request作用域的bean实例将被销毁,如在接收参数时可能需要一个bean实例来装载一些参数,显然每次请求参数几乎不会相同,因此希望bean实例每次都是足够新的而且只在request作用域范围内有效。关于Session可能你也已猜到,每当创建一个新的HTTP Session时就会创建一个Session作用域的Bean,并该实例bean伴随着会话的存在而存在。下面看一个测试程序:
@Component
@Scope(value = "singleton")
public class SingletonBean {
//......
}
@Component
@Scope(value = "prototype" , proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeBean {
//......
}
@Component
@Scope(value = "request" , proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestBean {
//......
}
@Component
@Scope(value = 以上是关于我把面试问烂了的⭐Spring面试题⭐总结了一下(带答案,万字总结,精心打磨,建议收藏)的主要内容,如果未能解决你的问题,请参考以下文章
我把面试问烂了的⭐Java并发编程⭐总结了一下(带答案,万字总结,精心打磨,建议收藏)
耗时一个月,我把问烂了的网络安全2023年必考面试题总结了一下
我把Java岗面试问烂了的题目总结了一下,终于找到一份实习工作!
面了6家大厂,我把问烂了的MySQL常见面试题总结了一下(带答案,万字总结,精心打磨,建议收藏)