Spring中常见的BeanFactory后处理器

常见的BeanFacatory后处理器

先给出没有添加任何BeanFactory后处理器的测试代码

public class TestBeanFactoryPostProcessor {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        context.close();
    }
}

在配置类中我们编写了如下信息

@Configuration
@ComponentScan("com.zmt.test5")
public class Config {

    @Bean
    public Bean2 bean2(){
        return new Bean2();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public DruidDataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }
}

同时还有一个Bean1添加了@Component注解并且能够被扫描到,所以理论上来讲,我们可以观察到五个beanName,那么执行测试代码观察输出结果

可以看到,这里只输出了一个beanName,我们可以推测出其他注解没有生效,那么接下来我们将常用的BeanFactory后处理器也注册到BeanFactory后,观察输出结果

public class TestBeanFactoryPostProcessor {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        //添加BeanFactory后处理器
        context.registerBean(ConfigurationClassPostProcessor.class);//用来解析 @ComponentScan @Bean @Import @ImportResource注解
        context.registerBean(MapperScannerConfigurer.class,bd -> {
            bd.getPropertyValues().add("basePackage","com.zmt.test.mapper");
        });//扫描Mapper,相当于@MapperScan注解

        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        context.close();
    }
}

添加Spring提供的BeanFactory后处理器之后,可以正常将Bean对象添加到context容器当中了,执行结果如下

接下来我们模拟实现在BeanFactory后处理器当中具体如何解析这些注解。 

模拟实现组件扫描

首先是模拟实现@ComponentScan注解是如何扫描包,获取类资源的

public class TestBeanFactoryPostProcessor {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        //模拟ConfigurationPostProcessor处理器中如何解析@ComponentScan注解
        ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        if (componentScan != null) {
            for (String s : componentScan.basePackages()) {
                //将com.zmt.test5.bean转化格式为classpath*:com/zmt/test5/bean/**/*.class
                String path = "classpath*:"+s.replace(".", "/")+"/**/*.class";
                //创建出一个元数据读取工厂,用来读取类资源信息
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                //通过getResource方法获取到path中的所有类资源
                Resource[] resources = context.getResources(path);
                for (Resource resource : resources) {
                    //读取类资源信息
                    MetadataReader reader = factory.getMetadataReader(resource);
                    System.out.println("类名:"+reader.getClassMetadata());
                    System.out.println("是否添加了@Component注解:"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
                    System.out.println("是否添加了@Component的派生注解:"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
                }
            }
        }

        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        context.close();
    }
}

运行结果如下,可以正常识别类上是否添加了@Component注解或是派生注解

能够扫描到类上添加的注解之后,我们是需要将注解添加到BeanDefinitionMap当中去的,那么继续完善我们的测试方法

public class TestBeanFactoryPostProcessor {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        //模拟ConfigurationPostProcessor处理器中如何解析@ComponentScan注解
        ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        if (componentScan != null) {
            for (String s : componentScan.basePackages()) {
                //将com.zmt.test5转化格式为classpath*:com/zmt/test5/**/*.class
                String path = "classpath*:" + s.replace(".", "/") + "/**/*.class";
                //创建出一个元数据读取工厂,用来读取类资源信息
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                //通过getResource方法获取到path中的所有类资源
                Resource[] resources = context.getResources(path);
                //创建Bean名称生成器
                AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                for (Resource resource : resources) {
                    //读取类资源信息
                    MetadataReader reader = factory.getMetadataReader(resource);
                    System.out.println("类名1:" + reader.getClassMetadata());
                    AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                    System.out.println("是否添加了@Component注解:" + annotationMetadata.hasAnnotation(Component.class.getName()));
                    System.out.println("是否添加了@Component的派生注解:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));

                    if (annotationMetadata.hasAnnotation(Component.class.getName()) ||
                            annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                        //如果该类添加了注解,需要添加到BeanDefinitionMap当中去,生成BeanDefinition对象
                        AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName())
                                .getBeanDefinition();
                        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
                        //生成Bean名称
                        String name = generator.generateBeanName(bd,beanFactory);
                        System.out.println("name:"+name);
                        //将BeanDefinition注册到beanFactory
                        beanFactory.registerBeanDefinition(name,bd);
                    }
                }
            }
        }

        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        context.close();
    }
}

