Spring常见面试知识点

1、谈谈对Spring IOC的理解?谈谈对Spring DI的理解?

IOC反转控制:将之前程序中需要手动创建对象的操作,交由Spring框架来实现,创建对象的操作被反转到了Spring框架。对象的生命周期由Spring来管理,直接从Spring那里去获取一个对象

DI依赖注入:Spring框架创建Bean对象时,动态的将依赖对象注入到Bean组件中,实现依赖对象的注入

2、依赖查找和依赖注入的区别

依赖查找是主动或手动的依赖查找方式,通常需要依赖容器或标准API实现。而依赖注入则是手动或自动依赖绑定的方式,无需依赖特定的容器和API

3、BeanFactory接口和ApplicationContext接口不同点

1)BeanFactory默认采用延迟初始化(lazy-load),第一次getBean时才会初始化Bean;ApplicationContext是会在加载配置文件时初始化Bean

2)BeanFactory提供完整的IOC服务支持;ApplicationContext是对BeanFactory扩展,它可以进行国际化处理、事件传递和Bean自动装配以及各种不同应用层的Context实现

在这里插入图片描述

4、Spring创建Bean对象的几种方式

1)、使用类构造器实例化(默认无参数)

    <bean id="accountService" class="com.hand.demo.service.AccountServiceImpl"/>

2)、使用静态工厂方法实例化(简单工厂模式)

public class StaticFactory {
    public static IAccountService getAccountService() {
        return new AccountServiceImpl();
    }
}
    <bean id="accountService" class="com.hand.demo.factory.StaticFactory" factory-method="getAccountService"/>

3)、使用实例工厂方法实例化(工厂方法模式)

public class InstanceFactory {
    public IAccountService getAccountService() {
        return new AccountServiceImpl();
    }
}
    <bean id="instanceFactory" class="com.hand.demo.factory.InstanceFactory"/>
    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"/>

5、Spring支持的几种Bean的作用域

  • singleton(单例):在Spring IOC容器中仅存在一个Bean实例(默认
  • prototype(多例):每个从容器中调用Bean时,都返回一个新的实例
  • request:将单个Bean定义范围限定为单个HTTP请求的生命周期,每个HTTP请求都会创建一个新的Bean
  • session:将单个Bean定义范围限定为HTTP Session的生命周期
  • application:将单个Bean定义范围限定为ServletContext的生命周期
  • webSocket:将单个Bean定义范围限定为WebSocket的生命周期

6、依赖注入的几种方式

1)、setter方法注入

public class User {
    private Long id;
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
public class UserHolder {
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "UserHolder{" +
                "user=" + user +
                '}';
    }
}

1)Xml配置

setter-injection.xml:

    <bean id="userHolder" class="com.hand.demo.injection.UserHolder">
        <property name="user" ref="user"/>
    bean>

    <bean id="user" class="com.hand.demo.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="Jack"/>
    bean>
 public class XmlSetterInjectionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        String xmlResourcePath = "classpath:/setter-injection.xml";
        //加载Xml资源、解析并且生成BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
        //依赖查找并且创建Bean
        UserHolder userHolder = beanFactory.getBean(UserHolder.class);
        System.out.println(userHolder);
    }
}

2)Java注解

setter-injection.xml:

    <bean id="user" class="com.hand.demo.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="Jack"/>
    bean>
public class AnnotationSetterInjectionDemo {
    public static void main(String[] args) {
        //创建ApplicationContext容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
        String xmlResourcePath = "classpath:/setter-injection.xml";
        //加载Xml资源、解析并且生成BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
        //注册配置类
        applicationContext.register(AnnotationSetterInjectionDemo.class);
        //启动Spring应用上下文
        applicationContext.refresh();
        //依赖查找并且创建Bean
        UserHolder userHolder = applicationContext.getBean(UserHolder.class);
        System.out.println(userHolder);
        //关闭Spring应用上下文
        applicationContext.close();
    }

