请你谈谈Spring IOC的理解?

1什么是IOC?

IOC:它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。对象之间的耦合度或者说依赖程度降低,对象资源变的容易管理;

2Spring是如何设计IOC容器的?BeanFactory && ApplicationContext

Spring 设计了两个接口用以表示容器:BeanFactory && ApplicationContext。BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。

1、BeanFactory 就是个 HashMap,Key 是 beanName,Value 是 Bean 实例。Spring里面最底层的接口:读取bean配置文档,包含了各种Bean的定义,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。我们可以称之为 “低级容器”。
ApplicationContext 可以称之为 “高级容器”,他继承了多个接口,因此具备了更多的功能:

1EnvironmentCapable:为applicationContext声明了获取激活profile、默认profile 方法的功能,也即Environment这个接口代表应用运行时的环境。
2MessageSource:支持消息的参数化和国际化
3ApplicationEventPublisher:事件发布
4ResourcePatternResolver:统一的资源文件访问方式
5ListableBeanFactory:继承了BeanFactory,实现了枚举方法列举出当前BeanFactory中所有的bean对象而不必根据name一个一个的获取。
6HierarchicalBeanFactory:是一个具有层级关系的Bean 工厂,拥有属性parentBeanFactory:当获取 Bean对象时,如果当前BeanFactory中不存在对应的bean,则会访问其直接 parentBeanFactory 以尝试获取bean 对象。载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

称 ApplicationContext 是 Spring 应用上下文。

2、BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。
ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

3、BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

4、BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

3BeanPostProcessor 与 BeanFactoryPostProcessor 的区别?

BeanFactoryPostProcessor 存在于容器启动阶段,提供 BeanFactory(底层 IoC 容器)的生命周期的回调,用于扩展 BeanFactory(实际为 ConfigurableListableBeanFactory)。BeanFactoryPostProcessor是在Bean被实例化之前对Bean的定义信息进行修改,那么Spring实现对自定义BeanFactoryPostProcessor的调用的,在refresh()方法中会调用invokeBeanFactoryPostProcessors(beanFactory),invokeBeanFactoryPostProcessors()方法的逻辑:遍历容器中的BeanFactoryPostProcessor,然后调用postProcessBeanFactory()方法,这个方法就是我们自定义BeanFactoryPostProcessor时需要去实现的方法。

BeanPostProcessor存在于对象实例化阶段, 提供 Bean 初始化前和初始化后的生命周期回调,允许对 Bean 进行扩展,添加一些自己的逻辑处理。我们就可以定义一个或者多个 BeanPostProcessor 接口的实现,然后注册到容器中。Spring 中如何使用 BeanPostProcessor 处理实例化对象:
在这里插入图片描述

Spring 中的 BeanPostProcessor 在实例化过程处于的位置分为两部分–前置处理和后置处理,而 BeanPostProcessor 接口也提供了两个可实现的方法,下面我们看一下源码:

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

postProcessBeforeInitialization:在实例化及依赖注入完成后,在任何初始化代码(比如配置文件中的init-method)调用之前调用;postProcessAfterInitialization:在初始化代码调用之后调用。此处需要注意的是:接口中的两个方法都要将传入的 bean 返回,而不能返回 null,如果返回的是 null 那么我们通过 getBean() 方法将得不到目标。

4FactoryBean 和 BeanFactory有什么区别?

BeanFactory 是 Bean 的工厂, ApplicationContext 的父类,IOC 容器的核心,负责生产和管理 Bean 对象。

FactoryBean 是 Bean,可以通过实现 FactoryBean 接口定制实例化 Bean 的逻辑,通过代理一个Bean对象,对方法前后做一些操作。我们可以通过 FactoryBean 帮助实现复杂的初始化逻辑,例如在 Spring 继集成 MyBatis 的项目中,Mapper 接口没有实现类是如何被注入的?其实 Mapper 接口就是一个 FactoryBean 对象,当你注入该接口时,实际得到的就是其 getObject() 方法返回的一个代理对象,关于数据库的操作都是通过该代理对象来完成。

FactoryBean的底层源码实现:

public interface FactoryBean<T> {
    @Nullable
    T getObject() throws Exception;
    @Nullable
    Class<?> getObjectType();
    // FactoryBean#getObject返回的bean实例是否是单例的
    // 如果是单例,那么FactoryBean#getObject将只会被调用一次
    default boolean isSingleton() {
        return true;
    }
}
public class SubBean {
}

@Service
public class FactoryBeanDemo implements FactoryBean<SubBean> {
    @Override
    public SubBean getObject() throws Exception {
        return new SubBean();
    }
    @Override
    public Class<?> getObjectType() {
        return SubBean.class;
    }
}
public void test() {
    applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
    Object subBean = applicationContext.getBean("factoryBeanDemo");
    System.out.println(subBean);
}
com.xiaoxizi.spring.factoryBean.SubBean@3e0e1046

可以看到,我们通过getBean("factoryBeanDemo")拿到的居然是factoryBean的实例,而不是我们@Service注解标记的FactoryBeanDemo的实例。
当我们向spring注册一个FactoryBean时,通过beanName获取到的将是FactoryBean#getObject方法返回的SubBean实例,而且注意看FactoryBean#isSingleton方法,说明我们也是可以指定getObject方法获取的实例是单例的还是多例的。

