1.IOC和DI区别?

IOC: 控制反转,将类的对象的创建交给Spring类管理创建.
DI: 依赖注入,将类里面的属性在创建类的过程中给属性赋值.
DI和IOC的关系: DI不能单独存在,DI需要在IOC的基础上来完成.


2.Spring的循环依赖的解决?

spring进行扫描->反射后封装成beanDefinition对象->放入beanDefinitionMap->遍历map->验证(是否单例、是否延迟加载、是否抽象)->推断构造方法->准备开始进行实例->去单例池中查,没有->去二级缓存中找,没有提前暴露->生成一个objectFactory对象暴露到二级缓存中->属性注入,发现依赖Y->此时Y开始它的生命周期直到属性注入,发现依赖X->X又走一遍生命周期,当走到去二级缓存中找的时候找到了->往Y中注入X的objectFactory对象->完成循环依赖。
1、为什么要使用X的objectFacory对象而不是直接使用X对象?
利于拓展,程序员可以通过beanPostProcess接口操作objectFactory对象生成自己想要的对象
2、是不是只能支持单例(scope=singleton)而不支持原型(scope=prototype)?
是。因为单例是spring在启动时进行bean加载放入单例池中,在依赖的bean开始生命周期后,可以直接从二级缓存中取到它所依赖的bean的objectFactory对象从而结束循环依赖。而原型只有在用到时才会走生命周期流程,但是原型不存在一个已经实例化好的bean,所以会无限的创建->依赖->创建->依赖->…。
3、循环依赖是不是只支持非构造方法?
是。类似死锁问题


3.Spring的AOP的理解?

Spring AOP就是面向对象的一种补充
利用切面的技术,去匹配拥有相同的方法所有的类,对这些方法实现增强处理(不改变源码)


4.谈谈事务的传播行为?

事务传播行为.png

PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,这是最常见的选择,也是Spring默认的事务传播行为。(required需要,没有新建,有加入)

PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。(supports支持,有则加入,没有就不管了,非事务运行)

PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。(mandatory强制性,有则加入,没有异常)

PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。(requires_new需要新的,不管有没有,直接创建新事务)

PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。(not supported不支持事务,存在就挂起)
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。(never不支持事务,存在就异常)

PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。(nested存在就在嵌套的执行,没有就找是否存在外面的事务,有则加入,没有则新建)

PROPAGATION_NEVER
总是非事务地执行,如果存在一个活动事务,则抛出异常。


5.Spring的常用注解并解释作用?

@Controller(肯除lei儿)

   用法:标记于一个类上面
   作用:用来注解这个bean(类)是MVC模型中的一个控制层,使分发处理器识别到该类,该类会被spring的auto-scan扫到纳入管理。通俗来,被这个标注类里面的方法加上@RequestMaping(“…”),就可以直接被浏览器调用然后做一些数据逻辑处理。
   实例解释:如果要在浏览器访问Action类中的carRun方法(本地)127.0.0.1:8080/car/carRun

@Request(瑞快死)Maping

   用法:1.标记于一个被@Controller标注的类上
      2.标记于被@Controller标注类里面的方法上面
   作用:用法1,表示该被标注类下面所有方法的父类标注(可以理解为所有用法2“继承”用法1)@RequestMaping有六个属性value、method、consumes、produces、params、headers
   value:指定浏览器请求的地址;
   method:指定请求的method类型, GET、POST、PUT、DELETE等;
   consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
   produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
   params:指定request中必须包含某些参数值是,才让该方法处理;
   headers:指定request中必须包含某些指定的header值,才能让该方法处理请求;
   实例解释:如果要在浏览器访问Action类中的carRun方法,只需要本地域名+端口号+该标注所定义的value值即可(127.0.0.1:8080/car/carRun)  
   @RequestMaping常用的记住value和method,

@Service

   用法:用于标注业务层组件(Service层)上
   作用:标注于业务层组件上表示定义一个bean,自动根据所标注的组件名称实例化一个首字母为小写的bean。
   实例解释:IocCarService类被标注为一个bean,bean名称为iocCarService,此时该类已经被spring纳入管理中,待使用