    @Bean
    public UserHolder userHolder(User user) {
        UserHolder userHolder = new UserHolder();
        userHolder.setUser(user);
        return userHolder;
    }
}

3)API配置元信息

setter-injection.xml:

    <bean id="user" class="com.hand.demo.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="Jack"/>
    bean>
public class ApiSetterInjectionDemo {
    public static void main(String[] args) {
        //创建ApplicationContext容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        //生成UserHolder的BeanDefinition
        BeanDefinition userHolderBeanDefinition = createUserHolderBeanDefinition();
        //注册UserHolder的BeanDefinition
        applicationContext.registerBeanDefinition("userHolder", userHolderBeanDefinition);
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
        String xmlResourcePath = "classpath:/setter-injection.xml";
        //加载Xml资源、解析并且生成BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
        //启动Spring应用上下文
        applicationContext.refresh();
        //依赖查找并且创建Bean
        UserHolder userHolder = applicationContext.getBean(UserHolder.class);
        System.out.println(userHolder);
        //关闭Spring应用上下文
        applicationContext.close();
    }

    /**
     * 为UserHolder生成BeanDefinition
     *
     * @return
     */
    public static BeanDefinition createUserHolderBeanDefinition() {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
        builder.addPropertyReference("user", "user");
        return builder.getBeanDefinition();
    }
}

2)、构造器注入

1)Xml配置

constructor-injection.xml:

    <bean id="userHolder" class="com.hand.demo.injection.UserHolder">
        <constructor-arg name="user" ref="user"/>
    bean>

    <bean id="user" class="com.hand.demo.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="Jack"/>
    bean>

2)Java注解

public class AnnotationConstructorInjectionDemo {
    public static void main(String[] args) {
        //创建ApplicationContext容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
        String xmlResourcePath = "classpath:/setter-injection.xml";
        //加载XML资源、解析并且生成BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
        //注册配置类
        applicationContext.register(AnnotationConstructorInjectionDemo.class);
        //启动Spring应用上下文
        applicationContext.refresh();
        //依赖查找并且创建Bean
        UserHolder userHolder = applicationContext.getBean(UserHolder.class);
        System.out.println(userHolder);
        //关闭Spring应用上下文
        applicationContext.close();
    }

    @Bean
    public UserHolder userHolder(User user) {
        UserHolder userHolder = new UserHolder(user);
        return userHolder;
    }
}

3)API配置元信息

public class ApiConstructorInjectionDemo {
    public static void main(String[] args) {
        //创建ApplicationContext容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        //生成UserHolder的BeanDefinition
        BeanDefinition userHolderBeanDefinition = createUserHolderBeanDefinition();
        //注册UserHolder的BeanDefinition
        applicationContext.registerBeanDefinition("userHolder", userHolderBeanDefinition);
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
        String xmlResourcePath = "classpath:/setter-injection.xml";
        //加载XML资源、解析并且生成BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
        //启动Spring应用上下文
        applicationContext.refresh();
        //依赖查找并且创建Bean
        UserHolder userHolder = applicationContext.getBean(UserHolder.class);
        System.out.println(userHolder);
        //关闭Spring应用上下文
        applicationContext.close();
    }

    /**
     * 为UserHolder生成BeanDefinition
     *
     * @return
     */
    public static BeanDefinition createUserHolderBeanDefinition() {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
        builder.addConstructorArgReference("user");
        return builder.getBeanDefinition();
    }
}

3)、字段注入

  • @Autowired:默认按照类型装配
  • @Resource:默认按照名称装配

4)、方法注入

public class AnnotationMethodInjectionDemo {
    private UserHolder userHolder;

    private UserHolder userHolder2;
    
    @Autowired
    public void initUserHolder(UserHolder userHolder) {
        this.userHolder = userHolder;
    }

    @Resource
    public void initUserHolder2(UserHolder userHolder2) {
        this.userHolder2 = userHolder2;
    }

