29 如何在所有的BeanDefinition注冊完成后,進行擴展
Bean工廠的后置處理器,在所有的Bean注冊完成后,就被執行。
public class A implements BeanFactoryPostProcessor {private String name = "a class";private B b; ?public String getName() {return name;} ?public void setName(String name) {this.name = name;} ?@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {//在所有的BeanDefinition注冊完成之后調用for (String beanDefinitionName : configurableListableBeanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}} }
30 Bean的生產順序由什么決定的?
創建一個ApplicationContext,會調用refresh方法,再去調用finishBeanFactoryInitialization(),完成BeanFactory的初始化,創建所有的BeanDefinitionNames,是個ArrayList,按照注冊順序,進行創建,依賴關系也會影響Bean的創建順序。
BeanDefinition的注冊順序的由來?
主要是由注解(配置)的解析順序進行的。
-
@Configuration
-
@Component
-
@Import 導入的一個類
-
@Bean
-
@Import——ImportBeanDefinitionRegister
也可以使用@Order來決定順序,后面注冊的會覆蓋掉前面的注冊的。
BeanDefinitionRegistryPostProcessor
31 Spring有哪幾種配置方式?
-
注解 Spring 2.5 +
-
Spring.xml
-
-
XML
-
Spring.xml <component-scan base-package=""/> @Component @Autowired
-
-
基于Java配置
-
JavaConfig Spring 3.0+
-
@Configuration @Component
-
32 JavaConfig 如何替代 Spring.XML的配置方式的
應用
-
以前XML
-
Spring容器是new ClassPathXMLApplicationContext()
-
Spring.XML
-
<Bean scope="" >
-
<component-scan />
-
引入外部屬性配置文件:<property-placeHodeler resource="">
-
指定其他配置文件 @PropertySource("classpath:db.properties")
-
<property name="pass" value="${mysql.password}"></property>
-
-
JavaConfig
-
Spring容器是 nw AnnotationConfigApplicationContext(javaConfig.class)
-
JavaConfig.java ===> @Configuration
-
@Bean @Scope @Lazy
-
@ComponentScan("")
-
引入外部配置文件@PropertySource() 外部屬性配置文件。
-
@Import 使用配置比較靈活 @Import({Config.class})
-
@Value("${mysql.pass}")
-
源碼
總的來說,整體的執行步驟都是一樣的
即:讀取配置文件或配置文件XML,解析配置文件或者XML配置文件,后面的步驟就是相同的,注冊BeanDefinition,生產Bean.
29 如何在所有的BeanDefinition注冊完成后,進行擴展
Bean工廠的后置處理器,在所有的Bean注冊完成后,就被執行。
public class A implements BeanFactoryPostProcessor {private String name = "a class";private B b; ?public String getName() {return name;} ?public void setName(String name) {this.name = name;} ?@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {//在所有的BeanDefinition注冊完成之后調用for (String beanDefinitionName : configurableListableBeanFactory.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}} }
30 Bean的生產順序由什么決定的?
創建一個ApplicationContext,會調用refresh方法,再去調用finishBeanFactoryInitialization(),完成BeanFactory的初始化,創建所有的BeanDefinitionNames,是個ArrayList,按照注冊順序,進行創建,依賴關系也會影響Bean的創建順序。
BeanDefinition的注冊順序的由來?
主要是由注解(配置)的解析順序進行的。
-
@Configuration
-
@Component
-
@Import 導入的一個類
-
@Bean
-
@Import——ImportBeanDefinitionRegister
也可以使用@Order來決定順序,后面注冊的會覆蓋掉前面的注冊的。
BeanDefinitionRegistryPostProcessor
31 Spring有哪幾種配置方式?
-
注解 Spring 2.5 +
-
Spring.xml
-
-
XML
-
Spring.xml <component-scan base-package=""/> @Component @Autowired
-
-
基于Java配置
-
JavaConfig Spring 3.0+
-
@Configuration @Component
-
32 JavaConfig 如何替代 Spring.XML的配置方式的
應用
-
以前XML
-
Spring容器是new ClassPathXMLApplicationContext()
-
Spring.XML
-
<Bean scope="" >
-
<component-scan />
-
引入外部屬性配置文件:<property-placeHodeler resource="">
-
指定其他配置文件 @PropertySource("classpath:db.properties")
-
<property name="pass" value="${mysql.password}"></property>
-
-
JavaConfig
-
Spring容器是 nw AnnotationConfigApplicationContext(javaConfig.class)
-
JavaConfig.java ===> @Configuration
-
@Bean @Scope @Lazy
-
@ComponentScan("")
-
引入外部配置文件@PropertySource() 外部屬性配置文件。
-
@Import 使用配置比較靈活 @Import({Config.class})
-
@Value("${mysql.pass}")
-
源碼
總的來說,整體的執行步驟都是一樣的
即:讀取配置文件或配置文件XML,解析配置文件或者XML配置文件,后面的步驟就是相同的,注冊BeanDefinition,生產Bean.
33 @Component @Controller @Repository @Service 有何區別?
Controller、Repository和Service都是基于Component注解。
@Controller 放在控制層
@Service 放在業務層
@Repository 放在數據層
提高代碼可閱讀性。
34 @Import有幾種用法。
-
直接指定類(如果配置類會按配置類正常解析、如果是個普通類就會解析成Bean)
-
寫個實現ImportSelector的實現類
public class MyInportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.xing.Import.heihei"};} }
一次性可以注冊多個。
-
實現ImportBeanDefinitionRegistrar,RegisterBeanDefinitions方法中,創建BeanDefinition。
35 如何讓自動注入沒有找到依賴Bean時不報錯
@Autowired(required = false)
36 如何讓找到多個依賴Bean的時候不報錯
可以是在主要Bean上加入:@Primary注解。
37 @Autowired的作用
@Autowired的是自動注入,先按照類型,再按照名字注入。
38 @Autowired和@Resource的區別
-
Autowired是Spring提供的,Resource是JDK提供的。
-
Autowired默認根據類型匹配注入,Resource根據名字進行匹配,然后再根據類型進行匹配。
39 @Autowired注解自動裝配的過程是怎樣的
記住:@Autowired是通過Bean的后置處理器,進行解析的,可以用在:構造方法、普通方法、參數、字段和注解上。
-
核心方法:buildAutowiringMetadata
-
通過反射獲取該類的每一個字段和方法,并且分別用 findAutowiredAnnotation方法遍歷每一個字段和方法,如果有@Autowired注解修飾,則返回注解相關屬性。最后這個方法返回的就是包含所有帶有autowire注解修飾的一個InjectionMetadata集合。
-
-
InjectionMetadata類,這個類由兩部分組成: targetClass目標類和我們要獲得的injectedElements集合。
-
實現注入邏輯:調用InjectionMetadata中的公共inject方法遍歷調用protect的inject方法
-
調用InjectionMetadata中的公共inject
-
遍歷調用protect的inject方法
40 @Configuration作用及原理解析
作用
-
@Configuration用來代替xml配置方式spring.xml配置文件<bean>
-
但是沒有@Configuration也可以配置@Bean
-
那加和不加有什么區別呢?
-
加了@Configuration會為配置類創建一個cblib動態代理(保證配置類中的@Bean是單例),@Bean方法的調用就會通過容器的getBean獲取。
-
原理
-
創建Spring上下文的時候會注冊一個解析配置類的處理器:ConfigurationClassPostProcessor(實現了BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor)
-
調用invokeBeanFactoryPostProcessor方法,就會去調用ConfigurationClassPostProcessor的對應的方法(postProcessBeanDefinitionRegistry)進行解析配置(解析配置類說白了就是去解析各種注解(@Bean,@Configuration,@Import ),就是注冊BeanDefinition)
-
ConfigurationClassPostProcessor.postProcessorBeanFactory創建cblib動態代理。
@Bean怎么保證Bean的單例的。
@Configuration如何保證@Bean單例語義?_@bean 單例-CSDN博客
-
@Bean方法返回的對象是單例的,需要在類上加上@Configuration
41 要將一個第三方的類配置成為Bean有哪些方式
-
@Bean
-
@Import(DruidDataSource.class) 和@ImportSelector不能干預實例化過程。
-
寫個類實現ImportBeanDefinitionRegistrar
-
實現registerBeanDefinitions
public class index implements ImportBeanDefinitionRegistrar { ?@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {RootBeanDefinition A = new RootBeanDefinition(A.class);//拿到props valuesA.getPropertyValues().addPropertyValue("name","wangxing");registry.registerBeanDefinition("a",A);} }
-
這樣可以干涉bean的創建
-
-
實現BeanDefinitionRegistryPostProcessor
-
也是拿到注冊器,和上面一樣。
-
-
后兩種方法都是在SpringIOC的擴展點拿到BeanDefinitionRegistrar,然后注入BeanDefinition。
42 為什么@ComponentScan 不設置basePackage也會掃描?
因為Spring在解析@ComponentScan的時候,拿到basePackage,如果沒有涉及basePackage會把當前所在類的位置,作為掃描包。
43 什么是AOP,能做什么?
AOP(Aspect - Originted Proaramming),一般稱為面向切面編程,用于將那些與業務無關,但卻對多個對象產生影響的公共行為和邏輯,抽取并封裝為一個可重用的模塊,這個模塊被命名為“切面”(Aspect),減少系統中的重復代碼,降低了模塊間的耦合度,同時提高了系統的可維護性。可用于權限認證、日志、事務處理等。 -
AOP、OOP在字面上雖然非常類似,但卻是面向不同領域的兩種設計思想。0OP(面向對象編程)針對業務處理過程的實體及其屬性和行為進行抽象封裝,以獲得更加清浙高效的邏輯單元劃分。而AOP作為面向對象的一種補充,則是針對業務處理過程中的切面進行提取,已達到業務代碼和公共行為代碼之間低耦合性的隔離效果。這兩種設計思想在目標上有著本質的差異。
44 在Spring中AOP如何使用?
鏈接:Spring中使用AOP的幾種方式_doaround-CSDN博客
XML方式
XML + 注解
注解方式
45 Spring AOP 中常見的名詞
-
切面(Aspect):在Spring AOP指的就是切面類,切面類會管理著通知和切入點。
-
連接點(Join point) :被增強的業務方法。
-
通知(Advice) : 就是需要增強到業務方法中的公共代碼,通知有很多種類分別可以在需要增強的業務方法不同位置進行(前置,后置,異常,返回,環繞通知)。
-
切入點(Pointcut):由切點表達式決定需要增強的方法,通常通過切點表達式進行。
-
目標對象(Target Object):增強的對象。
-
顧問:Advisor的一個包裝,Advisor是Pointcut以及Advice的一個結合,用來管理Advice和Pointcut。
-
織入(Weaving) :AspectJ中獨有的內容,Spring AOP :動態代理。為目標對象創建動態代理的過程叫做織入。
-
編譯期:切面在目標類編譯時被織入。AspectJ的織入編譯器是以這種
-
類加載期:切面在目標類加載到JVM時被織入。需要特殊的類加載器之可以在目標類被引入應用之前增強該目標類的字節碼。AspectJ5的加載時織入就支持以這種方式織入切面。
-
運行期:切面在應用運行的某個時刻被織入。一般情況下,在織入切面時,AOP容器會為目標對象動態地創建一個代理對象。SpringAOP就是以這種方式織入切面。
-
46 Spring通知有哪些類型?
AOP:在某個特定的連接點(方法),執行的動作(代碼)。
-
前置通知(Before)
-
后置通知(After)
-
返回通知(After-returning)
-
異常通知(After-throwing)
-
環繞通知(Around)
執行順序
正常: 前置 -> 方法 -> 后置 -> 返回。
異常:前置 -> 方法 -> 后置 -> 異常
Spring 5.2.7 之后
正常: 前置 -> 方法 -> 返回 -> 后置
異常: 前置 -> 方法 -> 異常 -> 后置
47 SpringAOP和AspectJ AOP有什么區別?
AspectJ AOP也是一個面向切面的開源框架,如果要使用Spring中的AOP功能,需要引入對應的AspectJ AOP 依賴,但SpringAOP使用的僅僅是AspectJ AOP 的切點解析和匹配,AspectJ AOP 中的一些注解。
-
AspectJ AOP 使用的靜態代理。
-
AspectJ是靜態代理的增強,所謂靜態代理,就是AOP框架會在編譯階段生成AOP代理類,因此也稱為編譯時增強,他會在編譯階段將Aspect(切面)織入到Java字節碼中,運行的時候就是增強之后的AOP對象。
-
-
Spring AOP 使用的是動態代理。
-
如果實現接口,是JDK的動態代理
-
沒有實現接口就是CGLIB實現動態代理
-
SpringAOP致力解決企業中開發的普遍問題,AspectJ是AOP編程的完全解決方案。還有SpringAOP的性能不如AspectJ,因為SpringAOP做的是動態代理,會在容器初始化的時候加載,會加深調用棧。
48 JDK動態代理和CGLIB動態代理的區別
-
如果實現接口,是JDK的動態代理
-
JDK會在運行的時候,為目標類生成一個動態代理類$proxy*.class
-
該代理類是實現了接目標類接口,并且代理類會實現接口所有的方法增強代碼。
-
調用時 通過代理類先去調用處理類進行增強,再通過反射的方式進行調用目標方法。從而實現AOP
-
-
沒有實現接口就是CGLIB實現動態代理
-
并且會重寫父類所有的方法增強代碼,CGLIB的底層是通過ASM在運行時動態的生成目標類的一個子類。 (還有其他相關類,主要是為增強調用時效率)會生成多個
-
調用時先通過代理類進行增強,再直接調用父類對應的方法進行調用目標方法。從而實現AOP。
-
final標記的類是無法實現CGLIB代理的。
-
-
JDK生成的快,調用慢;CGLIB生產慢,調用快,但是在JDK老版本中,CGLIB的速度會比JDK快,但是隨著JDK的更新,JDK是要比CGLIB快
33 @Component @Controller @Repository @Service 有何區別?
Controller、Repository和Service都是基于Component注解。
@Controller 放在控制層
@Service 放在業務層
@Repository 放在數據層
提高代碼可閱讀性。
34 @Import有幾種用法。
-
直接指定類(如果配置類會按配置類正常解析、如果是個普通類就會解析成Bean)
-
寫個實現ImportSelector的實現類
public class MyInportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.xing.Import.heihei"};} }
一次性可以注冊多個。
-
實現ImportBeanDefinitionRegistrar,RegisterBeanDefinitions方法中,創建BeanDefinition。
35 如何讓自動注入沒有找到依賴Bean時不報錯
@Autowired(required = false)
36 如何讓找到多個依賴Bean的時候不報錯
可以是在主要Bean上加入:@Primary注解。
37 @Autowired的作用
@Autowired的是自動注入,先按照類型,再按照名字注入。
38 @Autowired和@Resource的區別
-
Autowired是Spring提供的,Resource是JDK提供的。
-
Autowired默認根據類型匹配注入,Resource根據名字進行匹配,然后再根據類型進行匹配。
39 @Autowired注解自動裝配的過程是怎樣的
記住:@Autowired是通過Bean的后置處理器,進行解析的,可以用在:構造方法、普通方法、參數、字段和注解上。
-
核心方法:buildAutowiringMetadata
-
通過反射獲取該類的每一個字段和方法,并且分別用 findAutowiredAnnotation方法遍歷每一個字段和方法,如果有@Autowired注解修飾,則返回注解相關屬性。最后這個方法返回的就是包含所有帶有autowire注解修飾的一個InjectionMetadata集合。
-
-
InjectionMetadata類,這個類由兩部分組成: targetClass目標類和我們要獲得的injectedElements集合。
-
實現注入邏輯:調用InjectionMetadata中的公共inject方法遍歷調用protect的inject方法
-
調用InjectionMetadata中的公共inject
-
遍歷調用protect的inject方法
40 @Configuration作用及原理解析
作用
-
@Configuration用來代替xml配置方式spring.xml配置文件<bean>
-
但是沒有@Configuration也可以配置@Bean
-
那加和不加有什么區別呢?
-
加了@Configuration會為配置類創建一個cblib動態代理(保證配置類中的@Bean是單例),@Bean方法的調用就會通過容器的getBean獲取。
-
原理
-
創建Spring上下文的時候會注冊一個解析配置類的處理器:ConfigurationClassPostProcessor(實現了BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor)
-
調用invokeBeanFactoryPostProcessor方法,就會去調用ConfigurationClassPostProcessor的對應的方法(postProcessBeanDefinitionRegistry)進行解析配置(解析配置類說白了就是去解析各種注解(@Bean,@Configuration,@Import ),就是注冊BeanDefinition)
-
ConfigurationClassPostProcessor.postProcessorBeanFactory創建cblib動態代理。
@Bean怎么保證Bean的單例的。
@Configuration如何保證@Bean單例語義?_@bean 單例-CSDN博客
-
@Bean方法返回的對象是單例的,需要在類上加上@Configuration
41 要將一個第三方的類配置成為Bean有哪些方式
-
@Bean
-
@Import(DruidDataSource.class) 和@ImportSelector不能干預實例化過程。
-
寫個類實現ImportBeanDefinitionRegistrar
-
實現registerBeanDefinitions
public class index implements ImportBeanDefinitionRegistrar { ?@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {RootBeanDefinition A = new RootBeanDefinition(A.class);//拿到props valuesA.getPropertyValues().addPropertyValue("name","wangxing");registry.registerBeanDefinition("a",A);} }
-
這樣可以干涉bean的創建
-
-
實現BeanDefinitionRegistryPostProcessor
-
也是拿到注冊器,和上面一樣。
-
-
后兩種方法都是在SpringIOC的擴展點拿到BeanDefinitionRegistrar,然后注入BeanDefinition。
42 為什么@ComponentScan 不設置basePackage也會掃描?
因為Spring在解析@ComponentScan的時候,拿到basePackage,如果沒有涉及basePackage會把當前所在類的位置,作為掃描包。
43 什么是AOP,能做什么?
AOP(Aspect - Originted Proaramming),一般稱為面向切面編程,用于將那些與業務無關,但卻對多個對象產生影響的公共行為和邏輯,抽取并封裝為一個可重用的模塊,這個模塊被命名為“切面”(Aspect),減少系統中的重復代碼,降低了模塊間的耦合度,同時提高了系統的可維護性。可用于權限認證、日志、事務處理等。 -
AOP、OOP在字面上雖然非常類似,但卻是面向不同領域的兩種設計思想。0OP(面向對象編程)針對業務處理過程的實體及其屬性和行為進行抽象封裝,以獲得更加清浙高效的邏輯單元劃分。而AOP作為面向對象的一種補充,則是針對業務處理過程中的切面進行提取,已達到業務代碼和公共行為代碼之間低耦合性的隔離效果。這兩種設計思想在目標上有著本質的差異。
44 在Spring中AOP如何使用?
鏈接:Spring中使用AOP的幾種方式_doaround-CSDN博客
XML方式
XML + 注解
注解方式
45 Spring AOP 中常見的名詞
-
切面(Aspect):在Spring AOP指的就是切面類,切面類會管理著通知和切入點。
-
連接點(Join point) :被增強的業務方法。
-
通知(Advice) : 就是需要增強到業務方法中的公共代碼,通知有很多種類分別可以在需要增強的業務方法不同位置進行(前置,后置,異常,返回,環繞通知)。
-
切入點(Pointcut):由切點表達式決定需要增強的方法,通常通過切點表達式進行。
-
目標對象(Target Object):增強的對象。
-
顧問:Advisor的一個包裝,Advisor是Pointcut以及Advice的一個結合,用來管理Advice和Pointcut。
-
織入(Weaving) :AspectJ中獨有的內容,Spring AOP :動態代理。為目標對象創建動態代理的過程叫做織入。
-
編譯期:切面在目標類編譯時被織入。AspectJ的織入編譯器是以這種
-
類加載期:切面在目標類加載到JVM時被織入。需要特殊的類加載器之可以在目標類被引入應用之前增強該目標類的字節碼。AspectJ5的加載時織入就支持以這種方式織入切面。
-
運行期:切面在應用運行的某個時刻被織入。一般情況下,在織入切面時,AOP容器會為目標對象動態地創建一個代理對象。SpringAOP就是以這種方式織入切面。
-
46 Spring通知有哪些類型?
AOP:在某個特定的連接點(方法),執行的動作(代碼)。
-
前置通知(Before)
-
后置通知(After)
-
返回通知(After-returning)
-
異常通知(After-throwing)
-
環繞通知(Around)
執行順序
正常: 前置 -> 方法 -> 后置 -> 返回。
異常:前置 -> 方法 -> 后置 -> 異常
Spring 5.2.7 之后
正常: 前置 -> 方法 -> 返回 -> 后置
異常: 前置 -> 方法 -> 異常 -> 后置
47 SpringAOP和AspectJ AOP有什么區別?
AspectJ AOP也是一個面向切面的開源框架,如果要使用Spring中的AOP功能,需要引入對應的AspectJ AOP 依賴,但SpringAOP使用的僅僅是AspectJ AOP 的切點解析和匹配,AspectJ AOP 中的一些注解。
-
AspectJ AOP 使用的靜態代理。
-
AspectJ是靜態代理的增強,所謂靜態代理,就是AOP框架會在編譯階段生成AOP代理類,因此也稱為編譯時增強,他會在編譯階段將Aspect(切面)織入到Java字節碼中,運行的時候就是增強之后的AOP對象。
-
-
Spring AOP 使用的是動態代理。
-
如果實現接口,是JDK的動態代理
-
沒有實現接口就是CGLIB實現動態代理
-
SpringAOP致力解決企業中開發的普遍問題,AspectJ是AOP編程的完全解決方案。還有SpringAOP的性能不如AspectJ,因為SpringAOP做的是動態代理,會在容器初始化的時候加載,會加深調用棧。
48 JDK動態代理和CGLIB動態代理的區別
-
如果實現接口,是JDK的動態代理
-
JDK會在運行的時候,為目標類生成一個動態代理類$proxy*.class
-
該代理類是實現了接目標類接口,并且代理類會實現接口所有的方法增強代碼。
-
調用時 通過代理類先去調用處理類進行增強,再通過反射的方式進行調用目標方法。從而實現AOP
-
-
沒有實現接口就是CGLIB實現動態代理
-
并且會重寫父類所有的方法增強代碼,CGLIB的底層是通過ASM在運行時動態的生成目標類的一個子類。 (還有其他相關類,主要是為增強調用時效率)會生成多個
-
調用時先通過代理類進行增強,再直接調用父類對應的方法進行調用目標方法。從而實現AOP。
-
final標記的類是無法實現CGLIB代理的。
-
-
JDK生成的快,調用慢;CGLIB生產慢,調用快,但是在JDK老版本中,CGLIB的速度會比JDK快,但是隨著JDK的更新,JDK是要比CGLIB快