我们通过打断点查看beanFactory中是否将BeanDefinition信息注册到BeanDefinitionMap当中去,结果如下

至此已经实现了组件扫描,但是目前我们的实现是在容器初始化 [refresh()方法] 之前就做好了,我们应该将这些实现抽取到一个BeanFactory后处理器当中,等待refresh()方法回调,因此我们将这些实现代码放入一个组件扫描后处理器。

public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
    @Override //在执行context.refresh()方法时回调该方法
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        try {
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null) {
                for (String s : componentScan.basePackages()) {
                    //将com.zmt.test5转化格式为classpath*:com/zmt/test5/**/*.class
                    String path = "classpath*:" + s.replace(".", "/") + "/**/*.class";
                    //创建出一个元数据读取工厂,用来读取类资源信息
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    //通过getResource方法获取到path中的所有类资源
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    //创建Bean名称生成器
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    for (Resource resource : resources) {
                        //读取类资源信息
                        MetadataReader reader = factory.getMetadataReader(resource);
                        System.out.println("类名1:" + reader.getClassMetadata());
                        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                        System.out.println("是否添加了@Component注解:" + annotationMetadata.hasAnnotation(Component.class.getName()));
                        System.out.println("是否添加了@Component的派生注解:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));

                        if (annotationMetadata.hasAnnotation(Component.class.getName()) ||
                                annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            //如果该类添加了注解,需要添加到BeanDefinitionMap当中去,生成BeanDefinition对象
                            AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName())
                                    .getBeanDefinition();
                            if (configurableListableBeanFactory instanceof DefaultListableBeanFactory){
                                DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
                                String name = generator.generateBeanName(bd,beanFactory);
                                System.out.println("name:"+name);
                                beanFactory.registerBeanDefinition(name,bd);
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

修改此时的测试代码,将我们自定义的BeanFactory后处理器注册到context当中 

public class TestBeanFactoryPostProcessor {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        //模拟ConfigurationPostProcessor处理器中如何解析@ComponentScan注解
        context.registerBean(ComponentScanPostProcessor.class);

        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        context.close();
    }
}

模拟实现@Bean注册

public class TestBeanFactoryPostProcessor {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        //模拟@Bean注解解析
        //@Bean注解的解析与组件扫描不太一样,@Bean的解析是将配置类当作工厂类,而被Bean注解修饰的方法被当作工厂方法
        //用来读取配置类信息
        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        MetadataReader reader = factory.getMetadataReader(Config.class.getName());
        //获取被注解修饰的方法中被Bean注解修饰的方法
        Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata method : methods) {
            System.out.println(method);
            //因为是通过Config调用那些方法,因此这里不用指定类的名称,而是通过我们手动set设置bd的名称
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            //指定工厂类Config通过该类获取工厂方法,这里给工厂类的beanName即可
            builder.setFactoryMethodOnBean(method.getMethodName(),"config");
            AbstractBeanDefinition bd = builder.getBeanDefinition();
            //将BeanDefinition加入到BeanFactory
            context.registerBeanDefinition(method.getMethodName(),bd);

        }

        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        context.close();
    }
}

表面上以上代码就可以实现@Bean注解的解析,但实际上运行会出现如下错误

在创建sqlSessionFactoryBean时失败,这是因为在创建该对象时,需要注入一个参数,那么我们需要在创建BeanDefinition时需要指定Bean的装配模式

但是还是存在一个问题,那就是目前无法解析@Bean注解中的属性,比如说dataSource方法上的Bean注解中指定的初始化方法我们没有办法解析执行,因此,我们接着完善上面的代码,同时我们将这些实现代码同样抽取到一个BeanFactory后处理当中,那么具体的实现代码如下

public class AtBeanPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        try {
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            MetadataReader reader = factory.getMetadataReader(Config.class.getName());
            //获取被注解修饰的方法中被Bean注解修饰的方法
            Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata method : methods) {
                System.out.println(method);
                //获取方法上的注解属性信息,得到map集合后通过get方法获取初始化方法的方法名
                String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                //因为是通过Config调用那些方法,因此这里不用指定类的名称,而是通过我们手动set设置bd的名称
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
                //指定工厂类Config通过该类获取工厂方法,这里给工厂类的beanName即可
                builder.setFactoryMethodOnBean(method.getMethodName(),"config");
                //如果是工厂方法的参数需要自动装配那么需要指定装配模式
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                if (initMethod.length()>0){
                    //说明当前@Bean注解添加了initMethod属性
                    builder.setInitMethodName(initMethod);
                }
                AbstractBeanDefinition bd = builder.getBeanDefinition();
                //将BeanDefinition加入到BeanFactory
                DefaultListableBeanFactory context = (DefaultListableBeanFactory) beanFactory;
                context.registerBeanDefinition(method.getMethodName(),bd);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

模拟实现MapperScan

在实现MapperScan之前,我们先通过配置类了解一下Spring底层是如何将接口创建对象并加入beanFactory当中的

@Configuration
@ComponentScan("com.zmt.test5.bean")
public class Config {
    //...上面已经展示过的内容
    
    @Bean
    public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory){
        MapperFactoryBean<Mapper1> factoryBean = new MapperFactoryBean<>(Mapper1.class);
        factoryBean.setSqlSessionFactory(sqlSessionFactory);
        return factoryBean;
    }

    @Bean
    public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory){
        MapperFactoryBean<Mapper2> factoryBean = new MapperFactoryBean<>(Mapper2.class);
        factoryBean.setSqlSessionFactory(sqlSessionFactory);
        return factoryBean;
    }
}

这样添加Mapper存在一个问题,就是无法批量添加,只要存在一个Mapper接口就需要编写一个@Bean注解。因此我们需要实现类似于ComponentScan的注解一样,实现Mapper注解的扫描,这里我们就直接将实现代码写在BeanFactory后处理器当中了

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            //首先实现扫描包
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath*:com/zmt/test5/mapper/*.class");
            //获取类信息
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
            for (Resource resource : resources) {
                MetadataReader reader = factory.getMetadataReader(resource);
                ClassMetadata classMetadata = reader.getClassMetadata();
                if (classMetadata.isInterface()) {
                    //判断该类是否是接口,如果是接口那么再进行下一步
                    //生成bd,我们是通过MapperFactoryBean获取接口对象的,因此这里指定MapperFactoryBean类名
                    AbstractBeanDefinition bdFactoryBean = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                            //设置MapperFactoryBean构造方法中的需要生成的bean对象
                            .addConstructorArgValue(classMetadata.getClassName())
                            //需要注入sqlSessionFactory对象,这里选择根据类型注入
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();
                    //生成bean名称需要依赖genericBeanDefinition中的指定的通用Bean的className
                    //因为是根据MapperFactoryBean一个类生成所有的Mapper对象,因此采用MapperFactoryBean的bd生成beanName时
                    //会导致生成的都是同一个名称,从而导致后者bd覆盖前者bd,所以我们再次生成一个bd,这里保存的ClassName是各个Mapper类名
                    //在生成beanName时,bd选择各个Mapper接口生成的bd
                    AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                    String name = generator.generateBeanName(bd, beanFactory);
                    //注册到beanFactory中的还是bdFactoryBean
                    beanFactory.registerBeanDefinition(name,bdFactoryBean);
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

    }
}

通过断点观察,我们可以知道beanName是各个mapper接口类名,但是beanClass还是MapperFactoryBean

需要注意的是,我们在创建Mapper的bd时,需要注入sqlSessionFactory对象,因此,我们需要确保Spring中存在该对象,因此,我们需要将上文中的AtBeanPostProcessor处理器也要执行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/274616.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

判断电话号码是否重复-excel

有时候重复的数据不需要或者很烦人&#xff0c;就需要采取措施&#xff0c;希望以下的方法能帮到你。 1.判断是否重复 方法一&#xff1a; 1&#xff09;针对第一个单元格输入等号&#xff0c;以及公式countif(查找记录数的范围&#xff0c;需要查找的单元格&#xff09; 2…

node.js express框架开发入门教程

文章目录 前言一、Express 生成器&#xff08;express-generator&#xff09;二、快速安装1.express框架express-generator生成器安装2.使用pug视图引擎创建项目,projectName 为项目名称自定义 三、安装热更新插件 nodemon四、目录结构1. public文件夹2.routes路由其他请求方式…

解密负载均衡:如何平衡系统负载(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

连接GaussDB(DWS)报错:Invalid or unsupported by client SCRAM mechanisms

用postgres方式连接GaussDB(DWS)报错&#xff1a;Invalid or unsupported by client SCRAM mechanisms 报错内容 [2023-12-27 21:43:35] Invalid or unsupported by client SCRAM mechanisms org.postgresql.util.PSQLException: Invalid or unsupported by client SCRAM mec…

论文阅读《Rethinking Efficient Lane Detection via Curve Modeling》

目录 Abstract 1. Introduction 2. Related Work 3. BezierLaneNet 3.1. Overview 3.2. Feature Flip Fusion 3.3. End-to-end Fit of a Bezier Curve 4. Experiments 4.1. Datasets 4.2. Evalutaion Metics 4.3. Implementation Details 4.4. Comparisons 4.5. A…

Flink on K8S生产集群使用StreamPark管理

&#xff08;一&#xff09;直接部署&#xff08;手动测试用&#xff0c;不推荐&#xff09; Flink on Native Kubernetes 目前支持 Application 模式和 Session 模式&#xff0c;两者对比 Application 模式部署规避了 Session 模式的资源隔离问题、以及客户端资源消耗问题&am…

用好它们!没有你找不到的电子书

以前读书的时候很喜欢买纸质书籍 &#xff0c;但自从有了 iPad 和智能手机。我发现用智能设备看书似乎性价比更好&#xff0c;不仅可以随时随地阅读 而且还能省不少书的钱&#xff0c;因为电子书网上很多分享几个本人常用的电子书搜索工具。用好这几个工具99% 的书籍你都可以下…

PAT 乙级 1028 人口普查

解题思路&#xff1a;此题我想到俩种方法&#xff0c;一种是排序方法&#xff0c;一种是不排序的方法&#xff0c;首先都是看是否是有效年龄&#xff0c;然后一种是排序&#xff0c;另一种是维护最大值和最小值的变量&#xff0c;一定要注意如果有效数字是0那就只输出0就可以了…

新版ONENET的物联网环境调节系统(esp32+onenet+微信小程序)

新版ONENET的物联网环境调节系统&#xff08;esp32onenet微信小程序&#xff09; 好久没用onenet突然发现它大更新了&#xff0c;现在都是使用新版的物联网开放平台&#xff0c;只有老用户还有老版的多协议接入&#xff0c;新用户是没有的&#xff0c;所以我顺便更新一下新的开…

企业私有云容器化架构运维实战

什么是虚拟化: 虚拟化&#xff08;Virtualization&#xff09;技术最早出现在 20 世纪 60 年代的 IBM 大型机系统&#xff0c;在70年代的 System 370 系列中逐渐流行起来&#xff0c;这些机器通过一种叫虚拟机监控器&#xff08;Virtual Machine Monitor&#xff0c;VMM&#x…

会议室占用时间段 - 华为OD统一考试

OD统一考试 题解: Java / Python / C++ 题目描述 现有若干个会议,所有会议共享一个会议室,用数组表示各个会议的开始时间和结束时间, 格式为: [[会议1开始时间,会议1结束时间],[会议2开始时间,会议2结束时间]] 请计算会议室占用时间段。 输入描述 [[会议1开始时间,…

数据库——LAMP的搭建及MySQL基操

1.实验内容及原理 1. 在 Windows 系统中安装 VMWare 虚拟机&#xff0c;在 VMWare 中安装 Ubuntu 系统,并在 Ubuntu 中搭建 LAMP 实验环境。 2. 使用 MySQL 进行一些基本操作&#xff1a; &#xff08;1&#xff09;登录 MySQL&#xff0c;在 MySQL 中创建用户&#xff0c;并对…

关于增强监控以检测针对Outlook Online APT活动的动态情报

一、基本内容 2023年6月&#xff0c;联邦民事行政部门&#xff08;FCEB&#xff09;在其Microsoft 365&#xff08;M365&#xff09;云环境中发现了可疑活动。该机构迅速向Microsoft和网络安全和基础设施安全局&#xff08;CISA&#xff09;报告了此情况。经过深入调查&#x…

【架构】企业信息安全体系架构详解

企业信息安全体系架构来说,是包含技术、运维、管理3个层面。本文说的安全架构,主要集中讨论偏研发技术层面的安全架构。 安全性是软件研发技术体系,系统架构设计阶段的一个关键DFX能力,与可靠性、可服务性、性能等架构属性并列。由于安全性设计自身的特点,涉及到系统架构…

Zblog主题模板:zblog博客主题aymeleven

zblog主题模板&#xff1a;zblog博客主题aymeleven zblog博客主题aymeleven主要是以文字内容为主导&#xff0c;将页面的设计杂乱的图片和元素进行最小化或者去除&#xff0c;从而使整个页面更加简洁、清晰&#xff0c;突出信息的呈现。 下面介绍一下zblog主题模板:zblog博客主…

AI产品经理 - 技术课-要不要懂技术(上)

一、AI产品经理&#xff0c;要不要懂技术&#xff1a;笔者答案是肯定的 1.AI产品经理要懂产品方案 2.不懂算法&#xff0c;会遇到问题&#xff1a;没有核心竞争力&#xff0c;会边缘化

基于ssm的数据结构课程网络学习平台的设计与实现论文

数据结构课程网络学习平台 摘要 本文介绍了数据结构课程网络学习平台的开发全过程。通过分析企业对于数据结构课程网络学习平台的需求&#xff0c;创建了一个计算机管理数据结构课程网络学习平台的方案。文章介绍了数据结构课程网络学习平台的系统分析部分&#xff0c;包括可行…

【elk-day01】es和kibana搭建及验证---Mac-Docker

Mac系统使用Docker下载搭建和验证eskibana Docker下载安装es安装es验证kibana安装kibana验证 Docker下载安装 Docker Desktop官网安装下载地址 说明一下为什么要安装desktop版本的docker&#xff0c;因为docker作为工具使用&#xff0c;我们需要的是开箱即用&#xff0c;没有必…

【yolov5驾驶员和摩托车佩戴头盔的检测】

yolov5驾驶员和摩托车佩戴头盔的检测 数据集和模型yolov5驾驶员和摩托车佩戴头盔的检测yolov5驾驶员和摩托车佩戴头盔的检测可视化结果 数据集和模型 数据和模型下载&#xff1a; yolov5摩托车佩戴头盔和驾驶员检测模型 yolov5-6.0-helmat-mortor-1225.zipyolov3摩托车佩戴头…

Windows实现MySQL5.7主从复制(详细版)

使用免安装版本&#xff08;官网下载地址&#xff09; 在Windows上安装两种MySQL服务并同时开启服务 1.下载配置 打开解压文件所在位置&#xff0c;就新建一个配置文件my.ini。 2.主库安装 主库的my.ini配置文件如下&#xff1a; [mysqld] #设置主库端口&#xff0c;注意须是…