@Resource(瑞sao斯)

   用法:标注于字段上或者setter方法上,@Resource默认按ByName进行自动装配
   作用:用来自动装配Bean,激活一个命名资源的依赖注入。@Resource属性name可以定义被自动装配Bean的名称
   实例解释:在IocCarService类中IIocCarDao 的实现类已经被@Repository标注为一个被Spring管理的Bean,此时需要用到它的时候只需要在属性名称上标注@Resource用户自动装配就可以了。

@Autowired(奥特为儿)

该注解和@Resource的用法和作用基本一致。

@Repository(瑞泡死特瑞)

   用法:用户标注数据访问层组件(Dao层)
   作用:实现Dao访问;将类识别为Bean,同时它将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。
   实例解释:Dao层实现类IIocCarDaoImpl 被Spring装载为bean纳入管理中,bean名称为iIocCarDaoImpl。 同时它将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。

@Component(抗抛嫩特)

用法:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
作用:和前面@Service、@Repository、@Controller一样,只是它们比@Component更巨细化。

6. Spring Bean 的生命周期

image.png
Bean 生命周期的整个执行过程描述如下:

(1) 根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。

(2) 利用依赖注入完成 Bean 中所有属性值的配置注入。

(3) 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。

(4) 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

(5) 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

(6) 如果 BeanPostProcessor(破色思) 和 Bean 关联,则 Spring 将调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。

(7) 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。初始化 bean的时候执行,可以针对某个具体的bean进行配置。afterPropertiesSet 必须实现 InitializingBean接口。实现 InitializingBean接口必须实现afterPropertiesSet方法。

(8) 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

(9) 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

(10) 如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为 scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

(11) 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
[

](https://blog.csdn.net/haohaoxuexiyai/article/details/109747666)

7.spring创建Bean的几种方式

1. 使用XML文件进行创建

  1. <bean id="xxxx" class="xxxx.xxxx"/>

2. 使用注解

用@Component,@Service,@Controller,@Repository注解
这几个注解都是同样的功能,被注解的类将会被Spring 容器创建单例对象。
@Component : 侧重于通用的Bean类
@Service:标识该类用于业务逻辑
@Controller:标识该类为Spring MVC的控制器类
@Repository: 标识该类是一个实体类,只有属性和Setter,Getter

  1. @Component
  2. public class User{
  3. }

当用于Spring Boot应用时,被注解的类必须在启动类的根路径或者子路径下,否则不会生效。
如果不在,可以使用@ComponentScan标注扫描的路径。
spring xml 也有相关的标签

  1. @ComponentScan(value={"com.microblog.blog","com.microblog.common"})
  2. public class MicroblogBlogApplication {
  3. public static void main(String args[]){
  4. SpringApplication.run(MicroblogBlogApplication.class,args);
  5. }
  6. }

3. 使用@Bean注解,这个用在SpringBoot中

@Configuration 标识这是一个Spring Boot 配置类,其将会扫描 该类中是否存在@Bean 注解的方法,比如如下代码,将会创建 User对象并放入容器中。
@ConditionalOnBean 用于判断存在某个Bean时才会创建User Bean.
这里创建的Bean名称默认为方法的名称user。也可以 @Bean(“xxxx”)定义。

  1. @Configuration
  2. public class UserConfiguration{
  3. @Bean
  4.     @ConditionalOnBean(Location.class)
  5. public User user(){
  6. return new User();
  7. }
  8. }

4. 使用注解@Import,也会创建对象并注入容器中

  1. @Import(User.class)
  2. public class MicroblogUserWebApplication {
  3. public static void main(String args[]) {
  4. SpringApplication.run(MicroblogUserWebApplication.class, args);
  5. }
  6. }

5.使用ImportSelector或者ImportBeanDefinitionRegistrar接口,配合@Import实现。

在使用一些Spring Boot第三方组件时,经常会看到@EnableXXX来使能相关的服务,这里以一个例子来实现。
创建测试类

  1. @Slf4j
  2. public class House {
  3. public void run(){
  4. log.info("House run ....");
  5. }
  6. }
  7. @Slf4j
  8. public class User {
  9. public void run(){
  10. log.info("User run ....");
  11. }
  12. }
  13. @Slf4j
  14. public class Student {
  15. public void run(){
  16. log.info("Student run ....");
  17. }
  18. }

实现ImportSelector接口
selectImports方法的返回值为需要创建Bean的类名称。这里创建User类。

  1. @Slf4j
  2. public class MyImportSelector implements ImportSelector {
  3. @Override
  4. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  5. log.info("MyImportSelector selectImports ...");
  6. return new String[]{
  7. User.class.getName()};
  8. }
  9. }