public void test() {
    applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
    Object subBean = applicationContext.getBean("factoryBeanDemo");
    System.out.println(subBean);

    Object factoryBeanDemo = applicationContext.getBean("&factoryBeanDemo");
    System.out.println(factoryBeanDemo);
}
com.xiaoxizi.spring.factoryBean.SubBean@3e0e1046
com.xiaoxizi.spring.factoryBean.FactoryBeanDemo@24c1b2d2

也就是说,正常通过beanName从Spring容器中取的话,是只能取到subBean实例的,但是如果在beanName前面加上&符号,使用&beanName从Spring容器中获取,才能获取到FactoryBean实例本身。

5 FactoryBean的transformedBeanName处理&符号

getBean("&factoryBeanDemo")是可以获取到factoryBean的实例的,那么对于这个&符号,spring是在transformedBeanName中做初步处理的:

// AbstractBeanFactory#transformedBeanName
protected String transformedBeanName(String name) {
    // canonicalName 主要是通过别名找beanName的逻辑
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

// BeanFactoryUtils#transformedBeanName
public static String transformedBeanName(String name) {
    // 先说一下,这个BeanFactory.FACTORY_BEAN_PREFIX常量就是 & 符号
    if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        // 如果不是以 & 符号开头,那就直接返回了
        return name;
    }
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        // 把name的所有前置的&符号全部干掉
        // 比如 &&&factoryBean --> factoryBean
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        } while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}

6获取bean的核心逻辑:AbstractBeanFactory -> getBean() -> doGetBean()

protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
  throws BeansException {     
    
 // ****处理别名BeanName、处理带&符的FactoryBeanName
 final String beanName = transformedBeanName(name);
 Object bean;  
 
// ****先尝试从缓存中获取Bean实例,这个位置就是三级缓存解决循环依赖的方法
Object sharedInstance = getSingleton(beanName);   

if (sharedInstance != null && args == null) {    
 // ****1. 如果 sharedInstance 是普通的 Bean 实例,则下面的方法会直接返回
 // 2. 如果 sharedInstance 是FactoryBean类型,则需要获取 getObject 方法 
 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
  // ****1. 父 bean 工厂存在
  // 2. 当前 bean 不存在于当前bean工厂,则到父工厂查找 bean 实例
  BeanFactory parentBeanFactory = getParentBeanFactory();
  if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
   // ****获取 name 对应的 beanName,如果 name 是以 & 开头,则返回 & + beanName
   String nameToLookup = originalBeanName(name);                  
   // ****根据 args 参数是否为空,调用不同的父容器方法获取 bean 实例
   if (args != null) {
        return (T) parentBeanFactory.getBean(nameToLookup, args);
   } else {
        return parentBeanFactory.getBean(nameToLookup, requiredType);
   }
}     

 // ****1. typeCheckOnly,用于判断调用 getBean 方法时,是否仅是做类型检查
 // 2. 如果不是只做类型检查,就会调用 markBeanAsCreated 进行记录
  if (!typeCheckOnly) {
   markBeanAsCreated(beanName);
  }
  try {    
   // ****处理使用了 depends-on 注解的依赖创建 bean 实例
   String[] dependsOn = mbd.getDependsOn();
   if (dependsOn != null) {
    for (String dep : dependsOn) {   
     // ****监测是否存在 depends-on 循环依赖,若存在则会抛出异常
     if (isDependent(beanName, dep)) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
     }
     // ****注册依赖记录
     registerDependentBean(dep, beanName);
     try {    
      // ****加载 depends-on 依赖(dep 是 depends-on 缩写)
      getBean(dep);
     } catch (NoSuchBeanDefinitionException ex) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
     }
   }  
   // ****创建单例 bean 实例
   if (mbd.isSingleton()) {    
    // 把 beanName 和 new ObjectFactory 匿名内部类传入回调
    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
     @Override
     public Object getObject() throws BeansException {
      try {    
       // 创建 bean
       return createBean(beanName, mbd, args);
      } catch (BeansException ex) {
       // 创建失败则销毁
       destroySingleton(beanName);
       throw ex;
      }
     }
    });
   // 1. 如果 sharedInstance 是普通的 Bean 实例,则下面的方法会直接返回
   // 2. 如果 sharedInstance 是FactoryBean类型,则需要获取 getObject 方法 
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
   }
   //**** 创建多例的bean
   else if (mbd.isPrototype()) {
   try {
     beforePrototypeCreation(beanName);
     prototypeInstance = createBean(beanName, mbd, args);
    }
   // 1. 如果 sharedInstance 是普通的 Bean 实例,则下面的方法会直接返回
   // 2. 如果 sharedInstance 是FactoryBean类型,则需要获取 getObject 方法 
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
   }
   //**** 创建自定义scope
   else {
    String scopeName = mbd.getScope();
    final Scope scope = this.scopes.get(scopeName);
    if (scope == null) {
     throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    }
    try {
     Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
      @Override
      public Object getObject() throws BeansException {
       beforePrototypeCreation(beanName);
       try {
        return createBean(beanName, mbd, args);
       }
       finally {
        afterPrototypeCreation(beanName);
       }
      }
     });
     // 1. 如果 sharedInstance 是普通的 Bean 实例,则下面的方法会直接返回
     // 2. 如果 sharedInstance 是FactoryBean类型,则需要获取 getObject 方法 
     bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
   
