Java面试——Spring框架

1、什么是IOC和DI?

IOC(控制反转):即将创建及管理对象的权力交给Spring容器。Spring是一个轻型容器(light-weight Container),其核心是Bean工厂(Bean Factory),用以构造我们所需要的bean对象。能够让相互协作的软件组件保持松散耦合。降低了业务对象替换的复杂性,提高了组件之间的解耦。
DI(依赖注入):可以根据配置的依赖关系,将依赖的属性通过反射注入到Spring所管理的bean中。

2、Spring Bean的生命周期?

image.png

  1. Bean容器在配置文件中找到Spring Bean的定义以及相关的配置
  2. 实例化(Instantiation):Srping 容器使用Java反射API创建Bean的实例
  3. 填充属性(Populate Properties):Spring容器会为Bean的属性注入相应的值。这通常是通过依赖注入完成的。
  4. 如果Bean实现了BeanNameAware或BeanFactoryAware接口,Spring容器会调用相应的方法,传入Bean的ID和BeanFactory。如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文。
  5. Bean后处理器前置处理(Before Initialization):Spring容器会调用BeanPostProcessors的postProcessBeforeInitialization方法。
  6. 初始化(Initialization):
  • 如果Bean实现了InitializingBean接口,其afterPropertiesSet方法会被调用。
  • 如果Bean定义了init-method,该方法也会被调用。
  1. Bean后处理器后置处理(After Initialization):Spring容器会调用BeanPostProcessors的postProcessAfterInitialization方法。
  2. 使用Bean:此时,Bean已经准备就绪,可以被应用程序使用了。
  3. 销毁(Destruction):
  • 当容器关闭时,如果Bean实现了DisposableBean接口,其destroy方法会被调用。
  • 如果Bean定义了destroy-method,该方法也会被调用。

3、Spring框架中单例Bean是线程安全的吗?

是这样的,一般不会出现线程安全问题。在Spring中,绝大部分Bean都是无状态的,因此即使这些Bean默认是单例的,也不会出现线程安全问题。比如controller、service、dao这些类,这些类里面通常不会含有成员变量,因此他们被设计成单例的。如果这些类中定义了实例变量,就线程不安全了,所以尽量避免定义实例变量。
对于有状态的Bean,Spring采用ThreadLocal进行处理,使它们成为线程安全可以共享的对象。
也可以使用原型模式(prototype),每次使用时都会重新生成一个对象,解决了线程不安全的问题。

4、Spring Bean的循环依赖问题?

循环依赖:循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A。
循环依赖在Spring中是允许存在,Spring框架依据三级缓存已经解决了大部分的循环依赖。
①一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
②二级缓存:存放早期暴露出来的Bean对象,实例化后把对象放到这个Map中。(Bean可能只经过实例化,属性还未填充,生命周期未走完)
③三级缓存:存放早期暴露的Bean工厂。(用于解决代理对象的问题)
image.png
image.png

4.1、构造方法出现了循环依赖怎么解决?

A依赖于B,B依赖于A,注入的方式是构造函数
原因:由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构造函数的的依赖注入
解决方案:使用@Lazy进行懒加载,什么时候需要对象再进行bean对象的创建

5、Spring Bean有哪些配置方式?

  1. xml方式:在xml配置文件中,通过标签配置bean对象

  2. 注解方式:通过@Configuration + @Bean 方式配置对象

    通过@Component @Controller @Service @Repository 配置bean
    通过@ComponentScan(“扫描包名”)管理bean

6、Spring Bean的作用域有哪些?默认是哪种?

作用域限定了 Spring Bean 的作用范围,在 Spring 配置文件定义 Bean 时,通过声明 scope 配置项,可以灵活定义 Bean 的作用范围。 scope 配置项有 5 个属性,用于描述不同的作用域。

  1. singleton:使用该属性定义 Bean 时,IOC 容器仅创建一个 Bean 实例,IOC 容器每次返回的是同一个 Bean 实例。(默认)
  2. prototype:使用该属性定义 Bean 时,IOC 容器可以创建多个 Bean 实例,每次返回的都是一个新的实例。
  3. request:该属性仅对 HTTP 请求产生作用,使用该属性定义 Bean 时,每次 HTTP 请求都会创建一个新的 Bean,适用于 WebApplicationContext 环境。
  4. session:该属性仅用于 HTTP Session,同一个 Session 共享一个 Bean 实例。不同 Session 使用不同的实例。
  5. global-session:该属性仅用于 HTTP Session,同 session 作用域不同的是,所有的Session 共享一个 Bean 实例。

