SpringBoot中的starter:whywhatmake

news2024/10/7 16:26:25

1、为什么用starter

我们知道,springboot框架是为了简化spring框架开发推出的,那么,在之前的spring框架开发中,如果我们需要连接数据库,引入mybatis框架,需要怎么操作呢?我们需要去maven仓库找mybatis的jar包,还要找mybatis-spring的jar包,然后在applicationContext.xml文件中配置dataSource和mybatis相关信息,还有数据库驱动包等,可见,整个步骤相当麻烦。另外,我们还要考虑版本兼容的问题。因此,springboot为了简化开发,解决上述问题,引入了starter机制。

2、什么是starter

我们还以mybatis-springboot-starter为例,看看它都有哪些东西。找到对应的jar包,展开发现,内容很少,如下
在这里插入图片描述
pom.properties:配置了maven所需的version、groupId和artifactId信息
在这里插入图片描述
pom.xml:配置了所需要的jar包,我们重点关注下mybatis-spring-boot-autoconfigure这个包
在这里插入图片描述
MANIFEST.MF:配置了jar包的信息,这个我们不过多关注
spring.provides:主要给ide使用,也不过多关注
我们可以看到,这个starter很简单,一行实际代码都没有,那么它是如何工作的呢,我们看下上面说到的mybatis-spring-boot-autoconfigure这个包,看到autoconfigure有没有眼前一亮,这个自动装配之前在SpringBoot自动装配原理有过讲解,接下来,我们看下这个包。
在这里插入图片描述
pom.properties、pom.xml和MANIFEST.MF这三个不再赘述
additional-spring-configuration-metadata.json和spring-configuration-metadata.json这两个文件配置了一些properties属性信息,都属于ide配置提示,例如我们引入这个starter后,在配置文件输入mybatis,ide自动弹出一些属性配置
在这里插入图片描述
那么这两个文件有什么区别呢?如果pom.xml中引入了spring-boot-configuration-processor这个包,则会自动生成spring-configuration-metadata.json文件,如果需要手动修改里面的元数据,则可以在additional-spring-configuration-metadata.json中编辑,最终两个文件中的元数据会合并到一起。
spring.factories:这个文件的内容是key-value形式,key是EnableAutoConfiguration,value就是要自动装配的内容(即对应的XXXAutoConfiguration.java文件的类路径)。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

ConfigurationCustomizer.java:一个自定义回调接口
SpringBootVFS.java:用于扫面嵌套的jar包
MybatisProperties.java:一个JavaBean,配置了mybatis初始化需要的一些属性,接下看相当重要的MybatisAutoConfiguration.java文件的内容

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.mybatis.spring.boot.autoconfigure;

import java.util.Iterator;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.ClassPathMapperScanner;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
    private final MybatisProperties properties;
    private final Interceptor[] interceptors;
    private final ResourceLoader resourceLoader;
    private final DatabaseIdProvider databaseIdProvider;
    private final List<ConfigurationCustomizer> configurationCustomizers;

    public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
        this.properties = properties;
        this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable();
        this.resourceLoader = resourceLoader;
        this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();
        this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();
    }

    @PostConstruct
    public void checkConfigFileExists() {
        if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
            Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
            Assert.state(resource.exists(), "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
        }

    }

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }

        org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration();
        if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
            configuration = new org.apache.ibatis.session.Configuration();
        }

        if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
            Iterator var4 = this.configurationCustomizers.iterator();

            while(var4.hasNext()) {
                ConfigurationCustomizer customizer = (ConfigurationCustomizer)var4.next();
                customizer.customize(configuration);
            }
        }

        factory.setConfiguration(configuration);
        if (this.properties.getConfigurationProperties() != null) {
            factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }

        if (!ObjectUtils.isEmpty(this.interceptors)) {
            factory.setPlugins(this.interceptors);
        }

        if (this.databaseIdProvider != null) {
            factory.setDatabaseIdProvider(this.databaseIdProvider);
        }

        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }

        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }

        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        return factory.getObject();
    }

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        ExecutorType executorType = this.properties.getExecutorType();
        return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);
    }

    @Configuration
    @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
    @ConditionalOnMissingBean({MapperFactoryBean.class})
    public static class MapperScannerRegistrarNotFoundConfiguration {
        public MapperScannerRegistrarNotFoundConfiguration() {
        }

        @PostConstruct
        public void afterPropertiesSet() {
            MybatisAutoConfiguration.logger.debug("No {} found.", MapperFactoryBean.class.getName());
        }
    }

    public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
        private BeanFactory beanFactory;
        private ResourceLoader resourceLoader;

        public AutoConfiguredMapperScannerRegistrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
            ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

            try {
                if (this.resourceLoader != null) {
                    scanner.setResourceLoader(this.resourceLoader);
                }

                List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
                if (MybatisAutoConfiguration.logger.isDebugEnabled()) {
                    Iterator var5 = packages.iterator();

                    while(var5.hasNext()) {
                        String pkg = (String)var5.next();
                        MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
                    }
                }

                scanner.setAnnotationClass(Mapper.class);
                scanner.registerFilters();
                scanner.doScan(StringUtils.toStringArray(packages));
            } catch (IllegalStateException var7) {
                MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", var7);
            }

        }

        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
        }

        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    }
}

