街道辦的網(wǎng)站由誰(shuí)做的鄭州疫情最新動(dòng)態(tài)
1、條件裝配
??????? 在上一篇中,我們介紹了Spring,Spring MVC常見(jiàn)類的自動(dòng)裝配,在源碼中可見(jiàn)許多以@Conditional...開(kāi)頭的注解:
?????????@Conditional 注解是Spring 框架提供的一種條件化裝配的機(jī)制,它可以根據(jù)特定的條件來(lái)控制 Bean 的創(chuàng)建是否生效。
??????? 下面是一些常見(jiàn)的使用場(chǎng)景:
-
根據(jù)環(huán)境變量選擇性地加載 Bean:你可以根據(jù)應(yīng)用程序運(yùn)行的環(huán)境,比如開(kāi)發(fā)環(huán)境、測(cè)試環(huán)境或者生產(chǎn)環(huán)境,來(lái)決定是否加載某個(gè) Bean。例如,你可以通過(guò)@Conditional注解根據(jù)不同的環(huán)境變量來(lái)選擇性地加載數(shù)據(jù)庫(kù)配置。
-
根據(jù)系統(tǒng)屬性選擇性地加載 Bean:類似于環(huán)境變量,你也可以根據(jù)系統(tǒng)的某些屬性來(lái)選擇性地加載 Bean。這種情況下,你可以編寫自定義的條件判斷邏輯來(lái)決定是否滿足條件。
-
根據(jù)類路徑下是否存在特定的類來(lái)加載 Bean:有時(shí)你可能希望在類路徑下存在某些特定的類時(shí)才加載某個(gè) Bean。通過(guò)@Conditional注解,你可以指定類路徑下是否存在某個(gè)特定的類來(lái)決定 Bean 的加載。
-
自定義條件判斷邏輯:除了上述常見(jiàn)的條件判斷方式外,你還可以編寫自定義的條件判斷邏輯來(lái)決定是否加載 Bean。這樣你可以根據(jù)應(yīng)用程序的具體需求來(lái)定制條件。
??????? 下面我們通過(guò)一個(gè)案例演示下上面的第三條使用場(chǎng)景:
??????? 我們初始有五個(gè)類,分別是Bean1和Bean2,以及注冊(cè)Bean1的配置類MyConfig1,注冊(cè)Bean2的配置類MyConfig2,以及本項(xiàng)目的配置類Config。其中在本項(xiàng)目的配置類中,通過(guò)@Import(MyImportSelector.class) 去在MyImportSelector類中自動(dòng)裝配MyConfig1,MyConfig2。該過(guò)程此前已經(jīng)多次演示,這里就不重復(fù)貼代碼了。
??????? 我們想要的效果是,在MyConfig1中注冊(cè)Bean1時(shí),需要類路徑下包含"com.alibaba.druid.pool.DruidAbstractDataSource",注冊(cè)Bean2時(shí)則不需要包含DruidAbstractDataSource:
??????? 自定義一個(gè)組合注解@ConditionalOnClass ,在組合注解中,又包含了@Conditional(MyConditional1.class) 注解。
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(MyConditional1.class)
public @interface ConditionalOnClass {/*** 判斷是否存在* @return*/boolean exist();/*** 要判斷的類名* @return*/String value();
}
????????MyConditional1是真正的條件裝配類,用于編寫選擇的邏輯,需要實(shí)現(xiàn)Condition接口:
/*** 自定義條件裝配類,實(shí)現(xiàn)Condition接口,配合@Conditional()注解使用,編寫條件裝配邏輯*/
public class MyConditional1 implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {//得到@ConditionalOnClass 注解上的信息Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());boolean exist = (boolean) attributes.get("exist");String value = attributes.get("value").toString();boolean present = ClassUtils.isPresent(value, null);return exist == present;
// return ClassUtils.isPresent("com.alibaba.druid.pool.DruidAbstractDataSource",null);}
}
??????? 然后我們?cè)贛yConfig1和MyConfig2上加入組合注解:
@Configuration
@ConditionalOnClass(exist = true,value = "com.alibaba.druid.pool.DruidAbstractDataSource")
public class MyConfig1 {@Beanpublic Bean1 bean1(){return new Bean1();}
}
@Configuration
@ConditionalOnClass(exist = false,value = "com.alibaba.druid.pool.DruidAbstractDataSource")
public class MyConfig2 {@Beanpublic Bean2 bean2(){return new Bean2();}
}
??????? 在主類中獲取BeanDefinitionNames,最終能獲取到Bean1,因?yàn)楫?dāng)前類路徑下存在DruidAbstractDataSource,MyConfig1的@ConditionalOnClass? 注解條件成立。
2、FactoryBean
????????FactoryBean 是 Spring 框架中的一個(gè)接口,用于創(chuàng)建復(fù)雜對(duì)象或者需要特殊處理的對(duì)象實(shí)例。它允許開(kāi)發(fā)人員自定義對(duì)象的創(chuàng)建過(guò)程,靈活地控制對(duì)象的創(chuàng)建邏輯、生命周期和其他行為。
??????? 其中有三個(gè)重要方法:
- getObject():用于自定義創(chuàng)建對(duì)象實(shí)例的邏輯,并且返回由工廠創(chuàng)建的對(duì)象實(shí)例。
- getObjectType():這個(gè)方法用于返回由工廠創(chuàng)建的對(duì)象的類型。Spring 需要根據(jù)類型來(lái)判斷如何注入或者使用這個(gè)對(duì)象(非根據(jù)名稱)
- isSingleton(): 用于控制返回的對(duì)象是否為單例。
??????? 其中getObjectType() 方法,如果返回為null,那么從容器中根據(jù)類型獲取Bean對(duì)象就會(huì)報(bào)錯(cuò)。??????
??????? 使用示例:
@Component("bean1")
public class Bean1FactoryBean implements FactoryBean<Bean1> {private static final Logger log = LoggerFactory.getLogger(Bean1FactoryBean.class);// 決定了根據(jù)【類型】獲取或依賴注入能否成功//context.getBean(Bean1.class) 根據(jù)類型獲取 如果此方法返回null則會(huì)報(bào)錯(cuò)@Overridepublic Class<?> getObjectType() {return Bean1.class;}// 決定了 getObject() 方法被調(diào)用一次還是多次(單例還是多例)@Overridepublic boolean isSingleton() {return true;}/*** 得到bean1對(duì)象* @return* @throws Exception*/@Overridepublic Bean1 getObject() throws Exception {Bean1 bean1 = new Bean1();log.debug("create bean: {}", bean1);return bean1;}
}
?????? FactoryBean和此前多次提到的BeanFactory有什么區(qū)別?
- FactoryBean 是用于創(chuàng)建特定類型對(duì)象的工廠接口,負(fù)責(zé)對(duì)象的實(shí)例化和定制化,通常被用來(lái)創(chuàng)建一些比較復(fù)雜或者需要特殊處理的對(duì)象
- BeanFactory 是Spring IoC 容器的基礎(chǔ)接口,負(fù)責(zé)管理和控制所有 Bean 對(duì)象的創(chuàng)建、裝配和管理。
??????? 被 FactoryBean 創(chuàng)建的對(duì)象會(huì)有以下的特點(diǎn):
- Bean的創(chuàng)建、依賴注入、Aware 接口回調(diào)、前初始化流程都不會(huì)走
- 后初始化的流程會(huì)走,創(chuàng)建的Bean可以被代理增強(qiáng)
- 單例的產(chǎn)品不會(huì)存儲(chǔ)于 BeanFactory 的 singletonObjects 成員中, 而是另一個(gè) factoryBeanObjectCache 成員中
??????? 我們有一個(gè)Bean1,在上方的FactoryBean 使用案例中被創(chuàng)建:
public class Bean1 implements BeanFactoryAware {private static final Logger log = LoggerFactory.getLogger(Bean1.class);private Bean2 bean2;/*** 依賴注入bean2* @param bean2*/@Autowiredpublic void setBean2(Bean2 bean2) {log.debug("setBean2({})", bean2);this.bean2 = bean2;}public Bean2 getBean2() {return bean2;}/*** 初始化bean1*/@PostConstructpublic void init() {log.debug("init");}/*** 設(shè)置bean工廠* @param beanFactory* @throws BeansException*/@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {log.debug("setBeanFactory({})", beanFactory);}
}
??????? 并且加入一個(gè)處理器:
@Component
public class Bean1PostProcessor implements BeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(Bean1PostProcessor.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("bean1") && bean instanceof Bean1) {log.info("before [{}] init", beanName);}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("bean1") && bean instanceof Bean1) {log.info("after [{}] init", beanName);}return bean;}
}
??????? 我們從容器中獲取Bean1,發(fā)現(xiàn)Bean的創(chuàng)建、依賴注入、Aware 接口回調(diào)、前初始化流程對(duì)應(yīng)的信息都沒(méi)有被打印,但是后處理流程被執(zhí)行:
2024-05-08 19:53:17.326 [main] INFO com.itbaima.a43.Bean1PostProcessor.postProcessAfterInitialization:25 - after [bean1] init
com.itbaima.a43.Bean1@59d4cd39
??????? 如果我們要獲取FactoryBean 的Bean實(shí)例,雖然在FactoryBean 上通過(guò)@Component("bean1") 注解將FactoryBean 的ID設(shè)置成了bean1,但是如果通過(guò)
context.getBean("bean1")
獲取到的并非是FactoryBean 的Bean實(shí)例,而是Bean1實(shí)例。原因在于當(dāng) Spring 容器需要獲取 Bean1類型的 Bean 時(shí),它會(huì)調(diào)用 Bean1FactoryBean的 getObject()方法,從而得到 Bean1實(shí)例。
??????? 如果需要通過(guò)名稱獲取FactoryBean 的實(shí)例,需要:
context.getBean("&bean1")
3、@Indexed
??????? 假設(shè)我們現(xiàn)在有一個(gè)包a44,包下有三個(gè)加上了@Component注解的類,通常會(huì)這樣進(jìn)行組件掃描:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 組件掃描的核心類
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan(A44.class.getPackage().getName());
??????? Spring會(huì)將指定路徑下所有加上了相關(guān)注解的類加入DefaultListableBeanFactory中統(tǒng)一管理。但是在Spring 5及以后的版本,對(duì)上面的操作進(jìn)行了優(yōu)化:
??????? 首先會(huì)去編譯后項(xiàng)目的target-classes-META-INF文件夾下找到一個(gè)spring.components的文件,并以其中的內(nèi)容為準(zhǔn)加載 bean definition,否則再去遍歷包下所有 class 資源。
??????? 其底層就在于,在編譯時(shí)根據(jù) @Indexed 生成spring.components文件,而我們常見(jiàn)的Spring相關(guān)注解@Component?? 其中就包含了@Indexed :
??????? 要實(shí)現(xiàn)@Indexed? 優(yōu)化,還需要在項(xiàng)目中導(dǎo)入jar包:
<dependency><groupId>org.springframework</groupId><artifactId>spring-context-indexer</artifactId><optional>true</optional></dependency>
4、Spring代理的特點(diǎn)
??????? 我們寫了一個(gè)Aop類,需要對(duì)Bean1中的所有方法進(jìn)行增強(qiáng):
@Aspect
@Component
public class MyAspect {// 對(duì)所有方法增強(qiáng)@Before("execution(* com.itbaima.a45.Bean1.*(..))")public void before() {System.out.println("before");}
}
??????? Bean1:
@Component
public class Bean1 {private static final Logger log = LoggerFactory.getLogger(Bean1.class);protected Bean2 bean2;protected boolean initialized;@Autowiredpublic void setBean2(Bean2 bean2) {log.info("setBean2(Bean2 bean2)");this.bean2 = bean2;}@PostConstructpublic void info() {log.info("init");initialized = true;}public Bean2 getBean2() {log.info("getBean2()");return bean2;}public boolean isInitialized() {log.info("isInitialized()");return initialized;}}
??????? 在日志中,依賴注入和初始化的階段都已執(zhí)行,但是沒(méi)有被增強(qiáng),調(diào)用的是原始方法。
???????? 依賴注入和初始化階段都完成,拿到Bean后,再次去調(diào)用setBean2()和init() 方法,會(huì)被增強(qiáng),調(diào)用的是代理的方法。
??????? 即:依賴注入和初始化影響的是原有的方法
??????? 我們?cè)賹懸粋€(gè)方法查看下代理中的成員變量和目標(biāo)的成員變量的區(qū)別:
public static void showProxyAndTarget(Bean1 proxy) throws Exception {System.out.println(">>>>> 代理中的成員變量");System.out.println("\tinitialized=" + proxy.initialized);System.out.println("\tbean2=" + proxy.bean2);if (proxy instanceof Advised ) {Advised proxy1 = (Advised) proxy;System.out.println(">>>>> 目標(biāo)中的成員變量");Bean1 target = (Bean1) proxy1.getTargetSource().getTarget();System.out.println("\tinitialized=" + target.initialized);System.out.println("\tbean2=" + target.bean2);}}
>>>>> 代理中的成員變量
?? ?initialized=false
?? ?bean2=null
>>>>> 目標(biāo)中的成員變量
?? ?initialized=true
?? ?bean2=com.itbaima.a45.Bean2@7915bca3
??????? 這個(gè)方法的調(diào)用時(shí)機(jī)是從容器中獲取Bean(Bean已初始化完成)發(fā)現(xiàn)代理中的成員變量沒(méi)有賦值,都是默認(rèn)值。而目標(biāo)中的成員變量已經(jīng)賦值。( 在spring高級(jí)篇(三)代理的創(chuàng)建時(shí)機(jī)中提到過(guò),如果沒(méi)有循環(huán)依賴,代理是在初始化之后創(chuàng)建。)
??????? 說(shuō)明了代理與目標(biāo)是兩個(gè)對(duì)象,二者成員變量并不共用數(shù)據(jù),以及依賴注入和初始化的階段調(diào)用的是原始方法。
?????? 被static、final、private關(guān)鍵字修飾的方法均無(wú)法增強(qiáng),因?yàn)镃GLIB動(dòng)態(tài)代理是生成目標(biāo)的子類。(上一篇分析過(guò),當(dāng)properties配置文件中有spring.aop前綴的鍵,并且值為false時(shí),才會(huì)走JDK動(dòng)態(tài)代理)
5、@Value注入
??????? @Value用于將屬性值注入到 Spring 管理的 Bean 中。
????????可以直接將基本類型值或字符串值注入到 Bean 的屬性中,或者從外部配置文件/環(huán)境變量中讀取值:
public class Bean1 {@Value("${JAVA_HOME}")private String home;@Value("18")private int age;}
??????? 也可以使用 SpEL(Spring Expression Language)表達(dá)式來(lái)動(dòng)態(tài)地計(jì)算屬性值:
public class Bean2 {@Value("#{@bean3}") // SpringEL #{SpEL}private Bean3 bean3;
}@Component("bean3")
public class Bean3 {}
??????? 或者結(jié)合使用:
static class Bean4 {@Value("#{'hello, ' + '${JAVA_HOME}'}")private String value;}
??????? 下面來(lái)演示一下如何解析@Value 并獲取值:
????????ContextAnnotationAutowireCandidateResolver用于解析@Value ,還可以解析@Lazy注解
??????? 解析${}:
public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A46.class);DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();resolver.setBeanFactory(beanFactory);test1(context, resolver, Bean1.class.getDeclaredField("home"));}private static void test1(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {DependencyDescriptor dd1 = new DependencyDescriptor(field, false);// 獲取 @Value 的內(nèi)容String value = resolver.getSuggestedValue(dd1).toString();System.out.println(value);// 解析 ${}value = context.getEnvironment().resolvePlaceholders(value);System.out.println(value);
}
??????? 解析字符串/基本數(shù)據(jù)類型:(解析出的類型默認(rèn)都是字符串,需要使用轉(zhuǎn)換器轉(zhuǎn)換成真實(shí)的類型,這一點(diǎn)和MVC中的參數(shù)類型轉(zhuǎn)換類似。)
public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A46.class);DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();resolver.setBeanFactory(beanFactory);test2(context, resolver, Bean1.class.getDeclaredField("age"));}private static void test2(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {DependencyDescriptor dd1 = new DependencyDescriptor(field, false);// 獲取 @Value 的內(nèi)容String value = resolver.getSuggestedValue(dd1).toString();System.out.println(value);value = context.getEnvironment().resolvePlaceholders(value);System.out.println(value);System.out.println(value.getClass());Object age = context.getBeanFactory().getTypeConverter().convertIfNecessary(value, dd1.getDependencyType());System.out.println(age.getClass());}
??????? 解析SpEL表達(dá)式: 順序?yàn)?#xff1a;先解析@Value的內(nèi)容,然后解析${},最后解析#{}
public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A46.class);DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();resolver.setBeanFactory(beanFactory);test3(context, resolver, Bean2.class.getDeclaredField("bean3"));}private static void test3(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {DependencyDescriptor dd1 = new DependencyDescriptor(field, false);// 獲取 @Value 的內(nèi)容String value = resolver.getSuggestedValue(dd1).toString();System.out.println(value);// 解析 ${}value = context.getEnvironment().resolvePlaceholders(value);System.out.println(value);System.out.println(value.getClass());// 解析 #{} @bean3Object bean3 = context.getBeanFactory().getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(context.getBeanFactory(), null));// 類型轉(zhuǎn)換Object result = context.getBeanFactory().getTypeConverter().convertIfNecessary(bean3, dd1.getDependencyType());System.out.println(result);}
6、@Autowired注入底層
??????? 首先回顧一下本系列中關(guān)于@Autowired的知識(shí):
????????@Autowired 默認(rèn)是按照類型進(jìn)行裝配,也可以配合@Qualifier 進(jìn)行名稱裝配。當(dāng)@Autowired和@Resource 同時(shí)加在字段或者方法上時(shí),會(huì)以@Autowired 為準(zhǔn)。因?yàn)樗诤筇幚砥髋判蛑邢噍^于@Resource 靠前(在Spring底層入門第一篇中有過(guò)驗(yàn)證)。
??????? 這里我們演示下各種情況的@Autowired 注入:
??????? 其核心在于利用beanFactory的doResolveDependency()方法
??????? 分別對(duì)Bean2進(jìn)行方法注入,字段注入,以及包裝成Optional和ObjectFactory類型的注入:
static class Bean1 {@Autowiredprivate Bean2 bean2;@Autowired public void setBean2(Bean2 bean2) {this.bean2 = bean2;}@Autowiredprivate Optional<Bean2> bean3;@Autowired private ObjectFactory<Bean2> bean4;}@Component("bean2")static class Bean2 {@Overridepublic String toString() {return super.toString();}}
??????? 準(zhǔn)備一個(gè)Spring容器和Bean工廠:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A47_1.class);
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
??????? 根據(jù)成員變量和參數(shù)的值注入(首先要得到Bean2在Bean1中待注入的方法或字段):
// 1. 根據(jù)成員變量的類型注入DependencyDescriptor dd1 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);System.out.println(beanFactory.doResolveDependency(dd1, "bean1", null, null));System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");// 2. 根據(jù)參數(shù)的類型注入Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);System.out.println(beanFactory.doResolveDependency(dd2, "bean1", null, null));System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
??????? 如果結(jié)果包裝成其他引用數(shù)據(jù)類型,需要通過(guò)DependencyDescriptor的increaseNestingLevel()找到內(nèi)層真正需要注入的Bean,而非外層的包裝類。在注入完成后,再將結(jié)果進(jìn)行包裝。
// 3. 結(jié)果包裝為 Optional<Bean2>DependencyDescriptor dd3 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean3"), false);if (dd3.getDependencyType() == Optional.class) {dd3.increaseNestingLevel();Object result = beanFactory.doResolveDependency(dd3, "bean1", null, null);System.out.println(Optional.ofNullable(result));}System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");// 4. 結(jié)果包裝為 ObjectProvider,ObjectFactoryDependencyDescriptor dd4 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean4"), false);if (dd4.getDependencyType() == ObjectFactory.class) {dd4.increaseNestingLevel();ObjectFactory objectFactory = new ObjectFactory() {@Overridepublic Object getObject() throws BeansException {return beanFactory.doResolveDependency(dd4, "bean1", null, null);}};System.out.println(objectFactory.getObject());}System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
??????? 如果字段或者方法參數(shù)上加入了@Lazy注解,需要通過(guò)ContextAnnotationAutowireCandidateResolver解析@Lazy注解并且生成代理:
????????getLazyResolutionProxyIfNecessary() 方法:判斷需要解析的方法/字段上是否有@Lazy 注解,如果有就會(huì)生成代理:
// 5. 對(duì) @Lazy 的處理DependencyDescriptor dd5 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();resolver.setBeanFactory(beanFactory);Object proxy = resolver.getLazyResolutionProxyIfNecessary(dd5, "bean1");System.out.println(proxy);System.out.println(proxy.getClass());
??????? 以上就是@Autowired 四種注入情況的實(shí)現(xiàn)分析,其中ObjectFactory,@Lazy 注解也體現(xiàn)了延遲加載的思想,目的之一是為了解決單例容器注入多例Bean失效的情況。
??????? 下面我們?cè)傺菔編追N注入:
????????假設(shè)現(xiàn)在有一個(gè)接口以及三個(gè)實(shí)現(xiàn)類:
interface Service {}@Component("service1")static class Service1 implements Service {}@Component("service2")static class Service2 implements Service {}@Component("service3")static class Service3 implements Service {}
??????? 在目標(biāo)類中需要分別注入數(shù)組類型的serviceArray和Service集合類型的serviceList
static class Target {@Autowired private Service[] serviceArray;@Autowired private List<Service> serviceList;}
??????? 注入數(shù)組類型的serviceArray:
- 首先需要得到目標(biāo)類中待解析的字段
- 該字段的類型是數(shù)組,進(jìn)入if條件塊
- 得到組件的類型
- 獲取所有與組件類型匹配的 Bean 的名稱。
- 解析候選 Bean,并將解析得到的 Bean 添加到一個(gè)集合中
- 調(diào)用轉(zhuǎn)換器將集合轉(zhuǎn)換成為數(shù)組
private static void testArray(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("serviceArray"), true);if (dd1.getDependencyType().isArray()) {Class<?> componentType = dd1.getDependencyType().getComponentType();System.out.println(componentType);String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);List<Object> beans = new ArrayList<>();for (String name : names) {System.out.println(name);Object bean = dd1.resolveCandidate(name, componentType, beanFactory);beans.add(bean);}Object array = beanFactory.getTypeConverter().convertIfNecessary(beans, dd1.getDependencyType());System.out.println(array);}}
????????注入Service集合類型的serviceList
- 首先需要得到目標(biāo)類中待解析的字段
- 該字段的類型是集合,進(jìn)入if條件塊
- 獲取依賴類型的泛型參數(shù)類型(Service接口)。
- 獲取所有與泛型參數(shù)類型匹配的 Bean 的名稱。
- 解析候選 Bean,并將解析得到的 Bean 添加到一個(gè)集合中
private static void testList(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {DependencyDescriptor dd2 = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), true);if (dd2.getDependencyType() == List.class) {Class<?> resolve = dd2.getResolvableType().getGeneric().resolve();System.out.println(resolve);List<Object> list = new ArrayList<>();String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, resolve);for (String name : names) {Object bean = dd2.resolveCandidate(name, resolve, beanFactory);list.add(bean);}System.out.println(list);}}
??????? 由此可知,注入接口類型的數(shù)組或集合,實(shí)際注入的是接口所有的實(shí)現(xiàn)類
??????? 如果待注入的字段不是自定義的類或接口,而是Spring中自有的Bean,那么@Autowired 會(huì)如何進(jìn)行注入?
@Autowired
private ConfigurableApplicationContext applicationContext;
??????? Spring會(huì)將所有成品的bean放在DefaultListableBeanFactory的上級(jí)接口DefaultSingletonBeanRegistry中:
??????? Spring在執(zhí)行refresh()方法中的prepareBeanFactory()方法時(shí)
????????也會(huì)將BeanFactory,ApplicationContext等放在DefaultListableBeanFactory的resolvableDependencies成員變量里:
????????
????????resolvableDependencies是一個(gè)Map集合,Key存放的是類的字節(jié)碼文件,Value存放的是對(duì)應(yīng)的類
??????? 有了上述的知識(shí)儲(chǔ)備,我們以解析ApplicationContext的子類ConfigurableApplicationContext為例:
??????? 首先要得到目標(biāo)對(duì)象上待解析的字段,并封裝成DependencyDescriptor類型:
DependencyDescriptor dd3 = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), true);
??????? 然后通過(guò)反射得到key為BeanFactory時(shí)resolvableDependencies中的值
Field resolvableDependencies = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
resolvableDependencies.setAccessible(true);
Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) resolvableDependencies.get(beanFactory);
??????? 最后我們遍歷這個(gè)集合,因?yàn)樾枰馕龅腃onfigurableApplicationContext是Map中ApplicationContext的子類,我們需要利用.isAssignableFrom()? 方法進(jìn)行判斷,如果待注入的類型是Map中任何Key的子類,就獲取到該Key對(duì)應(yīng)的值進(jìn)行注入。
for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {// 左邊類型 右邊類型if (entry.getKey().isAssignableFrom(dd3.getDependencyType())) {System.out.println(entry.getValue());break;}}
??????? 所以注入ConfigurableApplicationContext,最終實(shí)際注入的是AnnotationConfigApplicationContext。
??????? 下面再演示一種情況,我現(xiàn)在有兩個(gè)類實(shí)現(xiàn)了一個(gè)泛型接口:
interface Dao<T> {}@Component("dao1") static class Dao1 implements Dao<Student> {}@Component("dao2") static class Dao2 implements Dao<Teacher> {}static class Student {}static class Teacher {}
??????? 需要對(duì)實(shí)現(xiàn)了Teacher泛型接口的類進(jìn)行注入
@Autowired
private Dao<Teacher> dao;
??????? 此時(shí)獲取到的是接口的類型DAO,也就是沒(méi)有區(qū)分泛型,無(wú)論是Dao1和Dao2都能找到:
DependencyDescriptor dd4 = new DependencyDescriptor(Target.class.getDeclaredField("dao"), true);Class<?> type = dd4.getDependencyType();ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();resolver.setBeanFactory(beanFactory);for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);System.out.println(bd);};
??????? 我們需要在for循環(huán)中加入.isAutowireCandidate()?? 條件判斷,對(duì)泛型進(jìn)行檢查。
if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd,name), dd4)) {System.out.println(name);System.out.println(dd4.resolveCandidate(name, type, beanFactory));
}
??????? 最后再演示一種對(duì)@Qualifier 注解的解析。注意與案例一的區(qū)別在于,注入字段的類型非Service接口數(shù)組,所以如果沒(méi)有加入@Qualifier ,Spring將無(wú)法識(shí)別注入哪一個(gè)實(shí)現(xiàn)類,從而報(bào)錯(cuò)。
@Autowired
@Qualifier("service2")
private Service service;
??????? 解析過(guò)程和上一個(gè)案例類似,依舊要使用.isAutowireCandidate()?? 方法進(jìn)行篩選,找出與@Qualifier("service2") 中的value同名的Service實(shí)現(xiàn)類。(否則會(huì)找到所有Service接口的實(shí)現(xiàn)類)
DependencyDescriptor dd5 = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);Class<?> type = dd5.getDependencyType();ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();resolver.setBeanFactory(beanFactory);for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);System.out.println(bd);// @Qualifier("service2")if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd,name), dd5)) {System.out.println(name);System.out.println(dd5.resolveCandidate(name, type, beanFactory));}}
??????? 在最后一個(gè)案例中,如果不使用@Qualifier 注解,又有同名的Bean,還有兩種方式去進(jìn)行區(qū)分注入:
- @Primary 注解:通過(guò)beanFactory的getMergedBeanDefinition(name).isPrimary()方法去匹配加上了該注解的Bean。注意:如果有兩個(gè)同名的Bean都加上了該注解,Spring依舊無(wú)法進(jìn)行區(qū)分。
private static void testPrimary(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {DependencyDescriptor dd = new DependencyDescriptor(Target1.class.getDeclaredField("service"), false);Class<?> type = dd.getDependencyType();for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {if (beanFactory.getMergedBeanDefinition(name).isPrimary()) {System.out.println(name);}}}
- 以待注入的字段名進(jìn)行區(qū)分,用DependencyDescriptor的getDependencyName() 方法獲取的字段名,和根據(jù)類型找到的字段名比較,如果一致就進(jìn)行注入。
private static void testDefault(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {DependencyDescriptor dd = new DependencyDescriptor(Target2.class.getDeclaredField("service3"), false);Class<?> type = dd.getDependencyType();for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {if(name.equals(dd.getDependencyName())) {System.out.println(name);}}}
7、事件監(jiān)聽(tīng)器
??????? 在Spring底層入門(一)中對(duì)于Spring自帶的事件發(fā)布器和監(jiān)聽(tīng)器有過(guò)介紹,它是ApplicationContext相較于BeanFactory而言做的擴(kuò)展功能,但是本身也具有很大的局限性,不能適配分布式架構(gòu)。
??????? 在本篇中,我們?cè)赟pring底層入門(一)的基礎(chǔ)上做一些擴(kuò)展講解:
??????? 首先復(fù)習(xí)一下,事件發(fā)布器和監(jiān)聽(tīng)器的存在是為了解耦:
??????? 例如我現(xiàn)在有一段業(yè)務(wù)代碼,需求是執(zhí)行完業(yè)務(wù)代碼后,發(fā)送短信和郵件:
@Component
public class MyService {public void doBusiness(){System.out.println("業(yè)務(wù)代碼");System.out.println("發(fā)送郵件");System.out.println("發(fā)送短信"); }}
??????? 如果像上面的代碼,那么每次如果需要修改發(fā)送郵件/發(fā)送短信的代碼,都需要對(duì)doBusiness() 方法進(jìn)行修改,引入監(jiān)聽(tīng)器和發(fā)布器乃至消息中間件就是為了解決這樣的問(wèn)題。
??????? 我們需要先自定義一個(gè)事件類:
/*** 事件發(fā)布器*/
public class MyEvent extends ApplicationEvent{public MyEvent(Object source) {super(source);}
}
??????? 實(shí)現(xiàn)事件監(jiān)聽(tīng)器有兩種方式:
- 實(shí)現(xiàn)ApplicationListener<>接口:
@Component
public class SendEmail implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent event) {System.out.println("發(fā)郵件");}
}
- 使用?@EventListener注解(底層依舊是解析ApplicationListener接口)
@Component
public class SendEmail{/*** 使用注解監(jiān)聽(tīng)事件* @param event*/@EventListenerpublic void sendEmail(MyEvent event) {System.out.println("發(fā)郵件"+Thread.currentThread().getName());}
}
??????? 在業(yè)務(wù)類中,需要注入ApplicationEventPublisher,調(diào)用.publishEvent() 方法注冊(cè)自定義事件。
@Component
public class MyService {@Autowiredprivate ApplicationEventPublisher publisher;public void doBusiness(){System.out.println("業(yè)務(wù)代碼");publisher.publishEvent(new MyEvent("MyService.doBusiness()"));}}
??????? 在.publishEvent() 方法的底層,實(shí)際上最后是調(diào)用了ApplicationEventMulticaster的子類SimpleApplicationEventMulticaster的multicastEvent()方法
?????????
??????? 而SimpleApplicationEventMulticaster類中有一個(gè)成員變量taskExecutor,用于控制是否異步監(jiān)聽(tīng)。
??????? 可以通過(guò)自定義SimpleApplicationEventMulticaster Bean的方式實(shí)現(xiàn)異步監(jiān)聽(tīng):
/*** 異步發(fā)送通知優(yōu)化 創(chuàng)建線程池* @return*/@Beanpublic ThreadPoolTaskExecutor threadPoolTaskExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(3);executor.setMaxPoolSize(10);executor.setQueueCapacity(100);return executor;}@Beanpublic SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor threadPoolTaskExecutor){SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();multicaster.setTaskExecutor(threadPoolTaskExecutor);return multicaster;}
??????? 我們?cè)偻ㄟ^(guò)自定義注解的方式簡(jiǎn)單演示一下?@EventListener注解的原理:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyEventListener {
}
??????? 在Config類中,將SmartInitializingSingleton注冊(cè)成Bean,然后編寫解析@MyEventListener 注解的邏輯:
- 得到BeanDefinition中所有的bean名稱,然后根據(jù)bean名稱獲取到bean實(shí)例。
- 找到Bean實(shí)例中所有的方法,判斷方法上是否加了@MyEventListener
- 符合條件則創(chuàng)建ApplicationListener接口的匿名內(nèi)部類,通過(guò)反射調(diào)用bean實(shí)例的方法。(注意:在調(diào)用前還需要判斷方法參數(shù)上的事件類型和Event是否匹配)
- 最后通過(guò) applicationContext的addApplicationListener() 方法進(jìn)行注冊(cè)監(jiān)聽(tīng)器。
??????? 復(fù)習(xí)一下,JAVA面向?qū)ο笾?#xff0c;多態(tài)的弊端:無(wú)法調(diào)用子類特有的方法
@Beanpublic SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext applicationContext) {return () -> {//得到BeanDefinition中所有的bean名稱for (String name : applicationContext.getBeanDefinitionNames()) {//根據(jù)bean名稱獲取到bean實(shí)例Object bean = applicationContext.getBean(name);//讀取自定義@MyEventListener注解//找到類中所有的方法for (Method method : bean.getClass().getMethods()) {//判斷方法上是否存在MyEventListener注解if (method.isAnnotationPresent(MyEventListener.class)) {//解析MyEventListener注解實(shí)際上還是調(diào)用ApplicationListener類//同時(shí)也體現(xiàn)了適配器模式,將MyEvent類型轉(zhuǎn)換為addApplicationListener需要的ApplicationListenerApplicationListener listener = new ApplicationListener() {@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println(event.getClass());//得到方法上所有的參數(shù)Class<?> parameterType = method.getParameterTypes()[0];//方法參數(shù)的事件類型和event的事件類型匹配才執(zhí)行if (parameterType.isAssignableFrom(event.getClass())) {try {method.invoke(bean, event);} catch (IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}}}};//多態(tài)的弊端:無(wú)法調(diào)用子類特有的方法
// applicationContext.addapplicationContext.addApplicationListener(listener);}}}};
8、事件發(fā)布器
??????? 上面提到過(guò),發(fā)布事件實(shí)際上底層最終調(diào)用的是ApplicationEventMulticaster的multicastEvent()方法
??????? 我們簡(jiǎn)單的模擬一下這個(gè)操作:
??????? ApplicationEventMulticaster是一個(gè)接口,如果直接用子類實(shí)現(xiàn),需要重寫其中所有的方法。但是ApplicationEventMulticaster中關(guān)鍵的方法是addApplicationListenerBean()和multicastEvent(),在模擬的過(guò)程中只想重寫這兩個(gè)方法,我們可以創(chuàng)建一個(gè)抽象類先去實(shí)現(xiàn)ApplicationEventMulticaster接口中的所有方法,這也是適配器模式的一種體現(xiàn):
/*** 不想實(shí)現(xiàn)ApplicationEventMulticaster中的所有方法時(shí),可以先定義一個(gè)抽象類實(shí)現(xiàn)所有方法,然后創(chuàng)建抽象類的匿名內(nèi)部類*/
public abstract class AbstractApplicationMulticaster implements ApplicationEventMulticaster {.../重寫了ApplicationEventMulticaster的所有方法
}
??????? 然后再自定義類去繼承抽象類AbstractApplicationMulticaster或創(chuàng)建AbstractApplicationMulticaster的匿名內(nèi)部類,去選擇性的重寫必要的方法:
- addApplicationListenerBean() 方法:用于收集所有的監(jiān)聽(tīng)器,存放在集合中。
- multicastEvent() 方法:用于遍歷集合中所有的監(jiān)聽(tīng)器,并且發(fā)布事件。
@Bean@SuppressWarnings("all")public ApplicationEventMulticaster applicationEventMulticaster(ConfigurableApplicationContext context){return new AbstractApplicationMulticaster() {private ArrayList<ApplicationListener> applicationListeners = new ArrayList<>();/*** 收集所有的監(jiān)聽(tīng)器* @param listenerBeanName*/@Overridepublic void addApplicationListenerBean(String listenerBeanName) {ApplicationListener bean = context.getBean(listenerBeanName, ApplicationListener.class);System.out.println(bean);applicationListeners.add(bean);}/*** 發(fā)布事件* @param event* @param eventType*/@Overridepublic void multicastEvent(ApplicationEvent event, ResolvableType eventType) {for (ApplicationListener applicationListener : applicationListeners) {applicationListener.onApplicationEvent(event);}}};}
??????? 但是上面的代碼存在一個(gè)弊端:在案例中所有的監(jiān)聽(tīng)器實(shí)現(xiàn)的ApplicationListener的泛型,都是自定義的監(jiān)聽(tīng)器MyEven,而Spring監(jiān)聽(tīng)器的類型是多種多樣的,上面的寫法沒(méi)有考慮到類型匹配:
??????? 需要解決類型匹配的問(wèn)題,需要改造以上的代碼
- 收集監(jiān)聽(tīng)器時(shí),需要獲得目前實(shí)現(xiàn)了ApplicationListener的泛型
- 然后將原始的 listener 封裝為支持事件類型檢查的 listener。(如果要支持多線程,只需要在GenericApplicationListener的onApplicationEvent() 方法中使用線程池提交任務(wù)即可)
// 收集監(jiān)聽(tīng)器@Overridepublic void addApplicationListenerBean(String name) {ApplicationListener listener = context.getBean(name, ApplicationListener.class);System.out.println(listener);// 獲取該監(jiān)聽(tīng)器支持的事件類型ResolvableType type = ResolvableType.forClass(listener.getClass()).getInterfaces()[0].getGeneric();System.out.println(type);// 將原始的 listener 封裝為支持事件類型檢查的 listenerGenericApplicationListener genericApplicationListener = new GenericApplicationListener() {// 是否支持某事件類型 真實(shí)的事件類型@Overridepublic boolean supportsEventType(ResolvableType eventType) {return type.isAssignableFrom(eventType);}@Overridepublic void onApplicationEvent(ApplicationEvent event) {executor.submit(() -> listener.onApplicationEvent(event));}};listeners.add(genericApplicationListener);}
- 發(fā)布事件時(shí),需要從GenericApplicationListener類型的List集合中進(jìn)行遍歷,只有類型匹配時(shí)才發(fā)布事件:
// 發(fā)布事件@Overridepublic void multicastEvent(ApplicationEvent event, ResolvableType eventType) {for (GenericApplicationListener listener : listeners) {if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {listener.onApplicationEvent(event);}}}
至此,Spring底層入門系列全部結(jié)束,后續(xù)進(jìn)入JVM篇學(xué)習(xí)。