目录

请谈一谈Spring中自动装配的方式有哪些?

依赖注入有哪几种方式?以及这些方法如何使用?

请问Spring中Bean的作用域有哪些?

什么是Spring IOC?Spring IOC的原理是什么?如果你要实现IOC要怎么做?

Spring中BeanFactory和ApplicationContext的区别是什么?

@Autowired和@Resource之间的区别

谈谈Spring Bean的生命周期?

谈一下Spring AOP?

Spring中实现AOP的几种方式?

Spring中循环依赖如何解决?说一下解决的原理


请谈一谈Spring中自动装配的方式有哪些?

自动装配即spring容器来帮我们进行bean的匹配和依赖注入(装配包含类型匹配和依赖注入,这是按实际功能进行的划分,我觉得这样更能理解两者关系),总的来说有两种方式:

  • 按名称装配:在容器中查找与依赖属性名字相同的bean进行注入。
  • 按类型装配:在容器中查找与依赖属性类型相同的bean进行注入。

在具体的实现上,可以通过Xml配置文件或注解来完成。

1. 如果通过xml配置文件实现,需要在<bean/>标签中设置autowire属性,有四个可选项:no、byName、byType、constructor。

  • no:不进行自动装配,必须手动设置ref属性指定注入对象。
  • byName:按名称装配。
  • byType:按类型装配。
  • constructor:构造器注入,本质上还是按类型装配,容器会根据构造器参数的类型来匹配,如果与依赖属性的类型相同且容器中有这个bean,就在对象实例化的时候通过这个构造器注入。

2. 如果通过注解来实现,可以选择@Autowired或@Resource注解,与xml中的byName和byType选项是一个道理。

  • @Autowired:按类型装配,可以放在属性、setter方法或构造器上。
  • @Resource:按名称装配,可以放在属性和setter方法上。

说了这么多,总结起来就是:自动装配可以分为按名称装配和按类型装配。具体实现可以通过配置xml文件或者使用注解来实现。

依赖注入有哪几种方式?以及这些方法如何使用?

  1. setter注入。通过类属性对应的setter方法来注入。
  2. constructor注入。通过构造器注入,构造器的参数类型必须与属性类型一致。

Xml配置文件:

Java Bean:

setter注入发生在bean实例化之后,constructor注入发生在bean实例化之前。上面Info是通过setter方法注入的,HelloSpring是通过constructor方法注入的。

请问Spring中Bean的作用域有哪些?

在spring5.x中,作用域有:

  • singleton:bean在容器中是单例存在的。
  • prototype:每次从spring容器中获取bean时都会创建一个新的实例。
  • request:每次http请求都会创建一个新的实例。仅在web环境下有效。
  • session:在每个http session的生命周期里,会创建一个新的实例。
  • application:在整个web生命周期里仅创建一个新的实例。
  • websocket:在websocket生命周期里仅创建一个新的实例。

注意:request、session、application和websocket作用域只有在web环境中才有效(使用web ApplicationContext,如XmlWebApplicationContext)。如果在非web环境下(如使用ClassPathXmlApplicationContext)使用这些作用域,会抛出IllegalStateException。

什么是Spring IOC?Spring IOC的原理是什么?如果你要实现IOC要怎么做?

1. 什么是Spring IOC?

IOC是Inversion of Control的缩写,意思是控制反转。传统上,对象是由程序员编写程序代码直接操控的,控制反转就是反过来把这些对象的操控权交给容器,通过容器来实现对象组件的装配和管理,由容器来创建对象并管理对象之间的依赖关系。Spring IOC就是由spring来负责控制对象的生命周期和对象间的关系。

2. Spring IOC的原理是什么?

IOC主要是获得依赖对象的过程被反转了,就是获得依赖对象的过程由自身管理变为了由IOC容器主动注入,所以Spring IOC中最主要的就是依赖注入,Spring IOC的原理可以引申为DI的原理。

比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

1. 通过xml配置文件进行属性注入:就是通过反射拿到待注入属性的Field,调用Field.set()来实现注入。

2. 通过注解来实现注入:判断注解是放到属性上还是方法上,然后通过反射进行注入。