这个文件由@Configuration标明这是一个配置类,配置了很多bean,例如SqlSessionFactorySqlSessionTemplate,最重要的是SqlSessionFactory,它用来创建SqlSession,用来操作数据库,@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})@ConditionalOnBean({DataSource.class})分别注明只有SqlSessionFactory类和SqlSessionFactoryBean类、DataSource实例时,该配置生效。@EnableConfigurationProperties({MybatisProperties.class})自动配置MybatisProperties.java中的属性,@AutoConfigureAfter({DataSourceAutoConfiguration.class})注明这个配置类在DataSourceAutoConfiguration之后配置。
通过上面的例子,我们可以发现,编写一个starter主要步骤如下:

  • 定义一个xxx-spring-boot-starter空项目,只包含pom.properties和pom.xml,pom.xml文件中包含xxx-spring-boot-autoconfigure包
  • 定义一个xxx-spring-boot-autoconfigure项目,包含xxxAutoConfiguration类,该类定义一些bean、spring.factories文件增加k-v键值对、XXXProperties类,定义配置值
    接下来,尝试自定义一个starter。

3、自定义starter

首先,建一个空项目randompwd-spring-boot-starter,pom文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example.zy</groupId>
    <artifactId>randompwd-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>randompwd-spring-boot-starter</name>
    <description>Demo for Custom Starter</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.example.zy</groupId>
            <artifactId>randompwd-spring-boot-autoconfigure</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

创建randompwd-spring-boot-autoconfigure项目,pom文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example.zy</groupId>
    <artifactId>randompwd-spring-boot-autoconfigure</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>randompwd-spring-boot-autoconfigure</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

在resource下创建META-INF文件夹,创建spring.factories文件,配置k-v键值对

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.zy.RandomPwdAutoConfiguration

新建RandomPwdAutoConfiguration.java文件,(可以先新建该文件再配置k-v值,防止出错,配置成功可以从k-v值点到该文件),代码如下

package com.example.zy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author zy
 * @version 1.0.0
 * @ClassName RandomPwdAutoConfiguration.java
 * @Description TODO
 * @createTime 2022/12/14
 */
//标记配置类
@Configuration
//检测到含有RandomPwdProperties类生效
@ConditionalOnClass(RandomPwdProperties.class)
//自动加载配置类
@EnableConfigurationProperties(RandomPwdProperties.class)
public class RandomPwdAutoConfiguration {
    @Autowired
    private RandomPwdProperties randomPwdProperties;
    @Bean
    public RandomPwdService randomPwdService(){
        return new RandomPwdService(randomPwdProperties.getLength());
    }
}

对应的RandomPwdProperties代码如下,它会把配置文件中以randompwd开头的配置读取

package com.example.zy;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @author zy
 * @version 1.0.0
 * @ClassName RandomPwdProperties.java
 * @Description TODO
 * @createTime 2022/12/14
 */
@ConfigurationProperties(prefix="randompwd")
public class RandomPwdProperties {
    private int length;


    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }
}


RandomPwdService代码如下

package com.example.zy;

import java.util.Random;

/**
 * @author zy
 * @version 1.0.0
 * @ClassName RandomPwdService.java
 * @Description TODO
 * @createTime 2022/12/14
 */
public class RandomPwdService {
    private int length;

    public RandomPwdService(int length) {
        this.length = length;
    }

