Spring?@Conditional通過(guò)條件控制bean注冊(cè)過(guò)程
Spring對(duì)配置類(lèi)的處理主要分為2個(gè)階段
配置類(lèi)解析階段
會(huì)得到一批配置類(lèi)的信息,和一些需要注冊(cè)的bean
bean注冊(cè)階段
將配置類(lèi)解析階段得到的配置類(lèi)和需要注冊(cè)的bean注冊(cè)到spring容器中
看一下什么是配置類(lèi),類(lèi)中有下面任意注解之一的就屬于配置類(lèi):
類(lèi)上有@Compontent注解,@Configuration注解,@CompontentScan注解,@Import注解,@ImportResource注解以及類(lèi)中有@Bean標(biāo)注的方法 的都是配置類(lèi)
判斷一個(gè)類(lèi)是不是一個(gè)配置類(lèi),是否的是下面這個(gè)方法,有興趣的可以看一下:
org.springframework.context.annotation.ConfigurationClassUtils#isConfigurationCandidate
spring中處理這2個(gè)過(guò)程會(huì)循環(huán)進(jìn)行,直到完成所有配置類(lèi)的解析及所有bean的注冊(cè)。
Spring對(duì)配置類(lèi)處理過(guò)程
源碼位置
org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
整個(gè)過(guò)程大致的過(guò)程
通常我們會(huì)通過(guò)new AnnotationConfigApplicationContext()傳入多個(gè)配置類(lèi)來(lái)啟動(dòng)spring容器
spring對(duì)傳入的多個(gè)配置類(lèi)進(jìn)行解析
配置類(lèi)解析階段:這個(gè)過(guò)程就是處理配置類(lèi)上面6中注解的過(guò)程,此過(guò)程中又會(huì)發(fā)現(xiàn)很多新的配置類(lèi),比如@Import導(dǎo)入的一批新的類(lèi)剛好也符合配置類(lèi),而被@CompontentScan掃描到的一些類(lèi)剛好也是配置類(lèi);此時(shí)會(huì)對(duì)這些新產(chǎn)生的配置類(lèi)進(jìn)行同樣的過(guò)程解析
bean注冊(cè)階段:配置類(lèi)解析后,會(huì)得到一批配置類(lèi)和一批需要注冊(cè)的bean,此時(shí)spring容器會(huì)將這批配置類(lèi)作為bean注冊(cè)到spring容器,同樣也會(huì)將這批需要注冊(cè)的bean注冊(cè)到spring容器
經(jīng)過(guò)上面第3個(gè)階段之后,spring容器中會(huì)注冊(cè)很多新的bean,這些新的bean中可能又有很多新的配置類(lèi)
Spring從容器中將所有bean拿出來(lái),遍歷一下,會(huì)過(guò)濾得到一批未處理的新的配置類(lèi),繼續(xù)交給第3步進(jìn)行處理
step3到step6,這個(gè)過(guò)程會(huì)經(jīng)歷很多次,直到完成所有配置類(lèi)的解析和bean的注冊(cè)
從上面過(guò)程中可以了解到:
可以在配置類(lèi)上面加上@Conditional注解,來(lái)控制是否需要解析這個(gè)配置類(lèi),配置類(lèi)如果不被解析,那么這個(gè)配置上面6種注解的解析都會(huì)被跳過(guò)
可以在被注冊(cè)的bean上面加上@Conditional注解,來(lái)控制這個(gè)bean是否需要注冊(cè)到spring容器中
如果配置類(lèi)不會(huì)被注冊(cè)到容器,那么這個(gè)配置類(lèi)解析所產(chǎn)生的所有新的配置類(lèi)及所產(chǎn)生的所有新的bean都不會(huì)被注冊(cè)到容器
一個(gè)配置類(lèi)被spring處理有2個(gè)階段:配置類(lèi)解析階段、bean注冊(cè)階段(將配置類(lèi)作為bean被注冊(cè)到spring容器)。
如果將Condition接口的實(shí)現(xiàn)類(lèi)作為配置類(lèi)上@Conditional中,那么這個(gè)條件會(huì)對(duì)兩個(gè)階段都有效,此時(shí)通過(guò)Condition是無(wú)法精細(xì)的控制某個(gè)階段的,如果想控制某個(gè)階段,比如可以讓他解析,但是不能讓他注冊(cè),此時(shí)就就需要用到另外一個(gè)接口了:ConfigurationCondition
ConfigurationCondition接口
相對(duì)于Condition接口多了一個(gè)getConfigurationPhase方法,用來(lái)指定條件判斷的階段,是在解析配置類(lèi)的時(shí)候過(guò)濾還是在創(chuàng)建bean的時(shí)候過(guò)濾。
Conditional使用的3步驟
自定義一個(gè)類(lèi),實(shí)現(xiàn)Condition或ConfigurationCondition接口,實(shí)現(xiàn)matches方法
在目標(biāo)對(duì)象上使用@Conditional注解,并指定value的指為自定義的Condition類(lèi)型
啟動(dòng)spring容器加載資源,此時(shí)@Conditional就會(huì)起作用了
阻止配置類(lèi)的處理
在配置類(lèi)上面使用@Conditional,這個(gè)注解的value指定的Condition當(dāng)有一個(gè)為false的時(shí)候,spring就會(huì)跳過(guò)處理這個(gè)配置類(lèi)。
自定義一個(gè)Condition類(lèi):
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.type.AnnotatedTypeMetadata; /** * 自定義控制器 * * @author maruifu * @date 2022-12-07 */ @Configuration public class DslLogCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 讀取配置文件application.yml中 spring.elasticsearch.dslLog: true 配置 YamlPropertiesFactoryBean y=new YamlPropertiesFactoryBean(); y.setResources(new ClassPathResource("application.yml")); return (Boolean) y.getObject().get("spring.elasticsearch.dslLog"); } }
matches方法內(nèi)部我們可以隨意發(fā)揮,此處為了演示效果讀取的配置文件。
來(lái)個(gè)配置類(lèi),在配置類(lèi)上面使用上面這個(gè)條件,此時(shí)會(huì)讓配置類(lèi)失效,如下:
import org.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.RestClients; /** * 用于打印dsl語(yǔ)句 */ @Configuration public class EsConfig { @Value("${spring.elasticsearch.rest.uris}") private String elasticsearchHost; @Conditional(DslLogCondition.class) @Bean(destroyMethod = "close") public RestHighLevelClient restClient() { ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo(elasticsearchHost).build(); RestHighLevelClient client = RestClients.create(clientConfiguration).rest(); return client; } }
1:使用了自定義的條件類(lèi)
2:通過(guò)@Bean標(biāo)注這restClient這個(gè)方法,如果這個(gè)配置類(lèi)成功解析,會(huì)將restClient方法的返回值作為bean注冊(cè)到spring容器
bean不存在的時(shí)候才注冊(cè)
IService接口有兩個(gè)實(shí)現(xiàn)類(lèi)Service1和Service1,這兩個(gè)類(lèi)會(huì)放在2個(gè)配置類(lèi)中通過(guò)@Bean的方式來(lái)注冊(cè)到容器,此時(shí)我們想加個(gè)限制,只允許有一個(gè)IService類(lèi)型的bean被注冊(cè)到容器。
可以在@Bean標(biāo)注的2個(gè)方法上面加上條件限制,當(dāng)容器中不存在IService類(lèi)型的bean時(shí),才將這個(gè)方法定義的bean注冊(cè)到容器,下面來(lái)看代碼實(shí)現(xiàn)。
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConfigurationCondition; import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Map; public class OnMissingBeanCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //獲取bean工廠 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //從容器中獲取IService類(lèi)型bean Map<String, IService> serviceMap = beanFactory.getBeansOfType(IService.class); //判斷serviceMap是否為空 return serviceMap.isEmpty(); }
上面matches方法中會(huì)看容器中是否存在IService類(lèi)型的bean,不存在的時(shí)候返回true
來(lái)一個(gè)配置類(lèi)負(fù)責(zé)注冊(cè)Service1到容器
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class BeanConfig1 { @Conditional(OnMissingBeanCondition.class) //@1 @Bean public IService service1() { return new Service1(); } }
再來(lái)一個(gè)配置類(lèi)負(fù)責(zé)注冊(cè)Service2到容器
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class BeanConfig2 { @Conditional(OnMissingBeanCondition.class)//@1 @Bean public IService service2() { return new Service2(); }
來(lái)一個(gè)總的配置類(lèi),導(dǎo)入另外2個(gè)配置類(lèi)
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import({BeanConfig1.class,BeanConfig2.class}) public class MainConfig { }
根據(jù)環(huán)境選擇配置類(lèi)
平常我們做項(xiàng)目的時(shí)候,有開(kāi)發(fā)環(huán)境、測(cè)試環(huán)境、線(xiàn)上環(huán)境,每個(gè)環(huán)境中有些信息是不一樣的,比如數(shù)據(jù)庫(kù)的配置信息,下面我們來(lái)模擬不同環(huán)境中使用不同的配置類(lèi)來(lái)注冊(cè)不同的bean
自定義一個(gè)條件的注解
import org.springframework.context.annotation.Conditional; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Conditional(EnvCondition.class) //@1 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface EnvConditional { //環(huán)境(測(cè)試環(huán)境、開(kāi)發(fā)環(huán)境、生產(chǎn)環(huán)境) enum Env { //@2 TEST, DEV, PROD } //環(huán)境 Env value() default Env.DEV; //@3 }
- @1:注意這個(gè)注解比較特別,這個(gè)注解上面使用到了@Conditional注解,這個(gè)地方使用到了一個(gè)自定義Conditione類(lèi):EnvCondition
- @2:枚舉,表示環(huán)境,定義了3個(gè)環(huán)境
- @3:這個(gè)參數(shù)用指定環(huán)境 上面這個(gè)注解一會(huì)我們會(huì)用在不同環(huán)境的配置類(lèi)上面
下面來(lái)3個(gè)配置類(lèi) 讓3個(gè)配置類(lèi)分別在不同環(huán)境中生效,會(huì)在這些配置類(lèi)上面使用上面自定義的@EnvConditional注解來(lái)做條件限定。
每個(gè)配置類(lèi)中通過(guò)@Bean來(lái)定義一個(gè)名稱(chēng)為name的bean,一會(huì)通過(guò)輸出這個(gè)bean來(lái)判斷哪個(gè)配置類(lèi)生效了。
下面來(lái)看3個(gè)配置類(lèi)的代碼
測(cè)試環(huán)境配置類(lèi)
package com.javacode2018.lesson001.demo25.test2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnvConditional(EnvConditional.Env.TEST)//@1 public class TestBeanConfig { @Bean public String name() { return "我是測(cè)試環(huán)境!"; } }
- @1指定的測(cè)試環(huán)境
開(kāi)發(fā)環(huán)境配置類(lèi)
package com.javacode2018.lesson001.demo25.test2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnvConditional(EnvConditional.Env.DEV) //@1 public class DevBeanConfig { @Bean public String name() { return "我是開(kāi)發(fā)環(huán)境!"; } }
- @1:指定的開(kāi)發(fā)環(huán)境
生產(chǎn)環(huán)境配置類(lèi)
package com.javacode2018.lesson001.demo25.test2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnvConditional(EnvConditional.Env.PROD) //@1 public class ProdBeanConfig { @Bean public String name() { return "我是生產(chǎn)環(huán)境!"; } }
- @1:指定的生產(chǎn)環(huán)境
下面來(lái)看一下條件類(lèi):EnvCondition
條件類(lèi)會(huì)解析配置類(lèi)上面@EnvConditional注解,得到環(huán)境信息。
然后和目前的環(huán)境對(duì)比,決定返回true還是false,如下:
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class EnvCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //當(dāng)前需要使用的環(huán)境 EnvConditional.Env curEnv = EnvConditional.Env.DEV; //@1 //獲取使用條件的類(lèi)上的EnvCondition注解中對(duì)應(yīng)的環(huán)境 EnvConditional.Env env = (EnvConditional.Env) metadata.getAllAnnotationAttributes(EnvConditional.class.getName()).get("value").get(0); return env.equals(curEnv); } }
@1:這個(gè)用來(lái)指定當(dāng)前使用的環(huán)境,此處假定當(dāng)前使用的是開(kāi)發(fā)環(huán)境,這個(gè)我們以后可以任意發(fā)揮,比如將這些放到配置文件中,此處方便演示效果。
Condition指定優(yōu)先級(jí)
多個(gè)Condition按順序執(zhí)行 @Condtional中value指定多個(gè)Condtion的時(shí)候,默認(rèn)情況下會(huì)按順序執(zhí)行,還是通過(guò)代碼來(lái)看一下效果。
下面代碼中定義了3個(gè)Condition,每個(gè)Condition的matches方法中會(huì)輸出當(dāng)前類(lèi)名,然后在配置類(lèi)上面同時(shí)使用這3個(gè)Condition
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.type.AnnotatedTypeMetadata; class Condition1 implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { System.out.println(this.getClass().getName()); return true; } } class Condition2 implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { System.out.println(this.getClass().getName()); return true; } } class Condition3 implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { System.out.println(this.getClass().getName()); return true; } } @Configuration @Conditional({Condition1.class, Condition2.class, Condition3.class}) public class MainConfig5 { }
指定Condition的順序 自定義的Condition可以實(shí)現(xiàn)PriorityOrdered接口或者繼承Ordered接口,或者使用@Order注解,通過(guò)這些來(lái)指定這些Condition的優(yōu)先級(jí)。
排序規(guī)則:先按PriorityOrdered排序,然后按照order的值進(jìn)行排序;也就是:PriorityOrdered asc,order值 asc
下面這幾個(gè)都可以指定order的值 接口:org.springframework.core.Ordered,有個(gè)getOrder方法用來(lái)返回int類(lèi)型的值 接口:org.springframework.core.PriorityOrdered,繼承了Ordered接口,所以也有g(shù)etOrder方法 注解:org.springframework.core.annotation.Order,有個(gè)int類(lèi)型的value參數(shù)指定Order的大小
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.core.annotation.Order; import org.springframework.core.type.AnnotatedTypeMetadata; @Order(1) //@1 class Condition1 implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { System.out.println(this.getClass().getName()); return true; } } class Condition2 implements Condition, Ordered { //@2 @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { System.out.println(this.getClass().getName()); return true; } @Override public int getOrder() { //@3 return 0; } } class Condition3 implements Condition, PriorityOrdered { //@4 @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { System.out.println(this.getClass().getName()); return true; } @Override public int getOrder() { return 1000; } } @Configuration @Conditional({Condition1.class, Condition2.class, Condition3.class})//@5 public class MainConfig6 { }
- @1:Condition1通過(guò)@Order指定順序,值為1
- @2:Condition2通過(guò)實(shí)現(xiàn)了Ordered接口來(lái)指定順序,
- @3:getOrder方法返回1
- @4:Condition3實(shí)現(xiàn)了PriorityOrdered接口,實(shí)現(xiàn)這個(gè)接口需要重寫(xiě)getOrder方法,返回1000
- @5:Condtion順序?yàn)?、2、3
ConfigurationCondition使用
ConfigurationCondition使用的比較少,很多地方對(duì)這個(gè)基本上也不會(huì)去介紹,Condition接口基本上可以滿(mǎn)足99%的需求了,但是springboot中卻大量用到了ConfigurationCondition這個(gè)接口。
ConfigurationCondition通過(guò)解釋比較難理解,來(lái)個(gè)案例感受一下:
來(lái)一個(gè)普通的類(lèi):Service
public class Service { }
來(lái)一個(gè)配置類(lèi),通過(guò)配置類(lèi)注冊(cè)上面這個(gè)Service
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class BeanConfig1 { @Bean public Service service() { return new Service(); } }
再來(lái)一個(gè)配置類(lèi):BeanConfig2
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class BeanConfig2 { @Bean public String name() { return "路人甲Java"; } }
來(lái)一個(gè)總的配置類(lèi)
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import({BeanConfig1.class, BeanConfig2.class}) public class MainConfig7 { }
現(xiàn)在我們有個(gè)需求
當(dāng)容器中有Service這種類(lèi)型的bean的時(shí)候,BeanConfig2才生效。
很簡(jiǎn)單吧,加個(gè)Condition就行了,內(nèi)部判斷容器中是否有Service類(lèi)型的bean,繼續(xù)
來(lái)個(gè)自定義的Condition
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class MyCondition1 implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //獲取spring容器 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //判斷容器中是否存在Service類(lèi)型的bean boolean existsService = !beanFactory.getBeansOfType(Service.class).isEmpty(); return existsService; } }
上面代碼很簡(jiǎn)單,判斷容器中是否有IService類(lèi)型的bean。
@Configuration @Conditional(MyCondition1.class) public class BeanConfig2 { @Bean public String name() { return "路人甲Java"; } }
結(jié)果name永遠(yuǎn)注冊(cè)不上
為什么? 在文章前面我們說(shuō)過(guò),配置類(lèi)的處理會(huì)依次經(jīng)過(guò)2個(gè)階段:配置類(lèi)解析階段和bean注冊(cè)階段,Condition接口類(lèi)型的條件會(huì)對(duì)這兩個(gè)階段都有效,解析階段的時(shí)候,容器中是還沒(méi)有Service這個(gè)bean的,配置類(lèi)中通過(guò)@Bean注解定義的bean在bean注冊(cè)階段才會(huì)被注冊(cè)到spring容器,所以BeanConfig2在解析階段去容器中是看不到Service這個(gè)bean的,所以就被拒絕了。
此時(shí)我們需要用到ConfigurationCondition了,讓條件判斷在bean注冊(cè)階段才起效。
自定義一個(gè)ConfigurationCondition類(lèi)
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConfigurationCondition; import org.springframework.core.type.AnnotatedTypeMetadata; public class MyConfigurationCondition1 implements ConfigurationCondition { @Override public ConfigurationPhase getConfigurationPhase() { return ConfigurationPhase.REGISTER_BEAN; //@1 } @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //獲取spring容器 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //判斷容器中是否存在Service類(lèi)型的bean boolean existsService = !beanFactory.getBeansOfType(Service.class).isEmpty(); return existsService; } }
- @1:指定條件在bean注冊(cè)階段,這個(gè)條件才有效 matches方法中的內(nèi)容直接復(fù)制過(guò)來(lái),判斷規(guī)則不變
修改BeanConfig2的類(lèi)容
將 @Conditional(MyCondition1.class) 替換為 @Conditional(MyConfigurationCondition1.class)
此時(shí)name這個(gè)bean被注入了。
可以再試試將BeanConfig1中service方法上面的@Bean去掉,此時(shí)Service就不會(huì)被注冊(cè)到容器
判斷bean存不存在的問(wèn)題,通常會(huì)使用ConfigurationCondition這個(gè)接口,階段為:REGISTER_BEAN,這樣可以確保條件判斷是在bean注冊(cè)階段執(zhí)行的。
對(duì)springboot比較熟悉的,它里面有很多@Conditionxxx這樣的注解,可以去看一下這些注解,很多都實(shí)現(xiàn)了ConfigurationCondition接口。
Spring中這塊的源碼 @Conditional注解是被下面這個(gè)類(lèi)處理的
org.springframework.context.annotation.ConfigurationClassPostProcessor
總結(jié)
@Conditional注解可以標(biāo)注在spring需要處理的對(duì)象上(配置類(lèi)、@Bean方法),相當(dāng)于加了個(gè)條件判斷,通過(guò)判斷的結(jié)果,讓spring覺(jué)得是否要繼續(xù)處理被這個(gè)注解標(biāo)注的對(duì)象
spring處理配置類(lèi)大致有2個(gè)過(guò)程:解析配置類(lèi)、注冊(cè)bean,這兩個(gè)過(guò)程中都可以使用@Conditional來(lái)進(jìn)行控制spring是否需要處理這個(gè)過(guò)程
Condition默認(rèn)會(huì)對(duì)2個(gè)過(guò)程都有效
ConfigurationCondition控制得更細(xì)一些,可以控制到具體那個(gè)階段使用條件判斷
以上就是Spring @Conditional通過(guò)條件控制bean注冊(cè)過(guò)程的詳細(xì)內(nèi)容,更多關(guān)于Spring @Conditional控制bean注冊(cè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 向Spring IOC 容器動(dòng)態(tài)注冊(cè)bean實(shí)現(xiàn)方式
- 解決Springboot全局異常處理與AOP日志處理中@AfterThrowing失效問(wèn)題
- SpringBoot項(xiàng)目使用aop案例詳解
- SpringBean和Controller實(shí)現(xiàn)動(dòng)態(tài)注冊(cè)與注銷(xiāo)過(guò)程詳細(xì)講解
- BeanDefinitionRegistryPostProcessor如何動(dòng)態(tài)注冊(cè)Bean到Spring
- Spring運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)bean的方法
- spring動(dòng)態(tài)注冊(cè)bean?AOP失效原理解析
相關(guān)文章
SpringBoot單元測(cè)試沒(méi)有執(zhí)行的按鈕問(wèn)題及解決
這篇文章主要介紹了SpringBoot單元測(cè)試沒(méi)有執(zhí)行的按鈕問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Springboot 整合 Java DL4J 實(shí)現(xiàn)農(nóng)產(chǎn)品質(zhì)量檢測(cè)系統(tǒng)(推薦)
本文詳細(xì)介紹了系統(tǒng)的搭建過(guò)程,包括技術(shù)選型、數(shù)據(jù)處理、模型訓(xùn)練和評(píng)估等關(guān)鍵步驟,系統(tǒng)采用卷積神經(jīng)網(wǎng)絡(luò),對(duì)水果成熟度和缺陷進(jìn)行識(shí)別,有效解決了傳統(tǒng)方法成本高、效率低的問(wèn)題,有助于提升農(nóng)產(chǎn)品檢測(cè)的科技含量和自動(dòng)化水平2024-10-10JAVA多線(xiàn)程Thread和Runnable的實(shí)現(xiàn)
java中實(shí)現(xiàn)多線(xiàn)程有兩種方法:一種是繼承Thread類(lèi),另一種是實(shí)現(xiàn)Runnable接口。2013-03-03利用java反射機(jī)制調(diào)用類(lèi)的私有方法(推薦)
下面小編就為大家?guī)?lái)一篇利用java反射機(jī)制調(diào)用類(lèi)的私有方法(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08Java 多線(xiàn)程并發(fā)AbstractQueuedSynchronizer詳情
這篇文章主要介紹了Java 多線(xiàn)程并發(fā)AbstractQueuedSynchronizer詳情,文章圍繞主題展開(kāi)想象的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下2022-06-06java獲取本地文件的多種方式實(shí)現(xiàn)與比較
這篇文章主要為大家詳細(xì)介紹了java獲取本地文件的多種方式實(shí)現(xiàn)與結(jié)果比較,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11Java的Flowable工作流之加簽轉(zhuǎn)簽詳解
這篇文章主要介紹了Java的Flowable工作流之加簽轉(zhuǎn)簽詳解,Flowable是一個(gè)開(kāi)源的工作流引擎,它提供了一套強(qiáng)大的工具和功能,用于設(shè)計(jì)、執(zhí)行和管理各種類(lèi)型的工作流程,需要的朋友可以參考下2023-11-11Idea中maven項(xiàng)目實(shí)現(xiàn)登錄驗(yàn)證碼功能
這篇文章主要介紹了Idea中maven項(xiàng)目實(shí)現(xiàn)登錄驗(yàn)證碼功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12詳解如何在SpringBoot項(xiàng)目中使用全局異常處理
在完整的項(xiàng)目開(kāi)發(fā)中,異常的出現(xiàn)幾乎是無(wú)法避免的;如果凡是有可能出現(xiàn)異常的地方,我們都手動(dòng)的使用try-catch將其捕獲的話(huà),會(huì)使得代碼顯得十分臃腫并且后期不好維護(hù)。本文介紹了pringBoot項(xiàng)目中使用全局異常處理的方法,需要的可以參考一下2022-10-10