国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【別再做XX外賣啦!和我從零到1編寫Mini版Easy-ES】完成一個Mapper模型

這篇具有很好參考價值的文章主要介紹了【別再做XX外賣啦!和我從零到1編寫Mini版Easy-ES】完成一個Mapper模型。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

【從零到1編寫Mini版Easy-ES】完成一個Mapper模型

作者:沈自在

代碼倉庫:https://gitee.com/tian-haoran/mini-easy-es

本節(jié)教程分支:https://gitee.com/tian-haoran/mini-easy-es/tree/course_02_create_mapper/

??注意:本項目會持續(xù)更新,直到功能完善

1 前置知識

1.1 Spring 相關(guān)

1.1.1 什么是 FactoryBean接口?

很多同學都知道BeanFactory接口,這個是大名鼎鼎的Spring中的核心接口,IOC的根本所在。而這個FactoryBean的作用是用來創(chuàng)建一類bean,它的源代碼是這樣的:

public interface FactoryBean<T> {
  // 獲取 ObjectType 的一個對象
 	T getObject() throws Exception;
  
  // 當前實現(xiàn)類所要創(chuàng)建的對象類型
  Class<?> getObjectType();
  
  default boolean isSingleton() {
		return true;
	}
}

【別再做XX外賣啦!和我從零到1編寫Mini版Easy-ES】完成一個Mapper模型,從零到1編寫Mini版Easy-ES,elasticsearch,大數(shù)據(jù),搜索引擎,java,spring boot

上面這個圖可以很簡單的去概括這個接口的作用,就是要一個對象,然后給一個對象的邏輯。

1.1.1.1 小小的深入一點

對于 FactoryBean接口,其實還有一個子接口,叫做SmartFactoryBean

public interface SmartFactoryBean<T> extends FactoryBean<T> {
	// 最核心的一個方法 --> 如果說這里返回 true 那么則會在 Spring容器初始化的時候就將這個Bean實例化
  default boolean isEagerInit() {
		return false;
	}
}

下面這段代碼則是對于SmartFactoryBean的迫切加載在Spring中的體現(xiàn):

// 這段代碼來自 DefaultListableBeanFactory -> 922 行
public void preInstantiateSingletons() throws BeansException {
		// 省略部分代碼
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
          // 核心點便是這里了
					if (bean instanceof FactoryBean) {
						FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged(
									(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
					getBean(beanName);
				}
			}
		}

	}
1.1.1.2 實戰(zhàn)一把

對于FactoryBean的使用其實只分為倆步:

  • 實現(xiàn)FactoryBean接口
  • 將實現(xiàn)類注入Bean工廠

下面這段代碼來自Easy ES的源碼,同理也可以在Mybatis的底層代碼中找到類似的設計(對SqlSession的FactoryBean),這樣便相當于托管給實現(xiàn)類創(chuàng)建一類Bean的能力

@Component // 注入BeanFactory
public class MapperFactoryBean<T> implements FactoryBean<T> {


    private final Class<T> mapperInterface;

    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @Override
    @SuppressWarnings("all")
    public T getObject() throws Exception {
        EsMapperProxy<T> esMapperProxy = new EsMapperProxy<>(mapperInterface);
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, esMapperProxy);
    }
		
    // 這個便是這個FactoryBean所要創(chuàng)建的類型
    @Override
    public Class<?> getObjectType() {
        return this.mapperInterface;
    }
}

1.1.2 BeanDefinitionRegistryPostProcessor擴展點

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
 void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}

首先看一下BeanDefinitionRegistryPostProcessor的父類BeanFactoryPostProcessor,您可能對BeanDefinitionRegistryPostProcessor有些陌生,但想必對BeanFactoryPostProcessor一定不陌生吧,這個是在Spring容器刷新時,創(chuàng)建完BeanFactory后會調(diào)用的后置處理器

// 代碼來自AbstractApplicationContext 545 行
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
		
				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// 嘿哥們,請注意,所有的BeanFactory后置處理器都是在這里被調(diào)用噠
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}
	}

從上一步點進去之后就可以看到下面這段:

