当前位置: 首页 > news >正文

(自用)配置文件优先级、SpringBoot原理、Maven私服

配置优先级

        之前介绍过SpringBoot中支持三类配置文件.properties、.yml和.yaml,他们三者之间也是有着优先级顺序的,为.properties➡.yml➡.yaml。

        同时SpringBoot为了增强程序的拓展性,除了支持配置文件属性配置,还支持Java系统属性和命令行参数的方式进行属性配置。

//java系统属性"-D"是固定的
-Dkey=value
-Dserver.port=8000
//命令行参数"--"是固定的
--key=value
--server.port=8000

        我们可以在可视化配置界面来配置这两个属性:

然后在Modify options下拉列表选中Add VM options和Program arguments,即可编写相关属性。

        两者之间,命令行参数的优先级更高,会优先生效,也就是说如图的配置中,实际运行的端口为9999。

        这是在IDEA中配置,但如果项目已经打包上线的话,配置相关属性的方法就又不一样了。我们上文的两项配置清空,再来运行maven生命周期中的打包程序,系统会将jar包存放到target目录下(如果版本过老,则需要引入spring-boot-maven-plugin依赖):

        直接在该目录下的路径框中输入cmd,输入java即可获取相关指令的介绍:

        其中options代表java系统属性,args代表命令行参数。
        然后输入java -jar 再按下Tab即可补齐jar包名字,按下回车运行,此时端口号因为尚未配置,仍为8080。按下ctrl+c关闭程序,再来配置相关的属性,注意Java系统属性应在前,命令行参数应该在后:

java -Dserver.port=9000 -jar chnApplication-0.0.1-SNAPSHOT.jar --server.port=8000

也就是说共有五种配置方式:

  1. application.yaml 文件配置
  2. application.yml文件配置
  3. application.properties文件配置
  4. java系统属性即可视化界面配置 (-Dxxx=xxx)
  5. 命令行参数 (–xxx=xxx)

        优先级则为从下往上,命令行优先级最高。

bean管理

获取bean

        默认情况下,Spring项目在启动时会自动创建IOC容器(spring容器),并自动创建bean放在IOC容器中,主动获取这些bean可以通过如下方式:

  • 根据bean名称获取:Object getBean(String name)
  • 根据bean类型获取:<T>T getBean(Class requiredType)
  • 根据bean名称和类型获取:<T>T getBean(String name, Class<T> requiredType)
    //IOC容器对象@Autowiredprivate ApplicationContext applicationContext;//获取bean对象@Testpublic void testGetBean(){//根据bean的名称获取//获取的对象为Object类,需强转DeptController bean1 = (DeptController) applicationContext.getBean("deptController");System.out.println(bean1);//根据bean的类型获取DeptController bean2 = applicationContext.getBean(DeptController.class);System.out.println(bean2);//根据bean的名称 及 类型获取DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);System.out.println(bean3);

        这就是三种获取bean的方式,但如果IOC容器中只有一个DeptController类型的bean,那这三个获取到的都是同一个bean,因为在Spring中bean默认是单例的。

        我没获取的bean是单例还是多例,实际上取决于bean的作用域。 

bean作用域

        在Spring中bean支持五种作用域,后三种在web环境下生效,因此我们主要看前两种:

  • singleton(单例):默认作用域。容器内同名称的bean只有一个实例。
  • prototype(原型):每次请求Bean时都会创建一个新的Bean实例。
  • request(请求):每个请求范围中,每个Bean只会有一个实例。
  • session(会话):每个会话范围中,每个Bean只会有一个实例。
  • application(应用):每个应用范围中,每个Bean只会有一个实例。

        设置作用域则可以借助注解@Scope("prototype"),内部为不同的属性。

        当使用默认的作用域(singleton)修饰类时,该Bean会在容器启动时就已实例化并放到IOC容器中。我们还可以通过注解@Lazy使bean延迟初始化,那么该Bean会在第一次使用时才会初始化。

        使用注解@Scope("prototype")时,每次请求该Bean时Spring容器都会创建一个新的Bean实例。即Bean实例是在第一次使用时创建的。该bean会在容器启动时就已实例化并放到IOC容器中,并且bean会在第一次使用时都进行初始化。

第三方bean

        之前使用的bean都是我们在项目中定义的,声明bean只需在类上加上注解@Component、 @Repository、@Service和@Controller(或者说@RestController)。但在实际开发中可能出现某个类是依赖提供的,或者说文件为只读属性无法修改等等特殊情况,这就要用到另外一个注解@Bean。

        当你需要将第三方库的类实例化并注册为Spring容器中的Bean时,可以使用@Bean注解。这些类可能不是用Spring的注解@Component及其衍生注解标注的,因此需要通过@Bean手动注册。

        我们可以定义一个方法,返回我们所需的类型的对象,并在该方法上加上@Bean,这样该类的实例就会自动注入到IOC之中。如果有多个外部bean,我们可以通过注解@Configuration声明一个配置类,在该类中定义方法实现多个bean注入。

        如果未指定bean的名称,则bean名称就是方法名(首字母小写)。
        如果指定bean的名称,则bean名称为自定义的名称。
        如果声明bean时需要依赖注入(就像某个类被@RestController修饰,类内部使用注解@Autowired注入其他bean的实例),则需要在方法中指定对应的方法形参即可。

@Configuration
public class Config {// 默认Bean声明@Beanpublic Default defaultBean() {return new Default();}// 指定名称的Bean声明@Bean(name = "customBean")public Custom customBean() {return new Custom();}// 依赖注入的Bean声明@Beanpublic Dependent dependentBean(Default defaultBean, Custom customBean) {return new Dependent(defaultBean, customBean);}
}

SpringBoot原理

        之前讲过:Spring是目前市面上最流行的java框架,其可以帮助我们快速的构建java项目,而在spring家族中有非常多优秀的框架,而所有的框架都是基于基础框架:Spring Framework实现的。
        如果我们直接基于Spring Framework框架进行开发会比较繁琐,因此我们可以通过SpringBoot来简化spring框架的开发(注意是简化而非替代)。其之所以简化了开发,是因为底层框架实现了起步依赖和自动配置。而我们要介绍的原理就是这两大功能的原理。

起步依赖

        如果使用spring框架进行开发,则需引入大量依赖如webmvc、servlet、jackson等等,还需要保证这些依赖的版本需相互匹配,否则会出现版本冲突等问题。而使用了SpringBoot框架后,只需要引入spring-boot-starter-web依赖即可,其他依赖会因为Maven的依赖传递自动引入。

自动配置

        自动配置是指应用程序在启动时,一些配置类、bean对象就自动的存入到IOC容器当中,不需要我们手动去开发,从而简化了开发,省去了繁琐的配置操作。