7、Spring Bean依赖注入(DI)的方式有哪些?

  1. setter注入:通过set方法注入依赖的bean
  2. 构造器注入:通过构造方法注入依赖的bean
  3. 注解注入:通过@Autowired 或 @Resource 注入依赖的bean

8、Spring Bean同名的覆盖问题?

默认情况:同一个配置文件中出现id相同的bean会报错,不同的配置文件出现id相同的bean,后加载的bean会将先加载的bean覆盖掉,称为bean的覆盖。
bean的覆盖不会报错,但可能影响我们的项目,可以通过属性设置不允许bean的覆盖,将allowBeanDefinitionOverriding设置为false。

9、Spring中常用的注解有哪些?

9.1、Spring 的常见注解有哪些?

注解说明
@Component、@Controller、@Service、@Repository

使用在类上用于实例化Bean
@Autowired自动装配注解,默认按类型装配
@Qualifier结合@Autowired一起使用用于根据名称进行依赖注入
@Scope标注Bean的作用范围
@Configuration指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan组件扫描,会扫描指定包下的@Component、@Controller、@Service、@Repository
@Bean使用在方法上,标注将该方法的返回值存储到Spring容器中
@Import使用@Import导入的类会被Spring加载到IOC容器中
@Aspect、@Before、@After、@Around、@Pointcut

用于切面编程(AOP)
@Value将配置文件中的值注入到指定的字段

9.2、SpringMVC常见的注解有哪些?

注解说明
@RequestMapping用于映射请求路径,可以定义在类上和方法上。用于类上,则表示类中的所有的方法都是以该地址作为父路径
@RequestBody注解实现接收http请求的json数据,将json转换为java对象
@RequestParam指定请求参数的名称
@PathVariable从请求路径下中获取请求参数(/user/{id}),传递给方法的形式参数
@ResponseBody注解实现将controller方法返回对象转化为json对象响应给客户端
@RequestHeader获取指定的请求头数据
@RestController@Controller + @ResponseBody
@ControllerAdvicecontroller的增强注解,用于统一处理功能:如统一异常处理
@ExceptionHandler异常处理
@CookieValue指定cookie中的参数

9.3、Springboot常见注解有哪些?

注解说明
@SpringBootConfiguration组合了- @Configuration注解,实现配置文件的功能
@EnableAutoConfiguration打开自动配置的功能,也可以关闭某个自动配置的选
@ComponentScanSpring组件扫描

10、@Autowired 和 @Resource 有什么区别?

  1. @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
  2. @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:

image.png

  1. @Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

11、什么是AOP,你们项目中有没有使用到AOP?

AOP,面向切面编程,通俗的说,就是通过AOP可以将应用程序的一些共同关注点(例如日志记录、性能监控、事务管理等)从业务逻辑中抽离出来,成为公共模块复用,降低耦合。
例如我们项目中的行为日志模块,需要记录管理员对每个模块的CRUD操作,如果实现这个功能需要在每个controller方法中都进行日志记录,代码冗余不易维护。通过AOP可以在一个单独的切面中完成这个功能。

8.1、AOP中有哪些核心概念

image.png

8.2、项目中使用AOP的场景

以用户行为日志举例AOP的使用
日志效果
image.png
定义日志注解,在需要记录日志的方法中加上注解
image.png
image.png
引入aop依赖
image.png
定义切面及切入点
image.png
定义环绕通知,记录日志内容
image.png
ThreadLocal获取当前登录用户信息
将日志行为信息保存数据库

