自定義注解實現(xiàn)Spring容器注入Bean方式(類似于mybatis的@MapperScans)
前言
本文通過自定義注解@MyService
和@MyServiceScans
,將SpringBoot項目中帶有@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注解
這個注解有一個數(shù)組參數(shù)basePackages
可以存放一個或者多個包的全路徑。
@Documented @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyServiceScans { String[] basePackages() default {}; }
3. 定義包掃描器
ClassPathBeanDefinitionScanner
作用就是在Spring啟動時自動掃描項目中的類,并創(chuàng)建并注冊它的bean定義,使得我們能在需要時從Spring上下文中取得所需的bean。因此,ClassPathBeanDefinitionScanner是實現(xiàn)Spring自動化配置的關(guān)鍵構(gòu)件。
這里我們自定義一個類來繼承ClassPathBeanDefinitionScanner
,同時重寫了一個帶有是否使用默認Bean過濾器的boolean useDefaultFilters
值的構(gòu)造器(因為我們后續(xù)要用自己的過濾器)。
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注解標記的類 */ addIncludeFilter(new AnnotationTypeFilter(MyService.class)); } @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { return super.doScan(basePackages); } }
4. 注冊Bean到容器
這里通過實現(xiàn)Spring的ImportBeanDefinitionRegistrar
接口并配合@Import
注解,實現(xiàn)Spring容器注入。
- ImportBeanDefinitionRegistrar是Spring框架的一部分。
- 它是一個接口,可以在運行時注冊額外的bean定義。
- 這工作一般是在應(yīng)用啟動時由Spring容器處理,但在某些情況下,開發(fā)者可能想要編程地控制bean的注冊。
- 當一個類實現(xiàn)ImportBeanDefinitionRegistrar接口時,Spring會調(diào)用該類的registerBeanDefinitions方法。
- 在這個方法中,開發(fā)者可以使用BeanDefinitionRegistry參數(shù)將自定義的bean定義添加到registry中。
- 當Spring后續(xù)創(chuàng)建并初始化beans時,這些新注冊的bean定義就會被考慮在內(nèi)。
4.1 @Myservice標識的類注入到容器
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, 實現(xiàn)了ClassPathBeanDefinitionScanner接口 // 當前MyClassPathBeanDefinitionScanner已被修改為掃描帶有指定注解的類 MyServiceClassPathBeanDefinitionScanner scanner = new MyServiceClassPathBeanDefinitionScanner(registry, false); scanner.registerFilters(); // 過濾帶有注解的類并注入到容器中 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注入進入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)過濾器(為了用戶無感知,不過濾@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; /** * 直接過濾 直接處理basePackages下面的所有類 */ public class MyServicePackageFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, @NonNull MetadataReaderFactory metadataReaderFactory) { // 為了用戶無感知,不用過濾出帶有@Myservice的類,直接處理basePackages下面的所有類 // return metadataReader.getAnnotationMetadata() // .hasAnnotation("com.whut.scanner.service"); return true; } }
4.3 導(dǎo)入自定義注解注冊類
這里我們設(shè)置被掃描的包為:com.whut.scaner.service2
@Configuration @Import({MyServiceRegister.class, MyServiceScansRegister.class}) @MyServiceScans(basePackages={"com.whut.scaner.service2"}) public class MyConfiguration { }
5. 測試
首先創(chuàng)建2個不同包下的Service,如下圖所示:
使用@MyService
注解標注的類。
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啟動類進行測試
@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(); } }
啟動項目后,控制臺打?。?/p>
_ _ |_ _ _|_. ___ _ | _
| | |\/|_)(_| | |_\ |_)||_|_\
/ |
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é)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Java將DOCX文檔解析為Markdown文檔的代碼實現(xiàn)
在現(xiàn)代文檔處理中,Markdown(MD)因其簡潔的語法和良好的可讀性,逐漸成為開發(fā)者、技術(shù)寫作者和內(nèi)容創(chuàng)作者的首選格式,然而,許多文檔仍然以Microsoft Word的DOCX格式保存,本文將介紹如何使用Java和相關(guān)庫將DOCX文檔解析為Markdown文檔,需要的朋友可以參考下2025-04-04zuulGateway 通過filter統(tǒng)一修改返回值的操作
這篇文章主要介紹了zuulGateway 通過filter統(tǒng)一修改返回值的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Java Metrics系統(tǒng)性能監(jiān)控工具的使用詳解
Metrics是一個Java庫,可以對系統(tǒng)進行監(jiān)控,統(tǒng)計一些系統(tǒng)的性能指標。本文就來和大家詳細聊聊這個工具的具體使用,希望對大家有所幫助2022-11-11