//**** 返回 Bean
return (T) bean;
}     

7getObjectForBeanInstance获取最终需要返回的bean实例

// 需要注意的是,这里传入了name和beanName两个值
// name是transformedBeanName之前的原始值,也就是我们调用getBean方法时传入的
// beanName就是转换后的啦,正常情况下(name没有前置的&标记),这两是一样的
// 如果mbd不为空,说明bean对象刚刚初始化完
getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
protected Object getObjectForBeanInstance(
    Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        // 如果name是带有&前缀的,说明我们是想获取factoryBean实例
        // 而不是获取factoryBean#getObject返回的实例
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        // 判断一下,如果你通过&xxx来获取bean实例,那你获取到的bean实例必须实现FactoryBean接口
        // 这种判断主要是杜绝意料之外的事情发生,比较beanName是用户指定的
        // 要是用户指定一个bean名称是&xxx但是实际上是不实现FactoryBean是不允许的
        // 启动就会报错
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        // 这里就直接把factoryBean实例返回出去了
        // 这就是我们getBean("&factoryBeanDemo")获取到factoryBean实例的原因
        return beanInstance;
    }

    // 能走到这里,其实说明name是一个正常的非&开头的name了
    if (!(beanInstance instanceof FactoryBean)) {
        // 这个时候,如果获取到的bean实例没有实现FactoryBean接口,
        // 是不需要特殊处理的,直接返回就行了
        // 对于正常的bean(没实现FactoryBean的),都是往这里返回的
        return beanInstance;
    }

    Object object = null;
    if (mbd != null) {
        // 如果mbd不为空,说明bean对象(FactoryBean)刚刚初始化完
        mbd.isFactoryBean = true;
    }
    else {
        // 不是bean对象(FactoryBean)刚刚初始化完,直接从缓存获取
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // 如果缓存中没有这个factoryBean对应的subBean
        // 或者是factoryBean刚初始化完的时候
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        // 从factoryBean获取subBean并且返回
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    // 这里返回了subBean
    return object;
}

可以看到,如果a实例是一个factoryBean的话,当我们调用getBean(“a”)时,是会创建a实例并触发它的factoryBean#getObject获取到subBean实例并返回的;而如果是使用getBean("&a"),则只会实例化a实例并返回factoryBean本身。

8IoC和DI的区别

IoC和DI其实是同一概念的不同角度描述。IoC强调的是将对象实例的创建控制权由spring容器来统一管理,需要的时候从容器中取出,而不是由调用者自身去创建,从而达到降低代码耦合性与硬代码的目的。依赖注入强调的是当调用者需要使用对象实例时,spring容器为调用者提供对象实例这个过程

9使用@Autowired注解自动装配的过程是怎样的?

使用@Autowired注解来自动装配指定的bean。在使用 @Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。

在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

10Spring注解的介绍?

注解用途
@Configuration用于定义配置类,可替换xml配置文件
@Bean它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文

1@Component, @Controller, @Repository, @Service 有何区别?

注解介绍
@Component将 java 类标记为 bean,它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller这将一个类标记为 Spring Web MVC 控制器,标有它的 Bean 会自动导入到 IoC 容器中
@Service在服务层类中使用更好的方式指定了意图
@Repository用于标注数据访问组件,即DAO组件

2@Required 注解有什么作用?

这个注解表明bean的属性必须在配置的时候设置,@Required注解的bean属性未被设置,容器将抛出BeanInitializationException。

3@Autowired 注解有什么作用?

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

4@Autowired和@Resource之间的区别?

@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

5@Qualifier 注解有什么作用?

当您创建多个相同类型的 bean希望仅使用属性装配其中一个 bean 时,您可以使用**@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义**。

11有哪些不同类型的依赖注入实现方式?

依赖注入是时下最流行的IoC实现方式,依赖注入分为Setter方法注入(Setter Injection)和构造器注入(Constructor Injection)在这里插入图片描述最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。

12Spring框架中的单例bean是线程安全的吗?

不是,Spring框架中的单例bean不是线程安全的。spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。实际上大部分时候 bean 无状态【不会保存数据】的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态【有数据存储功能】的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

13Spring如何处理线程并发问题?

在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。

14Spring 框架中都用到了哪些设计模式?

模式举例
工厂模式BeanFactory、ApplicationContext就是简单工厂模式的体现,用来创建对象的实例
单例模式Spring 中 bean 的默认作用域就是 singleton(单例)的
代理模式Spring的AOP功能用到了JDK的动态代理和CGLIB
模板方法用来解决代码重复的问题。比如jdbcTemplate
观察者模式一对多的依赖关系: 当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新 ApplicationListener
适配器模式Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值