原理

        我们来看一个例子,在B项目中需要引入A项目下的某个bean,按照之前的方法编写完成后却失败了,这是因为@Component和其他三注解需要被spring的组件扫描到才能生效,而在SpringBoot项目中,主方法上的@SpringBootApplication注解虽具备包扫描的作用,但只能扫描到当前包及其子包。而A项目下的某个bean属于第三方依赖提供的,无法扫描到。
        解决此问题可以有多种方法,我们依次来介绍:

@ComponentScan

        我们可以借助注解@ComponentScan({"当前包","第三方包"}),因为该注解会覆盖掉原本默认扫描的包,因此我们不仅要指定第三方包,还需指定当前包。

@ComponentScan({"org.example1","org.example2"})
@ServletComponentScan
@SpringBootApplication
public class ChnApplication {public static void main(String[] args) {SpringApplication.run(ChnApplication.class, args);}
}

        但之前我们引入过大量第三方依赖,如果全部这样引入不仅代码臃肿,执行效率还低。这就要提到第二种方案:import导入。

@Import

        使用@Import导入的类会被spring加载到IOC容器中,导入方式有很多,我们依次来看:导入普通类、导入配置类、导入ImportSelector接口实现类。其导入的都是数组,前两个导入的为类名,后一个导入的为String形式的全类名。
        ImportSelector接口有一个selectImports方法,其返回值为一个数组,该数组中封装了类名,我们可以将需要交给IOC容器管理的类的全类名封装在这个数组中。

//导入普通类——————————————————————————————————
//单个类
@Import(A.class)
//多个类
@Import({A1.class,A2.class})
@SpringBootApplication
public class ChnApplication {//主方法public static void main(String[] args) {SpringApplication.run(ChnApplication.class, args);}
//导入配置类——————————————————————————————————
//单个类
@Import(Config.class)
//多个类
@Import({Config1.class,Config2.class})
@SpringBootApplication
public class ChnApplication {//主方法public static void main(String[] args) {SpringApplication.run(ChnApplication.class, args);}
//导入ImportSelector接口实现类————————————————
@SpringBootApplication
@Import(MyImportSelector.class) // 导入ImportSelector实现类
public class ChnApplication {public static void main(String[] args) {SpringApplication.run(ChnApplication.class, args);}
}public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {// 返回需要导入的类的全类名数组return new String[]{"org.example.chnapplication.controller.Class1","org.example.chnapplication.controller.Class2"};}
}

        这样虽然相对来说简单些,但有时我们并不了解第三方包,想要准确的引入也很困难。我们可以让第三方依赖自己来指定,即使用第三方提供的注解,该注解一般为@EnableXXX,以Enable开头,该注解中封装了@Import注解,再在@Import注解后指定要导入的类。例如@EnableAutoConfiguration注解就是Spring Boot自动配置的关键所在。

        回想之前我们启动SpringBoot项目时,都是通过启动类,或者说引导类来启动的,该类上有着注解@SpringBootApplication,我们来看该注解的源码:

//@SpringBootApplication注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {@AliasFor(annotation = EnableAutoConfiguration.class)Class<?>[] exclude() default {};@AliasFor(annotation = EnableAutoConfiguration.class)String[] excludeName() default {};@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")String[] scanBasePackages() default {};@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")Class<?>[] scanBasePackageClasses() default {};@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}

        前四个注解为元注解,是修饰注解的注解,因此不需要看。
        @SpringBootConfiguration注解封装了@Configuration注解和@Indexed注解,而@Indexed注解是用来加速应用启动的,无需了解。因此@SpringBootConfiguration注解和@Configuration注解相同,都是声明了当前类是一个配置类。因此我们可以直接在启动类中来声明第三方的bean对象。

        @ComponentScan包扫描,默认扫描当前包及其子包。

        @EnableAutoConfiguration,为核心注解,该注解封装了@Import(AutoConfigurationImportSelector.class),而括号中的AutoConfigurationImportSelector就是ImportSelector接口的实现类。
        在该实现类中,实现了String[] selectImports()方法,其会一直读取到对应依赖META-INF目录下的spring.factories文件和org.springframework.boot.autoconfigure.AutoConfiguration.imports文件。两文件中包含了大量配置类的全类名。方法返回对应的类名,将其这些类的bean交给Spring的IOC进行管理。
        因此我们可以通过@bean注解声明bean对象,springboot项目在启动时就会加载配置文件中的配置类。

        在Spring Boot 2.7及之前的版本中,spring.factories是主要的自动配置注册方式。在Spring Boot 2.7到3.0的过渡期间,同时兼容spring.factories和新的自动配置导入方式。但在Spring Boot 3.0及以后的版本中,虽然仍然可以支持spring.factories,但官方更推荐使用新的自动配置导入方式。

        文件中有很多类,实际项目启动时因为注解@ConditionalXXX的限制,并不是所有的bean都注入IOC容器当中。

@Conditional

        按照一定的条件进行判断,满足条件后才会注册对应的bean对象到IOC容器当中。其可以加在方法上或者类上,分别代表对当前这个方法声明的bean生效,和对整个配置类生效。
        @Conditional是一个父注解,基于其衍生出了大量的子注解,我们主要介绍三个:

  • @ConditionalOnClass: 判断环境中是否有对应的类,可以通过name或者value属性来判断,通常使用全类名(不用也行)。有则注册 bean 到 IOC 容器。
  • @ConditionalOnMissingBean: 判断环境中没有对应的 bean,有则注册 bean 到 IOC 容器。默认根据声明的bean的类型来判断。其一般用来声明默认的bean,即有新定义的用新的,没定义则用默认的。
  • @ConditionalOnProperty: 判断配置文件中有对应属性和值,有则注册 bean 到 IOC 容器。
//@ConditionalOnClass—————————————————————————————————
//环境中存在该类才会将该bean加入IOC容器中
@Configuration
@ConditionalOnClass(name = "com.example.MyDependency")//使用name参数需要全类名
@ConditionalOnClass(value = {"com.example.ClassOne", "com.example.ClassTwo"})//使用value参数
public class MyConfig {
}
//@ConditionalOnMissingBean———————————————————————————
@Configuration
@ConditionalOnMissingBean//默认
@ConditionalOnMissingBean(MyBean.class)// 基于Bean类型
@ConditionalOnMissingBean(name = "myCustomBeanName")//基于bean的名称
@ConditionalOnMissingBean(type = "MyBean.class", name = "myCustomBeanName")//同时指定Bean类型和名称
public class MyConfig {
}
//@ConditionalOnProperty——————————————————————————————
@Configuration
@ConditionalOnProperty(prefix = "myapp", name = "enabled", havingValue = "true")//基本使用
//这个配置类 MyConfig 只有在 myapp.enabled 属性值为 true 时才会被加载。
@ConditionalOnProperty(prefix = "myapp", name = {"feature1.enabled", "feature2.enabled"}, havingValue = "true")//多个属性
//这个配置类 MyConfig 只有在 myapp.feature1.enabled 和 myapp.feature2.enabled 属性值都为 true 时才会被加载。
@ConditionalOnProperty(prefix = "myapp", name = "feature.enabled")//不指定 havingValue
//在这个例子中,只要 myapp.feature.enabled 属性存在且不是 false,MyConfig 就会被加载。
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public class MyFeatureConfig {
}

使用全类名,还是类名?