// 代碼來自 AbstractApplicationContext 746行
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 再從 invokeBeanFactoryPostProcessors 這里點進去
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

		// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
		// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
		if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}

接下來:

// 代碼來自:PostProcessorRegistrationDelegate 78行
if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
        // 查出來所有的 BeanDefinitionRegistryPostProcessor 后置處理器
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					BeanDefinitionRegistryPostProcessor registryProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					registryProcessors.add(registryProcessor);
				}
				else {
					regularPostProcessors.add(postProcessor);
				}
			}
    }
 // 執(zhí)行它們?。。。?/span>
 invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);

這便是這個擴展點的淵源啦

1.1.2.1 如何使用呢?
// 這是在手寫Mini版本Easy Es 中初期的一段代碼,用于替換Mapper的掃描和BeanDefinition
@Component
public class MapperScannerRegister implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 1. 掃描包
        Set<Class<?>> classes = ClassScanner.scanPackage("tax.szz.mini.test.mapper");
        for (Class<?> clazz : classes) {

            // 1. 創(chuàng)建 BeanDefinition
            RootBeanDefinition beanDefinition = new RootBeanDefinition(clazz);
            String beanClassName = clazz.getName();

            // 2. 設置 BeanName
            beanDefinition.setBeanClassName(beanClassName);
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
            beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            beanDefinition.setBeanClass(MapperFactoryBean.class);

            registry.registerBeanDefinition(StrUtil.lowerFirst(clazz.getSimpleName()), beanDefinition);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

1.1.3 Spring boot 的 spring.factories 機制

Spring Bootspring.factories 配置機制類似于 Java SPI,工程代碼中在 META-INF/spring.factories 文件中配置接口的實現(xiàn)類名稱,然后 Spring Boot 在啟動時掃描該配置文件并實例化配置文件中的Bean

比如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  tax.szz.mini.core.EsAutoConfiguration

Spring框架則會自己去掃描這個文件夾并把配置的這個類加載并且實例化

// 代碼來自 SpringFactoriesLoader 95行 (如果你看過Dubbo SPI部分的源碼的話會發(fā)現(xiàn)邏輯其實大差不差的)
 public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
		Assert.notNull(factoryType, "'factoryType' must not be null");
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
		}
		List<T> result = new ArrayList<>(factoryImplementationNames.size());
		for (String factoryImplementationName : factoryImplementationNames) {
      // 同時在這里你會發(fā)現(xiàn),Spring會講這些掃描到的類一個一個全部實例化
			result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
		}
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}

而這也是SpringBoot插件機制的來源(Starter)

2.2 動態(tài)代理相關(guān)

2.2.1 什么是動態(tài)代理?

動態(tài)代理是指在運行時創(chuàng)建代理對象的過程,而不是在編譯時確定。JDK動態(tài)代理利用Java的反射機制,在運行時動態(tài)生成代理類和代理對象,從而實現(xiàn)代理功能。這意味著我們可以在運行時為任何接口創(chuàng)建代理對象,而無需手動編寫代理類。

2.2.2 如何使用JDK動態(tài)代理?

使用JDK動態(tài)代理非常簡單,只需遵循以下幾個步驟:

  1. 定義一個接口:首先,我們需要定義一個接口,該接口將成為代理對象和被代理對象之間的契約。接口應包含代理對象和被代理對象共同的方法。
  2. 實現(xiàn)被代理類:創(chuàng)建一個實現(xiàn)接口的被代理類,該類將包含實際的業(yè)務邏輯。
  3. 創(chuàng)建InvocationHandler:實現(xiàn)InvocationHandler接口,并重寫invoke方法。invoke方法將在代理對象的方法被調(diào)用時執(zhí)行,我們可以在該方法中添加額外的邏輯。
  4. 創(chuàng)建代理對象:使用Proxy類的newProxyInstance方法創(chuàng)建代理對象。該方法接受三個參數(shù):類加載器、接口數(shù)組和InvocationHandler對象。通過調(diào)用該方法,我們將得到一個實現(xiàn)了指定接口的代理對象。
  5. 使用代理對象:現(xiàn)在,我們可以使用代理對象來調(diào)用接口中定義的方法。代理對象會在調(diào)用方法時自動觸發(fā)InvocationHandlerinvoke方法,從而允許我們在方法調(diào)用前后添加自定義的邏輯。

