亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Dubbo3和Spring?Boot整合過(guò)程源碼解析

 更新時(shí)間:2023年08月25日 10:58:54   作者:程序員小潘  
Dubbo首先是提供了一個(gè)單獨(dú)的模塊來(lái)和Spring Boot做整合,利用 Spring Boot自動(dòng)裝配的功能,配置了一堆自動(dòng)裝配的組件,本文介紹Dubbo3和Spring?Boot整合過(guò)程,需要的朋友一起看看吧

前言

Dubbo3 已經(jīng)從一開(kāi)始的 RPC 框架改頭換面,現(xiàn)在的定位是微服務(wù)框架,除了提供基本的 RPC 功能外,它還提供了一整套的服務(wù)治理方案。Dubbo 有自身的一套設(shè)計(jì)體系,不過(guò)通常很少單獨(dú)使用,更多的是和 Spring 整合在一起,本文分析下 Dubbo3 整合 Spring Boot 的源碼,版本是 3.1.10,Github 地址:https://github.com/apache/dubbo/tree/dubbo-3.1.10。

Dubbo3 專(zhuān)門(mén)提供了一個(gè)模塊來(lái)和 Spring Boot 做整合,模塊名是 dubbo-spring-boot ,下面有4個(gè)子模塊:

dubbo-spring-boot
-> dubbo-spring-boot-actuator
-> dubbo-spring-boot-autoconfigure
-> dubbo-spring-boot-compatible
-> dubbo-spring-boot-starter

一般我們直接引入 dubbo-spring-boot-starter 即可開(kāi)啟 Dubbo 的自動(dòng)裝配,在 Spring Boot 項(xiàng)目里使用 Dubbo。

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>3.1.10</version>
</dependency>

所以我們以這個(gè)模塊為切入點(diǎn),看看 Dubbo 做了什么。

入口模塊

dubbo-spring-boot-starter 模塊沒(méi)有代碼,只有一個(gè) pom.xml 引入了幾個(gè)必要的依賴(lài):

<dependencies>
    <!-- Spring Boot 依賴(lài) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- 日志 依賴(lài) -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>${log4j2_version}</version>
        <optional>true</optional>
    </dependency>
    <!-- Dubbo 自動(dòng)裝配 -->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-autoconfigure</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

我們直接看 dubbo-spring-boot-autoconfigure 模塊:

src/main
-> java/org/apache/dubbo/spring/boot/autoconfigure
---> BinderDubboConfigBinder.java
---> DubboRelaxedBinding2AutoConfiguration.java
-> resources/META-INF
---> spring.factories

這里用到了 Spring Boot 的自動(dòng)裝配功能,顯然就兩個(gè)類(lèi)是無(wú)法實(shí)現(xiàn)如此復(fù)雜的整合的,再看 pom.xml 發(fā)現(xiàn)該模塊又依賴(lài)了 dubbo-spring-boot-autoconfigure-compatible 模塊:

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-autoconfigure-compatible</artifactId>
    <version>${project.version}</version>
</dependency>

該模塊的 spring.factories 配置了一堆組件:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration,\
org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBindingAutoConfiguration,\
org.apache.dubbo.spring.boot.autoconfigure.DubboListenerAutoConfiguration
org.springframework.context.ApplicationListener=\
org.apache.dubbo.spring.boot.context.event.WelcomeLogoApplicationListener
org.springframework.boot.env.EnvironmentPostProcessor=\
org.apache.dubbo.spring.boot.env.DubboDefaultPropertiesEnvironmentPostProcessor
org.springframework.context.ApplicationContextInitializer=\
org.apache.dubbo.spring.boot.context.DubboApplicationContextInitializer

OK,到這里看源碼的思路基本就有了,Dubbo 利用 Spring Boot 自動(dòng)裝配的功能,提供了兩個(gè)自動(dòng)裝配的模塊,配置了一堆自動(dòng)裝配的組件,通過(guò)這些組件來(lái)和 Spring Boot 做整合。

服務(wù)發(fā)布

Dubbo3 整合 Spring 后,如何將加了 @DubboService 的服務(wù)發(fā)布出去?

服務(wù)發(fā)布的關(guān)鍵節(jié)點(diǎn):