  • 类名:当你使用 value 参数并传递一个类对象时,你可以直接使用类名,因为编译器能够解析它,并且类对象在编译时是已知的。
  • 全类名:当你使用 name 参数时,你必须提供全类名,因为这里你传递的是一个字符串,编译器无法直接解析类名,需要完整的包路径来定位类。

案例(自定义依赖)

        在实际的开发中,我们可能会用到很多第三方技术,并不是所有第三方技术都提供了与springboot整合start起步依赖。我们可以定义一些公共组件,并将其封装为SpringBoot的starter。

         starter包实现依赖管理功能,而autoconfigure实现自动配置功能。在项目中进行功能开发只需引入对应的起步依赖即可。

        在之前的案例中,我们使用aliyunoss需要很多步骤:1、pom.xml文件中引入依赖2、参照官方SDK改造工具类3、yml文件中配置相关参数4、通过实体类加载yml中的配置项5、工具类获取参数6、将工具类交给IOC容器管理7、注入对应的bean。

        封装之后,我们只需要引入依赖,注入对应的bean两步即可。

        我们来以一个例子来示范:自定义aliyun-oss-spring-boot-starter,完成阿里云OSS操作工具类AliyunOSSUtils的自动配置。引入起步依赖之后,要想使用阿里云OSS,注入AliyunOSSUtils直接使用即可。

        共可分为三步:

  • 创建 aliyun-oss-spring-boot-starter 模块
  • 创建 aliyun-oss-spring-boot-autoconfigure 模块,在starter中引入该模块
  • 在 aliyun-oss-spring-boot-autoconfigure 模块中的定义自动配置功能,并定义自动配置文件 META-INF/spring/xxx.imports

一、创建 aliyun-oss-spring-boot-starter 模块

         因为该文件仅仅作为依赖管理,所以无用模块都可以删除,只保留aliyun-oss-spring-boot-starter.iml文件和pom.xml文件。

没有aliyun-oss-spring-boot-starter.iml文件解决办法:

1.鼠标放到springboot模块上连按两次Ctrl

2.点击右侧Project选择要操作的模块

3.在中间输入框输入mvn  idea:module并回车执行

稍等片刻即可出现.iml文件

二、创建 aliyun-oss-spring-boot-autoconfigure 模块

        同样保留aliyun-oss-spring-boot-starter.iml文件和pom.xml文件,但不同的是还需保留src文件。src中主方法、配置文件、测试类都为无用文件,可删除,下图为删除无用文件后的目录:

在starter的pom.xml文件中中引入该模块

        <dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId><version>0.0.1-SNAPSHOT</version></dependency>

三、在 aliyun-oss-spring-boot-autoconfigure 模块中的定义自动配置功能

        和之前一样,创建AliOSSProperties类获取信息,创建AliOSSUtils类使用信息。会报错MutilpartFile类找不到,该类是springbootweb提供的,因此在aliyun-oss-spring-boot-autoconfigure 模块下的pom.xml文件中添加依赖:

        <!--阿里云依赖--><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.17.4</version></dependency>
<!--如果使用的是Java 9及以上的版本,则需要添加JAXB相关依赖。添加JAXB相关依赖示例代码如下:--><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version></dependency><dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency><!-- no more than 2.3.3--><dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><version>2.3.3</version></dependency><!--yml文件配置aliyun时出提示--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId></dependency><!--web开发起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

        不建议使用@Data,手动添加getset方法。

        同时因为不应该使其转化为bean,因此之前的@Component、@Autowired都不能再使用了。又因为题目要求"想使用阿里云OSS,注入AliyunOSSUtils直接使用即可",也就是说最终还需将AliOSSUtils类交给IOC来管理,这就需要创建自动配置类AliOSSConfig,在该类中声明AliOSSUtils类的bean。

        再回过来看,AliOSSUtils类中调用了aliOSSProperties.getEndpoint();但因无@Autowired,其内部并无实际值,我们需要为其创建getset方法,并在自动配置类中为其赋值。

        同时赋值需要调用aliOSSProperties.getEndpoint()中的属性,但此时aliOSSProperties.getEndpoint()已不再是bean,我们无法直接使用,可通过@EnableConfigurationProperties(AliOSSProperties.class)直接将该类转为bean放入到IOC容器中。该注解是Enable开头,不难想到其底层封装了@Import注解。同时该注解只能放在声明了bean的方法上或者配置类上。

        此时需要获取该bean,获取第三方bean的对象可以可以直接在方法形参中指定,因此直接将AliOSSProperties aliOSSProperties写入AliOSSUtils类的主方法形参中。

//AliOSSProperties类获取数据——————————————————————————
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketName;
//相关getset方法略
}
//AliOSSUtils类执行方法———————————————————————————————
public class AliOSSUtils {private AliOSSProperties  aliOSSProperties;//没有自动注入,需自行定义getset方法public AliOSSProperties getAliOSSProperties() {return aliOSSProperties;}public void setAliOSSProperties(AliOSSProperties aliOSSProperties) {this.aliOSSProperties = aliOSSProperties;}//实现上传图片到OSSpublic String upload(MultipartFile file) throws IOException {// 获取阿里云OSS参数String endpoint = aliOSSProperties.getEndpoint();String accessKeyId = aliOSSProperties.getAccessKeyId();String accessKeySecret = aliOSSProperties.getAccessKeySecret();String bucketName = aliOSSProperties.getBucketName();// 获取上传的文件的输入流InputStream inputStream = file.getInputStream();// 避免文件覆盖String originalFilename = file.getOriginalFilename();String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));//上传文件到 OSSOSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);ossClient.putObject(bucketName, fileName, inputStream);//文件访问路径String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;// 关闭ossClientossClient.shutdown();return url;// 把上传到oss的路径返回}
}//自动配置类AliOSSConfig——————————————————————————————
@Configuration//转为自动配置类
@EnableConfigurationProperties(AliOSSProperties.class)//导入AliOSSProperties类的bean
public class AliOSSConfig {@Beanpublic AliOSSUtils aliOSSUtils(AliOSSProperties aliOSSProperties) {AliOSSUtils aliOSSUtils = new AliOSSUtils();aliOSSUtils.setAliOSSProperties(aliOSSProperties);//赋值,原值从aliyunoss前缀的配置项封装过来return aliOSSUtils;}
}

再定义自动配置文件 META-INF/spring/xxx.imports