2.2.3 一個簡單的案例

// 這段代碼來自 Easy ES 中,可以直接 雙擊 shift 查到
// 而這段代碼的作用就是對 Mapper 進行代理,同時可以在MapperFactoryBean(下面那個代碼塊)中也可以看到它的核心邏輯就是 (定義Mapper 接口 -> 創(chuàng)建 Mapper 代理)
public class EsMapperProxy<T> implements InvocationHandler, Serializable {

    private final Class<T> mapperInterface;

    public EsMapperProxy(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        BaseEsMapper<?> baseEsMapper = new BaseEsMapperImpl<>();
        return method.invoke(baseEsMapper, args);
    }
}

public class MapperFactoryBean<T> implements FactoryBean<T> {


    private final Class<T> mapperInterface;

    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @Override
    @SuppressWarnings("all")
    public T getObject() throws Exception {
        EsMapperProxy<T> esMapperProxy = new EsMapperProxy<>(mapperInterface);
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, esMapperProxy);
    }

    @Override
    public Class<?> getObjectType() {
        return this.mapperInterface;
    }
}

2.2.4 動態(tài)代理的優(yōu)勢和應用場景

使用JDK動態(tài)代理有以下幾個優(yōu)勢:

  • 靈活性:動態(tài)代理允許我們在運行時為任意接口創(chuàng)建代理對象,無需手動編寫代理類,從而提供了更大的靈活性。
  • 解耦合:代理模式可以將代理對象和被代理對象解耦,使得它們可以獨立進行修改和擴展。
  • 橫切關(guān)注點處理:動態(tài)代理可以用于處理橫切關(guān)注點,例如日志記錄、性能監(jiān)控、事務管理等。我們可以通過在InvocationHandlerinvoke方法中添加相應的邏輯來實現(xiàn)這些功能。

JDK動態(tài)代理在以下場景中特別有用:

  • 日志記錄:通過在InvocationHandler中添加日志記錄邏輯,我們可以方便地記錄方法的調(diào)用信息,用于調(diào)試和分析。
  • 事務管理:通過在InvocationHandler中添加事務管理邏輯,我們可以實現(xiàn)對方法的事務性控制,例如開啟事務、提交事務、回滾事務等。
  • 權(quán)限控制:通過在InvocationHandler中添加權(quán)限驗證邏輯,我們可以對方法的調(diào)用進行權(quán)限控制,以確保只有具備相應權(quán)限的用戶才能執(zhí)行特定操作。

2 Mapper 模型設計

首先我們?nèi)シ治鲆幌乱獎?chuàng)建一個Mapper的映射需要做哪些工作:

  • 第一:需要掃描到 Mapper
  • 第二:Mapper 只是一個接口,我們需要提供一個實際操作(肯定是動態(tài)代理啦)

工程結(jié)構(gòu)如下:

.
| |____src
| | |____main
| | | |____resources
| | | |____java
|____pom.xml
|____mini-easy-es-core
| |____src
| | |____main
| | | |____resources
| | | | |____META-INF
| | | | | |____spring.factories
| | | |____java
| | | | |____tax
| | | | | |____szz
| | | | | | |____mini
| | | | | | | |____core
| | | | | | | | |____core
| | | | | | | | | |____BaseEsMapper.java
| | | | | | | | | |____BaseEsMapperImpl.java
| | | | | | | | |____proxy
| | | | | | | | | |____EsMapperProxy.java
| | | | | | | | |____register
| | | | | | | | | |____MapperFactoryBean.java
| | | | | | | | |____factory
| | | | | | | | | |____MapperScannerRegister.java
| | | | | | | | |____EsAutoConfiguration.java
|____mini-easy-es-test
| |____src
| | |____test
| | | |____java
| | | | |____tax
| | | | | |____szz
| | | | | | |____mini
| | | | | | | |____test
| | | | | | | | |____TestSpringApplication.java
| | | | | | | | |____api
| | | | | | | | | |____course_02
| | | | | | | | | | |____ApiTest.java
| | |____main
| | | |____java
| | | | |____tax
| | | | | |____szz
| | | | | | |____mini
| | | | | | | |____test
| | | | | | | | |____mapper
| | | | | | | | | |____DocumentMapper.java
| | | | | | | | |____document
| | | | | | | | | |____Document.java