举个简单的例子,我们找女朋友常见的情况是,我们到处去看哪里有长得漂亮身材又好的女孩子,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。(比如类A要使用类B,类B要使用类C,如果你在对象A创建之后就能使用完整功能,必须在实例化对象A之前将创建好的对象B注入到A中,同理也必须在实例化对象B之前将创建好的对象C注入到B中......这里只是一个类依赖另一个类的情况,如果A不仅仅依赖于B,还依赖于D、E、F呢?B不仅仅依赖于C,还依赖于G、H、I呢?那岂不是非常复杂了?所以容器帮我们解决了这个问题

Spring中BeanFactory和ApplicationContext的区别是什么?

BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。

BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。

ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

  • 继承MessageSource,因此支持国际化。

  • 能自动注册BeanPostProcessor和BeanFactoryPostProcessor。

  • 统一的资源文件访问方式。

  • 提供在监听器中注册bean的事件。

  • 同时加载多个配置文件。

  • 提供了更加完整的生命周期管理。

下面是Spring 5.2.7官方文档的说明:

@Autowired和@Resource之间的区别

@Autowired可用于:构造函数、成员变量、Setter方法;@Resource可以用于类、成员变量、setter方法。

@Autowired和@Resource之间的区别

  • @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

  • @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

谈谈Spring Bean的生命周期?

Spring Bean的生命周期可以分为五个阶段:

  1. 加载
  2. 实例化(Instantiation)
  3. 属性赋值(Polupate)
  4. 初始化(Initialization)
  5. 销毁(Destruction)

加载阶段,即使当Spring容器被创建的时候,会根据xml配置文件或Spring配置类来获取配置的bean信息,并将这些bean解析成BeanDefinition,存入到一个map中,key是beanName,value就是BeanDefinition。

实例化阶段,就是通过构造器或者工厂方法(xml中factory-bean指定工厂类,factory-method指定前面工厂类中的工厂方法,通过工厂方法来创建该bean对象)来实例化所有单例且非懒加载的bean(非单例的bean会在使用时实例化如getBean())。在实例化操作的前后会遍历所有的BeanDefinition,检查其是否实现了InstantiationAwareBeanPostProcessor接口,是则执行这个接口的前置(postProcessBeforeInitialization)和后置(postProcessAfterInitialization)方法。

属性赋值阶段,会根据名称或者类型使用反射进行依赖注入。

初始化阶段主要是执行自定义的初始化方法(如在xml配置文件中设置的init-method属性),如果bean实现了InitializingBean接口,还会调用bean的afterPropertiesSet()方法,实现InitializingBean接口的目的是为了对“bean的属性设置完成”这个事件做出反应。在初始化阶段前后,会遍历所有的BeanPostProcessor,并执行前置(postProcessBeforeInitialization)和后置(postProcessAfterInitialization)方法。同时在初始化之前也会执行所有的aware对象的相关方法,有一些aware(如BeanNameAware)是直接执行,有一些(如ApplicationContextAware)是通过ApplicationContextAwareProcessor接口的before方法执行,这个接口也是BeanPostProcessor的子接口,它会检查当前bean是否是Aware接口的实现类,是的话就执行Aware接口的特有方法。

请别再问Spring Bean的生命周期了! - 简书 (jianshu.com)

Spring源码系列 — Bean生命周期 - 怀瑾握瑜XI - 博客园 (cnblogs.com)

Spring AOP也是在初始化完成后,通过BeanPostProcessor的后置方法运行的。方法内容为,通过切点(如execution(* *.find*(..))
)对bean中的方法进行匹配,如果匹配成功就为这个bean生成代理对象并放入容器中来,之后从容器中获取的都是代理对象。

Spring AOP 源码分析 - 筛选合适的通知器 | 田小波的技术博客 (tianxiaobo.com)

class ApplicationContextAwareProcessor implements BeanPostProcessor {  ......@Overridepublic Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {AccessControlContext acc = null;if (System.getSecurityManager() != null &&(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {acc = this.applicationContext.getBeanFactory().getAccessControlContext();}if (acc != null) {AccessController.doPrivileged(new PrivilegedAction<Object>() {@Overridepublic Object run() {invokeAwareInterfaces(bean);return null;}}, acc);}else {invokeAwareInterfaces(bean);}return bean;}private void invokeAwareInterfaces(Object bean) {if (bean instanceof Aware) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);}if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}}}......}

销毁阶段,调用DisposableBean接口的destroy方法,若容器关闭,则调用所有bean的destroy方法。如果在xml中自定义了销毁方法(通过destroy-method属性),则调用自定义的方法。