        在resources目录下创建META-INF/spring两级目录。并在META下创建文件org.springframework.boot.autoconfigure.AutoConfiguration.imports,内部写上自动配置类的全类名com.aliyun.oss.aliyunoss.AliOSSConfig。

        此时就已完成封装,我们在其他包中新建一个类,并在pom.xml中引入aliyun-oss-spring-boot-starter,因为依赖传递,其又会自动引入aliyun-oss-spring-boot-autoconfigure依赖,autoconfigure依赖又引入了aliyunOSS的相关依赖,所需依赖都依次传递下来了,同时为了给public class AliOSSProperties类中的各属性赋值,我们还需配置该类所在的yml文件:

<!--        自行封装的依赖--><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-oss-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version></dependency>
#阿里云配置yml文件
aliyun:oss:endpoint: https://oss-cn-beijing.aliyuncs.com  # 阿里云OSS的端点accessKeyId: LTAI5t6nivgHXQ1rnBt3dudV  # 阿里云OSS的Access Key IDaccessKeySecret: EhrGz86soycvHNnK0V4PuZoDYgu4tm  # 阿里云OSS的Access Key SecretbucketName: chn-webapp  # 阿里云OSS的Bucket名称
//其他包的类调用bean
@RestController
public class TestController {@Autowiredpublic AliOSSUtils aliOSSUtils;@PostMapping("/upload")public String upload(MultipartFile file) throws IOException {//调用文件上传方法到阿里云OSS,返回值为获取图片的urlString url= aliOSSUtils.upload(file);return url;}
}

         此时再打开postman发起请求就可得到url:

总结

之前所学知识的分类图:

        而SpringMVC就是Spring框架中的Web开发模块, SpringMVC、Spring框架、Mybatis共同组成了SSM,但因效率较低,可以直接基于SpringBoot进行开发。

Maven高级

        之前我们了解了Maven的基本使用,这足已完成对于一些简单的项目的构建和管理,但需要开发一些中大型项目,这些知识便稍显不足。所以我们还需要学习一些Maven的高级功能。

分模块设计与开发

        顾名思义就是在设计java项目时将其拆分成多个模块进行开发,以方便模块间的互相调用和资源共享。

        回过头来看之前的员工管理系统,我们可以将各个功能都封装成一个模块,例如chnApp-pojo模块、chnApp-utils模块......然后将Controller、Saervice、Mapper三层封装成一个并引用其他模块的依赖即可。我们将原本的项目复制一份来进行操作。

        新建模块chnApp-pojo,注意该模块仅存放实体类,所以不应选择基于SpringBoot,只需创建一个Maven模块即可:

下一步中有一选项Parent先选择为none,后文会介绍。

         再将原本项目中的pojo包下的所有类复制到新项目中,建议包结构保持一致:

        然后再在pom.xml文件中添加lombok依赖。此时该模块就已完成,原模块下的pojo包可以删除了。此时原项目会因为找不到对应文件而报错,我们需在原项目的pom.xml文件中引入依赖:

 <!--原项目pojo文件中引入依赖--><dependency><groupId>com.chnMaven</groupId><artifactId>chn-pojo</artifactId><version>1.0-SNAPSHOT</version></dependency>

         此时相关工作就已完成,我们再以相同的方法新建模块chnApp-utils,这就完成了简单的模块拆分。

        注意这只是为了演示如何进行模块拆分,实际开发中分模块设计应先分模块,再进行代码编写,而非像这样对已完成的项目进行拆分。

继承和聚合

        此时已拆分为多模块,但问题也随之增多,为解决这些问题,就需要用到继承和聚合。

继承

        模块之间的继承与java里类与类的继承类似,接下来我们来看如何实现。

        在上文三个模块中,我们都引入了lombok的相关依赖,如果在大型项目中每个模块都配置一次该依赖,会非常臃肿。我们可以创建一个父工程,原先的三个工程都继承自该工程,此时三个工程中共有的依赖就可以定义在父工程之中,这就是继承。

        继承在Maven中指的是父项目能够将配置信息传递给它的子项目。这允许子项目共享相同的配置,比如依赖版本、插件配置、属性等。实现需要在各个子工程的pom.xml文件中定义一个<parent>...</parent>来描述其父工程。

        打开之前的SpringBoot工程,我们可以发现其pom.xml文件中都定义了一个父工程:

<!--SpringBoot项目共有的父工程--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.0</version><relativePath/> <!-- lookup parent from repository --></parent>

一、 创建Maven模块作为父工程,设置打包方式为pom(未设置的话默认为jar)。

        创建与上文相同,只有名称不同,不再赘述,创建完成后在pom.xml文件中<version>下添加语句<packaging>pom</packaging>以设置其打包方式为pom。

Maven中常见的打包方式 

  • jar:普通模块打包,springboot项目基本都是jar包(内嵌tomcat运行)
  • war:普通web程序打包,需要部署在外部的tomcat服务器中运行
  • pom:父工程或聚合工程,该模块不写代码,仅进行依赖管理

         原先的SpringBoot模块chnApplication已经默认继承了spring-boot-starter-parent,我们还需要使其继承新创建的模块chn-parent,但其和java一样只能单继承不能继承多个模块,我们可以和java一样通过多级继承来解决此问题,即chnApplication继承chn-parent模块,chn-parent模块再继承spring-boot-starter-parent模块。

        在chn-parent的pom.xml文件中添加spring-boot-starter-parent工程依赖,其中<relativePath>代表父工程的相对路径,如果为默认属性则代表会从本地仓库中进行查找。

<!--父亲工程的pom.xml文件-->
<?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 http://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>3.4.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.chnMaven</groupId><artifactId>chn-parent</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><properties><maven.compiler.source>23</maven.compiler.source><maven.compiler.target>23</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties>
</project>

        同时因为父工程只起到依赖管理的作用, 不写代码,所以src文件也可以删除。

二、在各个子工程的pom.xml文件中配置继承关系

        上文提到过<relativePath>代表父工程的相对路径,而../代表从该文件向外退两层再寻找后文中指定的路径:

    <parent><groupId>com.chnMaven</groupId><artifactId>chn-parent</artifactId><version>1.0-SNAPSHOT</version><relativePath>../chn-parent/pom.xml</relativePath></parent>

        因为groupId会继承自父类,因此在配置完继承关系后,原模块的groupId即可删除,其默认为父类的groupId。

