1、什么是Spring框架
Spring官网:spring.io/
Spring是一个轻量级java开发框架,旨在提高开发人员的开发效率已及系统的维护性。我们通常说的Spring框架指的是SpringFramework.
目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。
它是很多模块的集合,使用这些模块可以很方便的协助进行开发,主要包括,核心容器,数据访问/集成,Web,AOP(面向切面)、工具,消息和测试模块
核心技术:依赖注入(DI),AOP,事件(events),资源,i18n,验证,数据绑定,类型转换,SpEl。
测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
数据访问:事务,DAO支持,JDBC,ORM,编组XML。
Web支持:SpringMVC和Spring WebFlux Web框架。
集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
语言:Kotlin,Groovy,动态语言。
2、Spring优点
spring是轻量的,相对于其他框架来说
spring属于低侵入式设计,代码的污染极低;
spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
spring提供了AOP技术,支持将一些通用任务,如安全,事务,日志,权限等进行集中式管理,从而提供更好的复用。
spring对于主流的应用框架提供了集成支持。
spring对事务支持的很好,只需要配置即可,无需手动控制。
缺点:依赖反射,影响性能。
2、Spring5新特性
spring5整个框架基于java8
支持http 2.0
支持Kotlin函数式编程
Spring WebFlux响应式编程
3、列举一些重要的Spring模块
Spring Core:基础,可以说Spring其他所有的功能都需要依赖于该类库。主要提供IOC依赖注入功能。
Spring Aspects:该模块为与AspectJ的集成提供支持。
Spring AOP:提供了面向切面的编程实现。
Spring JDBC:java数据库链接。
Spring JMS:java消息机制;
Spring ORM:用于支持Hibernate等ORM工具
Spring Web:为创建Web应用程序提供支持。
Spring Test:提供了对Junit和TestNG测试支持。
4、@RestController和@Controller
单独使用@Controller不加@ResponseBody的话一般使用在要返回一个试图的情况,这种情况属于比较传统的Spring MVC应用,对应于前后端不分离的情况。
@RestController=@Controller+@ResponseBody
5、谈谈自己对于Spring IOC和AOP的理解
IOC:(Inverse of Control)控制反转,是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是个Map(key,value),Map中存放的是各种对象。
将对象之间的相互依赖关系交个IOC容器来管理,并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要
配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
AOP:(Aspect-Oriented Programming)面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理,日志处理,权限控制)封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于
未来的可扩展性和可维护性。Spring AOP是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建对象,而没有实现接口的对象,就无法使用JDK Proxy去进行代理了,这时候Spring
AOP会使用Cglib,生成一个被代理对象的子类来作为代理。
JDK Proxy:定义接口,实现InvocationHandler接口,并且实现接口中的invoke方法。
Cglib Proxy:实现MethodInterceptor接口,实现intercept方法。
6、Spring AOP和AspectJ AOP有什么区别
Spring AOP属于运行时增强,而AspectJ是编译时增强。SpringAOP基于代理(Proxying),而AspectJ基于字节码操作(ByteCode Manipluation)。
Spring AOP已经集成了AspectJ,AspectJ应该算得上是java生态系统中最完整的AOP框架了。AspectJ相比于Spring AOP功能更强大,但是Spring AOP相对来说比较简单。
如果切面比较少,那么两者性能差异不大,但是如果切面比较多的话,最好选择AspectJ,它比Spring AOP快很多。
7、Spring中的bean的作用域有哪些
sigleton:唯一bean实例,Spring中的bean默认都是单例的。
prototype:每次请求都会创建一个新bean实例、
request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP Session内有效。
global-session:全局session作用域,仅仅在基于potlet的web应用中才有意义,Spring5已经没有了。
8、Spring中的Bean是线程安全的吗
对于单例Bean,所有线程都是共享一个单例实例Bean,因此是存在资源的竞争。
如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如SpringMVC的Controller,Service,Dao等,这些Bean大多数是无状态的,只关注于方法本身。
对于有状态的Bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全方法,比如RequestContextHolder,TransactionSynchronizationManager,LocaleContextHolder等。
容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性。因此是否线程安全完全取决于Bean本身的特性。
单例Bean存在线程安全问题,主要是多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。
(1)在Bean中尽量避免定义可变的成员变量。(不太现实)
(2)在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLoal中。
9、@Component和@Bean的区别是什么
(1)、作用对象不同,@Component注解作用于类,@Bean作用于方法。
(2)、@Component通常是通过类路径扫描自动侦测已经自动装配到Spring容器中(可以使用@ComponentScan注解定义要扫描的路径从中找出标识了需要装配的类自动装配到Spring的bean容器中)。@Bean通常是是我们在标有该注解的
方法中定义产生这个bean,@Bean告诉了Spring这是某个类的示例,当需要用它的时候还给我。
(3)@Bean比@Component注解的自定义新更强,而且很多地方只能通过@Bean注解来注册bean。比如当我们应用第三方库中的类需要装配到Spring容器时,则只能通过@Bean来实现。@Bean则常和@Configuration注解搭配使用。
(4)@Bean注解的方法返回值是对象,可以在方法中为对象设置属性。Spring的Starter机制,就是通过@Bean注解定义Bean。
10、@Autowired和@Resource去呗
(1)@Autowired与@Resource都可以装配Bean,都可以写在字段上或setter方法上。
(2)@Autowired默认按照类型装配(这个注解属于Spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置他的required属性为false。如果想要使用名称装配可以结合@Qualififer注解使用。
(3)@Resource默认按照名称进行装配(这个注解属于J2EE),名称可以通过name属性进行制定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找。
11、类声明为Spring的bean的注解
@Component:通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪个层,可以使用@Component注解标注。
@Respository:对应持久层即Dao层,主要用于数据库相关操作。
@Service:对应服务层,主要涉及一些复杂的逻辑,需要用到Dao层。
@Controller:对应Spring MVC控制层,主要用户接受用户请求并调用Service层返回数据给前端。
12、Spring中的Bean生命周期
Bean容器找到配置文件中Spring Bea 的定义。
Bean容器利用Java Reflection API 创建一个Bean的实例。
如果涉及到一些属性值利用set()方法设置一些属性值。
如果Bean实现了BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。
如果Bean实现了BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。
如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法
当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
13、SpringMVC工作原理
MVC是一种设计模式,SpringMVC是一款很优秀的MVC框架。SPringMVC可以帮助开发人员进行更简单的web层开发,与spring集成。
(1)客户端(浏览器)发送请求,直接请求道DispatcherServlet.
(2)DipatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。
(3)解析到对应的Handler(也就是我们常说的Controlller控制器)后,开始由HandlerAdapter适配器处理。
(4)HandlerAdapter会根据Hander来调用真正的处理器来处理请求,并处理相信的业务逻辑。
(5)处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View.
(6)ViewResolver会根据逻辑View查找实际的View/
(7)DispaterServlet把返回的Model传到View(试图渲染)。
(8)把View返回给请求者(浏览器)。
14、Spring管理事务的方式
编程式事务,在代码中硬编码(不推荐使用)
声明式事务,在配置文件中配置(推荐使用)。基于XML的声明式事务,基于注解的声明式事务。
Spring事务的本质其实就是对数据库事务的支持,没有数据的事务支持,spring是无法提供事务功能的。真正的数据库层的书屋提交和回滚是通过binlog或者redo log实现的。
15、Spring事务中的隔离级别有哪几种
TransactionDefinition.ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ(可重复读)隔离级别Oracle默认采用READ_COMMITED隔离级别。
TransactionDefinition.ISOLATION_READ_UNCOMMITED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读和不可重复读。
TransactionDefinition.ISOLATION_READ_COMMITED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读和不可重复读仍有可能发生。
TrasnactionDefinition.ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
TransactionDefinition.ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间之间就完全不可能产生干扰,也就是说,该级别可以防止脏读,不可重复读和幻读。影响性能,通常情况不使用。
16、Spring事务中哪几种事务传播行为
支持当前事务情况:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建新事务。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则已非事务的方式继续运行。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常(mandatory:强制性)。
不支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:已非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:已非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUEIRED。
17、事务几种实现方式
编程式事务管理对于基于POJO的应用来说是唯一选择。需要在代码中调用beginTransaction(),commit(),rollback()等事务管理相关的方法,
基于TransactionProxyFactoryBean的声明式事务管理
基于@Transactional的声明式事务管理
基于Aspectj AOP配置事务
(1)编程式事务
//1、注入事务管理器对象
@Autowired
private PlatformTransactionManager txManager;
//2、开启事务
TransactionStatus status = txManager.getTransaction(new DefaultTransactionDefinition());
//3、提交
txManager.commit(status);
4、回滚
txManager.rollback(status);
(2)TransactionProxyFactoryBean实现事务
配置文件:applicationContext.xml
(3)基于@Transactional的声明式事务管理
Transactional(rollackFor=Exception.class);
transactionManager:指定事务管理器,值为’bean‘名称,这个主要用于多事务管理器情况下指定。
isolation:事务隔离级别,默认是Isolation.DEFAULT。
propagation:事务的传播行为,默认值为Propagation.REQUIRED。
timeOut:事务的超时时间,单位为秒
readOnly:该属性用于设置当前事务是否为只读事务,设置为true表示只读,false表示可读写,默认值为false。
rollbackFor:用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。默认为RuntimeException和Error
noRollbackFor:抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。
(4)基于Aspctj AOP配置事务
18、Spring事务失效的原因
(1)数据库引擎不支持事务,如Myisam
(2)没有被Spring管理的对象,没有注解,没有被加载成Bean,那么类就不会被Spring 管理
(3)方法不是Public,@Transactional只能用于public方法上,否则事务会失效,如果要用在非public上,可以开启AspectJ代理模式
(4)数据源没有配置事务管理器
(5)不支持事务,Propagation.NOT_SUPPORTED:不支持事务,已非事务方式运行,如果当前存在事务,则把当前事务挂起。
(6)自身调用问题,同一类方法方法中,A没有,B有,调用A,a->b就会失效。
原因:Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(Proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,
但是同类中的方法互相调用,相当于this.B(),此时B方法并非是代理类调用,而是直接通过原有的Bean直接调用的,所以失效了。
解决办法:a.通过ApplicationContextAware注入的上下文获取代理对象。b.通过AopContext获得代理对象。c.通过@Autowired注解注入代理对象。d.将A,B方法拆分到另一个类中。
(7)异常被catch调了
(8)异常类型错误。
17、BeanFactory和ApplicationContext区别
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring容器。其中ApplicationContext是BeanFactory的子接口。
(1)BeanFactory是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载,实例化,控制bean的生命周期,维护Bean之间的依赖关系。ApplicationContext接口作为BeanFactory
的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能。继承MessageSource,因此支持国际化;统一的资源文件访问方式:提供在监听器中注册bean的事件;同时加载多个配置文件;
载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次。
(2)BeanFactory采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。如果Bean的某一个属性没有注入,BeanFactory加载后,直至第一次使用getBean
方法才会抛出异常。ApplicationContext它是在容器启动时,一次性创建了所有Bean。这样启动时就可以发现Spring中存在的配置错误。ApplicationContext启动后预载入所有的单实例Bean。不足之处是占用内存
空间,当程序配置较多Bean时,程序启动较慢。
(3)BeanFactory通常以编程的方式被创建,ApplicationContext还能已声明的方式创建,如使用ContextLoader
(4)BeanFactory和ApplicationContext都支持BeanPostProcessor,BeanFactoryPostProcessor的使用,但两者区别是:BeanFactory需要手动注册,而ApplicationContext自动注册。
18、在applictionContext.xml文件中定义了一个bean,通过ApplicationContext实例对象的getBean方法获取到这个bean,背后的实现原理
Spring容器启动的时候会解析applictionContext.xml,将xml中定义的bean(如authService)解析成Spring内部的BeanDefinition,并已BeanName(authService)为key,BeanDefinition(authService响应的BeanDefinition)
为value存储到DefaultListableBeanFactory中的beanDefinitionMap属性中(其实它就是一个ConcurrentHashMap类型的属性),同时将beanName存入到beanDefinitionNames中(List类型),然后遍历beanDefinitionNames中
的beanName,进行bean的实例化并填充属性,在实例化过程中,如果有依赖没有被实例化将先实例化其依赖,然后实例化本身,实例化完成后将实例化存入单例bean的缓存中,当调用getBean方法时,到单例bean的缓存中查找,
如果找到并经过转换后返回这个实例(如AuthService实例),之后就可以直接使用了。
19、说一下XML文件的解析
代码中指定要加载XML文件后,Spring容器初始化的过程中,通过ResourceLoader接口的实现类,例如ClassPathXMLApplicationContext,将XML文件路径转化成对应的Resource文件,例如ClassPathResource,然后通过DocumentLoader
对Resource文件进行转换,转换成Document文件,接着通过DefaultBeanDefinitionDocumentReader对Document进行解析,并使用BeanDefinitionParserDelagate对元素进行解析,解析XML中定义的各个元素,存入BeanDefinition中。
20、详细说一下BeanDefinition是什么
一个对象的生命周期想要被Spring容器管理,那么它的类信息必须先转成Spring内部的数据结构,BeanDefinition就是Spring框架内部用来描述对象的类型的数据结构。例如类名、scope、属性、构造函数参数列表、依赖的bean、
是否是单例类、是否是懒加载等,其实就是Bean的定义信息存储的对应的这个BeanDefinition相应的属性中,后面对bean的操作就是直接对BenaDefinition进行,例如拿到这个BeanDefinition后,可根据里面的类名、构造函数
构造函数参数、使用反射进行对象创建。BeanDefinition是一个接口,是一个抽象的定义,实际使用的是其实现类,如ChiladBeanDefinition,RootBeanDefinition,GenericBeanDefinition等。BeanDefinition继承了AttributeAccessor
说明了它具有处理属性的能力;BeanDefinition继承了BeanMetadataElement,说明它可以持有Bean元数据元素,作用是可以持有XML文件的一个Bean标签对应的Object。
21、DefaultListableBeanFactory它在Spring框架中的作用是什么
DefaultListableBeanFactory是整个Bean加载的核心部分,是Spring注册及加载Bean的默认实现。DefaultListableBeanFactory间接实现了BeanFactory接口,而在BeanFactory中定义了很多和bean操作相关的方法。例如getBean,containsBean
isSingleton等,所以DefaultListableBeanFactory也相应有了这些操作。
22、BeanFactory是什么
BeanFactory是用于访问Spring Bean容器的根接口,是一个单纯的Bean工厂,也就是常说的IOC容器的顶层定义,各种IOC容器都是在其基础上为了满足不同需求而扩展的,包括经常使用的ApplicationContext。
23、BeanFactory和FactoryBean
BeanFactory定义了IOC容器的最基本形式,并提供了IOC容器应该遵守的最基本的接口,也就是Spring IOC所遵守的最底层和最基本的编程规范,它只是个接口,并不是IOC的具体实现。它的职责包括:
实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
FactoryBean,一般情况下,Spring通过反射机制利用bean的class属性实例化Bean,然后在某些情况下,实例化Bean过程比较复杂,需要在bean的定义中提供大量的配置信息。Spring为此提供了FacotyBean的工厂类
接口,用户可以通过实现该接口定制实例化Bean逻辑。
BeanFactory接口:容器顶级接口,定义容器的一些基础行为。
FactoryBean接口:借助他自定义Bean,作用类似于Bean创建方式的静态方法和实例化方法。
24、如果想在初始化前修改bean属性,如何实现
自定义一个BeanFactoryPostProcessor,让它实现BeanFactoryPostProcessor接口,并实现postProcessBeanFactory方法,在这个方法中可以初始化前修改bean的属性。
25、BeanFactoryPostProcess如何自动调用
在Spring容器初始化的过程中会自动出发,具体代码在AbstractApplicationContext勒种会调用invokeBeanFactoryPostProcessors方法,在这个方法中筛选出所有实现BeanFactoryPostProcessor接口的类名称,然后
遍历调用这些实现类的postProcessBeanFactory方法。
26、Spring容器中,所有定义的bean都会被初始化吗?
不是,默认只初始化所有未初始化的非懒加载的单例Bean,scope为其他值的bean会在使用到的时候进行初始化,如prototype。
27、Spring中bean初始化源码逻辑
单例bean的初始化,通过反射进行实例对象的创建,在进行属性填充时,如果依赖的对象没有创建,则先创建依赖对象,最后将bean实例加入单例bean实例的缓存中。
28、bean实例化中,Spring如何解决循环依赖
Spring只对单例bean的循环依赖进行了解决,同时如果是通过构造函数注入造成的循环依赖,Spring也没办法解决,只是抛出BeanCurrentlyInCreationException异常。如果是通过setter方式注入产生的循环依赖,
Spring在创建bean对象时,通过提前暴露一个ObjectFactory用来返回一个创建中的bean对象,从而使其它bean能够引用到这个bean。
核心思想:使用缓存将Bean的首次创建和二次创建进行逻辑区分。
见图Process-Spring循环依赖
getBean(String)是AbstractBeanFactory的方法,它内部调用了doGetBean方法,doGetBean又调用了getSingleton。getSingleton是DefaultSingletonBeanFactory的重载方法。DefaultSingletonBeanFactory维护了三个Map
用于缓存不同状态的Bean。
/**维护着所有创建完成的Bean**/
/**一级缓存,用于存放完全初始化Bean**/
private final Map<String,Object> singletonObjects=new ConcurrentHashMap<String,Object>(256);
/**维护着所有半成品的Bean**/
/**二级缓存,存放原始的bean对象(尚未填充属性)**/
private final Map<String,Object> earlySingletonObjects=new HashMap<String,Object>(16);
/**维护着创建中Bean的ObjectFactory**/
/**三级缓存,存放bean工厂对象**/
private final Map<String,ObnectFactory<?>> sigletonFactories=new HashMap<String,Object>(16);
检测循环依赖过程
A创建过程中需要B属性(population),于是A将自己放到三级缓存里面(addSingletonFactory),去实例化B
B实例化的时候发现需要A,于是B先查一级缓存,二级缓存,三级缓存找到了。然后把三级缓存里面的A放到二级缓存里面,并删除三级缓存里面的A。B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)。
然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将自己放到一级缓存里面。
29、Spring框架中用到了哪些设计模式
工厂模式:Spring使用工厂模式通过BeanFactory,ApplicationContext创建Bean对象。
代理模式:Spring AOP功能
单例模式:Spring中Bean默认都是单例的
模板方法模式,Spring中的jdbcTemplate,hibernateTemplate等对数据库操作的类,就是用的模板模式。
包装器模式:项目需要连接多个数据库,这种模式让我们可以根据用户需求随时切换不同的数据源
观察者模式:Spring事件驱动模型就是观察者模式一个应用
适配器模式:Spring AOP的增强或通知使用到了适配器模式。