    public static void main(String[] args) {
        //创建ApplicationContext容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
        String xmlResourcePath = "classpath:/setter-injection.xml";
        //加载XML资源、解析并且生成BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
        //注册配置类
        applicationContext.register(AnnotationMethodInjectionDemo.class);
        //启动Spring应用上下文
        applicationContext.refresh();
        //依赖查找 AnnotationMethodInjectionDemo Bean
        AnnotationMethodInjectionDemo demo = applicationContext.getBean(AnnotationMethodInjectionDemo.class);
        System.out.println(demo.userHolder == demo.userHolder2);//true
        //关闭Spring应用上下文
        applicationContext.close();
    }

    @Bean
    public UserHolder userHolder(User user) {
        UserHolder userHolder = new UserHolder(user);
        return userHolder;
    }
}

7、自动绑定的几种方式

  • no:无自动装配(默认),需要手动指定依赖注入对象
  • byName:根据被注入属性的名称作为Bean名称进行依赖查找,并将对象设置到该属性
  • byType:根据被注入属性的类型作为依赖类型进行查找,并将对象设置到该属性
  • constructor:特殊byType类型,用于构造器参数

8、Spring Bean的生命周期

Spring IOC容器实现分为两个阶段:容器启动阶段Bean实例化阶段

1)、容器启动阶段

容器依赖工具类BeanDefinitionReader对加载的Configuration MetaData进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了Bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry

Spring常见面试知识点_第1张图片

1)容器的扩展点:BeanFactoryPostProcessor

BeanFactoryPostProcessor可以对Bean配置元数据进行操作。也就是说,Spring容器允许BeanFactoryPostProcessor读取指定Bean的配置元数据(BeanDefinition),并可以在Bean被实例化之前修改它

2)、Bean实例化阶段

经过第一阶段,所有的Bean定义信息都通过BeanDefinition的方式注册到了BeanDefinitionRegistry中,当某个请求方法通过容器的getBean方法请求某个对象或者因依赖关系容器需要隐式地调用getBean方法时,就会触发Bean的实例化

隐式调用有如下两种情况:

  • 对于BeanFactory来说,对象实例化默认采用延迟初始化。当对象A被请求而需要第一次实例化的时候,如果它所依赖的对象B之前没有被实例化,那么容器会先实例化对象A所依赖的对象。这时容器内部就会首先实例化B以及对象A依赖的其他还没有被实例化的对象。这种情况是容器内部调用getBean(),对于本次请求的请求方是隐式的
  • ApplicationContext启动之后会实例化所有的Bean定义,是因为ApplicationContext在完成启动阶段后,紧接着调用注册到该容器的所有Bean定义的实例化方法getBean()

只有当对应某个Bean定义的getBean()方法第一次被调用时,不管是显示的还是隐式的,Bean实例化阶段的活动才会被触发,第二次被调用则会直接返回容器缓存的第一次实例化完的对象实例(多例模式除外)

Spring常见面试知识点_第2张图片

1)Bean的实例化与BeanWrapper

容器内部实现采用策略模式来决定使用何种方式初始化Bean实例

InstantiationStrategy是实例化策略的抽象接口,其直接子类SimpleInstantiationStrategy实现了通过反射来实例化对象实例,但不支持方法注入方式的对象实例化

CglibSubclassingInstantiationStrategy继承了SimpleInstantiationStrategy的以反射方式实例化对象的功能,并通过CGLIB的动态字节码生成功能,可以动态生成某个类的子类,进而满足了方法注入所需的对象实例化需求。默认情况下,容器内部采用的是CglibSubclassingInstantiationStrategy

容器只要根据相应bean定义的BeanDefinition取得实例化信息,结合CglibSubclassingInstantiationStrategy以及不同的Bean定义类型,就可以返回实例化完成的对象实例。但是,不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例