 三、在父工程中配置各个工程共有的依赖

        目前只有lombok依赖重复,在父工程中添加该依赖,并删除子工程中的该依赖即可。

        如果父工程和子工程中配置了相同的依赖但版本号不同,此时会以子工程中的版本号为准。
        因为该案例中我们先有了项目,然后才进行模块区分,为方便理解将夫、父工程和子工程平级,在实际开发中,应保持子工程在父工程下一级目录中以彰显继承关系。

版本锁定

        如果父工程中有依赖为部分几个模块共有而非全部模块共有,那么该依赖需要在每个模块中单独定义相同版本的依赖,如果需要更换依赖版本,因为模块过多很容易有所纰漏,这就要用到Maven的版本锁定功能来使所有的版本保持一致。

        我们可以在父工程中定义标签<dependencyManagement>来统一管理模块,内部填写依赖相关信息。但要注意,这里仅仅只是指定该依赖的版本号,并未引入依赖,在子工程中还需引入该依赖,但无需再填写版本号,因为父工程中已指定版本号。
        我们以utils中jwt的相关依赖为例:

<!--父工程--><dependencyManagement><dependencies><!--jwt依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>......</dependencies></dependencyManagement>
<!--utils子工程--><dependencies><!--jwt依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId></dependency>......</dependencies>

        此时如果需要更改版本号只需修改父类中的版本号即可,确保了整个所有子模块中该依赖的版本都相同。

        但因为依赖过多,即使都在父类中,也很难快速找到我们想要修改的依赖,这就可以用到自定义属性/引用属性,其和application.properties的操作方法类似,在父模块中定义标签<properties>来统一管理依赖,内部填写依赖的版本号,子类中以${自定义名}来代替版本号,建议自定义名体现出与依赖的关系。
        找到父工程中的<properties>标签,其已包含指定jdk版本号和编码格式的两条属性,我们再来添加需要添加的属性,然后修改父模块依赖中的版本号:

    <properties><maven.compiler.source>23</maven.compiler.source><maven.compiler.target>23</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!-- Lombok 版本 --><lombok.version>1.18.36</lombok.version><!-- JWT 版本 --><jjwt.version>0.9.1</jjwt.version>......略</properties><dependencyManagement><dependencies><!-- JWT 依赖 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>${jjwt.version}</version></dependency>......略</dependencies></dependencyManagement><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency></dependencies>

        同时原本的spring-boot-starter-web依赖在chn-parent的父模块spring-boot-starter-parent的父模块spring-boot-dependencies中已经进行了版本管理,因此我们无需再对其进行版本控制。

  •  <dependencyManagement>:统一管理依赖的版本号,而不实际引入这些依赖,还需在子工程中引入所需依赖(无需指定版本)。通常在多模块项目的父 pom.xml 中使用,以统一管理子模块的依赖版本。
  • <dependencies>:直接依赖,如果在父工程中配置了依赖,则子工程会直接继承下来。

聚合

        在分模块开发后,如果直接打包主程序chnApplication系统会报错,缺少对应的jar包,此时我们需要先将父模块和与其相关的子模块进行安装(可选择Maven面板上方的禁止图标以跳过测试),然后再对主程序进行打包。大型项目中有大量模块,各模块之间的关系错综复杂,此时操作就会变的非常繁琐。Maven的聚合即可解决该问题,实现项目的一键构建,包括编译、打包、安装等。 

        Maven 聚合是指将多个模块组合在一起进行统一构建的过程。在 Maven 中,这通常通过创建一个 “聚合模块”(也称为 “父模块”)来实现,要求该模块不包含实际的代码,而是用来管理一组相关的子模块。

        在聚合模块中通过<modules>标签来指定所需聚合的模块,其中加上<module>内部写上需要聚合的模块,书写方式和<relativePath>中的路径相同。

    <!--聚合其他模块--><modules><module>../chn-pojo</module><module>../chn-utils</module><module>../chnApplication</module></modules>

        此时在Maven聚合模块chn-parent下执行package,其余子模块也会执行对应的命令。

私服

        之前介绍过我们所拆分的模块是可以在各个项目组之间进行资源共享的,这个功能就需要通过Maven的私服来实现。

        实现步骤就是A项目组开发一jar包,将其上传到私服,其他项目组按需下载,其相当于远程仓库。各项目组查找依赖的顺序:本地仓库-私服(远程仓库)-中央仓库。一般情况下,一个公司/项目只需一个私服,因此我们无需自己搭建,只需知道怎样使用即可。

        私服一般包含很多仓库

  • maven-central:代理仓库,用于从中央仓库或镜像仓库拉取jar包。
  • maven-public:仓库组,默认包含其他几个Java仓库。
  • maven-releases:正式发行版,用于存储正式发布的jar包。
  • maven-snapshots:快照版,用于存储还未正式发布的jar包。

        在pom.xml文件中我们可以看到我们创建的模块默认为SNAPSHOT快照版,如果将SNAPSHOT改为RELEASE或者不写,其都会上传到RELEASE仓库中。

    <artifactId>chn-app</artifactId><version>0.0.1-SNAPSHOT</version><name>chn-app</name>

上传

        想要上传,首先需要将模块打包成jar包存放到本地仓库,然后通过Maven的指令deploy将jar包从本地仓库中的jar包发布到私服当中。

        首先访问私服需要一定的访问权限,我们需要在Maven中配置访问私服的用户名和密码,并在项目中指定上传资源的位置(url地址)。

        打开maven的settings.xml配置文件中,做如下配置:

一、servers标签中,配置访问私服的个人凭证(访问的用户名和密码)

<servers><server><id>maven-releases</id><username>admin</username><password>admin</password></server><server><id>maven-snapshots</id><username>admin</username><password>admin</password></server>
</servers>

二、在mirrors标签中配置我们自己私服的连接地址

   <mirror><id>maven-public</id><mirrorOf>*</mirrorOf><url>http://192.168.150.101:8081/repository/maven-public/</url></mirror>

三、在profiles标签中,增加如下配置,来允许使用snapshot快照版本的依赖,

<profile><id>allow-snapshots</id><activation><activeByDefault>true</activeByDefault></activation><repositories><repository><id>maven-public</id><url>http://192.168.150.101:8081/repository/maven-public/</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories>
</profile>

        此时上传所需的相关配置都已实现。 

下载

        想要从私服中下载jar包到本地仓库,我们需要首先知道私服的地址,即在Maven中配置私服的地址(url地址),这样项目便可直接调用本地仓库中的jar包。

        在项目的父模块的pom.xml文件中,增加如下配置,来配置项目发布的地址(也就是私服的地址):