谈一下Spring AOP?

AOP(Aspect-Oriented Programming),一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。AOP相关概念可以参考:Spring AOP 源码分析系列文章导读。

如何接入

实现元素

TODO(连接点,切点,切面,通知)

生成代理类

Spring启动的时候根据Spring AOP的相关配置(注解或xml配置)生成通知器类(Advisor,可以获取通知),然后通过代理BeanPostProcessor,在bean初始化结束后为每个bean查找合适的通知器,找到了就为bean生成代理对象并放入容器中,之后从容器中获取的就是代理对象。

public interface Advisor {Advice getAdvice();...
}

AOP中生成代理类总共有两种方式:JDKProxy和CGLIB。若目标bean实现了接口,就使用JDKProxy,否则使用CGLIB。也可以通过配置制定开启CGLIB代理。如设置下面属性为true。

<aop:aspectj-autoproxy proxy-target-class="true"/>

执行代理方法

AOP生成的代理类,业务增强的逻辑是放到拦截器中的(把before、after等通知封装成拦截器),在执行代理类的方法时先获取这个方法匹配的通知器,然后将通知器中的通知转化成相应的拦截器,最终获得一个ArrayList类型的拦截器列表。(先会去缓存中获取方法的拦截器列表,拿不到再自己组装

之后按顺序调用拦截器的invoke方法。调用的逻辑是,对于前置通知拦截器,invoke方法是先执行通知逻辑,然后调用下一个拦截器的invoke方法;对于后置通知拦截器,invoke方法先调用下一个拦截器的invoke方法,然后再执行通知逻辑,最后一个拦截器执行目标方法。这就使得目标方法是在前置通知和后置通知之间执行(看下面代码和图片)。

// 前置通知拦截器
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {......@Overridepublic Object invoke(MethodInvocation mi) throws Throwable {this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );return mi.proceed();}......
}
// ReflectiveMethodInvocation.proceed()@Overridepublic Object proceed() throws Throwable {//   如果是最后一个拦截器,执行目标方法if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}// 获取下一个拦截器,索引++Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);......// 执行下一个拦截器的invoke()interceptorOrInterceptionAdvice.invoke(this)......}

Spring中实现AOP的几种方式?

Spring AOP的实现发生在bean初始化之后,通过BeanPostProcessor接口类来生成代理类。因此在ApplicationContext构造完成之后,代理类已经被生成,后面从IOC容器获取的都是代理类。

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。当类实现了接口,就是用JDKProxy;否则使用cglib

  • JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。

  • 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

具体可以参见:【Java基础】代理模式、静态代理、动态代理、JDK与CGLIB代理区别、在sp
ring AOP中的应用_奋进的小白粥-CSDN博客。

Spring中循环依赖如何解决?说一下解决的原理

将依赖注入的方式改成setter注入即可。

Spring中出现循环依赖问题的场景最常见于使用构造器进行依赖注入的情况,因为使用构造器进行依赖注入时,要求被依赖属性的实例化发生在当前类被实例化之前。假设A和B相互依赖且都是用构造器进行注入,如果要实例化A,必须先创建B,保证容器中有B这个对象,A才能实例化成功;而实例化B的时候,又必须保证A先被创建,所以就会陷入一个死循环,A和B都不能被实例化。而采用setter注入时,依赖注入发生在bean实例化之后,在A和B被创建出来之后再去进行依赖注入,这样就能注入成功。

推荐参考文章:Spring-bean的循环依赖以及解决方式_惜暮-CSDN博客_spring循环依赖

Spring事务传播机制?

带你读懂Spring 事务——事务的传播机制 - 知乎

