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 可以称之为 “高级容器”,他继承了多个接口,因此具备了更多的功能:
1、EnvironmentCapable:为applicationContext声明了获取激活profile、默认profile 方法的功能,也即Environment这个接口代表应用运行时的环境。
2、MessageSource:支持消息的参数化和国际化
3、ApplicationEventPublisher:事件发布
4、ResourcePatternResolver:统一的资源文件访问方式
5、ListableBeanFactory:继承了BeanFactory,实现了枚举方法列举出当前BeanFactory中所有的bean对象而不必根据name一个一个的获取。
6、HierarchicalBeanFactory:是一个具有层级关系的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 |