【别再做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

本节教程分支:https://gitee.com/tian-haoran/mini-easy-es/tree/course_02_create_mapper/

⚠️注意:本项目会持续更新,直到功能完善

1 前置知识

1.1 Spring 相关

1.1.1 什么是 FactoryBean接口?

很多同学都知道BeanFactory接口,这个是大名鼎鼎的Spring中的核心接口,IOC的根本所在。而这个FactoryBean的作用是用来创建一类bean,它的源代码是这样的:

public interface FactoryBean<T> {
  // 获取 ObjectType 的一个对象
 	T getObject() throws Exception;
  
  // 当前实现类所要创建的对象类型
  Class<?> getObjectType();
  
  default boolean isSingleton() {
		return true;
	}
}

【别再做XX外卖啦!和我从零到1编写Mini版Easy-ES】完成一个Mapper模型,从零到1编写Mini版Easy-ES,elasticsearch,大数据,搜索引擎,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中的体现:

// 这段代码来自 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 实战一把

对于FactoryBean的使用其实只分为俩步:

  • 实现FactoryBean接口
  • 将实现类注入Bean工厂

下面这段代码来自Easy ES的源码,同理也可以在Mybatis的底层代码中找到类似的设计(对SqlSession的FactoryBean),这样便相当于托管给实现类创建一类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所要创建的类型
    @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容器刷新时,创建完BeanFactory后会调用的后置处理器

// 代码来自AbstractApplicationContext 545 行
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
		
				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// 嘿哥们,请注意,所有的BeanFactory后置处理器都是在这里被调用哒
				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);
				}
			}
    }
 // 执行它们!!!!
 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. 创建 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 文件中配置接口的实现类名称,然后 Spring Boot 在启动时扫描该配置文件并实例化配置文件中的Bean

比如:

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

Spring框架则会自己去扫描这个文件夹并把配置的这个类加载并且实例化

// 代码来自 SpringFactoriesLoader 95行 (如果你看过Dubbo SPI部分的源码的话会发现逻辑其实大差不差的)
 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) {
      // 同时在这里你会发现,Spring会讲这些扫描到的类一个一个全部实例化
			result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
		}
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}

而这也是SpringBoot插件机制的来源(Starter)

2.2 动态代理相关

2.2.1 什么是动态代理?

动态代理是指在运行时创建代理对象的过程,而不是在编译时确定。JDK动态代理利用Java的反射机制,在运行时动态生成代理类和代理对象,从而实现代理功能。这意味着我们可以在运行时为任何接口创建代理对象,而无需手动编写代理类。

2.2.2 如何使用JDK动态代理?

使用JDK动态代理非常简单,只需遵循以下几个步骤:

  1. 定义一个接口:首先,我们需要定义一个接口,该接口将成为代理对象和被代理对象之间的契约。接口应包含代理对象和被代理对象共同的方法。
  2. 实现被代理类:创建一个实现接口的被代理类,该类将包含实际的业务逻辑。
  3. 创建InvocationHandler:实现InvocationHandler接口,并重写invoke方法。invoke方法将在代理对象的方法被调用时执行,我们可以在该方法中添加额外的逻辑。
  4. 创建代理对象:使用Proxy类的newProxyInstance方法创建代理对象。该方法接受三个参数:类加载器、接口数组和InvocationHandler对象。通过调用该方法,我们将得到一个实现了指定接口的代理对象。
  5. 使用代理对象:现在,我们可以使用代理对象来调用接口中定义的方法。代理对象会在调用方法时自动触发InvocationHandlerinvoke方法,从而允许我们在方法调用前后添加自定义的逻辑。

2.2.3 一个简单的案例

// 这段代码来自 Easy ES 中,可以直接 双击 shift 查到
// 而这段代码的作用就是对 Mapper 进行代理,同时可以在MapperFactoryBean(下面那个代码块)中也可以看到它的核心逻辑就是 (定义Mapper 接口 -> 创建 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 动态代理的优势和应用场景

