ホームページ >Java >&#&チュートリアル >Spring Cloud のソースコード分析: パート 1
ソース コードを読んでいる友人を笑わないでください。最近のインタビューは 8 部構成のエッセイ程度であり、信頼性がありません。インタビューでは、プロジェクト、ソース コード、その他の質問をすることが目的です。 。ホイールを作るしかありません。そうしないととても疲れて退屈になってしまいます。
個人的には、ソースコードを読むための前提条件は、それを使用できることであると思います。一度慣れてしまえば、他の人がどのように実装しているかを推測できるようになります。関連する公式があれば、ドキュメントを読んでから公式ドキュメントを読んでください。
しかし、多くの公式文書の書き方が悪く、しばらく読むと混乱してしまうのが残念です。
最近、openfeign
ソース コードを研究していたときに、ソース コード内に重要な注釈 @Import
を見つけました。
プロジェクト起動クラス:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @EnableFeignClients(basePackages = {"com.tian.feign"}) @SpringBootApplication() public class MqApplication { public static void main(String[] args) { SpringApplication.run(MqApplication.class, args); } }
次に、feignclient
インターフェース:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @FeignClient(contextId = "userFeignClient", value = "charge-user-service") public interface UserFeignClient { /** * 邀请成功增加收益 * * @param invitedDto 邀请增加收益 * @return 邀请成功 */ @PostMapping("/user/invited/register") CommonResult<Boolean> invitedRegister(@RequestBody InvitedDto invitedDto); }
ユースケース:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @RestController @RequestMapping("/user") public class UserController { @Resource UserFeignClient userFeignClient; @PostMapping("/invited") public CommonResult invitedRegister(){ //省略不想搞的代码 return userFeignClient.invitedRegister(invitedDto); } }
上記よりコードでは、openfeign
のキー コードが次のとおりであることがわかります:
@EnableFeignClients(basePackages = {"com.tian.feign"})
##@FeignClient(contextId = "userFeignClient", value = "charge-user-service")
#userFeignClient.invitedRegister(invitedDto);
EnableFeignClients
@EnableFeignClients
这个注解在启动类上,我们肯定要重点关注。
小技巧:凡是以
@Enable
开头的各种注解基本上都是开启xxxx
。比如:@EnableFeignClients
表示开启feign客户端。
我们进入@EnableFeignClients
中
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] defaultConfiguration() default {}; Class<?>[] clients() default {}; }
我们通常只需要关心属性basePackages
,表示我们需要扫描的包目录。
如果既没有指定
basePackages
,也没有指定basePackageClasses
,则采用启动类所在的目录作为包扫描路径。默认是这种情况。
本文重点来了,在这个注解@EnableFeignClients
上有个注解@Import(FeignClientsRegistrar.class)
,这里到底是有什么作用?
@Import()
@Import()
アノテーションは Spring バージョン 3.0 で導入されました。リテラルの意味は import.
@Import
アノテーションの完全なクラス名は org.springframework.context.annotation.Import
です。これには Class6b3d0130bba23ae47fe2b8e8cddf0195[]
型のデフォルト値属性が 1 つだけあり、1 つ以上の Class オブジェクトを渡すことができることを示します。
アノテーションからわかるように、アノテーションには次の機能があります:
1 つ以上のコンポーネント クラス (通常は @Configuration 構成クラス) をインポートできます。このアノテーションの機能は次のとおりです。 Spring XML の # と同じ ##de3db5173ba162146856f74f89e5f81e要素は同じです。
@Configuration 構成クラス、
ImportSelect および
ImportBeanDefinitionRegistrar 実装クラスをインポートできます。
AnnotationConfigApplicationContext.register() メソッドに似ています。
@Configuration で定義されていない XML またはその他のリソースをインポートする必要がある場合は、
@ImportResource アノテーションを使用できます。
@Import
一个普通类 spring会将该类加载到spring容器中@Import
一个类,该类实现了ImportBeanDefinitionRegistrar
接口,在重写的registerBeanDefinitions
方法里面,能拿到BeanDefinitionRegistry
的注册器,能手工往beanDefinitionMap
中注册 beanDefinition
@Import
一个类 该类实现了ImportSelector
重写selectImports
方法该方法返回了String[]数组的对象,数组里面的类都会注入到spring容器当中。下面我们来聊聊@Import
在openfeign的这里是起到什么作用。
openfeign
中作用
回答上面的代码里
@Import(FeignClientsRegistrar.class)
这里导入的是FeignClientsRegistrar
类,我们再来看看他的类关系图:
从类关系图来看,FeignClientsRegistrar
实现了ImportBeanDefinitionRegistrar
接口。再结合@Import
的三种使用方式中的第二种方式,能手工往beanDefinitionMap
中注册 beanDefinition
。
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); }
这个方法registerBeanDefinitions()
是feign的核心入口方法,其中会做两件事:
注册默认的配置和注册所有的FeignClient。
registerDefaultConfiguration(metadata, registry)
这个方法是负责注册OpenFeign
的默认配置 ,逻辑相对简单:
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //获取@EnableFeignClients的全部属性 //@EnableFeignClients(basePackages = {"com.tian.feign"}) //这里的basePackages就是我们指定的熟悉 Map<String, Object> defaultAttrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } }
defaultAttrs
中内容如下:
但是这里我们只关注defaultConfiguration,我们并有对其进行设置,所以我们可以忽略他。重点是下面这个方法。
registerFeignClients(metadata, registry)
这里就是项目启动时和openfeign相关的核心代码,这也是@EnableFeignClients
和@FeignClient
两个注解关联起来的地方。
我们进入源码中:
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); Set<String> basePackages; Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName()); AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class); final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) { scanner.addIncludeFilter(annotationTypeFilter); basePackages = getBasePackages(metadata); } else { final Set<String> clientClasses = new HashSet<>(); basePackages = new HashSet<>(); for (Class<?> clazz : clients) { basePackages.add(ClassUtils.getPackageName(clazz)); clientClasses.add(clazz.getCanonicalName()); } AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() { @Override protected boolean match(ClassMetadata metadata) { String cleaned = metadata.getClassName().replaceAll("\\$", "."); return clientClasses.contains(cleaned); } }; scanner.addIncludeFilter( new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter))); } for (String basePackage : basePackages) { Set<BeanDefinition> candidateComponents = scanner .findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface"); Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); String name = getClientName(attributes); registerClientConfiguration(registry, name, attributes.get("configuration")); registerFeignClient(registry, annotationMetadata, attributes); } } } }
代码一行一行看是不是觉得很累,我给你总结好了。
上面的方法分为以下七个步骤:
basePackages
) を取得するために、@EnableFeignClients
アノテーションのすべての属性を取得します。 メソッドはスキャナー
ClassPathScanningCandidateComponentProvider;
## として使用します。
#次に、スキャナー ClassPathScanningCandidateComponentProvider
にアノテーション フィルター (AnnotationTypeFilter) を追加し、@FeignClient
アノテーションを含む ##次に、
getBasePackages(metadata) メソッドを使用して、指定されたパッケージのスキャン パスまたは
@EnableFeingClients アノテーション内のスキャン クラスを取得します (取得できない場合)。 、デフォルトでスキャンされます スタートアップ クラスが配置されているパッケージ パス;
scanner.findCandidateComponents(basePackage)
メソッドを使用して、@FeignClient
の注釈が付けられたすべてのインターフェイスをスキャンし、アセンブリの条件を満たします。パッケージ パス ; を
BeanDefinitionRegistry に登録し、
FeignClient の実際の登録操作を実行します。
openfeign の @Import アノテーションがここで使用されます。
FeignClient アノテーションでスキャンされたすべてのインターフェイス クラスは、Bean の形式で Spring IOC コンテナに登録されます。
@Import
共通クラス スプリングはクラスをスプリング コンテナにロードします@Import
A ImportBeanDefinitionRegistrar
インターフェースを実装するクラス。オーバーライドされた registerBeanDefinitions
メソッドで、BeanDefinitionRegistry
レジスタを取得し、それを beanDefinitionMap の Register に手動で追加できます。
#beanDefinition
#ImportSelector
OverrideselectImports
Method このメソッドを実装するクラスString[] 配列のオブジェクトを返し、配列内のクラスが Spring コンテナーに挿入されます。
以上がSpring Cloud のソースコード分析: パート 1の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。