自己总结的一些spring面试题相关推荐

  1. osgi框架和spring区别_最新100道大厂高频spring面试题附答案

    简介 这里是由多位互联网大厂架构师面试整理的出现频率最高的spring相关面试题,并为大家整理了完整的答案,赶紧收藏起来吧! 100道spring高频面试题 Spring概览 1.Spring是什么? ...

  2. 这些Spring面试题,你都会了吗?

    转载自   这些Spring面试题,你都会了吗? 问题一: 什么是Spring? Spring是Enterprise Java的开源开发框架.Spring Framework的核心功能可用于开发任何J ...

  3. Spring面试题(第一期)

    转载自 Spring面试题(第一期) Spring作为现在最流行的java web开发框架,Spring的应用及其原理的深入了解是每个Java开发程序员必经之路,下面10道面试题是Java开发程序员面 ...

  4. Spring面试题(70道,史上最全)

    转载自 Spring面试题(70道,史上最全) 1.什么是spring? 2.使用Spring框架的好处是什么? 3.Spring由哪些模块组成? 4.核心容器(应用上下文)模块. 5.BeanFac ...

  5. [转载] Spring面试题整理

    参考链接: Java中的动态方法Dispatch和运行时多态 Spring面试题整理 2018年03月07日 21:11:46 hrbeuwhw 阅读数:49116 Spring 概述 1. 什么是s ...

  6. 速度收藏!史上最全Spring 面试题 92 问!【附答案】高清PDF下载

    吐血整理了Spring面试题一共92题,并且全部归档整理成了一个PDF版本,获取方式在底部! String面试题(92题) 1.不同版本的 Spring Framework 有哪些主要功能? Vers ...

  7. 阿里面试必备:100个高频Spring面试题,助你一臂之力!

    100个高频Spring面试题,让面试也能聊出花! 1. Spring是什么? 2.Spring框架的好处? 3.Spring有哪些模块? 4.解释Core Container(Application ...

  8. 【Spring】Spring面试题

    Spring面试题 1.什么是Spring? 2.你们项目中为什么使用Spring框架? 3.@Resource和@Autowired的区别? 4.依赖注入的方式有几种? 5.说一下你对Spring的 ...

  9. Spring面试题及答案(2021年Spring面试题大全带答案)

    最近梳理2021最新 Spring 面试题 全家桶[附答案解析],包含了 Java基础.Spring.SpringMVC.Redis.SpringCloud.设计模式.等多个类型. 今天这篇是关于 S ...

  10. 10.Spring面试题

    Spring面试题 Spring面试题 1.不同版本的Spring Framework有哪些主要功能? 2.什么是Spring Framework? 3.列举Spring Framework的优点. ...

最新文章

  1. 异步请求之XMLHttpRequest篇
  2. 简单综合部署nagios环境
  3. 中国队蝉联国际奥数冠军,6名选手获5金1银,3人保送北大、3人保送清华
  4. FPGA之道(24)VHDL数据类型
  5. CSS 解决td里面内容太多把表格弄变形的原因,设置 自动换行。
  6. iOS 中KVC、KVO、NSNotification、delegate 总结及区别
  7. Red Hat Enterprise Linux Server release 5.6 安装 MongoDB 2.6.4
  8. 各种实用的 PHP 开源库推荐
  9. 算法竞赛入门经典|习题3-8, 循环小数(UVa202)
  10. jdbctemplate 批量操作
  11. 哪个类型的B端产品经理有前景?
  12. OJ(Online Judge)系统汇总
  13. Backbone入门教程
  14. php 防止造假ip攻击,php防止网站被攻击的应急代码
  15. 《学习笔记》Maven
  16. 【并行计算-CUDA开发】 NVIDIA Jetson TX1
  17. 【移入移出事件练习】【菜单】【选项卡】 -------this使用
  18. php i方法和get的区别,浅析PHP中的i++与++i的区别及效率
  19. 平板电脑(台电x80HD)安装Ubuntu18.04教程- Z3735系列CPU通用
  20. 小米8se怎么解屏幕锁_黔隆科技刷机教程小米5SPLUS忘记密码刷机解锁降级救砖解屏幕锁账户锁教程...

热门文章

  1. 常见到的mark到底是什么意思
  2. codeblocks出现Encoding Changed The saved doucument contained characters which were illeal
  3. 社区分享丨雪花啤酒的JumpServer堡垒机使用体会
  4. 正在播放2020Me比较特别的我_电视剧播放指数榜单,《燕云台》有望成为爆款,只因这两点真不错...
  5. 手持gps坐标设置_学术 | 许其凤院士:GPS移动定位与移动网络定位精度的分析
  6. 智能语音语义时代,产品经理怎么让AI更聪明?(效果向)
  7. [LeetCode解题报告] LCP 48. 无限棋局
  8. 昼短苦夜长,何不秉烛游
  9. 和菜头-当你在网络上被黑时
  10. 【前端】制作个人博客第三天