DubboAutoConfiguration#serviceAnnotationBeanProcessor ServiceBean后置處理器
  ServiceAnnotationPostProcessor#postProcessBeanDefinitionRegistry
    ServiceAnnotationPostProcessor#scanServiceBeans 掃描ServiceBean
      DubboClassPathBeanDefinitionScanner#scan
        ServiceAnnotationPostProcessor#processScannedBeanDefinition 處理BeanDefinition
          ServiceAnnotationPostProcessor#buildServiceBeanDefinition 構(gòu)建ServiceBeanDefinition
            BeanDefinitionRegistry#registerBeanDefinition 注冊(cè)BeanDefinition
              ServiceBean#afterPropertiesSet
                ModuleConfigManager#addService 交給ModuleConfigManager管理 此時(shí)還沒(méi)啟動(dòng)服務(wù)
DubboDeployApplicationListener#onApplicationEvent Spring事件監(jiān)聽(tīng)
  DubboDeployApplicationListener#onContextRefreshedEvent ContextRefreshed事件
    DefaultModuleDeployer#start 模塊部署啟動(dòng)
      DefaultModuleDeployer#exportServices 啟動(dòng)服務(wù) 暴露服務(wù)
      DefaultModuleDeployer#referServices 引用服務(wù)

AutoConfiguration

先看 dubbo-spring-boot-autoconfigure 模塊自動(dòng)裝配的配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBinding2AutoConfiguration

DubboRelaxedBinding2AutoConfiguration 是 Dubbo 提供的針對(duì) Spring Boot 2.0 的自動(dòng)裝配類(lèi),它先于 DubboRelaxedBindingAutoConfiguration 執(zhí)行,如果你用的是 Spring Boot 1.x 版本,則會(huì)觸發(fā)后者。核心:讀取 dubbo.scan.basePackages 配置,獲取要掃描的包路徑。

@ConditionalOnMissingBean(name = BASE_PACKAGES_BEAN_NAME)
@Bean(name = BASE_PACKAGES_BEAN_NAME)
public Set<String> dubboBasePackages(ConfigurableEnvironment environment) {
    // 讀取 dubbo.scan.basePackages 掃描包路徑 注冊(cè)Set到Spring容器
    PropertyResolver propertyResolver = dubboScanBasePackagesPropertyResolver(environment);
    return propertyResolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet());
}
@ConditionalOnMissingBean(name = RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME, value = ConfigurationBeanBinder.class)
@Bean(RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME)
@Scope(scopeName = SCOPE_PROTOTYPE)
public ConfigurationBeanBinder relaxedDubboConfigBinder() {
    return new BinderDubboConfigBinder();
}

再看 DubboAutoConfiguration,它主要做的事:

  • 注入 ServiceAnnotationPostProcessor,處理 ServiceBean
  • DubboSpringInitializer 的初始化,注冊(cè)一些核心bean
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@Configuration
@AutoConfigureAfter(DubboRelaxedBindingAutoConfiguration.class)
@EnableConfigurationProperties(DubboConfigurationProperties.class)
@EnableDubboConfig// 引入 DubboConfigConfigurationRegistrar
public class DubboAutoConfiguration {
    @ConditionalOnProperty(prefix = DUBBO_SCAN_PREFIX, name = BASE_PACKAGES_PROPERTY_NAME)
    @ConditionalOnBean(name = BASE_PACKAGES_BEAN_NAME)
    @Bean
    public ServiceAnnotationPostProcessor serviceAnnotationBeanProcessor(@Qualifier(BASE_PACKAGES_BEAN_NAME)
                                                                       Set<String> packagesToScan) {
        // 獲取掃描的包路徑
        // 注入 ServiceBean 后置處理器
        return new ServiceAnnotationPostProcessor(packagesToScan);
    }
}

ServiceAnnotationPostProcessor

Dubbo 對(duì)外提供的服務(wù),在 Spring 里面被封裝為 org.apache.dubbo.config.spring.ServiceBean

它繼承自 ServiceConfig,所以它是一個(gè)標(biāo)準(zhǔn)的 Dubbo Service,在整合 Spring 方面做了擴(kuò)展。ServiceAnnotationPostProcessor 是 Spring BeanFactory 的后置處理器,它的職責(zé)是:掃描 DubboService Bean,注冊(cè)到 Spring 容器。拿到需要掃描的包路徑,然后掃描 DubboService Bean:

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    this.registry = registry;
    // 掃描 DubboService Bean
    scanServiceBeans(resolvedPackagesToScan, registry);
}

