文章目录
- 前言
- 一、Spring
- 1、什么是spring?有什么优势?
- 2、Spring有哪些模块组成?
- 3、Autowired和Resource关键字的区别?
- 4、依赖注入的方式有几种分别是什么?
- 5、谈谈你对Spring的IOC(控制反转)的理解?
- 6、谈谈你对Spring的AOP理解?
- 7、SpringMVC的拦截器和过滤器有什么区别?执行顺序?
- 8、Spring 框架中都用到了哪些设计模式?
- 9、Spring事件监听的核心机制是什么?
- 10、Spring事务的失效原因?
- 11、Spring是如何解决Bean的循环依赖?
- 12、Spring中bean的生命周期?
- 13、BeanFactory 和FactoryBean有什么区别?
- 14、Spring事务传播行为有哪些?
- 15、使用@Lazy标注一个Bean类,会发生什么?
- 16、单例Bean中如何正确注入多例Bean?
- 17、抽象类能不能被注册为Bean?
- 18、Spring支持的几种bean的作用域?
- 19、请解释Spring MVC框架及其工作原理?
- 20、Spring常用的注解?
前言
分享互联网大厂Spring高频面试知识点(持续更新中)
一、Spring
1、什么是spring?有什么优势?
Spring是一个开源的Java/Java EE开发框架,用于构建企业级应用。它提供了一系列的功能,如依赖注入(DI)和面向切面编程(AOP),使得开发者能够更容易地创建和管理企业级应用。
Spring框架的主要优势包括:
1.简化开发:Spring通过提供大量的基础功能,如事务管理、数据访问、Web集成等,使得开发者能够更专注于业务逻辑的实现,而无需关心底层细节。
2.轻量级:Spring框架本身非常轻量,它只提供了必要的核心功能,而其他的功能则由用户根据需要选择性的引入。这使得Spring框架能够灵活地适应各种规模和复杂度的应用。
3.控制反转(IOC)和依赖注入(DI):这是Spring框架的核心特性之一。通过IOC和DI,Spring能够自动地管理对象之间的依赖关系,降低了对象之间的耦合度,提高了代码的可维护性和可重用性。
4.面向切面编程(AOP):Spring支持AOP编程范式,允许开发者将横切关注点(如日志、安全、事务等)从业务逻辑中分离出来,提高了代码的可读性和可维护性。
5.集成性:Spring能够轻松地与其他技术框架(如Struts、Hibernate等)集成,这使得开发者能够选择最适合自己项目的技术栈。
6.扩展性:Spring框架具有良好的扩展性,开发者可以根据自己的需求定制或扩展Spring的功能。
2、Spring有哪些模块组成?
1.Spring Core:核心类库,提供IOC服务。
2.Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等)。
3.Spring AOP:AOP服务。
4.Spring DAO:对JDBC的抽象,简化了数据访问异常的处理。
5.Spring ORM:对现有的ORM框架的支持。
6.Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传。
7.Spring MVC:提供面向Web应用的Model-View-Controller实现。
3、Autowired和Resource关键字的区别?
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
1. 共同点
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
2. 不同点
①. @Autowired
@Autowired为Spring提供的注解,需要导入org.springframework.beans.factory.annotation.Autowired。只按照byType注入。
publicclassTestServiceImpl {
// 用于字段上
@Autowired
privateUserDaouserDao;
@Autowired
public void setUserDao(UserDaouserDao) {
// 用于属性的方法上
this.userDao=userDao;
}
}
@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:
publicclassTestServiceImpl {
@Autowired
@Qualifier("userDao")
privateUserDaouserDao;
}
②. @Resource
publicclassTestServiceImpl {
// 下面两种@Resource只要使用一种即可
@Resource(name="userDao")
private UserDao userDao; // 用于字段上
@Resource(name="userDao")
public void setUserDao(UserDaouserDao) { // 用于属性的setter方法上
this.userDao=userDao;
}
}
注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。
@Resource装配顺序:
①. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③. 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。
4、依赖注入的方式有几种分别是什么?
依赖注入(Dependency Injection,简称DI)的方式主要有三种:构造器注入(Constructor Injection)、Setter方法注入(Setter Injection)和接口注入(Interface Injection,但较少使用)。
以下是这三种方式的优缺点:
1.构造器注入(Constructor Injection):
在构造方法中明确体现对其他类的依赖,代码可读性高。一旦对象初始化成功,其状态肯定是正确的。脱离了IOC框架,这个类仍然可以工作,符合POJO(Plain Old Java Object)的概念。
缺点:
1.当依赖的类很多时,构造函数可能会有很多参数,导致代码可读性下降(也被认为是代码坏味道)。
2.有些类(如MVC框架的Controller类)可能需要默认构造函数,一旦使用构造函数注入,就无法再使用默认构造函数。
3.类中的一些方法可能并不需要使用到所有的依赖。
2.Setter方法注入(Setter Injection):
在对象的整个生命周期内,可以随时动态地改变依赖,灵活性高。
缺点:
对象在创建后,被设置依赖对象之前这段时间状态可能是不正确的。
不直观,无法清晰地表示哪些属性是必须的。
与构造方法注入一样,当依赖的类很多时,Setter方法也可能有很多参数。
3.接口注入(Interface Injection) :
依赖类必须要实现指定的接口,然后实现该接口中的一个函数,该函数就是用于依赖注入。该函数的参数就是要注入的对象。
优点接口注入中,接口的名字、函数的名字都不重要,只要保证函数的参数是要注入的对象类型即可。
缺点:侵入行太强,不建议使用。
5、谈谈你对Spring的IOC(控制反转)的理解?
IOC是由专门的容器(如Spring容器)来帮忙创建对象,所有的类都在容器中登记。当需要某个对象时,不再需要主动new一个对象,而是告诉Spring容器,Spring容器会在适当的时机将对象主动创建并返回给调用者。
6、谈谈你对Spring的AOP理解?
在Spring AOP中,一个应用程序被划分为多个切面(Aspect),每个切面都定义了一些通知(Advice)和切入点(Pointcut)。通知是切面的动作,它描述了切面要做什么,而切入点则描述了何时执行这些动作。通过这些定义,Spring AOP可以在程序运行期间,将切面中的通知织入到目标对象中,从而在不修改目标对象代码的情况下,增加新的功能。
Spring AOP的实现原理主要是基于动态代理技术。当程序运行时,Spring容器会为那些需要被代理的目标对象创建代理对象。这些代理对象会拦截对目标对象的调用,并在调用前后执行相应的通知。通过这种方式,Spring AOP可以在不修改目标对象代码的情况下,实现对目标对象的功能增强。
应用场景:
1.事务管理:在数据库操作或其他需要事务管理的操作中,使用AOP可以将事务管理的逻辑与业务逻辑分离,使得事务的控制更加简单和集中。
2.安全性检查:通过AOP,可以在方法调用前进行安全性检查,例如检查用户的权限或身份验证,以确保只有授权用户可以访问特定的方法或资源。
3.性能监控:通过AOP,可以在方法调用前后记录方法的执行时间,以监控和优化系统的性能。
4.异常处理:使用AOP,可以集中处理方法中抛出的异常,例如将异常转换为统一的错误码或进行日志记录。
5.缓存管理:通过AOP,可以在方法调用前检查缓存中是否存在结果,并在方法调用后将结果存储到缓存中,以提高系统的响应速度。
7、SpringMVC的拦截器和过滤器有什么区别?执行顺序?
拦截器和过滤器在Web应用中都扮演着请求和响应处理的角色,但它们之间存在一些关键区别。
首先,归属不同。拦截器是SpringMVC框架的一部分,而过滤器是Servlet规范的一部分。拦截器主要用于对控制器层的请求进行处理,它们提供了更细粒度的控制,可以在请求进入控制器之前和之后执行特定的逻辑,例如身份验证、日志记录和权限检查。过滤器独立于SpringMVC,用于处理通用的请求和响应内容,例如字符编码、压缩和安全性。
其次,执行顺序也不同。拦截器的执行顺序由配置文件中的顺序决定,可以有多个拦载器,它们按照配置的顺序依次执行。而过滤器的执行顺序由web.xml文件中的配置顺序决定,同样可以有多个过滤器,按照配置的顺序执行。一般来说,首先执行过滤器,然后再执行拦截器。
最后,用途不同。拦截器用于对SpringMVC的请求和响应进行特定的业务处理,通常与控制器层的请求处理有关。过滤器用于对所有Servlet清求和响应进行通用性的处理,通常关注请求和响应内容,而不涉及具体的业务逻辑。总的来说,了解拦截器和过滤器之间的这些区别非常重要。在面试中,这种理解将有助于说明您在Web应用程序中如何处理请求和响应以及如何利用SpringMVC和Servlet规范的不同功能。
8、Spring 框架中都用到了哪些设计模式?
1. 简单工厂
BeanFactory:Spring的BeanFactory充当工厂,负责根据配置信息创建Bean实例。它是一种工厂模式的应用,根据指定的类名或ID创建Bean对象。
2. 工厂方法
Factoryean:FactoryBean接口允许用户自定义Bean的创建逻辑,实现了工厂方法模式。开发人员可以使用FactoryBean来创建复杂的Bean实例。
3. 单例模式
Bean实例:Spring默认将Bean配置为单例,确保在容器中只有一个共享的实例,这有助于节省资源和提高性能。
4. 适配器模式
SpringMVC中的HandlerAdapter:SpringMVC的HandlerAdapter允许不同类型的处理器适配到处理器接口,以实现统一的处理器调用。这是适配器模式的应用。
5. 装饰器模式
BeanWrapper:Spring的BeanWrapper允许在不修改原始Bean类的情况下添加额外的功能,这是装饰器模式的实际应用。
6. 代理模式
AOP底层:Spring的AOP(面向切面编程)底层通过代理模式来实现切面功能,包括JDK动态代理和CGLIB代理。
7. 观察者模式
Spring的事件监听:Spring的事件监听机制是观察者模式的应用,它允许组件监听和响应特定类型的事件,实现了松耦合的组件通信。
8. 策略模式
excludeFilters、includefilters:spring允许使用策略模式来定义包扫描时的过滤策略,如在@Componentscan注解中使用的excludeFilters和includeFilters。
9. 模板方法模式
Spring几乎所有的外接扩展:Spring框架的许多模块和外部扩展都采用模板方法模式,例如JdbcTemplate、HibernateTemplate等
10. 责任链模式
AOP的方法调用:Spring AOP通过责任链模式实现通知(Advice)的调用,确保通知按顺序执行。
9、Spring事件监听的核心机制是什么?
Spring事件监听的核心机制围绕观察者模式展开:
观察者模式: 它允许一个对象(称为主题或被观察者)维护一组依赖于它的对象(称为观察者),并在主题状态发生变化时通知观察者。
它包含三个核心:
1.事件:事件是观察者模式中的主题状态变化的具体表示,它封装了事件发生时的信息。在Spring中,事件通常是普通的Java对象,用于传递数据或上下文信息。
2.事件发布者:在Spring中,事件发布者充当主题的角色,负责触发并发布事件。它通常实现了ApplicationEventpublisher接口或使用注解@Autowired来获得事件发布功能。
3.事件监听器:事件监听器充当观察者的角色,负青监听并响应事件的发生。它实现了Applicationlistener接口,通过onApplicationEvent方法来处理事件。
10、Spring事务的失效原因?
1.方法是private也会失效,解决:改成public: spring的事务代理通常是通过Java动态代理或CGLIB动态代理生成的,这些代理要求目标方法是1.公开可访问的(public)。私有方法无法被代理,因此事务将无效。解决方法是将目标方法改为public或protected。
2.目标类没有配置为Bean也会失效,解决:配置为Bean:Spring的事务管理需要在Spring容器中配置的Bean上才能生效。如果目标类没有被配2置为Spring Bean,那么事务将无法被应用。解决方法是确保目标类被正确配置为Spring Bean。
3.自己捕获了异常,解决:不要捕获处理: Spring事务管理通常依赖于抛出未捕获的运行时异常来触发事务回滚。如果您在方法内部捕获了异常并处理了它,事务将不会回滚。解决方法是让异常在方法内部被抛出,以触发事务回滚。
4.使用CGLIB动态代理,但@Transactional声明在接口上: 默认情况下,Spring的事务代理使用基于接口的JDK动态代理。如果您将@Transactional注解声明在接口上,而日标类是使用CGLIB代理的,事务将不会生效。解决方法是将@Transactional注解移到目标类的方法上,或者配置Spring以使用CGLIB代理接口。
5.跨越多个线程的事务管理,解决:使用编程式事务或分布式事务: 如果您的应用程序在多个线程之间共享数据库连接和事务上下文,事务可能会失效,除非适当地配置事务传播属性。
6.事务传播属性或捕获异常等熟悉设置不正确:事务传播属性定义了事务如何传播到嵌套方法或外部方法。如果事务传播属性设置不正确,可能会导致事务失效或不符合预期的行为。
11、Spring是如何解决Bean的循环依赖?
Spring是如何解决的循环依赖: 采用三级缓存解决的就是三个Map 。关键: 一定要有一个缓存保存它的早期对象作为死循环的出口。
1. 一级缓存 singletonObjects存放可以使用的单例。
2. 二级缓存 earlySingletonObjects存放的是早期的bean,即半成品,此时还无法使用。
3. 三级缓存 singletonFactories是一个对象工厂,用于创建对象并放入二级缓存中。同时,如果对象有Aop代理,则对象工厂返回代理对象。
面试官还可能问:
1.二级缓存能不能解决循环依赖?
a.如果只是循环依赖导致的死循环的问题: 一级缓存就可以解决,但是解决在并发下获取不完整的Bean。
b.二级缓存完全解决循环依赖: 只是需要在实例化后就创建动态代理,不优化也不符合spring生命周期规范
2.Spring有没有解决多例Bean的循环依赖?
a.多例不会使用缓存进行存储(多例Bean每次使用都需要重新创建)
b,不缓存早期对象就无法解决循环
3.Spring有没有解决构造函数参数Bean的循环依赖?
a.构造函数的循环依赖也是会报错
b.可以通过人工进行解决:@Lazy
①. 就不会立即创建依赖的bean了
②. 而是等到用到才通过动态代理进行创建
12、Spring中bean的生命周期?
Bean生命周期:指定的就是Bean从创建到销毁的整个过程分4大步:
1.实例化
a.通过反射去推断构造函数进行实例化
b.实例工厂、静态工厂
2.依赖注入(DI)
a.解析自动装配(byname bytype constractor none @Autowired)
3.初始化
a.调用很多Aware回调方法
b.调用BeanPostProcessorpostProcessBeforelnitialization
c.调用生命周期回调初始化方法
d.调用BeanPostProcessor.postProcessAfterlnitialization,如果bean实现aop则会在这里创建动态代理
4.销毁
a.在spring容器关闭的时候进行调用
b.调用生命周期回调销毁方法
13、BeanFactory 和FactoryBean有什么区别?
BeanFactory 是Soring框架的核心接口之一,用于管理和获取Bean对象亦陈为bean的容器。使用了简单工厂模式,提供aetean方法用来获取bean。
FactoryBean 是一个bean,但是它是一个特殊的bean。
14、Spring事务传播行为有哪些?
REQUIRED:这是Spring中事务传播行为的默认选项。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
REQUIRES_NEW:如果当前存在事务,则挂起当前事务并创建一个新的事务。如果当前没有事务,则创建一个新的事务。
SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,则把当前事务挂起。
NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。
NESTED:如果当前存在事务,则在嵌套事务内运行。如果当前没有事务,其行为与REQUIRED一样
15、使用@Lazy标注一个Bean类,会发生什么?
如果一个类被@Lazy那么容器在启动过程中会判断当前类的注解信息中是否有@Lazy注解,如果有那么就不会实例化,只有你真正使用的时候才会去实例化,然后将该类注册到单例池中。
16、单例Bean中如何正确注入多例Bean?
1.使用@Lookup注解
@Component
public class SingletonBean {
@Lookup("prototypeBean")
public PrototypeBean getPrototypeBean() {
// 这里每次调用都会返回一个新的PrototypeBean实例
return null; // Spring会覆盖这个方法的实现
}
// ... 其他方法 ...
}
@Component
@Scope("prototype")
public class PrototypeBean {
// ... 类的实现 ...
}
2.使用ObjectFactory或ApplicationContext
@Component
public class SingletonBean {
private final ObjectFactory<PrototypeBean> prototypeBeanObjectFactory;
@Autowired
public SingletonBean(ObjectFactory<PrototypeBean> prototypeBeanObjectFactory) {
this.prototypeBeanObjectFactory = prototypeBeanObjectFactory;
}
public PrototypeBean getPrototypeBean() {
// 每次调用都会返回一个新的PrototypeBean实例
return prototypeBeanObjectFactory.getObject();
}
// ... 其他方法 ...
}
@Component
@Scope("prototype")
public class PrototypeBean {
// ... 类的实现 ...
}
@Component
public class SingletonBean implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public PrototypeBean getPrototypeBean() {
// 每次调用都会返回一个新的PrototypeBean实例
return applicationContext.getBean(PrototypeBean.class);
}
// ... 其他方法 ...
}
17、抽象类能不能被注册为Bean?
抽象类本身不能被直接注册为Spring框架中的Bean,因为抽象类不能被实例化。但是,有一些间接的方式可以让Spring管理基于抽象类的Bean。
1.使用FactoryBean:
可以创建一个实现了FactoryBean接口的类,该接口允许你返回一个对象实例,而不是直接返回bean本身。在这个FactoryBean的实现中,你可以创建一个基于抽象类的子类实例(例如,使用匿名内部类),并返回这个实例。这样,虽然你注册的是一个FactoryBean,但你可以通过它来间接地获取基于抽象类的Bean实例。
2.使用Java配置:
在Java配置类中,你可以使用@Bean注解来定义一个方法,该方法返回一个Bean实例。虽然你不能直接返回一个抽象类的实例,但你可以在这个方法中创建一个匿名子类(或者具体的子类)的实例并返回。
3.XML配置中的工厂方法:
如果你使用XML配置,你可以定义一个工厂方法,该方法返回一个基于抽象类的Bean实例。与Java配置类似,你需要定义一个具体的方法,该方法返回基于抽象类的子类实例。
18、Spring支持的几种bean的作用域?
singleton(单例):这是Spring的默认作用域。在Spring IoC容器中,针对一个bean定义只会有一个对象实例存在。Spring IoC容器会在第一次请求该bean时创建这个bean实例,并将其保存在IoC容器中,之后再次请求该bean时,就会直接返回已经创建的bean实例。
prototype(原型):每次对Bean的请求都会创建一个新的实例。这意味着每次通过容器的getBean()方法获取prototype范围的bean时,都将返回一个新的bean实例。prototype作用域的bean适合用在无状态的服务中,比如Spring MVC中的Controller。
request(请求):在每个HTTP请求中创建一个新的Bean实例。这个Bean实例仅在当前HTTP请求内有效。请求作用域的bean适用于处理HTTP请求的控制器或服务。
session(会话):在每个用户会话(Session)中创建一个新的Bean实例。对于同一用户的所有请求,都将使用相同的Bean实例。这个Bean实例仅在当前HTTP会话内有效。会话作用域的bean适用于保存用户特定的数据或状态。
application(全局):一个bean定义对应于单个ServletContext的生命周期。在Web应用中,这相当于ServletContext的生命周期。全局作用域的bean在整个Web应用中都是共享的。
websocket(WebSocket):一个bean定义对应于单个WebSocket的生命周期。这通常用于处理WebSocket通信。
19、请解释Spring MVC框架及其工作原理?
Spring MVC是一个基于Java的Web框架,它按照MVC设计模式设计,将Web应用划分为模型(Model)、视图(View)和控制器(Controller)三个部分。Spring MVC通过DispatcherServlet作为前端控制器来接收所有请求,并委托给相应的处理器映射、处理器适配器、处理器和视图解析器来处理这些请求。
20、Spring常用的注解?
1. @Autowired
作用:自动装配bean,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
原理:当Spring容器启动的时候,Spring容器会自动扫描到@Autowired注解,然后把@Autowired注解标注的变量自动注入进来。
注意:@Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。如果有多个匹配的Bean,则需要使用@Qualifier注解来指定Bean的名称。
2. @Component、@Service、@Repository、@Controller
作用:这些注解都用于定义Bean,并将Bean交给Spring容器管理。
原理:这四个注解都是@Component的衍生注解,只是表示了不同的层,作用还是一样的,都是将类定义成Bean类,然后交给Spring容器管理。
区别:@Component泛指各种组件,@Service在业务逻辑层使用(service层),@Repository在数据访问层使用(dao层),@Controller在展现层使用,控制器的声明(C)。
3. @Qualifier
作用:当存在多个同一类型的Bean时,可以用@Qualifier注解来指定。与@Autowired配合使用,消除特定bean自动装配的歧义。
4. @Resource
作用:和@Autowired的功能一样,都是用来自动装配bean的,只是@Resource默认按名称来装配,当找不到与名称匹配的bean才会按类型来装配。
5. @RequestParam
作用:用于处理Content-Type为application/x-www-form-urlencoded编码的内容,也可以用于其他类型的请求,如POST、DELETE等。
6. @RequestBody
作用:接收的参数是来自requestBody中,即请求体。一般用于处理非Content-Type: application/x-www-form-urlencoded编码格式的数据,比如:application/json、application/xml等类型的数据。
7. @Aspect、@After、@Before、@Around
作用:这些都是AOP(面向切面编程)相关的注解,用于定义切面、通知(advice)和切点(pointcut)等。