2.1 抽象Mapper

眾所周知,一個Mapper其實就是一個接口,比如我們在使用MybatisPlus時候,可能會去繼承BaseMapper以獲得一些基礎功能比如:

  • selectOne()
  • save()
  • 。。。

那我們也借鑒這種思想去定義一個 BaseEsMapper

public interface BaseEsMapper<T> {
		// 粗淺定義一個方法去創(chuàng)建索引
    Boolean createIndex(String indexName);
}

接下來有了基礎方法,那么肯定需要對方法進行實現(xiàn)啦,我們編寫一個BaseEsMapperImpl對此進行實現(xiàn)

public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
    @Override
    public Boolean createIndex(String indexName) {
        System.out.println("創(chuàng)建 Index");
        System.out.println("indexName = " + indexName);
        return Boolean.TRUE;
    }
}

okok stop, 聽我say一下

我們在用Mybatis Plus的時候是不是經(jīng)常會有這樣的寫法:

public class ApiTest {

    @Autowired
    private UserMapper userMapper;
}

對,沒錯,Mapper是要注入到Spring容器當中的,只有這樣,我們才可以用注解去自動注入

**(敲黑板)??注意:**現(xiàn)在我們的BaseEsMapper還沒有和我們將來自己的業(yè)務中的Mapper產(chǎn)生關(guān)系。那么縷一下思路,我們還差什么:

  • 業(yè)務Mapper要和BaseEsMapper產(chǎn)生一個關(guān)系
  • Mapper要注入到Spring中

【別再做XX外賣啦!和我從零到1編寫Mini版Easy-ES】完成一個Mapper模型,從零到1編寫Mini版Easy-ES,elasticsearch,大數(shù)據(jù),搜索引擎,java,spring boot

那么這個綁定關(guān)系就要用動態(tài)代理來維持了,比如下面這種實現(xiàn)

// 我們對 Mapper 接口代理和 BaseMapper 產(chǎn)生一個代理綁定關(guān)系
public class EsMapperProxy<T> implements InvocationHandler, Serializable {

    private final Class<T> mapperInterface;

    public EsMapperProxy(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        BaseEsMapper<?> baseEsMapper = new BaseEsMapperImpl<>();
        return method.invoke(baseEsMapper, args);
    }
}

接下來就是要把這種關(guān)系交給Spring來維持:

所謂維持就分為倆個點:

  • Mapper對象的創(chuàng)建
  • Mapper的注入

首先解決第一個問題,創(chuàng)建一個Mapper對象怎么辦,很明顯這是一類對象的創(chuàng)建,這個特點不就和 FactoryBean 很類似嘛

// 這下當要去獲取某個Mapper接口的時候,不久會調(diào)用getObject()拿到我們提供的Mapper和BaseMapper之間的綁定代理了嘛,神奇的一批
public class MapperFactoryBean<T> implements FactoryBean<T> {


    private final Class<T> mapperInterface;

    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @Override
    @SuppressWarnings("all")
    public T getObject() throws Exception {
        EsMapperProxy<T> esMapperProxy = new EsMapperProxy<>(mapperInterface);
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, esMapperProxy);
    }

    @Override
    public Class<?> getObjectType() {
        return this.mapperInterface;
    }
}

那么現(xiàn)在不就剩下了最后一步——注冊Mapper了嘛,請看下文~~

2.2 注冊Mapper

對于注冊Mapper一般會有倆個方法:

  • 自定義Scanner,比如去繼承ClassPathBeanDefinitionScanner然后重寫 **doScan()**方法
  • 實現(xiàn)BeanDefinitionRegistryPostProcessor,自己去掃描Mapper接口然后封裝BeanDefinition注冊