使用JDK动态代理有以下几个优势:

  • 灵活性:动态代理允许我们在运行时为任意接口创建代理对象,无需手动编写代理类,从而提供了更大的灵活性。
  • 解耦合:代理模式可以将代理对象和被代理对象解耦,使得它们可以独立进行修改和扩展。
  • 横切关注点处理:动态代理可以用于处理横切关注点,例如日志记录、性能监控、事务管理等。我们可以通过在InvocationHandlerinvoke方法中添加相应的逻辑来实现这些功能。

JDK动态代理在以下场景中特别有用:

  • 日志记录:通过在InvocationHandler中添加日志记录逻辑,我们可以方便地记录方法的调用信息,用于调试和分析。
  • 事务管理:通过在InvocationHandler中添加事务管理逻辑,我们可以实现对方法的事务性控制,例如开启事务、提交事务、回滚事务等。
  • 权限控制:通过在InvocationHandler中添加权限验证逻辑,我们可以对方法的调用进行权限控制,以确保只有具备相应权限的用户才能执行特定操作。

2 Mapper 模型设计

首先我们去分析一下要创建一个Mapper的映射需要做哪些工作:

  • 第一:需要扫描到 Mapper
  • 第二:Mapper 只是一个接口,我们需要提供一个实际操作(肯定是动态代理啦)

工程结构如下:

.
| |____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> {
		// 粗浅定义一个方法去创建索引
    Boolean createIndex(String indexName);
}

接下来有了基础方法,那么肯定需要对方法进行实现啦,我们编写一个BaseEsMapperImpl对此进行实现

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

okok stop, 听我say一下

我们在用Mybatis Plus的时候是不是经常会有这样的写法:

public class ApiTest {

    @Autowired
    private UserMapper userMapper;
}

对,没错,Mapper是要注入到Spring容器当中的,只有这样,我们才可以用注解去自动注入

**(敲黑板)⚠️注意:**现在我们的BaseEsMapper还没有和我们将来自己的业务中的Mapper产生关系。那么缕一下思路,我们还差什么:

  • 业务Mapper要和BaseEsMapper产生一个关系
  • Mapper要注入到Spring中

【别再做XX外卖啦!和我从零到1编写Mini版Easy-ES】完成一个Mapper模型,从零到1编写Mini版Easy-ES,elasticsearch,大数据,搜索引擎,java,spring boot

那么这个绑定关系就要用动态代理来维持了,比如下面这种实现

// 我们对 Mapper 接口代理和 BaseMapper 产生一个代理绑定关系
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);
    }
}

接下来就是要把这种关系交给Spring来维持:

所谓维持就分为俩个点:

  • Mapper对象的创建
  • Mapper的注入

首先解决第一个问题,创建一个Mapper对象怎么办,很明显这是一类对象的创建,这个特点不就和 FactoryBean 很类似嘛

// 这下当要去获取某个Mapper接口的时候,不久会调用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;
    }
}

那么现在不就剩下了最后一步——注册Mapper了嘛,请看下文~~

2.2 注册Mapper

对于注册Mapper一般会有俩个方法:

  • 自定义Scanner,比如去继承ClassPathBeanDefinitionScanner然后重写 **doScan()**方法
  • 实现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. 创建 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的时候去调用MapperFactoryBean去拿对象啦,一环扣一环就这样建立了联系
            beanDefinition.setBeanClass(MapperFactoryBean.class);
						// 3. 注册 BeanDefinition
            registry.registerBeanDefinition(StrUtil.lowerFirst(clazz.getSimpleName()), beanDefinition);
        }
    }

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

    }
}

2.3 自动注册

resources中创建目录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 测试一下

下面是测试工程结构:

.
|____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);
    }
}

那么执行这个测试的结果就是:

【别再做XX外卖啦!和我从零到1编写Mini版Easy-ES】完成一个Mapper模型,从零到1编写Mini版Easy-ES,elasticsearch,大数据,搜索引擎,java,spring boot文章来源地址https://www.toymoban.com/news/detail-766556.html