BeanWrapper接口有一个实现类BeanWrapperImpl,其作用就是对某个Bean进行包裹,然后对这个包裹的Bean进行操作,比如设置或者获取Bean的相应属性值,使用BeanWrapper对Bean实例操作很方便,可以免去直接使用反射API操作对象的繁琐

2)Aware接口

当对象实例化完成并且相关属性以及依赖设置完成之后,Spring容器会检查当前对象实例是否实现了一系列的以Aware命名结尾的接口定义。如果是,则将这些Aware接口定义中规定的依赖注入给当前对象实例。下面按照执行顺序进行介绍

BeanFactory中Aware接口

  • BeanNameAware:将对象实例的bean定义对应的beanName设置到当前对象实例
  • BeanClassLoaderAware:将对应加载当前bean的ClassLoader注入当前对象实例,默认会使用加载ClassUtils类的ClassLoader
  • BeanFactoryAware:BeanFactory容器将自身设置到当前对象实例

Spring常见面试知识点_第3张图片

ApplicationContext类型的容器在检测Aware接口并设置相关依赖的实现机制上,与以上几个接口处理方式有所不同,使用的是BeanPostProcessor方式

Spring常见面试知识点_第4张图片

Spring常见面试知识点_第5张图片

Spring常见面试知识点_第6张图片

ApplicationContext中Aware接口

  • EnvironmentAware
  • EmbeddedValueResolverAware
  • ResourceLoaderAware
  • ApplicationEventPublisherAware
  • MessageSourceAware
  • ApplicationContextAware

3)容器的扩展点:BeanPostProcessor

BeanPostProcessor接口提供Spring Bean初始化前和初始化后的生命周期回调,分别对应postProcessBeforeInitialization和postProcessAfterInitialization方法,允许对Bean进行扩展甚至是替换

public interface BeanPostProcessor {

  //在Bean初始化前调用
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

  //在Bean初始化后调用
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

4)Bean初始化

  • @PostConstruct标注方法
  • 实现InitializingBean接口的afterPropertiesSet()方法
  • 自定义初始化方法,使用的init-method属性指定方法名

5)DisposableBean和destroy-method

  • @PreDestroy标注方法
  • 实现DisposableBean接口的destroy()方法
  • 自定义销毁方法,使用的destroy-method属性指定方法名

9、容器的扩展点:FactoryBean

FactoryBean主要用来定制化Bean的创建逻辑,FactoryBean接口方法如下:

public interface FactoryBean<T> {

	//返回这个FactoryBean所创建的对象
	@Nullable
	T getObject() throws Exception;

	//返回这个FactoryBean所创建对象的类型
	@Nullable
	Class<?> getObjectType();

	//返回FactoryBean所创建的对象是否为单例,默认返回true
	default boolean isSingleton() {
		return true;
	}

}

假设我们定义了一个FactoryBean,名为myFactoryBean,当调用getBean("myFactoryBean")方法时返回的并不是这个FactoryBean,而是这个FactoryBean所创建的Bean,如果想获取到这个FactoryBean需要在名字前面拼接&,行如这种形式:getBean("&myFactoryBean")

public class MyFactoryBean implements FactoryBean<IAccountService> {
    public IAccountService getObject() throws Exception {
        System.out.println("执行创建Bean的逻辑");
        return new AccountServiceImpl();
    }

    public Class<?> getObjectType() {
        return IAccountService.class;
    }

    public boolean isSingleton() {
        return true;
    }
}
public interface IAccountService {
    void saveAccount();
}
public class AccountServiceImpl implements IAccountService {
    public AccountServiceImpl(){
        System.out.println("AccountServiceImpl被创建出来了");
    }

    public void saveAccount() {
        System.out.println("保存了账户");
    }
}
    <bean id="myFactoryBean" class="com.hand.demo.factorybean.MyFactoryBean"/>
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:bean.xml");
        IAccountService as = (IAccountService) ac.getBean("myFactoryBean");
        as.saveAccount();

执行结果:

执行创建Bean的逻辑
AccountServiceImpl被创建出来了
保存了账户