都可以解決問題,這里先暫且用BeanDefinitionRegistryPostProcessor頂著,這樣邏輯會更清晰一點

public class MapperScannerRegister implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 1. 掃描包
        Set<Class<?>> classes = ClassScanner.scanPackage("tax.szz.mini.test.mapper");
        for (Class<?> clazz : classes) {

            // 1. 創(chuàng)建 BeanDefinition
            RootBeanDefinition beanDefinition = new RootBeanDefinition(clazz);
            String beanClassName = clazz.getName();

            // 2. 設置 Bean的一些屬性
            beanDefinition.setBeanClassName(beanClassName);
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
            beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
          // ??注意這里(偷梁換柱),假設這里是UserMapper 那么這樣不就會在獲取 userMapper Bean的時候去調(diào)用MapperFactoryBean去拿對象啦,一環(huán)扣一環(huán)就這樣建立了聯(lián)系
            beanDefinition.setBeanClass(MapperFactoryBean.class);
						// 3. 注冊 BeanDefinition
            registry.registerBeanDefinition(StrUtil.lowerFirst(clazz.getSimpleName()), beanDefinition);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

2.3 自動注冊

resources中創(chuàng)建目錄META-INF并且在該目錄下添加文件spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  tax.szz.mini.core.EsAutoConfiguration
// 這樣就完成了一個基本的自動加載
@Configuration
public class EsAutoConfiguration {

    @Bean
    public MapperScannerRegister mapperScannerRegister() {
        return new MapperScannerRegister();
    }
}

2.4 測試一下

下面是測試工程結(jié)構(gòu):

.
|____src
| |____test
| | |____java
| | | |____tax
| | | | |____szz
| | | | | |____mini
| | | | | | |____test
| | | | | | | |____TestSpringApplication.java
| | | | | | | |____api
| | | | | | | | |____course_02
| | | | | | | | | |____ApiTest.java
| |____main
| | |____java
| | | |____tax
| | | | |____szz
| | | | | |____mini
| | | | | | |____test
| | | | | | | |____mapper
| | | | | | | | |____DocumentMapper.java
| | | | | | | |____document
| | | | | | | | |____Document.java

public class Document {
}

public interface DocumentMapper extends BaseEsMapper<DocumentMapper> {

}
@Disabled
@SpringBootTest(classes = TestSpringApplication.class)
public class ApiTest {

    @Autowired
    private DocumentMapper documentMapper;

    @Test
    void test(){
        documentMapper.createIndex("hello");
    }
}
@SpringBootApplication
public class TestSpringApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestSpringApplication.class, args);
    }
}

那么執(zhí)行這個測試的結(jié)果就是:

【別再做XX外賣啦!和我從零到1編寫Mini版Easy-ES】完成一個Mapper模型,從零到1編寫Mini版Easy-ES,elasticsearch,大數(shù)據(jù),搜索引擎,java,spring boot文章來源地址http://www.zghlxwxcb.cn/news/detail-766556.html

