自定義注解實(shí)現(xiàn)Spring容器注入Bean方式(類似于mybatis的@MapperScans)
前言
本文通過(guò)自定義注解@MyService
和@MyServiceScans
,將SpringBoot項(xiàng)目中帶有@MyService
或@MyServiceScans(basePackages={"com.whut.scaner.service"})
包內(nèi)的類注入到Spring容器中。
文字的目錄如下:
1. 導(dǎo)入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
2. 創(chuàng)建自定義注解
- @MyService注解
package com.whut.scaner.annotation; import java.lang.annotation.*; @Documented @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyService { }
- MyServiceScans注解
這個(gè)注解有一個(gè)數(shù)組參數(shù)basePackages
可以存放一個(gè)或者多個(gè)包的全路徑。
@Documented @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyServiceScans { String[] basePackages() default {}; }
3. 定義包掃描器
ClassPathBeanDefinitionScanner
作用就是在Spring啟動(dòng)時(shí)自動(dòng)掃描項(xiàng)目中的類,并創(chuàng)建并注冊(cè)它的bean定義,使得我們能在需要時(shí)從Spring上下文中取得所需的bean。因此,ClassPathBeanDefinitionScanner是實(shí)現(xiàn)Spring自動(dòng)化配置的關(guān)鍵構(gòu)件。
這里我們自定義一個(gè)類來(lái)繼承ClassPathBeanDefinitionScanner
,同時(shí)重寫了一個(gè)帶有是否使用默認(rèn)Bean過(guò)濾器的boolean useDefaultFilters
值的構(gòu)造器(因?yàn)槲覀兒罄m(xù)要用自己的過(guò)濾器)。
package com.whut.scaner.config; import com.whut.scaner.annotation.MyService; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.core.type.filter.AnnotationTypeFilter; import java.util.Set; public class MyServiceClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner { public MyServiceClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { super(registry, useDefaultFilters); } /** * @addIncludeFilter 將自定義的注解添加到掃描任務(wù)中 */ protected void registerFilters() { /** * 注入@MyService注解標(biāo)記的類 */ addIncludeFilter(new AnnotationTypeFilter(MyService.class)); } @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { return super.doScan(basePackages); } }
4. 注冊(cè)Bean到容器
這里通過(guò)實(shí)現(xiàn)Spring的ImportBeanDefinitionRegistrar
接口并配合@Import
注解,實(shí)現(xiàn)Spring容器注入。
- ImportBeanDefinitionRegistrar是Spring框架的一部分。
- 它是一個(gè)接口,可以在運(yùn)行時(shí)注冊(cè)額外的bean定義。
- 這工作一般是在應(yīng)用啟動(dòng)時(shí)由Spring容器處理,但在某些情況下,開(kāi)發(fā)者可能想要編程地控制bean的注冊(cè)。
- 當(dāng)一個(gè)類實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口時(shí),Spring會(huì)調(diào)用該類的registerBeanDefinitions方法。
- 在這個(gè)方法中,開(kāi)發(fā)者可以使用BeanDefinitionRegistry參數(shù)將自定義的bean定義添加到registry中。
- 當(dāng)Spring后續(xù)創(chuàng)建并初始化beans時(shí),這些新注冊(cè)的bean定義就會(huì)被考慮在內(nèi)。
4.1 @Myservice標(biāo)識(shí)的類注入到容器
package com.whut.scaner.config; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; @Slf4j public class MyServiceRegister implements ImportBeanDefinitionRegistrar { /** * 方法1: 將帶有@MyService注解的類注入到Spring容器 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //自定義的掃描類MyClassPathBeanDefinitionScanner, 實(shí)現(xiàn)了ClassPathBeanDefinitionScanner接口 // 當(dāng)前MyClassPathBeanDefinitionScanner已被修改為掃描帶有指定注解的類 MyServiceClassPathBeanDefinitionScanner scanner = new MyServiceClassPathBeanDefinitionScanner(registry, false); scanner.registerFilters(); // 過(guò)濾帶有注解的類并注入到容器中 scanner.doScan("com.whut.scaner"); } }
4.2 @MyServiceScans包掃描注入
package com.whut.scaner.config; import com.whut.scaner.annotation.MyServiceScans; import com.whut.scaner.filter.MyServicePackageFilter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; import org.springframework.lang.NonNull; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.List; @Slf4j public class MyServiceScansRegister implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata , @NonNull BeanDefinitionRegistry beanDefinitionRegistry) { AnnotationAttributes annotationAttrs = AnnotationAttributes .fromMap(annotationMetadata.getAnnotationAttributes(MyServiceScans.class.getName())); if (annotationAttrs == null) { log.warn("EsMapperScan not exist"); return; } //構(gòu)造掃描器,并將spring的beanDefinitionRegistry注入到掃描器內(nèi),方便將掃描出的BeanDefinition注入進(jìn)入beanDefinitionRegistry MyServiceClassPathBeanDefinitionScanner scanner = new MyServiceClassPathBeanDefinitionScanner(beanDefinitionRegistry, false); List<String> basePackages = new ArrayList<>(); for (String pkg : annotationAttrs.getStringArray("basePackages")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } //添加相關(guān)過(guò)濾器(為了用戶無(wú)感知,不過(guò)濾@MyService注解,直接處理basePackages下面的所有類) scanner.addIncludeFilter(new MyServicePackageFilter()); //掃描并注入 scanner.doScan(StringUtils.toStringArray(basePackages)); } }
MyServicePackageFilter
package com.whut.scaner.filter; import lombok.NonNull; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; /** * 直接過(guò)濾 直接處理basePackages下面的所有類 */ public class MyServicePackageFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, @NonNull MetadataReaderFactory metadataReaderFactory) { // 為了用戶無(wú)感知,不用過(guò)濾出帶有@Myservice的類,直接處理basePackages下面的所有類 // return metadataReader.getAnnotationMetadata() // .hasAnnotation("com.whut.scanner.service"); return true; } }
4.3 導(dǎo)入自定義注解注冊(cè)類
這里我們?cè)O(shè)置被掃描的包為:com.whut.scaner.service2
@Configuration @Import({MyServiceRegister.class, MyServiceScansRegister.class}) @MyServiceScans(basePackages={"com.whut.scaner.service2"}) public class MyConfiguration { }
5. 測(cè)試
首先創(chuàng)建2個(gè)不同包下的Service,如下圖所示:
使用@MyService
注解標(biāo)注的類。
package com.whut.scaner.service; import com.whut.scaner.annotation.MyService; @MyService public class UserService { public void test() { System.out.println("我是UserService的test()方法"); } }
放在指定被掃描的包下(com.whut.scaner.service2
)的類。
package com.whut.scaner.service2; public class StudentService { public void test() { System.out.println("我是StudentService的test()方法"); } }
在SpringBoot啟動(dòng)類進(jìn)行測(cè)試
@SpringBootApplication public class App { public static void main( String[] args ) { ConfigurableApplicationContext context = SpringApplication.run(App.class, args); UserService userService = context.getBean(UserService.class); userService.test(); StudentService studentService = context.getBean(StudentService.class); studentService.test(); } }
啟動(dòng)項(xiàng)目后,控制臺(tái)打印:
_ _ |_ _ _|_. ___ _ | _
| | |\/|_)(_| | |_\ |_)||_|_\
/ |
3.5.3.2
2023-10-17 14:19:43.086 INFO 23988 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-10-17 14:19:43.093 INFO 23988 --- [ main] com.whut.scaner.App : Started App in 1.987 seconds (JVM running for 3.286)
我是UserService的test()方法
我是StudentService的test()方法
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Java將DOCX文檔解析為Markdown文檔的代碼實(shí)現(xiàn)
在現(xiàn)代文檔處理中,Markdown(MD)因其簡(jiǎn)潔的語(yǔ)法和良好的可讀性,逐漸成為開(kāi)發(fā)者、技術(shù)寫作者和內(nèi)容創(chuàng)作者的首選格式,然而,許多文檔仍然以Microsoft Word的DOCX格式保存,本文將介紹如何使用Java和相關(guān)庫(kù)將DOCX文檔解析為Markdown文檔,需要的朋友可以參考下2025-04-04zuulGateway 通過(guò)filter統(tǒng)一修改返回值的操作
這篇文章主要介紹了zuulGateway 通過(guò)filter統(tǒng)一修改返回值的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10一起來(lái)學(xué)習(xí)JAVA的運(yùn)算符
這篇文章主要為大家詳細(xì)介紹了JAVA的運(yùn)算符,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03Java Metrics系統(tǒng)性能監(jiān)控工具的使用詳解
Metrics是一個(gè)Java庫(kù),可以對(duì)系統(tǒng)進(jìn)行監(jiān)控,統(tǒng)計(jì)一些系統(tǒng)的性能指標(biāo)。本文就來(lái)和大家詳細(xì)聊聊這個(gè)工具的具體使用,希望對(duì)大家有所幫助2022-11-11Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(52)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-08-08