   <distributionManagement><!-- release版本的发布地址 --><repository><id>maven-releases</id><url>http://192.168.150.101:8081/repository/maven-releases/</url></repository><!-- snapshot版本的发布地址 --><snapshotRepository><id>maven-snapshots</id><url>http://192.168.150.101:8081/repository/maven-snapshots/</url></snapshotRepository></distributionManagement>

        注意,这里的两个仓库id需与servers标签中,访问私服的个人凭证中的仓库id相同。

        然后直接运行 deploy 生命周期即可完成上传(上传之前建议跳过单元测试)

相关文章:

(自用)配置文件优先级、SpringBoot原理、Maven私服

配置优先级 之前介绍过SpringBoot中支持三类配置文件.properties、.yml和.yaml&#xff0c;他们三者之间也是有着优先级顺序的&#xff0c;为.properties➡.yml➡.yaml。 同时SpringBoot为了增强程序的拓展性&#xff0c;除了支持配置文件属性配置&#xff0c;还支持Java系统属…...

在windows系统中使用labelimg对图片进行标注之工具安装及简单使用

一.背景 还是之前的主题&#xff0c;使用开源软件为公司搭建安全管理平台&#xff0c;从视觉模型识别安全帽开始。我是从运行、训练、标注倒过来学习的。本次主要是学习标注工具labelimg的安装及简单使用。 二.下载 LabelImg是一款广受欢迎的开源图像标注工具&#xff0c;为计…...

数字图像处理技术期末复习

1. 已知图像的分辨率和深度&#xff0c;怎么求图像的存储空间&#xff08;位&#xff0c;字节&#xff0c;KB&#xff09;&#xff1f; 题目&#xff1a; 已知图像的分辨率和深度&#xff0c;怎么求图像的存储空间&#xff08;位&#xff0c;字节&#xff0c;KB&#xff09;&a…...

点云空洞的边界识别提取 pso-bp 神经网络的模型来修复点云空洞 附python代码

代码是一个Python程序,用于处理3D点云数据,特别是检测和修复点云中的孔洞区域。 1. **导入库**: - `numpy`:用于数学运算。 - `open3d`:用于处理3D数据和可视化。 - `torch`:PyTorch库,用于深度学习。 - `torch.nn`和`torch.optim`:PyTorch的神经网络和优…...

【AutoDL】通过【SSH远程连接】【vscode】

小帅碎碎念 0. 起因1. SSH信息获取2. 给你的vscode安装支持SSH远程连接的插件3. SSH远程连接入口4. 输入密码登陆5. 总结 0. 起因 之前使用AutoDL和Jupyter进行代码编辑和执行确实很方便&#xff0c;尤其是对于交互式数据分析项目。然而&#xff0c;也存在一些限制和不便之处&…...

ubuntu22.04编译安装Opencv4.8.0+Opencv-contrib4.8.0教程

本章教程,主要记录在Ubuntu22.04版本系统上编译安装安装Opencv4.8.0+Opencv-contrib4.8.0的具体过程。 一、下载opencv和opencv-contrib包 wget https://github.com/opencv/opencv/archive/refs/tags/4.8.0.zip wget https://github.com/opencv/opencv_contrib/archive/refs/…...

短链接服务

一 功能描述 1.短链接是将长连接转化为短连接使得链接变得美观清爽&#xff0c;让用户点击率更高&#xff0c;同时规避原始链接中一些关键词、域名屏蔽等问题&#xff0c;最终利用短链每次跳转都需要经过后端的特性&#xff0c;在跳转过程中做异步埋点&#xff0c;用于效果数据…...

【Vue3学习】setup语法糖中的ref,reactive,toRef,toRefs

在 Vue 3 的组合式 API&#xff08;Composition API&#xff09;中&#xff0c;ref、reactive、toRef 和 toRefs 是四个非常重要的工具函数&#xff0c;用于创建和管理响应式数据。 一、ref 用ref()包裹数据,返回的响应式引用对象&#xff0c;包含一个 .value 属性&#xff0…...

Halcon中dots_image(Operator)算子原理及应用详解

在HALCON中&#xff0c;dots_image算子是一个用于增强图像中圆点效果的强大工具&#xff0c;特别适合于点的分割&#xff0c;以及OCR&#xff08;光学字符识别&#xff09;应用程序中增强点状印刷字体。以下是对dots_image (ImageResult, DotImage, 5, ‘dark’, 2)算子原理及应…...

【C语言】库函数常见的陷阱与缺陷(四):内存内容操作函数[5]--memchr

C语言中的memchr函数用于在内存块中搜索一个特定的字符(实际上是unsigned char类型的值),并返回该字符第一次出现的指针。虽然这个函数在内存搜索中非常有用,但它也存在一些陷阱。 一、功能与用法 功能:memchr函数在指定的内存块中搜索第一次出现的特定字符,并返回一个…...

【P2P】【Go】采用go语言实现udp hole punching 打洞 传输速度测试 ping测试

服务器端 udpserver/main.go package mainimport ("fmt""net""sync""sync/atomic" )var (clientCounter uint64 0 // 客户端连接计数器mu sync.Mutex )func main() {addr, err : net.ResolveUDPAddr("udp", &q…...

【附源码】Electron Windows桌面壁纸开发中的 CommonJS 和 ES Module 引入问题以及 Webpack 如何处理这种兼容

背景 在尝试让 ChatGPT 自动开发一个桌面壁纸更改的功能时&#xff0c;发现引入了一个 wallpaper 库&#xff0c;这个库的入口文件是 index.js&#xff0c;但是 package.json 文件下的 type:"module"&#xff0c;这样造成了无论你使用 import from 还是 require&…...

【SpringBoot 调度任务】

在 Spring Boot 中实现调度任务&#xff08;Scheduled Tasks&#xff09;&#xff0c;通过使用 EnableScheduling 和 Scheduled 注解来完成。 添加依赖启用调度任务支持创建调度任务运行应用程序 添加依赖 pom.xml 文件中有以下依赖项&#xff1a; <dependency><gro…...

Android v4和v7冲突

android.useAndroidXtrue android.enableJetifiertruev4转成AndroidX...

【HarmonyOS之旅】HarmonyOS开发基础知识(一)

目录 1 -> 应用基础知识 1.1 -> 用户应用程序 1.2 -> 用户应用程序包结构 1.3 -> Ability 1.4 -> 库文件 1.5 -> 资源文件 1.6 -> 配置文件 1.7 -> pack.info 1.8 -> HAR 2 -> 配置文件简介 2.1 -> 配置文件的组成 3 -> 配置文…...

【排序算法】——插入排序

目录 前言 简介 基本思想 1.直接插入排序 2.希尔排序 代码实现 1.直接插入排序 2.希尔排序 总结 1.时空复杂度 2.稳定性 尾声 前言 排序(Sorting) 是计算机程序设计中的一种重要操作&#xff0c;它的功能是将一个数据元素&#xff08;或记录&#xff09;的任意序列&…...

Vue todoList小项目记录

最初代码 简单搭一个vue2的小项目 App.vue <template><div id"app"><!-- 容器 --><div class"todo-container"><div class"todo-wrap"><!-- 头部 --><MyHeader :addTodo"addTodo"></…...

SQL题目笔记

一、根据需求创建表&#xff08;设计合理的数据类型、长度)...

电脑开机提示error loading operating system怎么修复?

前一天电脑还能正常运行&#xff0c;但今天启动时却显示“Error loading operating system”&#xff08;加载操作系统错误&#xff09;。我已经仔细检查了硬盘、接线、内存、CPU和电源&#xff0c;确认这些硬件都没有问题。硬盘在其他电脑上可以正常使用&#xff0c;说明不是硬…...

Nginx 在不同操作系统下的安装指南

Nginx 在不同操作系统下的安装指南 一、Linux 系统下 Nginx 的安装 &#xff08;一&#xff09;基于 Ubuntu 系统 更新软件包列表 打开终端&#xff0c;首先执行sudo apt-get update命令。这一步是为了确保系统的软件包列表是最新的&#xff0c;能够获取到最新版本的 Nginx 及…...

景联文科技入选中国信通院发布的“人工智能数据标注产业图谱”

近日&#xff0c;由中国信息通信研究院、中国人工智能产业发展联盟牵头&#xff0c;联合中国电信集团、沈阳市数据局、保定高新区等70多家单位编制完成并发布《人工智能数据标注产业图谱》。景联文科技作为人工智能产业关键环节的代表企业&#xff0c;入选图谱中技术服务板块。…...

Nginx - 负载均衡及其配置(Balance)

一、概述 定义&#xff1a;在多个计算机&#xff08;计算机集群&#xff09;、网络连接、CPU、磁盘驱动器或其他资源中分配负载目标&#xff1a;最佳化资源使用、最大化吞吐率、最小化响应时间、避免过载功能&#xff1a;使用多台服务器提供单一服务&#xff08;服务器农场&am…...

MySQL存储引擎-存储结构

Innodb存储结构 Buffer Pool(缓冲池)&#xff1a;BP以Page页为单位&#xff0c;页默认大小16K&#xff0c;BP的底层采用链表数据结构管理Page。在InnoDB访问表记录和索引时会在Page页中缓存&#xff0c;以后使用可以减少磁盘IO操作&#xff0c;提升效率。 ○ Page根据状态可以分…...

数据资产入表 解锁智慧城市新潜力

在21世纪的科技浪潮中&#xff0c;智慧城市以信息技术为核心&#xff0c;以数据为血液&#xff0c;通过智能化、精细化的管理&#xff0c;让城市变得更加智慧、更加宜居。而数据资产入表&#xff0c;正是这一变革中的关键一环&#xff0c;它不仅推动了科技的进步&#xff0c;更…...

按类别调整目标检测标注框的写入顺序以优化人工审核效率

引言 在目标检测数据标注审核过程中&#xff0c;我们常常会遇到以下情况&#xff1a;某些小目标的检测框嵌套在大目标检测框内&#xff0c;而在模型进行预标注后&#xff0c;这些小目标的框可能被写入到了大目标框的下层。在人工审核阶段&#xff0c;标注审核人员需要手动移动…...

深入理解YOLO系列目标检测头的设定方式

目录 YOLOv1的检测头结构 1. 网络结构概述 2. 结构细节 3. 优缺点 YOLOv2的检测头结构 1. 网络结构概述 2. 结构细节 3. 优缺点 YOLOv3的检测头结构 1. 网络结构概述 2. 结构细节 3. 优缺点 总结&#xff1a;YOLO 系列检测头的结构演变 YOLOv1的检测头结构 1. 网络…...

智慧农业物联网解决方案:道品科技水肥一体化

在当今科技飞速发展的时代&#xff0c;农业也迎来了一场深刻的变革。智慧农业物联网解决方案中的水肥一体化技术&#xff0c;正逐渐成为现代农业发展的重要助推器。它不仅提高了农业生产效率&#xff0c;还实现了精准施肥和灌溉&#xff0c;为农业可持续发展带来了新的机遇。 …...

单片机上电后程序不运行怎么排查问题?

1.电源检查。使用电压表测量单片机的电源电压是否正常&#xff0c;确保电压在规定的范围内&#xff0c;如常见的5V。 2.复位检查。检查复位引脚的电压是否正常&#xff0c;在单片机接通电源时&#xff0c;复位引脚通常会有一个高电平&#xff0c;按下复位按钮时&#xff0c;复位…...

OceanBase 数据库分布式与集中式 能力

OceanBase分布式数据库与集中式数据库的差异 分布式数据库能解决金融行业最有挑战的高并发低延迟的核心交易系统的稳定性、扩展性、高性能问题。OB之所以一直强调分布式是说它具备很强的数据处理能力&#xff0c;当然从OB4.0开始也支持集中式了。 在实际业务场景中20%是分布式…...

C#多线程

C#中的多线程编程是开发高效并发应用程序的关键技术之一&#xff0c;它允许程序同时执行多个任务&#xff0c;从而提升应用程序的响应速度和性能。为了更好地理解C#中的多线程使用和定义&#xff0c;我们可以从以下几个方面来探讨&#xff1a;线程的基本概念、创建线程的方法、…...

Apache HTTP 服务器深度性能优化

引言 在前几篇文章中&#xff0c;我们讨论了基础和高级性能优化策略。现在&#xff0c;我们将深入探讨一些具体的优化实践&#xff0c;帮助您实现更精细的控制&#xff0c;并确保Apache服务器在各种复杂环境中都能保持最佳性能。 1. 细粒度的Apache配置调整 1.1 MPM参数微调…...

芯片级IO (Pad) Ring IP Checklist

SoC top顶层数字后端实现都会涉及到IO Ring &#xff08;PAD Ring&#xff09;的设计。这里面包括VDD IO,VDDIO IO, Signal IO, Corner IO&#xff0c;Filler IO&#xff0c;IO power cut cell等等。 数字后端零基础入门系列 | Innovus零基础LAB学习Day2 数字IC后端实现TOP F…...

无界wujie网址

文档网址&#xff1a;微前端是什么 | 无界 demo&#xff1a;https://wujie-micro.github.io/demo-main-vue/react17...

vulnhub靶场【DriftingBlues】之6

前言 靶机&#xff1a;DriftingBlues-6&#xff0c;IP地址192.168.1.63&#xff0c;因为重装靶机后期为192.168.1.64 攻击&#xff1a;kali&#xff0c;IP地址192.168.1.16 都采用虚拟机&#xff0c;网卡为桥接模式 主机发现 使用arp-scan -l或netdiscover -r 192.168.1.1…...

心情追忆- Nginx + OpenResty 构建高可用网关

之前&#xff0c;我独自一人开发了一个名为“心情追忆”的小程序&#xff0c;旨在帮助用户记录日常的心情变化及重要时刻。我从项目的构思、设计、前端&#xff08;小程序&#xff09;开发、后端搭建到最终部署。经过一个月的努力&#xff0c;通过群聊分享等方式&#xff0c;用…...

太速科技-527-基于3U VPX XCZU15EG+TMS320C6678的信号处理板

基于3U VPX XCZU15EGTMS320C6678的信号处理板 一、板卡概述 本板卡系我司自主研发的基于3U VPX风冷、导冷架构的信号处理板&#xff0c;适用于高速图像处理等。芯片采用工业级设计。 板卡采用标准3U VPX架构&#xff0c;板上集成一片Xilinx公司ZynqUltraScale系列F…...

Vue3源码笔记阅读1——Ref响应式原理

本专栏主要用于记录自己的阅读源码的过程,希望能够加深自己学习印象,也欢迎读者可以帮忙完善。接下来每一篇都会从定义、运用两个层面来进行解析 定义 运用 例子:模板中访问ref(1) <template><div>{{str}}</div> </template> <script> impo…...

多音轨视频使用FFmpeg删除不要音轨方法

近期给孩子找宫崎骏动画&#xff0c;但是有很多是多音轨视频但是默认的都是日语&#xff0c;电视上看没办法所以只能下载后删除音轨文件只保留中文。 方法分两步&#xff0c;先安装FFmpeg在转文件即可。 第一步FFmpeg安装 FFmpeg是一个开源项目&#xff0c;包含了处理视频的…...

AtomGit 开源生态应用开发赛报名开始啦

目录 1、赛项背景2、赛项信息3、报名链接4、赛题一&#xff1a;开发者原创声明&#xff08;DCO&#xff09;应用开发赛题要求目标核心功能 5、赛题二&#xff1a;基于 OpenHarmony 的开源社区应用开发简介赛题要求 6、参赛作品提交初赛阶段决赛阶段 7、参赛作品提交方式 1、赛项…...

使用 NVIDIA DALI 计算视频的光流

引言 光流&#xff08;Optical Flow&#xff09;是计算机视觉中的一种技术&#xff0c;主要用于估计视频中连续帧之间的运动信息。它通过分析像素在时间维度上的移动来预测运动场&#xff0c;广泛应用于目标跟踪、动作识别、视频稳定等领域。 光流的计算传统上依赖 CPU 或 GP…...

C语言学习day23:WriteProcessMemory函数/游戏内存数据修改工具开发

简言&#xff1a; 上一章我们说了获取应用进程的某数据&#xff08;data&#xff09;&#xff0c;这一章我们就说说修改内存地址的数据。想要修改内存&#xff0c;那么就需要我们另一个WinAPI函数&#xff1a;WriteProcessMemory()函数。 WriteProcessMemory()函数 函数原型…...

利用 html_table 函数轻松获取网页中的表格数据

背景/引言 在数据爬取的过程中&#xff0c;网页表格数据往往是研究人员和开发者的重要目标之一。无论是统计分析、商业调研还是信息整理&#xff0c;表格数据的结构化特性都使其具有较高的利用价值。然而&#xff0c;如何快速、准确地从网页中提取表格数据始终是爬虫技术的一个…...

Postman接口测试:全局变量/接口关联/加密/解密

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 全局变量和环境变量 全局变量&#xff1a;在postman全局生效的变量&#xff0c;全局唯一 环境变量&#xff1a;在特定环境下生效的变量&#xff0c;本环境内唯一 …...

手机银行模拟器,一款高仿真银行app的模拟器,可以修改姓名 卡号 余额 做转账记录 做流水

&#x1f4f1;手机银行模拟器让你自由定制你的金融生活。无论是流水账单、金额&#xff0c;还是个人信息&#xff0c;一切都可以按照你的意愿来模拟修改&#xff0c;让你体验模拟器带来的快乐&#xff01; 链接&#xff1a;https://pan.quark.cn/s/c2f614f3447f 提取码&#…...

HT7183:16V, 4.5A的DC-DC升压转换器,常用在数码相机里

HT7183描述&#xff1a; HT7183是一款高功率异步升压转换器&#xff0c;集成120mΩ功率开关管&#xff0c;为便携式系统提供高效的小尺寸解决方案。具有2.6V至5.5V输入电压范围&#xff0c;可为各类不同供电的应用提供支持。该器件具备3A开关电流能力&#xff0c;并且能够提供高…...

Cobalt Strike 4.8 用户指南-第十四节 Aggressor 脚本

14.1、什么是Aggressor脚本 Aggressor Script 是Cobalt Strike 3.0版及更高版本中内置的脚本语言。Aggressor 脚本允许你修改和扩展 Cobalt Strike 客户端。 历史 Aggressor Script 是 Armitage 中开源脚本引擎Cortana的精神继承者。Cortana 是通过与 DARPA 的网络快速跟踪计…...

【Qt】QWidget中的常见属性及其功能(二)

目录 六、windowOpacity 例子&#xff1a; 七、cursor 例子&#xff1a; 八、font 九、toolTip 例子&#xff1a; 十、focusPolicy 例子&#xff1a; 十一、styleSheet 计算机中的颜色表示 例子&#xff1a; 六、windowOpacity opacity是不透明度的意思。 用于设…...

对象的克隆 单例模式

1) 如何实现对象的克隆&#xff1f; 1、为什么需要实现对象的克隆&#xff1f; 在某些情况下&#xff0c;需要创建一个与现有对象完全相同的副本&#xff0c;这就是对象克隆。 例如&#xff0c;在需要对对象进行备份、在不同的上下文中使用相同的类型的对象或者实现某些设计…...

预处理内容

预处理是干什么的呢&#xff1f; 分为三点&#xff1a; 1.宏替换 2.头文件导入 3.删除注释 #ifdef #include <iostream> // 定义一个宏&#xff0c;表示当前处于调试模式&#xff0c;在实际调试时可以定义这个宏&#xff0c;发布时取消定义#define DEBUG MODE int ma…...

Docker笔记

1 安装docker b11et3un53m.feishu.cn/wiki/Rfocw7ctXij2RBkShcucLZbrn2d 项目的资料地址(飞书) 当使用docker pull +名字 拉取镜像时报 Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for co…...