到了這里,關(guān)于【別再做XX外賣啦!和我從零到1編寫Mini版Easy-ES】完成一個Mapper模型的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • Python從零到一構(gòu)建項目

    Python從零到一構(gòu)建項目

    隨著互聯(lián)網(wǎng)的發(fā)展,網(wǎng)絡上的信息量急劇增長,而獲取、整理和分析這些信息對于很多人來說是一項艱巨的任務。而Python作為一種功能強大的編程語言,它的爬蟲能力使得我們能夠自動化地從網(wǎng)頁中獲取數(shù)據(jù),大大提高了效率。本文將分享如何從零到一構(gòu)建一個簡單的網(wǎng)絡爬

    2024年02月09日
    瀏覽(29)
  • 從零到一發(fā)布 NPM 包

    從零到一發(fā)布 NPM 包

    如果你負責前端的基礎能力建設,發(fā)布各種功能/插件包猶如家常便飯,所以熟悉對 npm 包的發(fā)布與管理是非常有必要的,故此有了本篇總結(jié)文章。本篇文章一方面總結(jié),一方面向社區(qū)貢獻開箱即用的 npm 開發(fā)、編譯、發(fā)布、調(diào)試模板,希望幫助到有需要的同學。 辛苦整理良久

    2023年04月08日
    瀏覽(24)
  • Qt - 從零到壹的 打地鼠 游戲

    Qt - 從零到壹的 打地鼠 游戲

    ?????歡迎收看西北風的blog,好男人就是我,我就是西北風。? Gitee 地址?W_A_Mole · NTC_jason/cc語言 - 碼云 - 開源中國 (gitee.com) 目錄 ??一:創(chuàng)建一個主窗體 ??二.:添加主窗口背景圖片以及相關(guān)部件? ??2.1 添加資源文件 ?2.1.1 添加資源文件 2.1.2 在項目中添加資源文件 ??

    2024年02月01日
    瀏覽(48)
  • Eclipse的android 安裝(從零到有)

    Eclipse的android 安裝(從零到有)

    ①JDK下載地址:https://www.oracle.com/downloads/; ②點擊Java(JDK) for Developers; ③然后選擇自己需要的jdk下載即可; ④雙擊運行下載好的jdk文件,可以更改安裝路徑; ⑤安裝jdk成功后,將jdk的安裝路徑填寫在環(huán)境變量中。變量名:JAVA_HOME,變量值:jdk的安裝路徑; ⑥編輯Path變量,

    2023年04月08日
    瀏覽(15)
  • c# 從零到精通 for語句

    c# 從零到精通 for語句 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test08 { class Program { static void Main(string[] args) { int[] myint = new int[10]; //聲明一個具有10個元素的整型數(shù)組 myint[0] = 0; //向數(shù)組中添加第一個元素 myint[1] = 1; //向數(shù)組中添加第二個元素 my

    2024年02月08日
    瀏覽(27)
  • c# 從零到精通-封裝一個類

    c# 從零到精通-封裝一個類 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test05 { /// /// 自定義類,封裝加數(shù)和被加數(shù)屬性 /// class MyClass { private int x = 0; //定義int型變量,作為加數(shù) private int y = 0; //定義int型變量,作為被加數(shù) /// /// 加數(shù) /// public int X {

    2024年02月08日
    瀏覽(25)
  • AIGC - Stable Diffusion 搭建【從零到一】

    解決音頻內(nèi)容GPU運算時遇到ffmpeg引起問題 write_py audio_spleeter.txt

    2024年02月09日
    瀏覽(41)
  • 大數(shù)據(jù)小白初探Hbase從零到入門

    目錄 1.前言 2.初識Hbase 2.1?有了HDFS為什么還需要HBase呢? 2.2 HBase主要做什么的? 2.3 HBase架構(gòu)特點? 2.4 HBase的適用場景? 2.5 HBase的數(shù)據(jù)模型和物理儲存格式? 2.5.1 邏輯表結(jié)構(gòu) 2.5.2 物理存儲 2.5.3 分布式集群框架部署圖 2.5.4 HBase的邏輯本質(zhì): 2.5.5 HBase的物理存儲方案:列簇式存

    2024年01月18日
    瀏覽(20)
  • 【一】從零到1設計一個喪葬行業(yè)小程序

    初步構(gòu)想 背景:丈母娘家開了一家喪葬行業(yè)的門面,每年就過年幾天開一下,但是這樣也導致每到過年就必須守在門面,無法過一個輕松的年,所以就想做一款小程序,只用于本地鄉(xiāng)鎮(zhèn)里進行推廣。 主營:煙花、爆竹、鞭炮、紙錢紙屋、香燭、花圈、農(nóng)作工具、零食煙酒、

    2024年01月23日
    瀏覽(21)
  • IDEA從零到精通(35)之各種搜索功能

    IDEA從零到精通(35)之各種搜索功能

    作者名:編程界明世隱 簡介:CSDN博客專家,從事軟件開發(fā)多年,精通Java、JavaScript,博主也是從零開始一步步把學習成長、深知學習和積累的重要性,喜歡跟廣大ADC一起打野升級,歡迎您關(guān)注,期待與您一起學習、成長、起飛! 我本來是一直用eclipse和myeclipse的老程序員了,

    2023年04月09日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包