到了这里,关于【别再做XX外卖啦!和我从零到1编写Mini版Easy-ES】完成一个Mapper模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 从零到一发布 NPM 包

    如果你负责前端的基础能力建设,发布各种功能/插件包犹如家常便饭,所以熟悉对 npm 包的发布与管理是非常有必要的,故此有了本篇总结文章。本篇文章一方面总结,一方面向社区贡献开箱即用的 npm 开发、编译、发布、调试模板,希望帮助到有需要的同学。 辛苦整理良久

    2023年04月08日
    浏览(52)
  • Python从零到一构建项目

    随着互联网的发展,网络上的信息量急剧增长,而获取、整理和分析这些信息对于很多人来说是一项艰巨的任务。而Python作为一种功能强大的编程语言,它的爬虫能力使得我们能够自动化地从网页中获取数据,大大提高了效率。本文将分享如何从零到一构建一个简单的网络爬

    2024年02月09日
    浏览(62)
  • Qt - 从零到壹的 打地鼠 游戏

    ❤️‍🔥欢迎收看西北风的blog,好男人就是我,我就是西北风。✨ Gitee 地址 W_A_Mole · NTC_jason/cc语言 - 码云 - 开源中国 (gitee.com) 目录 🟥一:创建一个主窗体 🟣二.:添加主窗口背景图片以及相关部件  🔸2.1 添加资源文件  2.1.1 添加资源文件 2.1.2 在项目中添加资源文件 🔸

    2024年02月01日
    浏览(39)
  • Eclipse的android 安装(从零到有)

    ①JDK下载地址:https://www.oracle.com/downloads/; ②点击Java(JDK) for Developers; ③然后选择自己需要的jdk下载即可; ④双击运行下载好的jdk文件,可以更改安装路径; ⑤安装jdk成功后,将jdk的安装路径填写在环境变量中。变量名:JAVA_HOME,变量值:jdk的安装路径; ⑥编辑Path变量,

    2023年04月08日
    浏览(33)
  • 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个元素的整型数组 myint[0] = 0; //向数组中添加第一个元素 myint[1] = 1; //向数组中添加第二个元素 my

    2024年02月08日
    浏览(49)
  • c# 从零到精通-封装一个类

    c# 从零到精通-封装一个类 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test05 { /// /// 自定义类,封装加数和被加数属性 /// class MyClass { private int x = 0; //定义int型变量,作为加数 private int y = 0; //定义int型变量,作为被加数 /// /// 加数 /// public int X {

    2024年02月08日
    浏览(41)
  • AIGC - Stable Diffusion 搭建【从零到一】

    解决音频内容GPU运算时遇到ffmpeg引起问题 write_py audio_spleeter.txt

    2024年02月09日
    浏览(53)
  • 大数据小白初探Hbase从零到入门

    目录 1.前言 2.初识Hbase 2.1 有了HDFS为什么还需要HBase呢? 2.2 HBase主要做什么的? 2.3 HBase架构特点? 2.4 HBase的适用场景? 2.5 HBase的数据模型和物理储存格式? 2.5.1 逻辑表结构 2.5.2 物理存储 2.5.3 分布式集群框架部署图 2.5.4 HBase的逻辑本质: 2.5.5 HBase的物理存储方案:列簇式存

    2024年01月18日
    浏览(38)
  • 【一】从零到1设计一个丧葬行业小程序

    初步构想 背景:丈母娘家开了一家丧葬行业的门面,每年就过年几天开一下,但是这样也导致每到过年就必须守在门面,无法过一个轻松的年,所以就想做一款小程序,只用于本地乡镇里进行推广。 主营:烟花、爆竹、鞭炮、纸钱纸屋、香烛、花圈、农作工具、零食烟酒、

    2024年01月23日
    浏览(39)
  • 大数据小白初探HDFS从零到入门(一)

    目录 1. 前言 2. 大数据的诞生 3.发展趋势及应用 4.离线计算和实时计算 5.大数据的特性         前两天把Hbase的初级入门知识整理了下,在文章中提到了“HDFS”这个大数据的基础,有同事小伙伴想要了解下这方面的知识,今天我把之前整理的内容也给同事讲了下,顺便我把他

    2024年01月20日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包