前書き
毎日のJavaWeb開発では、FactoryBeanクラスを確認または使用する必要がありました。多くの人は、おそらくこのクラスとBeanFactoryの違いを理解していなかったか、インタビューで、SpringのBeanFactoryとFactoryBeanの違いについて尋ねられることがあります。。おそらく私たちの多くはFactoryBeanを拡張して使用するようにカスタマイズしていませんが、どのフレームワークがそれに適用されているかを確認できますか?例:FeignのFeignClientFactoryBean、mybatis-springのMapperFactoryBean、SqlSessionFactoryBeanなどはすべて、 SpringのFactoryBeanインターフェイスを実装し、Beanの作成プロセスをカスタマイズし、動的プロキシを介してFeignインターフェイスとMapperインターフェイスのプロキシオブジェクトを生成するように拡張され、下部の多くの複雑さを保護します。 Http接続やsqlスプライシングなどの繰り返しのビジネスロジックの場合、開発時に実装クラスを作成する必要はありません。
一般に、Springはclass属性を使用して、リフレクションメカニズムを介してBeanをインスタンス化する実装クラスを指定します。場合によっては、Beanをインスタンス化するプロセスがより複雑になります。従来の方法に従う場合は、ここで多くの構成情報を提供する必要があります。構成方法の柔軟性には限界がありますが、現時点では、コーディング方法を使用することで簡単なスキームが得られる場合があります。春は提供のファクトリインターフェイスorg.springframework.bean.factory。FactoryBeanのため、この目的を、そしてユーザーはこのインタフェースを実装することにより、Beanのインスタンス化のロジックをカスタマイズすることができます。
- FactoryBeanは、特定のタイプのBeanインスタンスを生成できるファクトリBeanです。その最大の機能の1つは、Bean作成プロセスをカスタマイズできるようにすることです。
- BeanFactoryは、Springコンテナの基本的で重要なクラスです。BeanFactoryは、SpringコンテナでBeanを作成および管理できます。これには、Beanを作成するための統一されたプロセスがあります。
FactoryBeanインターフェースは、次のように定義されています。
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
// 这个方法,我们可以自定义返回bean实例
@Nullable
T getObject() throws Exception;
// 返回bean类型,可判断传入的class类型是否一致
@Nullable
Class<?> getObjectType();
// 默认实现:true-单例,flase-原型
default boolean isSingleton() {
return true;
}
}
トピックに入る前に、少しデザートを用意し、簡単なテスト例を書いてから、FactoryBeanのソースコードをゆっくりと調べて、Beanの作成プロセスをカスタマイズする方法を分析しましょう。スペースが少し長いため、この記事では最初に分析に焦点を当てます。 Class <?> getObjectType()メソッド、次の記事ではgetObject()を詳細に分析します。
注文関連のサービスインターフェイスを定義する
public interface IOrderService {
void saveOrder();
}
サービス実装クラス、Spring Beanアノテーションはありません:@Service、およびカスタムFactoryBeanを介してBeanを取得する方法をテストするために、xmlでBeanを構成する必要はありません。
//@Service
public class OrderServiceImpl implements IOrderService {
@Override
public void saveOrder() {
System.out.println("-----创建订单------");
}
}
カスタムFactoryBeanの実装:OrderFactoryBean、@ Componentアノテーションを追加することを忘れないでください。このBeanは、Spring iocコンテナー(orderFactoryBean)によって管理されます。
/**
* @description: 自定义FactoryBean实现
* @author: stwen_gan
* @date:
**/
@Component
public class OrderFactoryBean implements FactoryBean {
/**
* 自定义bean的创建过程
*/
@Override
public Object getObject() throws Exception {
System.out.println("-----调用OrderFactoryBean.getObject----");
// 1、直接new
IOrderService orderService = new OrderServiceImpl();
return orderService;
// 2、通过动态代理,无需实现类,如 Mybatis的xxxMapper接口
// OrderMapper bean = (OrderMapper) Proxy.newProxyInstance(OrderFactoryBean.class.getClassLoader(), new Class[]{OrderMapper.class}, new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// // todo
// System.out.println(method.getName());
// return null;
// }
// });
// return bean;
}
/**
* 返回bean的class类型
*/
@Override
public Class<?> getObjectType() {
return IOrderService.class;
}
/**
* 是否单例,默认单例
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}
便宜上、getObject()メソッドでOrderServiceImpl実装クラスを直接新規作成できます。もちろん、動的プロキシを介してプロキシオブジェクトを生成することもできます。ここでは分析せず、コメントアウトしており、注意を払わずに自分でテストできます。
テストカテゴリ:
/**
* @description: 测试
* @author: stwen_gan
* @date:
**/
public class FactoryBeanTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext(MyConfig.class);
//测试自定义FactoryBean实现getObject产生bean
IOrderService bean = context.getBean(IOrderService.class);
System.out.println(bean);
System.out.println("---------------------");
System.out.println(context.getBean("orderFactoryBean"));// bean是OrderServiceImpl,并不是OrderFactoryBean
System.out.println(context.getBean("orderFactoryBean"));// 测试是否单例
System.out.println(context.getBean("orderFactoryBean"));// 测试是否单例
}
}
実行出力:
上記のisSingletonメソッドはtrue(シングルトン)を返すため、取得されたBeanインスタンスはすべて同じであることがわかります。
質問:
- このIOrderServiceインターフェイスとOrderServiceImpl実装クラスでは、Beanアノテーション(通常のインターフェイス/クラスのみ)を追加しませんでしたが、context.getBeanメソッドを介してIoCコンテナから取得できます。なぜですか。
- context.getBean( "orderFactoryBean")によって取得されたBeanオブジェクトは、OrderFactoryBeanオブジェクト自体ではなく、OrderFactoryBean.getObjectメソッドで返されるオブジェクトOrderServiceImplです。どのように実装されますか?
- OrderFactoryBeanオブジェクト自体を取得したい場合はどうなりますか?
SpringのFactoryBeanのの謎を理解するために、我々は直接でgetBean方法、で始まるメソッドの AbstractApplicationContext、以下のように多くのサブクラスでは、そのメソッドをオーバーロード:
// 根据Class类型获取bean
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
// 检查BeanFactory的激活状态
assertBeanFactoryActive();
//getBeanFactory()获取到的是一个DefaultListableBeanFactory的默认实例
return getBeanFactory().getBean(requiredType);
}
// 根据beanName获取bean
@Override
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
ここでは、クラスタイプに従ってBeanを取得するgetBean(Class <T> requiredType)メソッドのみを分析し、渡したのはIOrderService.classタイプです。
その中で、getBeanFactory()はDefaultListableBeanFactoryのデフォルトインスタンスを取得するため、DefaultListableBeanFactoryに移動して、getBeanメソッドの特定の実装を確認する必要があります。
DefaultListableBeanFactory#getBean(Class <T> requiredType)
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
return getBean(requiredType, (Object[]) null);
}
@Override
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
// 根据传入的Class类型、参数等解析bean
Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
if (resolved == null) {
throw new NoSuchBeanDefinitionException(requiredType);
}
return (T) resolved;
}
上記のコードでは、resolveBeanメソッドに焦点を当てています。
@Nullable
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
// 解析bean
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
//如果当前Spring容器获取不到相应的bean信息,则从父容器中获取
//SpringMVC是一个很典型的父子容器
BeanFactory parent = getParentBeanFactory();
if (parent instanceof DefaultListableBeanFactory) {
return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
}
else if (parent != null) {
ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
if (args != null) {
return parentProvider.getObject(args);
}
else {
return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
}
}
return null;
}
resolveNamedBeanメソッドのフォローアップ、beanNameの解決方法に焦点を当てます。
@Nullable
private <T> NamedBeanHolder<T> resolveNamedBean(
ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
//这个方法是根据传入的Class类型来获取BeanName,因为一个接口可以有多个实现类的情况(多态),
//所以这里返回的是一个String数组,这个过程也比较复杂。
//这里需要注意的是,我们调用getBean方法传入的type为com.example.demo.spring.factorybean.IOrderService类型,但是我们没有在Spring容器中注入IOrderService类型的Bean
//正常来说我们在这里是获取不到beanName的,但是事实是不是这样呢?看下面我们对getBeanNamesForType的分析
String[] candidateNames = getBeanNamesForType(requiredType);
// 如果有多个BeanName,则筛选合适的BeanName
// 自动注入相关的判断
if (candidateNames.length > 1) {
// 自动注入的bean候选者
List<String> autowireCandidates = new ArrayList<>(candidateNames.length);
for (String beanName : candidateNames) {
if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
autowireCandidates.add(beanName);
}
}
if (!autowireCandidates.isEmpty()) {
// list 转 String 数组,放入候选的beanNames中
candidateNames = StringUtils.toStringArray(autowireCandidates);
}
}
//如果BeanName只有一个, 我们调用getBean方法来获取Bean实例来放入到NamedBeanHolder中
//这里getBean是根据beanName,bean的Class类型和参数来获取bean
if (candidateNames.length == 1) {
String beanName = candidateNames[0];
return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
}
else if (candidateNames.length > 1) {
// 将候选bean放入一个map
Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
for (String beanName : candidateNames) {
//从单例池中根据beanName获取该bean对象
if (containsSingleton(beanName) && args == null) {
// 根据beanName获取bean
Object beanInstance = getBean(beanName);
candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
}
else {
//单例池中没有获取到该bean,则通过getType方法继续获取Bean实例
candidates.put(beanName, getType(beanName));
}
}
//有多个Bean实例的话 则取带有Primary注解或者带有Primary信息的Bean
String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
if (candidateName == null) {
//如果没有Primary注解或者Primary相关的信息,则取优先级高的Bean实例
candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
}
// 最终获取到一个合适的beanName后,就可以根据Class类型,继续调用getBean方法获取Bean实例
if (candidateName != null) {
Object beanInstance = candidates.get(candidateName);
if (beanInstance == null || beanInstance instanceof Class) {
beanInstance = getBean(candidateName, requiredType.toClass(), args);
}
return new NamedBeanHolder<>(candidateName, (T) beanInstance);
}
// 如果都没有获取到,则抛异常
if (!nonUniqueAsNull) {
throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
}
}
return null;
}
上記のメソッドで渡したタイプはcom.example.demo.spring.factorybean.IOrderServiceインターフェイスタイプですが、このインターフェイスのBeanまたはSpringコンテナの実装クラスを構成していないため、getBeanNamesForTypeから取得するにはどうすればよいですか。 beanNameはどうですか?
以下では、getBeanNamesForTypeを分析します。
@Override
public String[] getBeanNamesForType(ResolvableType type) {
return getBeanNamesForType(type, true, true);
}
@Override
public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
Class<?> resolved = type.resolve();
// type解析不为空
if (resolved != null && !type.hasGenerics()) {
return getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit);
}
else {
return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
}
}
@Override
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
}
//先从缓存中获取:根据Class类型获取符合的beanNames
Map<Class<?>, String[]> cache =
(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
String[] resolvedBeanNames = cache.get(type);
// 获取到直接返回
if (resolvedBeanNames != null) {
return resolvedBeanNames;
}
// 获取不到,则调用doGetBeanNamesForType方法获取符合的beanNames
// 下面会详细分析此方法
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
//判断我们传入的类能不能被当前类加载加载
if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
//放入到缓存中,方便下次直接从缓存中获取
//key:IOrderService,Value:orderFactoryBean
cache.put(type, resolvedBeanNames);
}
return resolvedBeanNames;
}
見つかったデバッグと分析を実行します。キャッシュキャッシュ、重要なのは、クラスのcom.example.demo.spring.factorybean.IOrderServiceを渡したことですが、これはカスタム値のFactoryBean実装OrderFactoryBean対応するbeanName --orderFactoryBeanです( @ComponentアノテーションがOrderFactoryBeanクラスに追加され、Spring IoC管理に追加されます)。このbeanNameはorderServiceではありません。これもこの記事の焦点です。この問題を理解することは非常に重要です。OrderFactoryBeanに対応するbeanNameをIOrderService.classにどのように関連付けますか?
したがって、doGetBeanNamesForTypeメソッドを分析する必要があります。これは、着信IOrderService.Classタイプを介してOrderFactoryBeanのbeanNameを取得する方法です。
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
List<String> result = new ArrayList<>();
// Check all bean definitions.
//循环检查IoC中所有的beanName,这个是在Spring容器启动解析Bean的时候放入到这个List中的
for (String beanName : this.beanDefinitionNames) {
// Only consider bean as eligible if the bean name
// is not defined as alias for some other bean.
// 只有当bean名称没有定义为其他bean的别名时,才是符合要求的
if (!isAlias(beanName)) {
try {
//根据beanName获取RootBeanDefinition
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Only check bean definition if it is complete.
// 这里检查beanDefinition信息的完整性
if (!mbd.isAbstract() && (allowEagerInit ||
(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
// 根据beanName与RootBeanDefinition 判断是否FactoryBean的子类
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
// 类型、beanName等信息是否匹配
boolean matchFound = false;
boolean allowFactoryBeanInit = allowEagerInit || containsSingleton(beanName);
boolean isNonLazyDecorated = dbd != null && !mbd.isLazyInit();
// 普通bean(非FactoryBean子类)
if (!isFactoryBean) {
if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
}
// FactoryBean 子类
else {
if (includeNonSingletons || isNonLazyDecorated ||
(allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
// 下面会重点分析此isTypeMatch方法
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
// matchFound=false时
if (!matchFound) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
//如果不匹配,需要给beanName添加一个统一的前缀&,&beanName 表示这是FactoryBean子类对应的beanName
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
}
// matchFound=true,则表示符合,添加此beanName到result返回
if (matchFound) {
result.add(beanName);
}
}
}
}
}
// 省略部分无关代码。。。。
return StringUtils.toStringArray(result);
}
上記のメソッドでは、最初にSpringのすべてのbeanNameをループし、beanNameに従って対応するRootBeanDefinitionを取得し、次に 渡したResolvableTypeタイプ(つまりIOrderService.class)に従って現在のbeanNameと一致するかどうかを判断します。matchFound= isTypeMatch( beanName、type、allowFactoryBeanInit)。
我々が通過するときのbeanNameは、対応する当社にOrderFactoryBeanと、それはFactoryBeanのサブクラスであることがわかり、それは他を入力し、[使用するisTypeMatchのそれはかどうかを判断するための方法を一致するIOrderService.classを私たちが渡された。同様に、それが一致していることを発見し、デバッグ、次の。つまり、matchFound = trueであり、最後にこのbeanNameが結果に追加されます。
その中で、beanName = FACTORY_BEAN_PREFIX + beanName;(FACTORY_BEAN_PREFIX = "&")、ここでmatchFound = falseの場合、FactoryBeanサブクラスに対応するbeanNameにプレフィックス&を追加するのはなぜですか?
-> FactoryBeanサブクラスオブジェクトBean自体を取得するには、 context.getBean( " & orderFactoryBean")を渡して、自分でテストし、次のように出力します。
com.example.demo.spring.factorybean.OrderFactoryBean@1f0f1111
以下は、条件付きブレークポイントを追加する、デバッグのための小さなトリックです。
そこで、ここではisTypeMatchメソッドを分析します。これは、判断のタイプを一致させる方法です。OrderFactoryBeanに対応するbeanNameとIOrderServiceはどのように関連付けられますか?
protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException {
//转换beanName,这里只探讨beanName为orderFactoryBean时isTypeMatch返回的结果
String beanName = transformedBeanName(name);
boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);
//这里是用AbstractApplicationContext的子类来从Spring容器中获取Bean,
//获取beanName为orderFactoryBean的Bean实例 (IoC是可以获取到的,因为我们有在OrderFactoryBean类上添加@Component注解)
Object beanInstance = getSingleton(beanName, false);// beanName:orderFactoryBean-->OrderFactoryBean
if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
//判断获取到的OrderFactoryBean是否是FactoryBean的实例
if (beanInstance instanceof FactoryBean) {
//这里判断beanName是不是以&开头,这里明显不是(“orderFactoryBean”),这里可以想一下什么情况下会有&开头的Bean,上面也有说明
if (!isFactoryDereference) {
//这里就是从OrderFactoryBean实例中获取type类型,下面会分析一下这个方法
Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
//从OrderFactoryBean中获取到的type类型和我们传入的类型是不是同一种类型,是则直接返回true
return (type != null && typeToMatch.isAssignableFrom(type));
}
else {
return typeToMatch.isInstance(beanInstance);
}
}
// 省略。。。
その中で、オブジェクトbeanInstance = getSingleton(beanName、false)、Springコンテナから対応するBeanを取得するには、次の質問があります。
AbstractApplicationContextのサブクラスを使用してSpringコンテナからBeanを取得することと、BeanFactoryのサブクラスを使用してコンテナからBeanを取得することの違いは何ですか?
前者はbeanNameに対応するBeanインスタンス自体を取得し(orderFactoryBean:OrderFactoryBean)、後者はBeanFactoryサブクラスのカスタムgetObject()メソッドによって返されるインスタンスオブジェクトを取得します(つまり、orderFactoryBean:OrderServiceImpl)。
以下で、getTypeForFactoryBeanを分析します
@Nullable
protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
try {
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged((PrivilegedAction<Class<?>>)
factoryBean::getObjectType, getAccessControlContext());
}
else {
//看到这是不是很熟悉了,实际调用FactoryBean实例的getObjectType()方法
return factoryBean.getObjectType();
}
}
// 省略。。。
}
上記の分析のレイヤーを介して、呼び出しは最終的に値を取得するgetObjectTypeメソッドのカスタムOrderFactoryBeanを見つけました:com.example.demo.spring.factorybean.IOrderService、そして私たちはタイプが同じタイプであることを渡します。したがって、分析によれば、ここではtrueが返されます。isTypeMatchがtrueを返す場合、返すbeanNameはorderFactoryBeanです。したがって、最終的に理解できます。context.getBean(IOrderService .class)を呼び出し、OrderFactoryBeanに対応するbeanNameを配置し、IOrderServiceを関連付けます。
総括する
IOrderService / OrderServiceImplをSpringBeanとして構成しませんでした。つまり、Springコンテナーに対応するBeanDefinitionがなく、対応するBeanをIOrderService.classから取得できませんが、ファクトリBean(OrderFactoryBean)をカスタマイズしました。IOrderServiceのタイプは次のように関連しています。
IOrderService bean = context.getBean(IOrderService.class)を介してタイプに従ってBeanを 取得すると、基になるSpringは最初に対応するbeanNameを取得します。
最初にSpringコンテナ内のすべてのbeanNameをループし、次にbeanNameに従って対応するBeanDefinitionを取得します。現在のBeanがFactoryBeanのタイプである場合、対応するBeanインスタンスはbeanNameに従ってSpringコンテナから取得され、取得したBeanインスタンスのgetObjectTypeメソッドを呼び出して取得します。クラスタイプに移動して、このクラスタイプと渡したクラスが同じタイプであるかどうかを確認します。その場合は、この例に対応するこのbeanNameを返します。orderFactoryBeanに従ってOrderFactoryBeanインスタンスを取得し、OrderFactoryBeanのgetObjectTypeメソッドを呼び出して、戻り値IOrderService.classを取得します。渡したタイプと一致しているため、ここで取得するbeanNameはorderFactoryBeanです。換言すればここでのbeanNameこのorderFactoryBeanマッピングIOrderServiceタイプ:順序、すなわちIOrderServiceのbeanName orderFactoryBeanの種類に対応します。この記事を通じて、Factoryの3つのメソッドにおけるgetObjectTypeの役割を理解しました。次の記事では、次の分析を行います。getBean(String name、Class requiredType、Object ... args)このメソッド、FactoryBeanが最終的にカスタムBeanを返す方法。
最後に、FactoryBeanフローチャートの簡単なバージョンを作成し、渡したクラスタイプ(IOrderService.calss)とカスタムFactoryBeanサブクラスがどのように関連しているかを説明しました。つまり、getBean(IOrderService.class)を呼び出します。 、最下層は最終的にfactoryBean.getObjectType()を呼び出して、コンテキスト全体を明確にするのに役立ちます(クリックして画像を拡大します)。
参照:https://blog.csdn.net/zknxx/article/details/79572387
●なぜアリババは90秒で100億に抵抗できるのですか?-サーバー側の高同時分散アーキテクチャの進化
● B2Beコマースプラットフォーム--ChinaPayUnionPay電子決済機能
● Zookeeperの分散ロックを学び、インタビュアーに感心してあなたを見てもらいましょう
● SpringCloudeコマーススパイクマイクロサービス-Redisson分散ロックソリューション
もっと良い記事をチェックして、公式アカウントを入力してください-私にお願いします-過去に素晴らしい
深くソウルフルなパブリックアカウント0.0