目录
- 1. 你认为Spring的核心是什么
- 2. 如何理解约定优于配置
- 3. spring boot starter有什么用
- 4. Spring用到了哪些设计模式
- 5. Springboot的启动流程
- 6. Spring Boot自动装配的过程
- 7. Spring Boot注解
- 8. Spring的核心是什么
- 9. 说一说Spring容器
- 10. 说一说你对Spring IoC的理解
- 11. 说一说你对Spring AOP的理解
- 12. AOP的使用场景
- 13. Spring AOP不能对哪些类进行增强
- 14. 为什么不全用CGLib还要有jdk动态代理
- 15. Spring如何管理事务
- 16. @Transactional注解的用法
- 17. @Transactional注解失效场景
1. 你认为Spring的核心是什么
- spring是一个开源框架,是一个生态和基石
- spring为了简化企业开发而生的,使得开发更加优雅和简洁
- spring是一个IOC和AOP容器框架,IOC是控制反转,就是本来自己创建,但是现在我交给spring容器让他帮我创建;AOP是面向切面编程;容器包含并管理应用对象的生命周期,好比桶装水,spring是桶而对象就是水
2. 如何理解约定优于配置
- 约定优于配置也称为按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性,具体如下
- 开发人员只需要规定应用中不符合约定的部分
- 在没有规定配置的地方,采用默认配置,以力求最简配置为核心思想
- 例如maven的目录结构即resource文件夹,src-main-java等;默认配置文件必须是application命名的yml文件或者properties文件;yml文件中的属性等
3. spring boot starter有什么用
- s p r i n g b o o t springboot springboot通过提供众多起步依赖 ( s t a r t e r ) (starter) (starter)降低项目依赖的复杂度,starter本质是一个Maven项目对象模型 ( P r o j e c t O b j e c t M o d e l , P O M ) (Project Object Model, POM) (ProjectObjectModel,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能,很多 s t a r t e r starter starter的命名都暗示了它们提供的功能
- 简单来说就是引入一些依赖和初始化配置,通过 s t a r t e r starter starter导入包
4. Spring用到了哪些设计模式
- 简单工厂,
BeanFactory
就是典型的工厂模式,通过传入一个唯一标识来获得Bean对象 - 工厂方法,实现了
FactoryBean
接口的Bean是一类叫做factory的bean,其特点是spring在使getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean的getObject()方法的返回值 - 单例模式,Spring依赖注入bean实例默认时单例的,Spring的依赖注入都是发生在AbstractBeanFactory的getBean里,getBean的doGetBean方法调用getSingleton完成bean的创建
- 代理模式,AOP底层就是动态代理模式的实现,即切面在应用运行的时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象创建动态地创建一个代理对象。SpringAOP就是以这种方式织入切面的
- 观察者模式,Spring的事件驱动模型使用的是观察者模式,Spring中观察者模式常用的地方是listener的实现
等等
5. Springboot的启动流程
- 首先创建项目之后会生成一个默认的
*Application
入口类,我们是通过该类的run
方法启动Spring Boot项目的,通过run
方法进行SpringApplication类的实例化操作,然后再针对实例化对象调用另外一个run方法完成整个项目的初始化和启动,如下图
源码分析
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();// 返回最精确的当前时间, 以毫微秒为单位
DefaultBootstrapContext bootstrapContext = createBootstrapContext();// 初始化启动上下文
ConfigurableApplicationContext context = null;
configureHeadlessProperty();// 设置不用显示器也能启动
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);// 启动监听
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 创建ApplicationArguments对象
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);// 加载属性配置
Banner printedBanner = printBanner(environment);// 打印banner图标
context = createApplicationContext();// 创建容器
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 准备容器
refreshContext(context);// 初始化容器
afterRefresh(context, applicationArguments);// 初始化之后执行
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);// 时长统计
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);// 打印启动日志
}
listeners.started(context, timeTakenToStartup);// 通知监听器, 容器完成启动
callRunners(context, applicationArguments);// 调用ApplicationRunner和CommandLineRunner的运行方法
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);// 异常处理
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);// 通知监听器容器正在运行
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
6. Spring Boot自动装配的过程
- 使用Spring Boot的时候,我们只需要引入相应的
s
t
a
r
t
e
r
s
starters
starters,Spring Boot启动时便会自动加载相关依赖,配置相关的初始化参数,以最快捷、简单的方式对第三方软件进行集成,这便是Spring Boot的自动配置功能,核心部分如下图
- 整个自动装配的过程是首先springboot通过@EnableAutoConfiguration注解开启自动配置,加载spring.factories中加载的各种AutoConfiguration类,当某个AutoConfiguration类满足其注解@Conditional指定的生效条件时,实例化该AutoConfiguration类中定义的Bean,并注入Spring容器,就可以完成依赖框架的自动化配置
7. Spring Boot注解
7.1. @SpringBootApplication注解
- 在Spring Boot入口类中,唯一的一个注解就是@SpringBootApplication,它是Spring Boot项目的核心注解,这里面主要有三个注解,@SpringBootConfiguration作用是标注这个类是一个配置类,它是@Configuration的派生注解,@EnableAutoConfiguration作用是启动Spring应用程序上下文时进行自动配置,它会尝试猜测项目可能需要的Bean,自动配置通常是基于项目classpath中引入的类和已定义的Bean来实现的。在此过程中,被自动配置的组件来自项目自身和项目依赖的jar包中,@ComponentScan定义用于扫描的路径中找出标识了需要装配的类自动装配到spring的bean容器中
7.2 @Import注解
- @EnableAutoConfiguration的关键功能是通过@Import注解导入的ImportSelector来完成的,从源码得知@Import(AutoConfigurationImportSelector.class)是@EnableAutoConfiguration注解的组成部分,也是自动配置功能的核心实现者
7.3 @Conditional注解
- @Conditional注解是Spring4.0版本引入的新特性,可根据是否满足指定的条件来决定是否进行Bean的实例化及装配,如设定当类路径下包含某个jar包的时候才会对注解的类进行实例化操作。总之就是根据一些特定条件来控制Bean实例化的行为
8. Spring的核心是什么
- Spring框架包含众多模块,如Core、Testing、Data Access、Web Servlet等,其中Core是整个Spring框架的核心模块,Core模块包含了IoC容器、AOP功能、数据绑定、类型转换等一系列的基础功能,而这些功能以及其他模块的功能都是建立在IoC和AOP之上的,所以IoC和AOP是Spring框架的核心
- IoC(Inversion of control)是控制反转的意思,这是一种面向对象编程的设计思想,在不采用这种思想的情况下,我们需要自己维护对象与对象之间的依赖关系,很容易造成对象之间耦合度过高,在一个大型的项目中十分的不利于代码的维护。IoC可以解决这种问题,它可以帮我们维护对象之间的依赖关系,降低对象之间的耦合度
- 那么IoC的实现方式是什么呢?答案是DI(Dependency Injection),也就是依赖注入,实现依赖注入的关键是IoC容器,它的本质是一个工厂
- AOP是面向切面编程思想,这种思想是对OOP的补充,它可以在OOP的基础上进一步提升编程的效率。简单说,他可以解决一些组件的共性需求如权限检查,记录日志,事务管理等,在AOP思想下,我们可以把解决共性需求的代码独立出来,然后通过配置的方式,声明这些代码在什么地方,什么时机调用,当满足调用条件时,AOP会将该业务代码织入到我们指定的位置,从而统一解决了问题,又不需要修改这一批组件的代码
9. 说一说Spring容器
- Spring主要提供了两种类型的容器, B e a n F a c t o r y BeanFactory BeanFactory和 A p p l i c a t i o n C o n t e x t ApplicationContext ApplicationContext
- B e a n F a c t o r y BeanFactory BeanFactory是基础类型的IoC容器,提供完成的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,而且功能要求不是很严格的场景, B e a n F a c t o r y BeanFactory BeanFactory是比较合适的IoC容器选择
- A p p l i c a t i o n C o n t e x t ApplicationContext ApplicationContext是在BeanFactory的基础上构建的,是相对比较高级的容器实现,除了拥有 B e a n F a c t o r y BeanFactory BeanFactory的所有支持, A p p l i c a t i o n C o n t e x t ApplicationContext ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等。 A p p l i c a t i o n C o n t e x t ApplicationContext ApplicationContext所管理的对象,在该容器启动之后,默认全部初始化并绑定完成。所以相对于 B e a n F a c t o r y BeanFactory BeanFactory来说, A p p l i c a t i o n C o n t e x t ApplicationContext ApplicationContext要求更多的系统资源,同时,因为在启动时就完成了所有初始化。容器启动时间较 B e a n F a c t o r y BeanFactory BeanFactory也会长一些。在那些系统资源充足,而且要求更多功能的场景中, A p p l i c a t i o n C o n t e x t ApplicationContext ApplicationContext类型的容器也是比较合适的选择
10. 说一说你对Spring IoC的理解
- IoC(Inversion of Control)是控制反转的意思,这是一种面向对象的编程思想,在不采用这种思想的情况下,我们需要自己维护对象与对象之间的依赖关系,很容易造成对象之间的耦合度过高,在一个大型项目中这十分的不利于代码的维护。IoC则可以解决问题,它可以帮我们维护对象与对象之间的依赖关系,降低对象之间的耦合度
- IoC实现的方式是 D I ( D e p e n d e n c y I n j e c t i o n ) DI(Dependency\ Injection) DI(Dependency Injection),也就是依赖注入,实现依赖注入的关键是IoC容器,它的本质就是一个工厂
- 在具体的实现中,主要有三种注入方式
- 构造方法注入,就是被注入对象可以在它的构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。然后,IoC Service Provider会检查被注入的对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用
- setter方法注入,通过setter方法,可以更改相应的对象属性。所以,当前对象只要为其依赖对象所相应的属性添加setter方法,
- 接口注入,被注入对象如果想要IoC Service Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。IoC Service Provider最终通过这些接口来了解,应该为被注入对象注入什么依赖对象。相对于前两种依赖注入方式,接口注入比较死板和繁琐
总体来说,构造方法注入和setter方法注入因为其侵入性较弱,且易于理解和使用,所以是现在是使用最多的注入方法。而接口注入因为侵入性较强,近年来已经不流行了
11. 说一说你对Spring AOP的理解
- AOP(Aspect Oriented Programming)是面向切面编程,它是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面,所谓切面,相当于应用对象间的横切点
- 切点(Pointcut):是一个基于正则表达式的表达式,通常一个pointcut会选取程序中的某些我们感兴趣的执行点,或者是程序执行点的集合
- 连接点(JointPoint):对应的是具体被拦截的对象,因为Spring只支持方法,所以被拦截的对象往往就是特定的方法,AOP通过动态代理技术把它织入到相应的流程中
- 通知(Advice):在选取出来的JointPoint上要执行的操作、逻辑,分为前置、后置、环绕、事后返回和异常通知,它会根据约定织入流程中
- 切面(Aspect):是一个可以定义切点、各类通知和引入的内容,SpringAOP将通过它的信息来增强Bean的功能或者将对应的方法织入流程
- 织入(Weaving):把切面应用到目标对象来创建新的advised对象的过程
- 目标对象(Target):即被代理对象
两种实现方式分别是JDK动态代理和CGLib动态代理
- JDK动态代理:是Java提供的动态代理技术,可以在运行的时候创建接口的运行实例。SpringAOP默认采用这种方式,在接口的代理实例中织入代码
- CGLib动态代理:采用底层的字节码技术,在运行时创建子类代理的实例。当目标对象不存在接口时,SpringAOP就会采用这种方式,在子类实例中织入代码
12. AOP的使用场景
- SpringAOP为IoC的使用提供了更多的便利。一方面,应用可以直接使用AOP的功能,设计应用的横切关注点,把跨越应用程序多个模块的功能抽象出来,并通过简单的AOP应用,灵活的编制到模块中,比如可以通过AOP实现应用程序中的日志功能。另一方面,在Spring内部,一些支持模块也是通过AOP来实现的,比如事务处理,从这两个角度已经可以看到SpringAOP的核心地位了
13. Spring AOP不能对哪些类进行增强
- Spring AOP只能对IoC容器中的Bean进行增强,对于不受容器管理的对象不能增强
- 由于CGLib采用动态创建的方式生成代理对象,所以不能对final修饰的类进行代理
14. 为什么不全用CGLib还要有jdk动态代理
- 性能方面,CGLib创建的代理对象比JDK动态代理创建的代理对象高得多。但是CGLib在创建代理对象所花费的时间比JDK多很多。所以,对于单例的对象无需频繁创建代理对象,采用CGLib动态代理比较合适。反之,对于多例的对象因为需要频繁的创建对象,则JDK动态代理更合适
15. Spring如何管理事务
- Spring支持两种事务模型分别是编程式事务和声明式事务,编程式事务主要是使用TransactionTemplate模板,我们可以将事务管理的范围控制的更为精确,声明式事务主要通过@Transactional注解说明事务特征来实现
16. @Transactional注解的用法
@Transactional
注解可以作用于接口、接口方法、类以及类方法上。当作用在类上时,该类的所有public方法都将具有该类型的事务属性,同时,我们也可以在方法级别上使用该标注来覆盖类级别的定义- Spring建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外
@Transactional
注解应该只被应用到public方法上,这是由Spring AOP的本质决定的。如果你在protected、private或者默认可见性的方法上使用@Transactional
注解,这将被忽略,也不会抛出任何异常 - 默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用
@Transactional
注解进行修饰
17. @Transactional注解失效场景
- 注解在非public方法上,因为在AOP代理的时候事务拦截器在目标方法执行前后进行拦截,CGLib的intercept方法或JDK的invoke方法(它们间接调用的一个方法)会去检查这修饰符是不是public,不是就不会获取
@Transactional
的属性配置信息,事务无效但不报错 @Transactional
注解属性propagation
设置错误,设置成了非事务方式@Transactional
注解属性rollbackFor
设置错误,需要设置自定义异常回滚,否则按默认未检查uncheck
异常,或者Error
才回滚事务,其他异常不会触发回滚事务- 在同一个类中方法调用,A和B在同一个类里面,A没有声明事务,而B声明了,如果A中调用了B,在外部调用方法A后,B的事务是不会起作用的。为什么?因为使用Spring AOP代理造成的,只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理
- 异常被catch,如果A的try方法中调用了B,但B中抛出了异常,被A的catch语句捕获,那么注解失效
- 数据库引擎不支持事务,使用了myIsam之类的