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

在這裏插入圖片描述

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實例化階段的活動纔會被觸發,第二次被調用則會直接返回容器緩存的第一次實例化完的對象實例(多例模式除外)

在這裏插入圖片描述

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容器將自身設置到當前對象實例

在這裏插入圖片描述

ApplicationContext類型的容器在檢測Aware接口並設置相關依賴的實現機制上,與以上幾個接口處理方式有所不同,使用的是BeanPostProcessor方式

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

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()方法
  • 自定義初始化方法,使用<bean>的init-method屬性指定方法名

5)DisposableBean和destroy-method

  • @PreDestroy標註方法
  • 實現DisposableBean接口的destroy()方法
  • 自定義銷燬方法,使用<bean>的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)、什麼是循環依賴

循環依賴指的是多個對象之間的依賴關係形成一個閉環

在這裏插入圖片描述

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<T> 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)    

在這裏插入圖片描述

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循環依賴注入爲例,流程圖如下:

在這裏插入圖片描述

  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做動態代理的

在這裏插入圖片描述

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 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章