    public  String randomPwd(){
        char charr[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
        char charq[] = "1234567890".toCharArray();
        StringBuilder sb = new StringBuilder();
        Random r = new Random();
        for (int x = 0; x < length - 6; ++x) {

            sb.append(charr[r.nextInt(charr.length)]);
        }
        for (int x = 0; x < 6; ++x) {
            sb.append(charq[r.nextInt(charq.length)]);
        }
        return sb.toString();
    }
}

将这两个打包,然后我们新建一个测试类,或者在其他项目上简单测试,引入我们的starter

<dependency>
            <groupId>com.example.zy</groupId>
            <artifactId>randompwd-spring-boot-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

配置文件敲入random,可以看到,提示了配置
在这里插入图片描述
然后写一个测试接口

@Autowired
    private RandomPwdService randomPwdService;
    @RequestMapping("/randomPwd")
    public String randomPwd(){
        return randomPwdService.randomPwd();
    }

启动项目,测试接口,发现正常使用,至此,自定义Starter成功。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

当 chatGPT 被职场 PUA ,笑麻了

大家最近是不是被 chatGPT 刷屏了&#xff1f;简单来说&#xff0c;chatGPT 是一个智能聊天引擎。 那 chatGPT 和小爱同学、 siri 有什么区别呢&#xff1f; 如果体验过的朋友&#xff0c;能感受到区别还是很大&#xff0c;chatGPT 的智能表现过于优秀&#xff0c;远远超过了这…

深入浅出DPDK KNI核心技术

一、KNI KNI全称&#xff1a;Kernel NIC Interface&#xff0c;内核网卡接口&#xff0c;允许用户态程序访问linux控制平面。 在DPDK报文处理中&#xff0c;有些报文需要发送到内核协议栈进行处理&#xff0c;如GTP-C控制报文 如果报文数量较少&#xff0c;可以使用内核提供…

Lua热更新

Lua热更新解决方案 文章目录Lua热更新解决方案1.AB包1.1 AB包概述1. 从0开始的Lua语法1.1 HelloWorld1.2 数据类型1.3 字符串1.4 运算符1.5 条件语句1.6 循环语句1.7 函数1.8 数组1.9 迭代器1.10 字典&#xff0c;类&#xff0c;对象1.11 多脚本执行1.12 特殊语法1.13 协程1.14…

hive时间和字符串互转,时间函数

hive里经常需要将字符串转化为date或timestamp 或者转化为日期格式的string 先说一个简单的 cast(xx as date/string/timestamp) 这个大多情况都可以用 1.to_date to_date只保留年月日,参数必须是string类型的yyyy-MM-dd HH:mm:ss或者date或timestamp类型,返回值是date类型,…

尚太科技开启招股:预计募资总额22亿元,业绩增长迅猛

12月19日&#xff0c;石家庄尚太科技股份有限公司&#xff08;下称“尚太科技”&#xff0c;SZ:001301&#xff09;开启招股&#xff0c;将在深圳证券交易所主板上市。本次冲刺上市&#xff0c;尚太科技的发行价格为33.88元/股&#xff0c;发行数量为6494.37万股&#xff0c;募…

MySQL Binlog温故知新

目录 一、什么是Binlog 二、Binlog文件记录模式 三、Binlog 日志内容 四、常用的binlog日志操作命令 五、binlog日志中间件 一、什么是Binlog Binlog &#xff08;Binary log&#xff09;是MySQL的二进制日志&#xff0c;以二进制的形式记录了对于数据库的变更&#xff0…

Tesseract-OCR 和cnocr/cnstd

Tesseract-OCR学习系列 Tesseract-OCR学习系列&#xff08;四&#xff09;API - 简书 参考文档&#xff1a;https://github.com/tesseract-ocr/tesseract/wiki/APIExample 这篇文章介绍了GetComponentImages等基础api的用法 Python 自动识别图片文字—保姆级OCR实战教程 Py…

使用界面配置静态路由(保姆级教程)

啰嗦几句 因为写的很详细&#xff0c;保姆级别的&#xff0c;所以看起来内容很多&#xff0c;但是东西就那一点&#xff0c;你自己配个2、3遍就会了。期末考试也不用担心这个实验了。 使用Cisco Packet Tracer这个软件。 原文件下载 补充&#xff1a;下载后使用Cisco Packet T…

有源电力滤波器——低压配电系统

安科瑞 华楠 一、谐波的危害 ● 使公用电网中的设备产生附加谐波损耗&#xff0c;降低电网、输电及用电设备的使用效率。 ● 在三相四线制系统中&#xff0c;由于零线流过大量的3n次谐波电流&#xff0c;造成零线过热。 ● 谐波会产生额外的热效应&#xff0c;引起用电设备…

GitLab创建新分支并同步其它分支的内容(IDEA)

拉取项目到本地 选择对应项目并复制clone地址 打开IDEA&#xff0c;选择File–》New–》Project from Version Control 在弹出的对话框中粘贴刚才复制的地址 完成后项目就被拉取到本地对应目录中了 创建新分支 这里以master分支为例&#xff0c;其它分支同理 首先在GitL…

Linux Red Hat 8.0 find命令练习

find&#xff1a;主要用来查找字符串 常用参数&#xff1a; -name 搜索文件名 -iname 搜索文件名时大小写不敏感 -type d 目录、 f 普通文件、 l 符号链接 -size 指定搜索文件大小 -perm 按执行权限来查找 -user 按…

在线考试网站

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 模块划分&#xff1a;老师模块、班级模块、学生模块、课程模块、试题模块、试卷模块、 组卷模块、考试模块、答题模块 …

什么是项目风险管理?要如何执行风险管理?

项目风险管理是项目管理中重要组成部分之一&#xff0c;其影响着项目是否能成功最终交付的关键。虽然项目经理都无法准确地预测未来项目结果&#xff0c;但可以应用一种精简的风险管理流程来预测项目管理中的不确定性&#xff0c;并将这些不确定因素的影响降到最低。因此如何执…

基于Python+Django+Vue+MYSQL的邯郸地标美食导游平台

项目介绍 我的家乡是邯郸市。邯郸市我国为数不多的3000年没有改变过名字城市。我的家乡就是邯郸。在我的家乡有非常多的旅游景点和美食。为了让更多的人了解到邯郸的旅游景点和好吃的美食信息&#xff0c;我通过pythonvueelementui开发了本次的邯郸家乡网红旅游景点美食导游平…

CSS高级 DAY2

目录 选择器进阶 复合选择器 后代选择器 子代选择器 并集选择器 交集选择器 hover伪类选择器&#xff08;就是鼠标悬停选择器&#xff09; 背景有关属性 背景颜色 背景图片 背景平铺 背景位置 背景的复合属性 元素显示模式 块级元素 行内元素 行内块元素 元素显示…

SimpleDateFormat线程不安全解析以及解决方案

我们都知道SimpleDateFormat是Java中用来日期格式化的一个类&#xff0c;可以将特定格式的字符转转化成日期&#xff0c;也可将日期转化成特定格式的字符串。比如 将特定的字符串转换成日期 public static void main(String[] args) throws ParseException {SimpleDateFormat…

windows11打开隐藏的gpedit.msc本地组策略编辑器以及禁止自动更新系统

目录打开隐藏的gpedit.msc本地组策略编辑器windows11禁止自动更新系统打开隐藏的gpedit.msc本地组策略编辑器 1.新建.txt文本文件&#xff0c;输入如下代码 echo off pushd “%~dp0” dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtension…

a股行情接口功能特点

a股行情接口功能特点&#xff0c;如下&#xff1a; 1、支持A股历史交易查询&#xff1b; 2、包括股票数据&#xff1b; 3、每日A股收盘后更新当日交易数据&#xff0c;停牌不更新&#xff1b; 4、支持多股历史数据一次查询&#xff1b; 5、支持任何时间段查询&#xff1b;…

Linux进程概念(二)

Linux进程概念进程状态普通操作系统层面理解运行与阻塞挂起与阻塞Linux是怎么做的孤儿进程进程优先级什么是优先级如何改变优先级其他概念进程状态 进程状态分有&#xff1a; 运行 新建 就绪 挂起 阻塞 等待 停止 挂机 死亡… 状态其实就是返回的整形&#xff0c;就像某个数字…

【毕业设计_课程设计】基于 SVM 分类器的动作识别系统(源码+论文)

文章目录0 项目说明1 研究目的2 研究方法2.1 动作采集2.2 动作识别2.3 智能家居模拟3 论文目录4 项目工程0 项目说明 基于 SVM 分类器的动作识别系统 提示&#xff1a;适合用于课程设计或毕业设计&#xff0c;工作量达标&#xff0c;源码开放 1 研究目的 本项目对经典 SVM 二…