例: 基于AOP实现读写分离数据源动态处理
配置读写两个数据源
image.png
使用AOP根据方法名,动态选择数据源
image.png

8.3、具体如何使用AOP

定义切面类,我们使用注解的方式 @Aspect @Component
定义切入点表达式,可以通过扫描包的方式,也可以通过指定注解的方式
定义通知,通知分为5种,前置,后置,异常,最终,环绕通知

8.4、AOP的工作原理

AOP的底层工作原理就是使用动态代理对目标业务进行增强,而动态代理主要有JDK和CGLIB两种动态代理实现。
JDK是自带的动态代理实现,要求被代理的类必须实现接口。
CGLIB是开源的动态代理实现,需要引入依赖,要求被代理的类必须可以被继承。

12、Filter、Interceptor和AOP的区别?

  1. AOP主要使用的是动态代理,过滤器使用的主要是函数回调,拦截器使用的是反射机制。

  2. 一个请求过来,先进行过滤器处理,看程序受否受理该请求。过滤器放过后,程序中的拦截器进行处理,处理完后进入被AOP动态代理重新编译过的主要业务类进行处理。

  3. Filter:和框架无关,可以控制最初的http请求,但是更细一点的类和方法控制不了。

    Interceptor:可以控制请求的控制器和方法,但控制不了请求方法里的参数(用于处理页面提交的请求响应并进行处理,例如做国际化,做主题更换,过滤等)<br />Aspect:可以自定义切入的点,有方法的参数,但是拿不到http请求,可以通过其他方式如RequestContextHolder获得。<br />都可以实现权限检查,日志记录。不同的是使用的范围不同,规范不同,深度不同。
    

13、Spring中的事务是如何实现的?

Spring支持编程式事务管理和声明式事务管理两种方式。

  1. 编程式事务控制:需使用TransactionTemplate来进行实现,对业务代码有侵入性,项目中很少使用
  2. 声明式事务管理:声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

13.1、Spring中事务失效的场景有哪些?

  1. 方法不是public修饰
  2. 方法用final修饰
  3. 没有被Spring管理
  4. 数据库引擎不支持事务(InnoDB支持事务)
  5. 开发者自己捕获了异常,又没有手动抛出
  6. 抛出检查异常,配置rollbackFor属性为Exception

13.2、Spring事务的传播行为有哪些?

  1. Propagation_REQUIRED:表示如果当前存在一个事务,则加入该事务,否则将新建一个事务;这个是Spring事务默认传播行为。
  2. Propagation_REQUIRED_NEW:表示不管是否存在事务,都创建一个新的事务,原来的事务挂起,新的事务执行完毕,继续执行老的事务。
  3. Propagation_SUPPORTS:表示如果当前存在事务,就加入该事务;如果当前没有事务,那就不使用事务。
  4. Propagation_NOT_SUPPORTED:表示不使用事务;如果当前存在事务,就把当前事务暂停,以非事务方式执行。
  5. Propagation_MANDATORY:表示必须在一个已有的事务中执行,如果当前没有事务,则抛出异常。
  6. Propagation_NEVER:表示以非事务方式执行,如果当前存在事务,则抛出异常。
  7. Propagation_NESTED:这个是嵌套事务,如果当前存在事务,则在嵌套事务内执行;如果当前不存在事务,则创建一个新的事务;嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚;可以理解为:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作。

14、SpringMVC的执行流程?

image.png
版本1:视图版本,jsp
①用户发送出请求到前端控制器DispatcherServlet
②DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
③HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
④DispatcherServlet调用HandlerAdapter(处理器适配器)
⑤HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
⑥Controller执行完成返回ModelAndView对象
⑦HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
⑧DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)
⑨ViewReslover解析后返回具体View(视图)
⑩DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
⑪DispatcherServlet响应用户
版本2:前后端开发,接口开发
①用户发送出请求到前端控制器DispatcherServlet
②DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
③HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
④DispatcherServlet调用HandlerAdapter(处理器适配器)
⑤HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
⑥方法上添加了@ResponseBody
⑦通过HttpMessageConverter来返回结果转换为JSON并响应

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值