Dubbo 會(huì)在類(lèi)路徑下掃描,所以會(huì) new 一個(gè) DubboClassPathBeanDefinitionScanner 掃描器,它依賴(lài)于 Spring 的 ClassPathBeanDefinitionScanner,將類(lèi)路徑下加了 @DubboService 、 @Service 的類(lèi)封裝成 BeanDefinition,然后注冊(cè)到容器:

private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
    // 實(shí)例化一個(gè)ClassPath掃描器
    DubboClassPathBeanDefinitionScanner scanner =
            new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
    // 掃描bean的包含規(guī)則 有@DubboService @Service注解
    for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
        scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
    }
    // 排除規(guī)則
    ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
    scanner.addExcludeFilter(scanExcludeFilter);
    for (String packageToScan : packagesToScan) {
        // 掃描包路徑
        scanner.scan(packageToScan);
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
        for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
            // 處理掃描到的BeanDefinition -> 注冊(cè)
            processScannedBeanDefinition(beanDefinitionHolder);
            servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
        }
        servicePackagesHolder.addScannedPackage(packageToScan);
    }
}

ServiceBean 注冊(cè)到容器了,Spring 會(huì)正常實(shí)例化 bean。又因?yàn)?ServiceBean 實(shí)現(xiàn)了 InitializingBean 接口,所以會(huì)觸發(fā)它的 afterPropertiesSet() ,ServiceBean 會(huì)把自己交給 ModuleConfigManager 管理。

Tips:服務(wù)現(xiàn)在還沒(méi)啟動(dòng),目前只是先收集 ServiceBean。

public void afterPropertiesSet() throws Exception {
    if (StringUtils.isEmpty(getPath())) {
        if (StringUtils.isNotEmpty(getInterface())) {
            setPath(getInterface());
        }
    }
    //register service bean
    ModuleModel moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
    // 交給 ModuleConfigManager 管理
    moduleModel.getConfigManager().addService(this);
    moduleModel.getDeployer().setPending();
}

DubboDeployApplicationListener

DubboConfigConfigurationRegistrar 會(huì)初始化 DubboSpringInitializer,初始化的時(shí)候會(huì)向 Spring 容器注冊(cè)一堆 bean,其中就包含 DubboDeployApplicationListener。它是 Spring 應(yīng)用程序的事件監(jiān)聽(tīng)器,它監(jiān)聽(tīng)到 ContextRefreshedEvent 事件會(huì)啟動(dòng) Dubbo 服務(wù);監(jiān)聽(tīng)到 ContextClosedEvent 事件關(guān)閉 Dubbo 服務(wù)。

public void onApplicationEvent(ApplicationContextEvent event) {
    if (nullSafeEquals(applicationContext, event.getSource())) {
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }
}

啟動(dòng)服務(wù)其實(shí)就是觸發(fā) ModuleDeployer#start() ,Dubbo 會(huì)啟動(dòng)相關(guān)組件,然后暴露服務(wù)和應(yīng)用服務(wù)。

private void onContextRefreshedEvent(ContextRefreshedEvent event) {
    ModuleDeployer deployer = moduleModel.getDeployer();
    Assert.notNull(deployer, "Module deployer is null");
    // 啟動(dòng)模塊部署
    Future future = deployer.start();
    if (!deployer.isBackground()) {
        try {
            // 等待啟動(dòng)完成
            future.get();
        } catch (InterruptedException e) {
            logger.warn(CONFIG_FAILED_START_MODEL, "", "", "Interrupted while waiting for dubbo module start: " + e.getMessage());
        } catch (Exception e) {
            logger.warn(CONFIG_FAILED_START_MODEL, "", "", "An error occurred while waiting for dubbo module start: " + e.getMessage(), e);
        }
    }
}

至此,服務(wù)啟動(dòng)完畢。

服務(wù)引用

Dubbo3 整合 Spring 后,如何注入加了 @DubboReference 的屬性/方法,完成服務(wù)引用?

