1. 什么是IOC和DI?
IOC(控制反转):它把传统上由程序代码直接操控的对象的调用权交给spring容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
底层
- 工厂模式
-
- 通过工厂的指定方法获取对象。反射创建对象
-
-
- BeanFactory:Spring容器的顶层接口,默认容器中的Bean使用的是懒加载
- ApplicationContext:BeanFactory子接口,一般使用这个居多,支持的是即时加载
- AnnotationConfigApplicationContext:注解式的Spring容器
- ClassPathXmlApplicationContext:配置文件形式的Spring容器
-
- 被Spring管理的对象统一称为bean
DI依赖注入,可以根据配置的依赖关系,将依赖的属性通过反射注入到Spring所管理的bean中。注入的bean和被注入的bean必须在同一个容器中。
2. Spring Bean有哪些配置方式?
xml方式: 在xml配置文件中,通过标签 配置bean对象
注解方式: 通过@Configuration + @Bean 方式配置对象
通过@Component @Controller @Service @Repository 配置bean
通过 @ComponetScan("扫描包名") 管理bean
3. springbean的线程安全问题
在spring中,绝大部分bean都是无状态的,只会对Bean的成员变量进行查询操作。因此即使这些bean默认是单例的,也不会出现线程安全问题。(比如controller、service、dao这些类,这些类里面通常不会含有成员变量,因此它们被设计成单例的。如果这些类中定义了实例变量,就线程不安全了,所以尽量避免定义实例变量。)
对于有状态的bean,spring采用ThreadLocal进行处理,使它们成为线程安全可以共享的对象。
对于有状态的bean,也可以使用原型模式(prototype),每次使用时都会重新生成一个对象,解决了线程不安全的问题。
4. Spring Bean的作用域有哪些? 默认是哪种?
Spring框架支持以下五种bean的作用域:默认是singleton
singleton :bean在每个Spring ioc容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
web项目里还有:
request:每次http请求都会创建一个bean
session:在一个HTTP Session中,一个bean定义对应一个实例。
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。
5. SpringBean依赖注入(DI)的方式有哪些?
- Filer属性注入,注解注入
@Autowierd,@Rescource
优点:不被spring容器托管的对象无法自动注入
缺点:可能存在循环依赖问题,并且使用到那个bean的时候才会报错
被final修饰的属性无法使用
- 构造器注入,也是spring官方推荐的方式
优点:对象初始化完成后,就可以获得可使用的对象
检查循环依赖,如果存在循环依赖,spring在启动时就会报错
缺点:注入对象很多的情况下,构造器参数列表很长
使用lombok下的@RequiredArgsConstructor
- setter方法注入
优点:灵活控制,可以选择性的注入
可以检查循环依赖
6. Spring中常用的注解有哪些?
@ComponentScan 组件扫描, 会扫描指定包下的 @Component @Controller @Service @Repository
@Service 标注当前类为容器的对象,代表服务组件
@Repository 标注当前类为容器的对象,代表持久组件
@Component 标注当前类为容器的对象,代表组件
@Controller 标注当前类为容器的对象,代表控制组件
@RestController:@Controller + @ResponseBody
@RequestMapping:@GetMapping,@PostMapping,@PutMapping,@DeleteMapping
@RequestParam,@RequestBody,@PathVariable
@ExceptionHandler:异常处理器中处理异常的注解
@Autowired 自动装配注解 默认按类型装配
@Qualifier:指定装配,消除歧义
@Transactional 事务注解
@Value 将配置文件中的值,注入到指定字段
@Bean 配置bean对象 标签作用一致
@Configuration 标注当前的java类是一个配置类
@Transactional: 用于声明事务,通常用在Service层的方法
@CrossOrigin:解决跨域问题
7. Autowired和Resource注解有什么区别?
1、 @Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。
2、 @Autowired是spring提供的注解,默认按类型匹配,有个required属性,默认值是true,表示强制要求bean实例的注入,应用启动的时候,如果不存在对应类型的Bean就会报错。如果不希望自动注入,可以设置它的required属性为false。其次,如果在SpringIoc容器中存在多个相同类型的Bean,启动也会报错。可以搭配@Primary和@Qualifier注解进行使用。@Primary表示主要的Bean,优先使用。@Qualifier是根据名字筛选。
3、@Resource(这个注解来自JSR-250),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
8. 是否了解容器初始化流程
- 创建配置
基于xml 或 基于注解 配置Spring Bean
- 配置解析
读取xml内容,并得到一个Document对象
解析Document对象,遍历bean标签的节点
将每一个Bean标签封装成一个BeanDefinition对象
- 注册bean定义到容器中
将bean的定义对象 存储到容器中的map集合中
map集合: 在容器的核心实现类 DefaultListableBeanFactory中, ConcurrentHashMap
以 bean标签中配置的id作为key
以 beanDefinition对象作为value存储到map集合中
4.初始化所有单例对象
完成注册后,查看所有注册bean定义
如果是非抽象 、 并且不是懒加载的单例对象会被立刻创建
创建出来的单例对象会存储到singletonObjects map集合中 也是一个ConcurrentHashMap
9. 是否了解容器getBean流程
IOc容器getBean流程
1.判断单例map集合中是否已经创建该对象有返回没有下一流程
2.判断是否存在父容器,且父容器是否包含该对象包含:调用父容器的getBean没有下一流程
3.准备尝试创建对象,beanDefinitionMap中获取bean定义没有抛出NoSuchBeanDefinition有下一流程
4.判断bean定义的scope属性单例调用createBean创建对象加入单例map集合多例调用createBean创建对象其它5.createBean创建对象获取要创建bean的class判断是否使用工厂方法创建对象调用工厂方法创建对象运行到这里说明是使用构造器创建根据构造器参数的个数、类型确定到底使用哪个构造器创建使用JDK反射方法创建对象
6.依赖注入调用populateBean方法进行依赖注入
10. 是否了解SpringBean的循环依赖问题(三级缓存)
循环依赖发生在两个或两个以上的bean互相持有对方,形成闭环。Spring框架允许循环依赖存在,并通过三级缓存解决大部分循环依赖问题:
1.一级缓存:缓存已完成初始化的bean对象。
2.二级缓存:缓存未初始化bean对象。
3.三级缓存:缓存 objectFactory,用于解决代理对象问题。
- 你的项目中有没有循环依赖问题?怎么解决?
- 实际开发中,尽量手动去避免循环依赖的产生
- 如 AService 需要调用 BService,而 BService 需要调用 AService
- 解决方案一
-
- AService 注入 BService
- BService 注入 AMapper
- 解决方案二
-
- AService 注入 BService
- 把BService要调用AService的方法提取到一个新的Service,如 CService
-
-
- BService 注入 CService
-
11. 是否了解Spring同名Bean的覆盖问题
默认情况:同一个配置文件中出现id相同的bean会报错,不同的配置文件出现id相同的bean后加载的bean会将先加载的bean覆盖掉称为bean的覆盖
bean的覆盖不会报错,但可能影响我们的项目,可以通过属性设置不允许bean的覆盖allowBeanDefinitionOverriding设置为false
12. 是否了解SpringBean的生命周期
Spring中bean的生命周期包括以下步骤:
1.通过BeanDefinition获取bean的定义信息。
2.调用构造函数实例化bean。
3.进行bean的依赖注入,例如通过setter方法或@Autowired注解。
4.处理实现了Aware 接口的bean。
5.执行BeanPostProcessor的前置处理器。
6.调用初始化方法,如实现了InitializingBean接口或自定义的init-method。
7.执行BeanPostProcessor的后置处理器,可能在这里产生代理对象。
8.最后是销毁bean。
13. spring如何管理事务
Spring实现事务的本质是利用AOP完成的。它对方法前后进行拦截,在执行方法前开启事务,在执行完目标方法后根据执行情况提交或回滚事务。通过在类或方法上加@Transactiong注解来管理事务
14. spring中事务失效的场景有哪些
- 访问权限问题
访问权限不是public
- 方法用final修饰
某个方法被final/static修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能
- 方法内部调用
同一类中的方法直接调用,会导致事务失效
解决方法:
- 新加一个Service方法
- 在该Service中注入自己
- 通过AopContent类
- 未被spring管理
使用Spring事务,是有一个前置条件,那就是对象需要交给Spring进行管理,需要创建bean实例。
通常情况下,我们通过@Contrlller``@Service``@Component``@Repository等注解,实现将bean实例化喝依赖注入的功能。
- 多线程调用
同一个事务,其实指同一个数据库连接,只有拥有同一个数据库连接才能同事提交和回滚。如果在不同的线程中,拿到的数据库连接肯定是不一样的。所以事务也是不同的
- 表不支持事务
表的引擎不支持事务。InnoDB支持
Spring中事务未生效的场景之事务未回滚
- 错误的传播特性
只有三种事务传播特性才会新建事务REQUIRED``REQUIRED_NEW``NESTED
- 自己吞了异常
开发者自己捕获了异常,同时没有手动抛出。想要Spring能够正常回滚,则必须要抛出它能够处理的异常,如果没有抛出异常,Spring则会认为程序是正常的。
- 手动抛了别的异常
人员自己捕获了异常,又手动抛出了异常:Exception,事务同样不会回滚。因为Spring事务,默认情况下只会回滚RunTimeException,和Error(错误),对于普通的Exception(非运行时异常),它是不会回滚的。
- 自定义了回滚异常
rollbackFor默认值为UncheckedException,包括了RuntimeException和Error. 当我们直接使用@Transactional不指定rollbackFor时,Exception及其子类都不会触发回滚。
所以,建议一般情况下,将该参数设置成:Exception或Throwable。
- 嵌套事务回滚多了
使用了嵌套的内部事务,原本是希望调用 roleService.doOtherThing 方法时,如果出现了异常,只回滚 doOtherThing 方法里的内容,不回滚 userMapper.insertUser 里的内容,即回滚保存点。但事实是,insertUser 也回滚了。
因为 doOtherThing 方法出现了异常,没有手动捕获,会继续往上抛,到外层 add 方法的代理方法中捕获了异常。所以,这种情况是直接回滚了整个事务,不只回滚单个保存点。
15. Spring事务的传播行为有哪些
16. 什么是aop
AOP,即面向切面编程,在Spring中用于将那些与业务无关但对多个对象产生影响的公共行为和逻辑抽取出来,实现公共模块复用,降低耦合。常见的应用场景包括公共日志保存和事务处理。
通俗地说,就是通过AOP可以将应用程序的一些共同关注点(例如日志记录、性能监控、事务管理等)从业务逻辑中抽离出来,以便于重复使用。
例如我们项目中的行为日志模块,需要记录管理员对每个模块的CRUD操作,如果实现这个功能需要在每个controller方法中都进行日志记录,代码冗余不易维护。 通过AOP可以在一个单独的切面中完成这个功能
17. SpringMVC执行流程
前后端不分离
(1)用户发送请求至前端控制器DispatcherServlet;
(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
(4)DispatcherServlet 调用 HandlerAdapter处理器适配器;
(5)HandlerAdapter 经过适配调用具体处理器(Handler,也叫后端控制器);
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回具体View;
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户。
前后端分离项目
前面一样
(5)HandlerAdapter 经过适配调用具体处理器(Handler,也叫后端控制器);
(6)方法上加了@RequestBody
(7)通过HttpMessageConverter将返回结果转换为json并响应