10、Spring循环依赖及解决方式

1)、什么是循环依赖

循环依赖指的是多个对象之间的依赖关系形成一个闭环

Spring常见面试知识点_第7张图片

2)、Spring的所有对象都支持循环依赖吗?

单例模式下默认是支持的,但是如果通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖;多例模式不支持

3)、怎么检测是否存在循环依赖

Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了

4)、案例

public class A {
    private B b;

    public void setB(B b) {
        this.b = b;
    }
}
public class B {
    private A a;

    public void setA(A a) {
        this.a = a;
    }
}

circular-dependence.xml:

    <bean id="a" class="com.hand.demo.circularDependence.A">
        <property name="b" ref="b"/>
    bean>

    <bean id="b" class="com.hand.demo.circularDependence.B">
        <property name="a" ref="a"/>
    bean>
        //创建ApplicationContext容器
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                "classpath:circular-dependence.xml");
        A a = (A) applicationContext.getBean("a");
        System.out.println(a);

5)、实现原理分析

Bean从实例化到依赖注入核心代码调用如下:

		AbstractApplicationContext.refresh()
								↓
								↓
    //实例化所有剩余的(非延迟初始化)单例            
		finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)
								↓
								↓
		DefaultListableBeanFactory.preInstantiateSingletons()
								↓
								↓	
    //循环调用所有剩余的(非延迟初始化)单例的getBean()方法            
		AbstractBeanFactory.getBean(String name)   
								↓
								↓	
		doGetBean(final String name, @Nullable final Class requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly)
								↓
								↓	
		//检查缓存中是否有手动注册的单例					
		DefaultSingletonBeanRegistry.getSingleton(String beanName)	→→	getSingleton(String beanName, boolean allowEarlyReference)		
								↓
								↓		
		DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory singletonFactory)
								↓
								↓	
    //创建Bean实例            
		AbstractAutowireCapableBeanFactory.createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
								↓
								↓				
		doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)	
								↓
								↓	    
		createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)    
								↓
								↓	  		
		//如果earlySingletonExposure是true,将单例对象的引用通过ObjectFactory保存下来,然后将该ObjectFactory缓存在三级缓存singletonFactories中				
		earlySingletonExposure == true	→→	DefaultSingletonBeanRegistry.addSingletonFactory(String beanName, ObjectFactory singletonFactory) 		
								↓
								↓	  	
		//执行依赖注入						
		populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)   
								↓
								↓	 
    //初始化Bean,调用Spring xml中的init方法            
		initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)    

Spring常见面试知识点_第8张图片

Bean的创建最为核心的三个方法:

  • createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
  • populateBean:填充属性,这一步主要是对Bean的依赖属性进行填充
  • initializeBean:调用Spring xml中的init方法

循环依赖主要发生在第二步(populateBean),也就是field属性注入的处理

三级缓存核心逻辑

创建Bean的时候Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取,如果还是获取不到就从三级缓存singletonFactories中取(Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外提前暴露依赖的引用,根据对象引用能定位到堆中的对象,其原理是基于Java的引用传递),取到后从三级缓存移动到了二级缓存完全初始化之后将自己放入到一级缓存中