服務(wù)引用的關(guān)鍵節(jié)點(diǎn):

DubboConfigConfigurationRegistrar#registerBeanDefinitions
  DubboSpringInitializer#initialize Dubbo引用初始化
    DubboBeanUtils#registerCommonBeans 注冊(cè)公共bean
      ReferenceAnnotationBeanPostProcessor#postProcessBeanFactory 遍歷BeanDefinition
        AbstractAnnotationBeanPostProcessor#findInjectionMetadata 查找注入點(diǎn)
          ReferenceAnnotationBeanPostProcessor#prepareInjection 準(zhǔn)備注入
            ReferenceAnnotationBeanPostProcessor#registerReferenceBean 注冊(cè)ReferenceBean
      ReferenceAnnotationBeanPostProcessor#postProcessPropertyValues bean屬性值后置處理
        AnnotatedInjectElement#inject 注入
          ReferenceBean#getObject 獲取注入對(duì)象
            ReferenceBean#createLazyProxy 創(chuàng)建延遲代理
              DubboReferenceLazyInitTargetSource#createObject 調(diào)用bean時(shí)才創(chuàng)建真正的Proxy
                ReferenceConfig#get 引用服務(wù) 創(chuàng)建Proxy

ReferenceAnnotationBeanPostProcessor

DubboSpringInitializer 初始化的時(shí)候會(huì)注冊(cè) ReferenceAnnotationBeanPostProcessor 類(lèi),它的職責(zé)是完成 @DubboReference 依賴(lài)的注入。

它是 BeanFactoryPostProcessor 的子類(lèi),是 Spring BeanFactory 的后置處理器,Spring 啟動(dòng)時(shí)會(huì)觸發(fā) postProcessBeanFactory() 。首先是遍歷容器中所有的 BeanDefinition,解析 BeanClass 上的屬性或方法是否有加相關(guān)注解,也就是尋找注入點(diǎn)。

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    // 遍歷所有已注冊(cè)的BeanDefinition > 查找注入點(diǎn) > 準(zhǔn)備注入
    String[] beanNames = beanFactory.getBeanDefinitionNames();
    for (String beanName : beanNames) {
        Class<?> beanType;
        if (beanFactory.isFactoryBean(beanName)) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
        if (beanType != null) {
            // 查找注入點(diǎn)
            AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
            // 準(zhǔn)備注入
            prepareInjection(metadata);
        }
    }
}

AnnotatedInjectionMetadata 元數(shù)據(jù)解析完以后,Spring 會(huì)把注入點(diǎn)封裝成 ReferenceBean,同時(shí)注冊(cè)到 Spring 容器,AnnotatedFieldElement 只是記錄一下引用的 beanName,后續(xù)依賴(lài)注入時(shí)就可以直接從 Spring 容器獲取對(duì)應(yīng)的 bean 實(shí)例了。

protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws BeansException {
    try {
        // 遍歷注入點(diǎn) 注冊(cè) ReferenceBean 此時(shí)記錄的只是一個(gè)referenceBeanName 還沒(méi)注入
        for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) {
            if (fieldElement.injectedObject != null) {
                continue;
            }
            Class<?> injectedType = fieldElement.field.getType();
            AnnotationAttributes attributes = fieldElement.attributes;
            String referenceBeanName = registerReferenceBean(fieldElement.getPropertyName(), injectedType, attributes, fieldElement.field);
            fieldElement.injectedObject = referenceBeanName;
            // 緩存起來(lái)
            injectedFieldReferenceBeanCache.put(fieldElement, referenceBeanName);
        }
    }
}

postProcessPropertyValues() 才是真正的依賴(lài)注入:

public PropertyValues postProcessPropertyValues(
    PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
    try {
        // 已經(jīng)解析過(guò)了,這里直接從緩存獲取
        AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
        prepareInjection(metadata);
        // 依賴(lài)注入
        metadata.inject(bean, beanName, pvs);
    }
    return pvs;
}

AnnotatedInjectionMetadata 會(huì)把收集到的注入點(diǎn) 挨個(gè)進(jìn)行注入,最終調(diào)用 AnnotatedInjectElement#inject() 。

protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    // ReferenceBean#getObject() 拿到一個(gè)延遲代理對(duì)象
    Object injectedObject = getInjectedObject(attributes, bean, beanName, getInjectedType(), this);
    if (member instanceof Field) {// 屬性注入
        Field field = (Field) member;
        ReflectionUtils.makeAccessible(field);
        field.set(bean, injectedObject);
    } else if (member instanceof Method) {// 方法注入
        Method method = (Method) member;
        ReflectionUtils.makeAccessible(method);
        method.invoke(bean, injectedObject);
    }
}

ReferenceBean

依賴(lài)注入的對(duì)象被封裝為 ReferenceBean,它是一個(gè) FactoryBean,所以在注入的時(shí)候,其實(shí)會(huì)調(diào)用 ReferenceBean#getObject() 獲取 bean 實(shí)例。

public T getObject() {
    if (lazyProxy == null) {
        createLazyProxy();
    }
    return (T) lazyProxy;
}

Dubbo 這里并沒(méi)有直接去引用遠(yuǎn)程服務(wù),而是先創(chuàng)建了一個(gè) LazyProxy,Dubbo 希望在真正發(fā)起 RPC 調(diào)用時(shí)才去引用服務(wù),為什么這么做呢?LazyProxy 的創(chuàng)建過(guò)程,利用 JDK 動(dòng)態(tài)代理,創(chuàng)建了一個(gè)空殼的代理對(duì)象:

private void createLazyProxy() {
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());
    proxyFactory.addInterface(interfaceClass);
    Class<?>[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();
    for (Class<?> anInterface : internalInterfaces) {
        proxyFactory.addInterface(anInterface);
    }
    if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) {
        try {
            Class<?> serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader);
            proxyFactory.addInterface(serviceInterface);
        } catch (ClassNotFoundException e) {
        }
    }
    this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader);
}

調(diào)用 LazyProxy 的方法會(huì)觸發(fā) JdkDynamicAopProxy#invoke() ,它會(huì)在第一次調(diào)用非 Object 方法時(shí)創(chuàng)建實(shí)際的對(duì)象。創(chuàng)建實(shí)際的對(duì)象由 DubboReferenceLazyInitTargetSource 完成:

private class DubboReferenceLazyInitTargetSource extends AbstractLazyCreationTargetSource {
    @Override
    protected Object createObject() throws Exception {
        // 首次調(diào)用 ReferenceBean 方法才會(huì)創(chuàng)建
        return getCallProxy();
    }
    @Override
    public synchronized Class<?> getTargetClass() {
        return getInterfaceClass();
    }
}

getCallProxy() 其實(shí)就是調(diào)用 ReferenceConfig#get() 引用服務(wù)。

private Object getCallProxy() throws Exception {
    if (referenceConfig == null) {
        throw new IllegalStateException("ReferenceBean is not ready yet, please make sure to call reference interface method after dubbo is started.");
    }
    synchronized (((DefaultSingletonBeanRegistry)getBeanFactory()).getSingletonMutex()) {
        return referenceConfig.get();
    }
}

尾巴

Dubbo3 和 Spring Boot 整合的過(guò)程可謂是一波三折,過(guò)程還是挺繞的,除了要了解 Dubbo 的底層原理,還要對(duì) Spring Boot 的原理、各種后置處理器很熟悉才行。Dubbo 首先是提供了一個(gè)單獨(dú)的模塊來(lái)和 Spring Boot 做整合,利用 Spring Boot 自動(dòng)裝配的功能,配置了一堆自動(dòng)裝配的組件。首先是讀取到要掃描的包路徑,然后掃描 DubboService Bean,并把它注冊(cè)到 Spring 容器,而后統(tǒng)一交給 ModuleConfigManager 管理;再通過(guò)監(jiān)聽(tīng) ContextRefreshedEvent 事件來(lái)完成服務(wù)的啟動(dòng)和發(fā)布。針對(duì) DubboReference 的注入,則依賴(lài) ReferenceAnnotationBeanPostProcessor 后置處理器,它會(huì)遍歷 Spring 容器內(nèi)所有的 BeanDefinition,然后查找注入點(diǎn),把注入點(diǎn)封裝成 ReferenceBean 并注冊(cè)到 Spring 容器,依賴(lài)注入時(shí)再通過(guò)容器獲取對(duì)應(yīng)的 bean 實(shí)例。Dubbo 采用的是延遲注入的方式,默認(rèn)會(huì)注入一個(gè)空殼的 LazyProxy 對(duì)象,在首次調(diào)用 RPC 方法時(shí)再去調(diào)用底層的服務(wù)引用方法。