实现ImportBeanDefinitionRegistrar接口
beanDefinitionRegistry.registerBeanDefinition用于设置需要创建Bean的类名称。这里创建House类。

  1. @Slf4j
  2. public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  3. @Override
  4. public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
  5. log.info("MyImportBeanDefinitionRegistrar registerBeanDefinitions .....");
  6. BeanDefinition beanDefinition = new RootBeanDefinition(House.class.getName());
  7. beanDefinitionRegistry.registerBeanDefinition(House.class.getName(),beanDefinition);
  8. }
  9. }

创建一个配置类
这里创建Student类。

  1. @Configuration
  2. public class ImportAutoconfiguration {
  3. @Bean
  4. public Student student(){
  5. return new Student();
  6. }
  7. }

创建EnableImportSelector注解
EnableImportSelector注解上使用@Import,引入以上的三个类。

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Documented
  3. @Target(ElementType.TYPE)
  4. @Import({MyImportSelector.class,ImportAutoconfiguration.class,MyImportBeanDefinitionRegistrar.class})
  5. public @interface EnableImportSelector {
  6. String value();
  7. }

测试

  1. @EnableImportSelector(value = "xxx")
  2. @SpringBootApplication
  3. public class ImportDemoApplication {
  4. public static void main(String[] args) {
  5. ConfigurableApplicationContext context = SpringApplication.run(ImportDemoApplication.class, args);
  6. User user = context.getBean(User.class);
  7. user.run();
  8. Student student = context.getBean(Student.class);
  9. student.run();
  10. House house = context.getBean(House.class);
  11. house.run();
  12. }
  13. }

输出,可以看到,三个类User Student House都创建成功,都可从Spring 容器中获取到。

  1. 2019-06-20 17:53:39.528 INFO 27255 --- [ main] com.springboot.importselector.pojo.User : User run ....
  2. 2019-06-20 17:53:39.530 INFO 27255 --- [ main] c.s.importselector.pojo.Student : Student run ....
  3. 2019-06-20 17:53:39.531 INFO 27255 --- [ main] c.springboot.importselector.pojo.House : House run ....

8. IOC的3种注入方式

接口注入:
接口注入模式因为历史较为悠久,在很多容器中都已经得到应用。但由于其在灵活性、易用性上不如其他两种注入模式,因而在 IOC的专题世界内并不被看好。

Setter 注入:
对于习惯了传统 javabean 开发的程序员,通过 setter 方法设定依赖关系更加直观。
如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时设值注入模式则更为简洁。
如果用到了第三方类库,可能要求我们的组件提供一个默认的构造函数,此时构造子注入模式也不适用。

构造器注入:
在构造期间完成一个完整的、合法的对象。
所有依赖关系在构造函数中集中呈现。
依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对“不变”的稳定状态。
只有组件的创建者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中。

9. Spring Bean的生命周期

image.png

image.png

Bean生命周期一般有下面的四个阶段:

  • Bean的定义
  • Bean的初始化
  • Bean的生存期
  • Bean的销毁

生命周期

  1. 解析xml配置或注解配置的类,得到BeanDefinition;
  2. 通过BeanDefinition反射创建Bean对象;
  3. 对Bean对象进行属性填充;
  4. 回调实现了Aware接口的方法,比如BeanNameAware;
  5. 调用BeanPostProcessor(普哦塞色)的初始化前方法;
  6. 调用init初始化方法
  7. 调用BeanPostProcessor的初始化后的方法此时会进行AOP;
  8. 将创建的Bean对象放入一个Map中;
  9. 业务使用Bean对象;
  10. Spring容器关闭时调用Disposable(抵死抛了bou)Bean的destroy()(抵死做)方法;