核心源码如下:

	//维护着所有创建完成的Bean bean name-->bean instance (一级缓存)
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	//维护着创建中Bean的ObjectFactory bean name-->ObjectFactory (三级缓存)
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	//维护着所有半成品的Bean (二级缓存)
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //从singletonObjects获取已创建的Bean
		Object singletonObject = this.singletonObjects.get(beanName);
    //如果没有已创建的Bean,但是该Bean正在创建中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
        //从earlySingletonObjects获取已经实例化的Bean
				singletonObject = this.earlySingletonObjects.get(beanName);
        //如果没有实例化的Bean,但是参数allowEarlyReference为true
				if (singletonObject == null && allowEarlyReference) {
          //从singletonFactories获取ObjectFactory
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
            //使用ObjectFactory获取Bean实例
						singletonObject = singletonFactory.getObject();
            //将Bean实例放入earlySingletonObjects中,并从singletonFactories中移除
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

以A、B循环依赖注入为例,流程图如下:

Spring常见面试知识点_第9张图片

  1. 在创建A的时候,会将实例化的A通过addSingletonFactory()方法缓存,然后执行依赖注入B
  2. 注入会走B的创建流程,最后B又会执行依赖注入A
  3. 由于第一步已经缓存了A的引用,再次创建A时可以通过getSingleton()方法得到这个A的提前引用(拿到最开始缓存的objectFactory,通过它取得对象引用),这样B的依赖注入就完成了
  4. B创建完成后,代表A的依赖注入也完成了,那么A也创建成功了

加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

6)、如何关闭循环依赖

        //创建ApplicationContext容器
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
        //设置配置文件路径
        applicationContext.setConfigLocation("classpath:circular-dependence.xml");
        //关闭循环依赖
        applicationContext.setAllowCircularReferences(false);
        //启动Spring应用上下文
        applicationContext.refresh();
        A a = (A) applicationContext.getBean("a");
        System.out.println(a);

11、Spring AOP的底层实现原理?

Spring AOP采用动态代理机制(JDK动态代理)和字节码生成技术(CGLIB动态代理)实现,二者都是在运行期间为目标对象生成一个代理对象,而将横切逻辑织入到这个代理对象中,系统最终使用的是织入了横切逻辑的代理对象,而不是真正的目标对象

  • JDK动态代理的核心是InvocationHandler接口和Proxy类,它通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。默认情况下,如果Spring AOP发现目标对象实现了相应接口,则采用JDK动态代理为其生成代理对象实例
  • 如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB是一个代码生成的类库,可以在运行时动态的生成某个类的子类,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLI做动态代理的

Spring常见面试知识点_第10张图片

12、Spring AOP和AspectJ AOP有什么区别?

Spring AOP属于运行时增强,而AspectJ是编译时增强。Spring AOP基于代理,而AspectJ基于字节码操作

13、Spring事务的传播行为

传播行为 含义 备注
REQUIRED 当方法调用时,如果不存在当前事务,那么就创建事务;如果之前当方法已经存在事务了,那么就沿用之前的事务 Spring默认的传播行为
SUPPORTS 当方法调用时,如果不存在当前事务,那么不启用事务;如果存在当前事务,那么就沿用当前事务
MANDATORY 方法必须在事务内运行 如果不存在当前事务,那么就抛出异常
REQUIRES_NEW 无论是否存在当前事务,方法都会在新的事务中运行 事务管理器会打开新的事务运行该方法
NOT_SUPPORTED 不支持事务,如果不存在当前事务也不会创建事务;如果存在当前事务,则挂起它,直至该方法结束后才恢复当前事务
NEVER 不支持事务,只有在没有事务的环境中才能运行它 如果方法存在当前事务,则抛出异常
NESTED 嵌套事务,也就是调用方法如果抛出异常只回滚自己内部执行的SQL,而不回滚主方法的SQL

14、SpringMVC的工作原理

Spring常见面试知识点_第11张图片

参考

Spring Framework中文文档:https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/core.html#beans

《Spring揭秘》

《Spring官网读书笔记》:https://blog.csdn.net/qq_41907991/category_9601507.html

《@Autowired注解的实现原理》:https://juejin.im/post/5d9487b55188252dfc5727da

《简单说说Spring的循环依赖》:https://juejin.im/post/5da727046fb9a04e2e4b1ca6

《互联网公司 Spring 面试大全(100 题)》:https://gitchat.csdn.net/activity/5d3c724bdf8cc803e66f1e18#61springbeans

《Spring常见问题总结(补充版)》:https://mp.weixin.qq.com/s/wcK2qsZxKDJTLIGqEIyaNg

你可能感兴趣的:(Java面试复习总结)