到此這篇關(guān)于Dubbo3和Spring Boot整合過(guò)程的文章就介紹到這了,更多相關(guān)Dubbo3和Spring Boot整合內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java對(duì)象集合按照指定元素順序排序的實(shí)現(xiàn)

    Java對(duì)象集合按照指定元素順序排序的實(shí)現(xiàn)

    最近在對(duì)一個(gè)集合列表的數(shù)據(jù)進(jìn)行排序,需求是要集合數(shù)據(jù)按照一個(gè)排序狀態(tài)值進(jìn)行排序,而這個(gè)狀態(tài)值,不是按照從小到大這樣的順序排序的,而是要按照特定的順序,所以本文給大家介紹了Java對(duì)象集合按照指定元素順序排序的實(shí)現(xiàn),需要的朋友可以參考下
    2024-07-07
  • 基于Transactional事務(wù)的使用以及注意說(shuō)明

    基于Transactional事務(wù)的使用以及注意說(shuō)明

    這篇文章主要介紹了Transactional事務(wù)的使用以及注意說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Elasticsearch查詢(xún)Range Query語(yǔ)法示例

    Elasticsearch查詢(xún)Range Query語(yǔ)法示例

    這篇文章主要為大家介紹了Elasticsearch查詢(xún)Range Query語(yǔ)法示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • java快速排序和選擇排序?qū)崿F(xiàn)實(shí)例解析

    java快速排序和選擇排序?qū)崿F(xiàn)實(shí)例解析

    這篇文章主要為大家介紹了java快速排序和選擇排序?qū)崿F(xiàn)實(shí)例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Java Mybatis框架多表操作與注解開(kāi)發(fā)詳解分析

    Java Mybatis框架多表操作與注解開(kāi)發(fā)詳解分析

    MyBatis 是一款優(yōu)秀的持久層框架,它支持自定義 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作。MyBatis 可以通過(guò)簡(jiǎn)單的 XML 或注解來(lái)配置和映射原始類(lèi)型、接口和 Java POJO為數(shù)據(jù)庫(kù)中的記錄
    2021-10-10
  • SpringBoot實(shí)現(xiàn)elasticsearch 查詢(xún)操作(RestHighLevelClient 的案例實(shí)戰(zhàn))

    SpringBoot實(shí)現(xiàn)elasticsearch 查詢(xún)操作(RestHighLevelClient 

    這篇文章主要給大家介紹了SpringBoot如何實(shí)現(xiàn)elasticsearch 查詢(xún)操作,文中有詳細(xì)的代碼示例和操作流程,具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-07-07
  • 基于Java SSM框架開(kāi)發(fā)圖書(shū)借閱系統(tǒng)源代碼

    基于Java SSM框架開(kāi)發(fā)圖書(shū)借閱系統(tǒng)源代碼

    本文給大家介紹了基于Java SSM框架開(kāi)發(fā)圖書(shū)借閱系統(tǒng),開(kāi)發(fā)環(huán)境基于idea2020+mysql數(shù)據(jù)庫(kù),前端框架使用bootstrap4框架,完美了實(shí)現(xiàn)圖書(shū)借閱系統(tǒng),喜歡的朋友快來(lái)體驗(yàn)吧
    2021-05-05
  • java中Path和ClassPath用法比較

    java中Path和ClassPath用法比較

    在本篇文章里小編給大家分享了關(guān)于java中Path和ClassPath用法比較內(nèi)容,有需要的朋友們學(xué)習(xí)下。
    2019-01-01
  • Java控制臺(tái)版五子棋的簡(jiǎn)單實(shí)現(xiàn)方法

    Java控制臺(tái)版五子棋的簡(jiǎn)單實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于Java控制臺(tái)版五子棋的簡(jiǎn)單實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 圖片疊加效果Java代碼實(shí)現(xiàn)

    圖片疊加效果Java代碼實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了圖片疊加效果Java代碼實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02

最新評(píng)論