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

SpringBoot笔记——(狂神说)——待续

路线

javase: OOPmysql:持久化
html+css+js+jquery+框架:视图,框架不熟练,css不好;

javaweb:独立开发MVC三层架构的网站了∶原始
ssm :框架:简化了我们的开发流程,配置也开始较为复杂;
war: tomcat运行
spring再简化: SpringBoot - jar:内嵌tomcat;微服务架构!服务越来越多: springcloud;

什么是SpringBoot?

什么是Spring
Spring是一个开源框架,2003年兴起的一个轻量级的Java开发框架,作者: Rod Johnson 。
Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。

Spring是如何简化Java开发的
为了降低Java开发的复杂性,Spring采用了以下4种关键策略:
1、基于POJO的轻量级和最小侵入性编程;个
2、通过IOC,依赖注入(DI)和面向接口实现松耦合;
3、基于切面(AOP)和惯例进行声明式编程;
4、通过切面和模版减少样式代码;

SpringBoot呢,就是一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,官方说是简化开发,约定大于配置, you can "just run",能迅速的开发web应用,几行代码开发一个http接口。
所有的技术框架的发展似乎都遵循了一条主线规律:从个复杂应用场景衍生一种规范框架,人们只需要进行各种配置而不需要自己去实现它,这时候强大的配置功能成了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,嫌弃原先的各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。
是的这就是Java企业级应用->J2EE-> spring-> springboot的过程。
随着Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用Spring、更容易的集成各种常用的中间件、开源软件;
Spring Boot基于Spring 开发,Spirng Boot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring 的解决方案,而是和Spring框架紧密结合用于提升Spring 开发者体验的工具。SpringBoot以约定大于配置的核心思想,默认帮我们进行了很多设置,多数Spring Boot应用只需要很少的Spring 配置。同时它集成了大量常用的第三方库配置(例如Redis、MongoDB、Jpa、
RabbitMQ、Quartz等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用,

Spring Boot出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,Spring Boot已经当之无愧成为Java领域最热门的技术。
Spring Boot的主要优点:
·为所有Spring开发者更快的入门
·开箱即用,提供各种默认配置来简化项目配置·内嵌式容器简化Web项目
·没有冗余代码生成和XML配置的要求

什么是微服务架构?

MVC、MVVM、

微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成―系列小服务的组合;可以通过http的方式进行互通。要说微服务架构,先得说说过去我们的单体应用架构。

所谓单体应用架构(all in one)是指,我们将一个应用的中的所有应用服务都封装在一个应用中。
无论是ERP、CRM或是其他什么系统,你都把数据库访问,web访问,等等各个功能放到一个war包内。
·这样做的好处是,易于开发和测试;也十分方便部署;当需扩展时,只需要将war复制多份,然后放到多个服务器上,再做个负载均衡就可以了。
·单体应用架构的缺点是,哪怕我要修改一个非常小的地方,我都需要停掉整个服务,重新打包、部署这个应用war包。特别是对于一个大型应用,我们不可能吧所有内容都放在一个应用里面,我们如何维护、如何分工合作都是问题。

all in one的架构方式,我们把所有的功能单元放在一个应用里面。然后我们把整个应用部署到服务器上。如果负载能力不行,我们将整个应用进行水平复制,进行扩展,然后在负载均衡。
所谓微服务架构,就是打破之前all in one的架构方式,把每个功能元素独立出来。把独立出来的功能元素的动态组合,需要的功能元素才去拿来组合,需要多一些时可以整合多个功能元素。所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。
这样做的好处是:
1.节省了调用资源。
2.每个功能元素的服务都是一个可替换的、可独立升级的软件代码。

如何构建微服务
一个大型系统的微服务架构,就像一个复杂交织的神经网络,每一个神经元就是一个功能元素,它们各自完成自己的功能,然后通过http相互请求调用。比如一个电商系统,查缓存、连数据库、浏览页面、结账、支付等服务都是一个个独立的功能服务,都被微化了,它们作为一个个微服务共同构建了一个庞大的系统。如果修改其中的一个功能,只需要更新升级其中一个功能服务单元即可。
但是这种庞大的系统架构给部署和运维带来很大的难度。于是,spring为我们带来了构建大型分布式微服务的全套、全程产品:
·构建一个个功能独立的微服务应用单元,可以使用springboot,可以帮我们快速构建一个应用;

.大型分布式网络服务的调用,这部分由spring cloud来完成,实现分布式;
·在分布式中间,进行流式数据计算、批处理,我们有spring cloud data flow。

. spring为我们想清楚了整个从开始构建应用到大型分布式应用全流程方案。

快速入门和基础

创建项目 

官方提供了一个快速构建项目的网站: Idea也集成了

https://start.spring.io/

创建项目可勾选依赖,web的依赖勾选上。官网下载zip解压后idea中项目中import module导入pom文件或导入根目录选maven即可然后pom文件中右键add as maven项目即可。或idea在项目中直接创建一个spring initoliar相同方式,选maven和jar和java版本springboot版本和依赖。

写代码

 在引导类同级目录下创建包写代码

启动

直接运行引导类即可

Tomcat started on port(s): 8080 (http) with context path ''  访问8080即可,且默认无项目虚拟路径。

springboot项目内置了tomcat可独立运行:

或者使用maven的打包后,找到该jar包的根目录,然后敲cmd,然后使用java -jar 运行即可,如下:

C:\Users\kongdeyi\Desktop\IdeaProject666\spring-boot\quickstart\target>java -jar .\quickstart-0.0.1-SNAPSHOT.jar

引导类和pom文件 

引导类本身就是一个bean ,@SpringBootApplication中有@Config注解。
<parent>有一个父项目  ,gav坐标(grout、artifict,verson),

spring-boot-starter依赖为启动器
所有的springboot的依赖都是以spring-boot-starter-开头的。 
. dependencies:项目具体依赖,这里包含了spring-boot-starter-web用于实现HTTP接口(该依赖中包含了Spring MVC),官网对它的描述是:使用Spring MVC构建Web (包括RESTful)
应用程序的入门者,使用Tomcat作为默认嵌入式容器。集成了tomcat.dispatcherServLet, xmL......; spring-boot-starter-test用于编写单元测试的依赖包。更多功能模块的使用我们将在后面逐步展开。
o build:构建配置部分。默认使用了spring-boot-maven-plugin,配合spring-boot-starter-parent就可以把Spring Boot应用打包成JAR来直接运行。

application配置文件

properties和yml和yaml都可

改端口号

server.prot

改banner

resource根目录下键banner.txt文件,复制下面网站选择好看的bannercopy后粘贴即可。Ascii艺术字实现个性化Spring Boot启动banner图案,轻松修改更换banner.txt文件内容,收集了丰富的banner艺术字和图,并且支持中文banner下载,让你的banner好玩儿更有意思。-bootschool.net

1、spring-boot-dependencies核心依赖在父工程中,这就是在引入很多依赖时为什么不用写版本号。 
2、pom的parent(spring-boot-starter-parent)点进去,里面还有一个parent(spring-boot-dependencies)再点进去,这里管理了大量的jar包版本。
3、spring-boot-starter-paren里面配置了资源过滤application.yml. . .还有很多东西
pom中的springboot启动器  如spring-boot-start,可自己去写。说白了就是Springboot的启动场景;. springboot会将所有的功能场景,都变成一个个的启动器。我们要使用什么功能,就只需要找到对应的启动器就可以了。可参考下面网址的start启动器:
https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/reference/html/using-spring-boot.html#using-boot-starter

自动装配原理

启动类

@SpringBootApplication点进去,除了@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited四个标准注解外还有@ComponentScan扫描的注解外,就剩下了两个核心组合注解:@SpringBootConfiguration  @EnableAutoConfiguration。


对于@SpringBootConfiguration注解中有一个@Configuration,说明引导类为配置类,点进去@Configuration,只有一个@Component,说明引导类也是一个组件。


对于@EnableAutoConfiguration,见名知意自动配置。点进去,发现除了4个元注解外,

@AutoConfigurationPackage自动配置包 和@Import({AutoConfigurationImportSelector.class})导入选择器  两个注解。


其中@AutoConfigurationPackage中点进去有个@Import({Registrar.class})导入自动注册配置包。


其中@Import({AutoConfigurationImportSelector.class})才是核心,我们跟进参数中的AutoConfigurationImportSelector.class自动导入选择器类跟进,

有个selectImports选择组件的方法,这里加载了一些元数据。

往下还有个getAutoConfigurationEntry获得自动配置实体方法中有行代码为:

List<String> configurations = this.getCandidateConfigurations(annotationMetad ata, attributes);  //这个getCandidateConfigurations为获取候选的配置,

我们跟进这个getCandidateConfigurations方法:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {//可在左侧依赖中,找到Maven: org.springframework.boot:spring-boot-autoconfigure:2.5.4中META-INF中的spring-factories中搜索WebMvcAutoConfiguration后跟进这个类,里面有很多配置,还能找到如WebMvcProperties跟进这个类List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");return configurations;}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {  return EnableAutoConfiguration.class;    //标柱了的类,就可获取其类下面的所有配置,比如引导类。}

其中的loadFactoryNames方法,获取所有的加载配置

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoader == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}

其中又有个loadSpringFactories方法位于同Java文件下,下面三行代码的位置,这个方法有个 classLoader.getResources("META-INF/spring.factories");来获取系统资源和项目资源。然后使用了一个while(urls.hasMoreElements()) {}从这些资源中遍历了所有的nextElement元素,自动配置,遍历完后封装为Properties供我们使用。

可在左侧依赖中,找到Maven: org.springframework.boot:spring-boot-autoconfigure:2.5.4中META-INF中的spring-factories中搜索WebMvcAutoConfiguration后跟进这个类,里面有很多配置,还能找到如WebMvcProperties跟进这个类

思考:这么多自动配置为什么有的没有生效,需要导入对应的start才能有作用?

因为spring-factories中的配置的这些类中基本都使用@ConditionalOnXXX来判断,判断条件成立才会加载这个类,才会生效:详见https://www.cnblogs.com/zjdxr-up/p/15685923.html

package org.springframework.boot.autoconfigure.condition

@ConditionalOnBean: 当容器中有指定的Bean的条件下

@ConditionalOnClass:当类路径下有指定的类的条件下

@ConditionalOnExpression:基于SpEL表达式作为判断条件

@ConditionalOnJava:基于JVM版本作为判断条件

@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置

@ConditionalOnMissingBean:当容器中没有指定Bean的情况下

@ConditionalOnMissingClass:当类路径下没有指定的类的条件下

@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下

@ConditionalOnProperty:指定的属性是否有指定的值

@ConditionalOnResource:类路径下是否有指定的资源

@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean

@ConditionalOnWebApplication:当前项目是Web项目的条件下

         

那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。


我们怎么知道哪些自动配置类生效;

我们可以通过启用debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
#开启springboot的调试类
debug=true 

然后启动项目,就会打印出三种:

Positive matches:代表已经启用且生效的   

Negative matches:代表没有匹配成功的类没生效的 可能没引依赖

Unconditional classes:代表没有条件的

 结论 

 springboot所有自动配置都是在启动的时候扫描并加载: spring. factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功!

  1. springboot在启动的时候,从类路径下/META-INF/ spring.factories获取指定的值;
  2. 将这些自动配置的类导入容器,自动配置就会生效,帮我进行自动配置!
  3. 以前我们需要自动配置的东西,现在springboot帮我们做了!
  4. 整合javaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.2.0.RELEASE.jar这个包下
  5. 它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器;
  6. 容器中也会存在非常多的xxxAutoConfiguration的文件(内部很多的方法加了@Bean),就是这些类给容器中导入了这个场景需要的所有组件;并自动配置,@Configuration, JavaConfig!
  7. 有了自动配置类,免去了我们手动编写配置文件的工作! 

这就是自动装配的原理! 

1) . SpringBoot启动会加载大量的自动配置类
2)、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
xxxxAutoConfigurartion:自动配置类;给容器中添加组件

xxxxProperties:封装配置文件中相关属性; 

主启动类怎么运行?

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

SpringApplication

这个类主要做了以下四件事情
1.推断应用的类型是普通的项目还是Web项目

         在其有参构造方法中判断改项目是不是web项目。

        this.webApplicationType = WebApplicationType.deduceFromClasspath();

2.查找并加载所有可用初始化器,设置到initializers属性中

3.找出所有的应用程序监听器,设置到listeners属性中

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.sources = new LinkedHashSet();this.bannerMode = Mode.CONSOLE;this.logStartupInfo = true;this.addCommandLineProperties = true;this.addConversionService = true;this.headless = true;this.registerShutdownHook = true;this.additionalProfiles = Collections.emptySet();this.isCustomEnvironment = false;this.lazyInitialization = false;this.applicationContextFactory = ApplicationContextFactory.DEFAULT;this.applicationStartup = ApplicationStartup.DEFAULT;this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = this.deduceMainApplicationClass();}

 4.推断并设置main方法的定义类,找到运行的主类

可参考下面博客的springboot启动流程图

springboot启动流程源码解析(带流程图)_springboot启动流程图_wuweixianzheng的博客-CSDN博客

yaml语法讲解

springboot的所有配置可参考下面官网: 

Spring Boot Reference Guide

之前:

标记语言
以前的配置文件,大多数都是使用xml来配置;比如一个简单的端口配置,我们来对比下yaml和xmlyaml配置:

server:prot: 8080
server:servlet:context-path: /spring-hello    #需改掉默认/的虚拟目录

xml配置:

<server><port>8081<port> </server>

创建application.yml文件 

官方推荐yml而不是properties,且properties只能保存键值对而且还容易乱码,我们删除application.properties,resource下新建application.yml文件

yml语法

可参见SpringBoot基础认识_阳光明媚UPUP的博客-CSDN博客

key冒号 空格value ,层级缩进为点的作用。


# 自定义yml属性
country: 英国
birthday: 2002-11-15
user:name: 王六age: 22
user2:{name: "张三",age: 22}  #行内写法
# 单个数组
fruit:- apple- banana
maps: {k1: v1, k2: v2}
#  单个数组
fruit2: [apple,banana,pain apple]
#对象数组
userList:- name: zhangsanage: 18- name: lisiage 17
#对象数组
userlist3: [{name:zhangsan,age:18},{name:lisi,age:17}]
#对象数组
userList2:-name: zhangsanage: 18-name: lisiage 17

yml中写数据的作用?

可以给实体类赋值

@ConfigurationProperties赋值yml给类

可参考SpringBoot基础认识_阳光明媚UPUP的博客-CSDN博客

1、通过注解对一个实体类有set方法的属性映射

application.yml

person:name: 张三age: 18happy: yesbirthday: 2021/02/01maps: {k1: v1, k2: v2}lists: [hello,hi,helloworld]dog:name: wangwangage: 3

Person.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "person")  //加上这个注解,就会把yml中person打头的配置数据直接和提供set方法的属性对号入座,spring容器中的该bean就是属性有值的了
public class Person {private String name;private Integer age;private Boolean happy;private Date birthday;private Map<String,Object> maps;private List<Object> lists;private Dog dog;
}

测试用例

@SpringBootTest
class QuickstartApplicationTests {@Autowiredprivate Person person;@Testvoid contextLoads() {System.out.println(dog);System.out.println(person);}
}

所以,我们看springboot的自动装配如jdbc.DataSourceAutoConfiguration这个类中就有@EnableConfigurationProperties({DataSourceProperties.class}),继续跟进DataSourceProperties.class就有@ConfigurationProperties( prefix = "spring.datasource" )

所以我们在yml中配置的以spring-datasource打头的一些数据,就能够被这个文件读取并set方式注入给其属性。

1、这在properties文件的赋值需要使用@PropertySource("classpath:application.properties")

2、无论是yml还是properties文件中的数据,在实体类中通过@Value("${name}")这种el表达式可获得

yml还可使用一些占位符

person:id: ${random.uuid}random: ${random.int}hello: ${person.name}name: 张三age: 18happy: yesbirthday: 2021/02/01maps: {k1: v1, k2: v2}lists: [hello,hi,helloworld]dog:name: ${person.hello:默认值}后面的值  #如果没有person.hello这个配置就会使用默认值 ,结果就是默认值后面的值age: 3

对比@ConfigurationProperties和@Value

@ConfigurationProperties  功能:批量注入配置文件中的属性

@Value   功能:一个个指定

@ConfigurationProperties  支持松散绑定(松散语法)   不支持SpEL   支持JSR303数据校验   支持复杂类型封装   

@Value   不支持松散绑定(松散语法)   支持SpEL   不支持JSR303数据校验   不支持复杂类型封装  

所谓松散绑定就是如yml中last-name,但我们Person中的属性为lastName并提供set方法,也可用@ConfigurationProperties绑定过来

yml或properties配置文件中怎么配 

然后找Maven: org.springframework.boot:spring-boot-autoconfigure:2.5.4中META-INF中的spring-factories中的XXAutoConfiguration。这个类中可能包含很多类。找到相应类的@EnableConfigurationProperties(XXProperties.class)中的参数点进去,找@ConfigurationProperties( prefix = "spring.info" )这就是yml中需要配置的前缀,然后本类的成员属性且提供set方法的就是可配置的内容

JSR303数据校验@Validated

spring-boot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。我们这里来写个注解让我们的name只能支持Email格式

关于JSR 303的一些注解可参考:JSR-303 - 简书 

或者引入spring-boot-starter-validation依赖后找这个包javax.validation.constraints

 Person中我们给name字段加上一个@Email校验

import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Email;@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "person")  //加上这个注解,就会把yml中person打头的配置数据直接和提供set方法的属性对号入座,spring容器中的该bean就是属性有值的
@Validated
public class Person {@Email(message = "邮箱格式不合法")   //也可加上message参数,validation中的注解验证赋给该字段的值是否为邮箱private String name;private Integer age;private Boolean happy;private Date birthday;private Map<String,Object> maps;private List<Object> lists;private Dog dog;
}

还是引用上面的yml中的内容,由于yml中的name不是邮箱类型的,所以启动报错。

多环境配置及配置文件的位置

可参考SpringBoot运维_阳光明媚UPUP的博客-CSDN博客

三个配置工程优先级properties>yml>yaml

不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留

 yml文件可写的位置:

1.jar包同级的config目录下的application.yml,打包前也可写在src的我同级目录下创建config文件夹中创建application.yml文件。

2.jar包同级目录下创建的application.yml,打包前也可写在src同级目录下创建application.yml文件

3.resource下创建config文件夹,里面写application.yml

4.resource下的application.yml

上面优先级1最高,递进到4最低

也可多环境开发在yml中选择要使用那一套配置 

resource下的application.yml将这个配置文件进行copy,复制3份到resource目录下

一定要以application-(环境名)命名,dev\pro\test。,且要保留那个application命名的配置文件。删除配置中其他东西,只留一些配置相关的:

spring:profiles:active: dev

也可就一个application.yml中使用多文档模块

使用---分割多环境 

server:port: 8081spring:profiles:active: dev---
server:port: 8081
spring:profiles: dev---
server:port: 8081
spring:profiles: test

默认使用第一个,如果加上spring.profiles.active指定就以指定的优先

Springboot Web开发

1、静态资源分析

WebMvcAutoConfiguration中的WebMvcAutoConfigurationAdapter有个方法如下: 

        public void addResourceHandlers(ResourceHandlerRegistry registry) {if (!this.resourceProperties.isAddMappings()) {logger.debug("Default resource handling disabled");} else {this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {registration.addResourceLocations(this.resourceProperties.getStaticLocations());if (this.servletContext != null) {ServletContextResource resource = new ServletContextResource(this.servletContext, "/");registration.addResourceLocations(new Resource[]{resource});}});}}

1、对于WebJars静态资源

官网:WebJars - Web Libraries in Jars

可以以maven的形式引入如jequery的坐标。引入后,就会在jar包依赖中看到/META-INF/resources/webjars/,上面代码中意思就是你只需浏览器输入80:webjars/**,就可替代80://META-INF/resources/webjars/**。

2、对其他静态资源 

还配置了一个静态资源在getStaticPathPattern(properties文件中的值为/**,当前目录下所有内容都可识别了)以后只要url写/**就可找静态资源内容

然后就是WebMvcAutoConfiguration中的WebMvcAutoConfigurationAdapter类中的public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,...)有参构造上有个ResourceProperties,我们跟进这个类ResourceProperties extends Resources,再跟进这个父类,父类Resources有个属性,又这4个路径classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};

所以静态资源可放到:resource下的目录resources目录、static目录、public目录下和META-INF/resources/这个webjar的东西。

优先级

resource>static >public

一般public放公共资源,static放静态资源图片等等,resource放一些upload上传的文件。

测试

直接在resource下的目录resources目录、static目录、public目录下这三个目录下创建一个a.html,访问80/a.html即可访问到。

3、但,如果yml中配置静态资源路径,上面就不生效了,因为上面源码中写着呢

配置文件中这么配spring.mvc.static-path-pattern=/hello/** 。上面源码中默认会失效。 但一般不在配置文件中这么写。没意义。

总结:

1.在springboot,我们可以使用以下方式处理静态资源
 webjars loca7host:8080/webjars/
 public,static,/**,resources1ocalhost:8080/2.优先级:resources>static(默认) >public

2、首页

  WebMvcAutoConfiguration中有个类EnableWebMvcConfiguration,类中有方法

        @Beanpublic WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());return welcomePageHandlerMapping;}

里面有个getWelcomePage又找了getIndexHtml

        private Resource getIndexHtml(Resource location) {try {Resource resource = location.createRelative("index.html");if (resource.exists() && resource.getURL() != null) {return resource;}} catch (Exception var3) {}return null;}

找的是resource下的index.html

测试

resource下的目录resources目录、static目录、public目录下随便创建index.html。直接访问http://localhost:8080/

3、Thymeleaf模板引擎

resource下的templates目录下的所有页面,只能通过controller来跳转!这个需要模板引擎的支持!

1、Thymeleaf官网: Thymeleaf
2、Thymeleaf在Github 的主页: GitHub - thymeleaf/thymeleaf: Thymeleaf is a modern server-side Java template engine for both web and standalone environments.

3、Spring官方文档:Spring Boot Reference Guide,找到我们对应的版本

从上方第三个网址找到官网找到spring-boot-starter-thymeleafhttps://github.com/spring-projects/spring-boot/blob/v2.1.6.RELEASE/spring-boot-project/spring-boot-starters/spring-boot-starter-thymeleaf/pom.xml

		<dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-java8time</artifactId></dependency>

模板引擎的作用就是我们来写一个页面模板,比如有些值呢,是动态的,我们写一些表达式。而这些值,从哪来呢,我们来组装一些数据,我们把这些数据找到。然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。只不过呢,就是说不同模板引擎之间,他们可能这个语法有点不一样。其他的我就不介绍了,我主要来介绍一下SpringBoot给我们推荐的Thymeleaf模板引擎,这模板引擎呢,是一个高级语言的模板引擎,他的这个语法更简单。而且呢,功能更强大。 

双击shift直接搜索这个类ThymeleafProperties,就是其配置类。里面有如下配置

    public static final String DEFAULT_PREFIX = "classpath:/templates/";public static final String DEFAULT_SUFFIX = ".html";

所以默认配置,html需要放到template目录下才会生效 

测试:

resource下的template目录下创建test.html,然后controller跳转

@RestController
public class HelloController {@RequestMapping("/test")public String index(){return "test";}
}

使用方法:

详见下方的网页中的使用方法

Tutorial: Using Thymeleaf

基本使用

1、html中引入命名空间

2、使用th打头的属性

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>test</title>
</head>
<body>
test test test
<br>
<div th:text="${msg}"></div><br>
<h3 th:each="user:${users}" th:test="${user}"></h3>
</body>
</html>

o Variable Expressions: ${...}    普通如request域
o Selection Variable Expressions: *{...}   

o Message Expressions: #{...}    国际化的properties文件中取值
o Link URL Expressions: @{...}     路径如src和href等

[[#{login.remember}]]也可这样写行内的,使用两个中括号

所有的htmL元素都可以被thymeLeaf替换接管:th:元素名  如果是图片路径等需th:href="@{/}或th:src="@{/}"且/代表根路径

3、controller使用sprigmvc的那一套如HttpServletRequest或ModuleAndView或Modle去向request域中放数据

@Controller
public class HelloController {@RequestMapping("/test")public String index(HttpServletRequest request){request.setAttribute("msg","你好啊");String  [] users = new String[]{"zhangsan","wangwu"};request.setAttribute("users",users);return "test";}
}

也可配置如关闭thymeleaf缓存

spring:thymeleaf:cache: false

4、MVC配置原理

先查看官网Spring MVC Auto-configuration部分:29. Developing Web Applications

扩展springmvc

双击shift查找ContentNegotiatingViewResolver,有个resolveViewName方法  

            List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);

获取候选的视图得到最好的视图,跟进这个getCandidateViews来遍历所有的视图解析器,并封装成对象,添加到候选的视图

拓展springmvc

//扩展springMvc
//如果。你想diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配!
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {//ViewResolver 实现了视图解析器接口的类,我们就可以把它看做视图解析器@Beanpublic ViewResolver myViewResolver(){return new MyViewResolver();}public static class MyViewResolver implements ViewResolver{@Overridepublic View resolveViewName(String s, Locale locale) throws Exception {return null;}}
}

测试 

测试一下自定义的这个配置的视图解析器有没有生效:

因为所有的请求都需要经过DispatcherServlet,我们双击shift搜索一下这个类,其中有个doService调用了doDispatch,进入这个doDispatch方法,方法头打个断点,然后debug运行启动类。

运行一个请求http://localhost:8080/test

进入断点后,点开debug中的this,点开viewResolvers,发现有以下几个:除了默认的1和2,还有引入thymeleaf的,还有第四个为刚刚上面自定义的MyViewResolver。

1 = {BeanNameViewResolver@5654} 
0 = {ContentNegotiatingViewResolver@5653} 
2 = {ThymeleafViewResolver@5655} 
3 = {MyMvcConfig$MyViewResolver@5656} 
4 = {ViewResolverComposite@5657} 
5 = {InternalResourceViewResolver@5658} 

5、扩展SpringMvc

 先查看官网Spring MVC Auto-configuration部分:29. Developing Web Applications

接着上面的MVC原理中的写的MyMvcConfig

先双击shift搜一下DispatcherServletAutoConfiguration,其中的EnableWebMvcConfiguration类中,有个mvcConversionService方法

        @Beanpublic FormattingConversionService mvcConversionService() {Format format = this.mvcProperties.getFormat();WebConversionService conversionService = new WebConversionService((new DateTimeFormatters()).dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));this.addFormatters(conversionService);return conversionService;}

默认先查找mvc的propoerties中的配置,也就是可在配置文件中配置一些规则。

我们跟进上面getFormat(),再跟进方法的返回值类型WebMvcProperties.Format就跟到了WebMvcProperties中,就有Format这个类和其属性,yml配置文件就可以写了,且yml有提示。

修改SpringBoot的默认配置
方式一
这么多的自动配置,原理都是一样的,通过这个WebMVC的自动配置原理分析,我们要学会一种学习方式,通过源码探究,得出结论;这个结论一定是属于自己的,而且一通百通。
SpringBoot的底层,大量用到了这些设计细节思想,所以,没事需要多阅读源码!得出结论;SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@bean),如果有就用用户配置的,如果没有就用自动配置的;如果有些组件可以存在多个,比如我们的视图解析器,就将用户配置的和自己默认的组合起来!

我们扩展到额springmvc,由于继承了,所以可以写很多重写方法,可generate看一下

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
}

如加一个视图跳转的 

//如果我们要扩展springmvc.官方建议我们这样去做!
@Configuration
//@EnableWebMvc  // 获取容器中所有的webmvcConfig  这个注解就是导入一个类@Import({DelegatingWebMvcConfiguration.class}),官方不让我们加这个注解
public class MyMvcConfig implements WebMvcConfigurer {//视图跳转@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/hello").setViewName("test");//路径hello就会跳转到test.html了}
}

至于为什么不能加@EnableWebMvc

这个注解跟进后导入了一个类@Import({DelegatingWebMvcConfiguration.class})

我们双击shift还是搜WebMvcAutoConfiguration,搜索WebMvcAutoConfigurationAdapter,这个类上方注解也导入了一个类@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})

EnableWebMvcConfiguration这个类跟进后发现继承了DelegatingWebMvcConfiguration

跟进这个父类,发现里面有个

    @Autowired( required = false )public void setConfigurers(List<WebMvcConfigurer> configurers) {if (!CollectionUtils.isEmpty(configurers)) {this.configurers.addWebMvcConfigurers(configurers);}}

setConfigurers获取容器中所有的webmvcConfig

还有一点就是DelegatingWebMvcConfiguration这个类继承了WebMvcConfigurationSupport

因为WebMvcAutoConfiguration上方加了下面这个注解@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

所以如果在我们自己拓展的mvc配置类MyMvcConfig上方加上@EnableWebMvc,就会在容器中加上这个DelegatingWebMvcConfiguration这个类继承了WebMvcConfigurationSupport父类,就会导致WebMvcAutoConfiguration文件整个大类失效。整个这个mvc配置的大类中所以东西不起作用了

另:自己写start的做法就是写一个configuration和一个properties,放到Maven: org.springframework.boot:spring-boot-autoconfigure:2.5.4中的org.springframework.boot.autoconfigure包下即可。写configuration里一定要存在conditionOn

在springboot中,有非常多的xxxx Configuration帮助我们进行扩展配置,只要在源码中或公司源码中看见了这个东西,我们就要关注一下这个类拓展了什么东西。

项目:员工管理系统

1、先用模拟数据

引入了thymeleaf后,html要放到resource下的template目录下。静态资源放到resource下的static

注意点,所有页面的静态资源都需要使用thymeleaf接管;@

实体类
com.kdy.pojo​​​​​​​

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {private Integer id;private String departmentName;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {private Integer id;private String lastName;private String email;private Integer gender;  //0女   1男private Department department;private Date birth;
}

Dao层:模拟数据:不连数据库,先用内存中的假数据:

//部门Dao
@Repository
public class DepartmentDao {//模拟数据库的数据private static Map<Integer, Department> departments = null;static {   //该类加到堆的静态区时,静态代码块执行唯一的一次departments = new HashMap<Integer, Department>();  //创建一个部门表departments.put(101,new Department(101,"教学部"));departments.put(102,new Department(102,"市场部"));departments.put(103,new Department(103,"教研部"));departments.put(104,new Department(104,"运营部"));departments.put(105,new Department(105,"后勤部"));}public Collection<Department> getDepartments(){return departments.values();}public Department getDepartmentById(Integer id){return departments.get(id);}
}
//员工Dao
@Repository
public class EmployeeDao {//模拟数据库的数据private static Map<Integer, Employee> employees = null;@Autowiredprivate DepartmentDao  departmentDao;static {   //该类加到堆的静态区时,静态代码块执行唯一的一次employees = new HashMap<Integer, Employee>();  //创建一个部门表employees.put(1001,new Employee(1001,"zhangsan","26559484@qq.com",1,new Department(101,"教学部"),new Date()));employees.put(1002,new Employee(1002,"zhangsan","26559484@qq.com",1,new Department(101,"教学部"),new Date()));employees.put(1003,new Employee(1003,"zhangsan","26559484@qq.com",1,new Department(101,"教学部"),new Date()));employees.put(1004,new Employee(1004,"zhangsan","26559484@qq.com",1,new Department(101,"教学部"),new Date()));employees.put(1005,new Employee(1005,"zhangsan","26559484@qq.com",1,new Department(101,"教学部"),new Date()));}private static Integer intId = 1005;private static Integer getIntId(){return intId++;}public void save(Employee employee){//假设参数传递了员工name,emial,性别,和部门id,员工id不知道传没传if (employee.getId()==null){Integer newID = getIntId();employee.setId(newID);}employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));}public Collection<Employee> getAll(){return employees.values();}public Employee getEmployeeById(Integer id){return employees.get(id);}public void deleteByEmployeeId(Integer id){employees.remove(id);}
}

2、国际化

先确保Idea的setting的File Encodings配置都是UTF-8

需setting的plugin加一个resource bundle edit的插件并开启。 

1、国际化英文international  ,先在resource目录下创建  i18n  目录

2、在i18n目录下创建login.properties文件和login_zh_CN.properties 文件创建后会多了一个Resource Bundle 'login'的虚拟文件夹把这两个配置包裹起来,右击这个虚拟文件夹Resource Bundle 'login',可new -》Add Property File,点击右侧加号,可输入如en_us后点击ok,就生成了一个login_en_US.properties文件

关于国际化的符号可见:国际化语言符号_weixin_33785108的博客-CSDN博客

3、当我们点开login_en_uS.properties或login_zh_CN.properties,发现Idea的下方多了一个Resource Bundle按钮,我们点一下,出现一个界面,且选中了login这个配置,我们点击界面中的加号new Property,弹窗输入login.tip点击ok,就发现login.properties文件中多了个login.tip=的配置。

4、我们还是换回刚刚点击Resource Bundle按钮的login_en_uS.properties或login_zh_CN.properties中,界面中选中了login.tip右侧有三个配置文件文本框login_en_uS.properties或login_zh_CN.properties和login.properties的,我们分别输入其相应语言,如默认的login.proeprties和中文的login_zh_CN.properties文件输入请登录,在login_en_uS.properties文件中输入Please Sign in

其实第4步也可自己一个个配置文件中的配置,比如配置完默认的再拷贝到别的语言的properties中修改数值。

5、然后修改html中使用thymeleaf获取国际化配置文件中的内容。#{}来获取

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>indedx</title>
</head>
<body>
<div style="align-content: center"><h1 th:text="#{login.tip}"></h1><input type="text" th:placeholder="#{login.username}"><br/><br/><input type="passord" th:placeholder="#{login.password}"><br/><br/><input type="checkbox">[[#{login.remember}]]<br/><br/><button type="submit" >[[#{login.btn}]]</button><br/><br/><!--之前html的href带参数需要写?,现thymeleaf带参数直接写括号即可--><a th:href="@{/index.html(l='zh_CN')}">中文</a><a th:href="@{/index.html(l='en_US')}">English</a>
</div>
</body>
</html>

也可:双击shift搜索一下MessageSourceAutoConfiguration,然后还能找到其配置文件MessageSourceProperties。

6、html中给超链接切换中英文国际化

<!--之前html的href带参数需要写?,现thymeleaf带参数直接写括号即可--><a th:href="@{/index.html(lang='zh_CN')}">中文</a>     <a th:href="@{/index.html(lang='en_US')}">English</a>

7、自己写一个LocaleResolver 

 ​​​​​​​自定义地址解析配置

我们双击shift搜索WebMvcAutoConfiguration找EnableWebMvcConfiguration类中有个localeResolver方法,这个方法中默认使用new‘了个AcceptHeaderLocaleResolver跟进这个类AcceptHeaderLocaleResolver implements LocaleResolver发现实现了LocaleResolver,所以我们可自己写一个类来实现

com.kdy.config

public class MyLocaleResolver implements LocaleResolver {//解析请求@Overridepublic Locale resolveLocale(HttpServletRequest request) {String language = request.getParameter("l");  //请求参数内容Locale local = Locale.getDefault();   //默认的localeif(!StringUtils.isEmpty(language)){String[] split = language.split("_");local = new Locale(split[0],split[1]);}return local;}@Overridepublic void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {}
}

并在springmvc拓展中配上这个bean

com.kdy.config

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {//视图跳转@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");//路径/就会跳转到index.html了registry.addViewController("/index").setViewName("index");registry.addViewController("/index.html").setViewName("index");}@Bean("localeResolver")   //将自定义国际化的对象放到spring接管的容器中就生效了public LocaleResolver myLocaleResolver(){return new MyLocaleResolver();}
}

3、登录功能的实现

由于暂时没有数据库,我们让所有请求都登录成功。 

@Controller
public class LoginController {@RequestMapping("/login")public String login(@RequestParam("username") String username, @RequestParam("password")String password, Model model){if (!StringUtils.isEmpty(username) && "123456".equals(password)) {return "redirect:main.html";//在springmvc拓展配置中加了一个路径main.html映射到dashboard.html文件的配置。这里就可以直接这样重定向了}else{model.addAttribute("msg","用户名或密码错误!");return "index";}}
}
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {//视图跳转@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");//路径/就会跳转到index.html了registry.addViewController("/index").setViewName("index");registry.addViewController("/index.html").setViewName("index");registry.addViewController("/main.html").setViewName("dashboard");//这里映射一个虚拟的main.html路径}@Bean("localeResolver")   //将自定义国际化的对象放到spring接管的容器中就生效了public LocaleResolver myLocaleResolver(){return new MyLocaleResolver();}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>indedx</title>
</head>
<body>
<div style="align-content: center"><h1 th:text="#{login.tip}"></h1>
<!--    //如果msg的值为空,则不显示消息- --><p style="color: #ff0000" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p><form th:action="@{/login}"><input type="text" name="username" th:placeholder="#{login.username}"><br/><br/><input type="passord" name="password" th:placeholder="#{login.password}"><br/><br/><input type="checkbox">[[#{login.remember}]]<br/><br/><button type="submit" >[[#{login.btn}]]</button><br/><br/></form><!--之前html的href带参数需要写?,现thymeleaf带参数直接写括号即可--><a th:href="@{/index.html(l='zh_CN')}">中文</a><a th:href="@{/index.html(l='en_US')}">English</a>
</div>
</body>
</html>

4、登录拦截器

登录成功后才能进入到主页面,否则要重新登录

1、先写一个springmvc阶段学的过滤器

//@WebFilter(urlPatterns = "/main.html")这个是Javaweb的filter的在springmvc中是没效果的。
public class LoginHandlerInterceptor implements HandlerInterceptor{@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession();Object loginUser = session.getAttribute("loginUser");if (loginUser==null){request.setAttribute("msg","没有权限请先登录");request.getRequestDispatcher("/index.html").forward(request,response);return false;}else{return true;}}
}

2、配置这个springmvc的拦截去到springboot的WebMvc配置中让其自动装配

回到我们自己拓展的springmvc的配置

public class MyMvcConfig implements WebMvcConfigurer {}方法体中看重写的父类WebMvcConfigurer 方法有没有和拦截器接近的。有个addInterceptors方法。

com.kdy.config  重写这个addInterceptors方法

public class MyMvcConfig implements WebMvcConfigurer {//视图跳转@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");//路径/就会跳转到index.html了registry.addViewController("/index").setViewName("index");registry.addViewController("/index.html").setViewName("index");registry.addViewController("/main.html").setViewName("dashboard");//这里映射一个虚拟的main.html路径}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","/login","/css/*","/js/**","/img/**");//还可排除一些静态资源。}@Bean("localeResolver")   //将自定义国际化的对象放到spring接管的容器中就生效了public LocaleResolver myLocaleResolver(){return new MyLocaleResolver();}
}

主页面dashboard.html中加上session中读取的用户名。 

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>dashboard</title>
</head>
<body>
<h3>用户名:[[${session.loginUser}]]</h3>
</body>
</html>

5、展示员工数据

 

相关文章:

SpringBoot笔记——(狂神说)——待续

路线 javase: OOPmysql:持久化 htmlcssjsjquery框架:视图&#xff0c;框架不熟练&#xff0c;css不好; javaweb:独立开发MVC三层架构的网站了∶原始 ssm :框架:简化了我们的开发流程&#xff0c;配置也开始较为复杂; war: tomcat运行 spring再简化: SpringBoot - jar:内嵌tomca…...

Linux TCP编程流程

一、TCP编程流程 TCP 提供的是面向连接的、可靠的、字节流服务。TCP的服务器端和客户端编程流程如下&#xff1a; 1.socket()方法 用来创建一个套接字&#xff0c;有了套接字就可以通过网络进行数据的收发。这也是为什么进行网络通信的程序首先要创建一个套接字。创建套接字时…...

pyqt5 QuickStart

在使用pyqt5之前&#xff0c;建议下载一个Anaconda环境&#xff0c;这样下载python包更方便&#xff0c;本篇文章是建立在已经安装好Anaconda的情况下使用的。IDE就是标准的PyCharm了。 一、pyqt包的安装 python终端执行下面两个安装命令&#xff1a; pip install PyQt5 pip …...

Qt6 for Windows 环境搭建(Visual Studio)

作者&#xff1a; 一去、二三里 个人微信号&#xff1a; iwaleon 微信公众号&#xff1a; 高效程序员 在 Windows 中&#xff0c;如果想要开发 Qt 应用程序&#xff0c;可以选择多种方式&#xff1a; Qt Creator MinGW 编译器Qt Creator MSVC 编译器Visual Studio&#xff0…...

探索未知世界:桌面端3D GIS引领地理信息新时代

近年来&#xff0c;桌面端的三维地理信息系统&#xff08;3D GIS&#xff09;在地理信息领域迎来了显著的发展&#xff0c;为我们带来了更深入、更丰富的地理空间认知和数据分析体验。从城市规划到环境保护&#xff0c;从资源管理到应急响应&#xff0c;桌面端的3D GIS正逐渐成…...

微信小程序 趣味学习与益智游戏系统APP

管理员、用户可通过HBuilder系统手机打开系统&#xff0c;注册登录后可进行管理员后端&#xff1b;首页、个人中心、用户管理、学生分类管理、学一学管理、玩一玩管理、听一听管理、试题管理、练一练管理、系统管理、考试管理&#xff0c;用户前端&#xff1b;首页、学一学、玩…...

【单片机】UART、I2C、SPI、TTL、RS232、RS422、RS485、CAN、USB、SD卡、1-WIRE、Ethernet等常见通信方式

在单片机开发中&#xff0c;UART、I2C、RS485等普遍在用&#xff0c;这里做一个简单的介绍 UART通用异步收发器 UART口指的是一种物理接口形式(硬件)。 UART是异步&#xff08;指不使用时钟同步&#xff0c;依靠帧长进行判断&#xff09;&#xff0c;全双工&#xff08;收发…...

【Java从0到1学习】13 Java IO流

1. 流 1.1 流的概念 流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中&#xff0c;管道是一条不间断的字节流&#xff0c;用来实现程序或进程间的通信&#xff0c;或读写外围设备、外部文件等。 一个流&#xff0c;必有源端和目的端&#xff0c;它们可以是计算机内存的…...

linux并发服务器 —— 多进程并发(四)

进程概述 程序是包含一系列信息的文件&#xff0c;描述了如何在运行时创建一个进程&#xff1b; 进程是正在运行的程序的实例&#xff0c;可以用一个程序来创建多个进程&#xff1b; 用户内存空间包含程序代码以及代码所使用的变量&#xff0c;内核数据结构用于维护进程状态…...

华为云Stack的学习(四)

五、Service OM资源管理 1.Service OM简介 1.1 Service OM介绍 在华为云Stack解决方案中&#xff0c;Service OM是FusionSphere OpenStack的操作管理界面&#xff0c;是资源池&#xff08;计算、存储、网络&#xff09;以及基础云服务的管理工具。 1.2 Service OM定位 Serv…...

Midjourney 完整版教程(从账号注册到设计应用)

目录 一、Midjourney 介绍 二、Midjourney 的AI出图示例 三、手把手教你上手Midjourney 1、账号&初始化 1.1 账号注册登录 1.2 账号付费 1.3 账号初始化 2、Midjourney的基础设置 3、Midjourney 出图步骤。 (一)直接描述出图 (二)垫图生图。 4、Midjourney的…...

保护香港服务器的方法

保护香港服务器的方法 当你把一个香港服务器完全留给一个组织、应用程序或个人使用时&#xff0c;它被称为香港服务器租用。在这种类型的主机配置中&#xff0c;客户端将会借出整个服务器&#xff0c;并且不允许其他任何人使用它。 如果您计划使用香港服务器&#xff0c;安全性…...

Redis——》Pipeline

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…...

Git企业开发控制理论和实操-从入门到深入(六)|多人协作开发

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…...

通过 ChatGPT 学习 Python

先决条件 您需要一个 OpenAI 帐户才能开始与 ChatGPT 交互。如果您还没有这样做,请在 OpenAI 网站上注册一个帐户。 什么是 ChatGPT? GPT(Generative Pre-training Transformer)是 OpenAI 开发的一种语言模型,它使用深度学习技术生成类似人类的文本。ChatGPT 是 GPT 模…...

开发卡牌gamefi游戏需要多少钱?

卡牌游戏作为一种受欢迎的游戏形式&#xff0c;吸引了众多开发者的关注。然而&#xff0c;开发一款成功的卡牌游戏需要全面考虑多个方面的因素&#xff0c;其中之一就是资金投入。本文将从专业性和投入回报的角度&#xff0c;探讨开发一款卡牌游戏所需的资金投入。 一、专业性的…...

linux服务TCP参数配置

Linux TCP参数配置 阿里云规范 1.【推荐】高并发服务器建议调小 TCP 协议的 time_wait 超时时间。 说明&#xff1a;操作系统默认 240 秒后&#xff0c;才会关闭处于 time_wait 状态的连接&#xff0c;在高并发访问下&#xff0c;服务器端会因为处于 time_wait 的连接数太多&am…...

部署Spring Boot项目

上传jar包 之前在新建Spring Boot项目[1]使用mvn install的方式&#xff0c;已经构建出jar包。 通过scp或rz/sz&#xff0c;将该jar包上传到服务器 执行java -jar hello-0.0.1-SNAPSHOT.jar,发生如下报错&#xff1a; Exception in thread "main" java.lang.Unsuppo…...

Java 中数据结构LinkedList的用法

LinkList 链表&#xff08;Linked list&#xff09;是一种常见的基础数据结构&#xff0c;是一种线性表&#xff0c;但是并不会按线性的顺序存储数据&#xff0c;而是在每一个节点里存到下一个节点的地址。 链表可分为单向链表和双向链表。 一个单向链表包含两个值: 当前节点…...

jmeter递增压测线程组配置

jmeter递增压测线程组配置 新建线程组线程组参数详解及填写其他指标设置 新建线程组 操作位置如图&#xff1a; 线程组参数详解及填写 其他指标设置 其他指标设置可参考另一篇文章&#xff1a; 链接: jmeter 在linux服务器中执行性能测试、监听服务器资源指标...

hutool工具

Hutool是一个Java工具包 参考&#xff1a;https://www.hutool.cn/ <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>4.6.3</version> </dependency>Convert类型转换工具类 //转换为…...

Redis.conf 配置文件详解

1、units 单位 配置大小单位&#xff0c;开头定义了一些基本的度量单位&#xff0c;只支持 bytes&#xff0c;不支持bit&#xff0c;并且对大小写 不敏感。 2、INCLUDES 包含 类似于 Spring 配置文件&#xff0c;可以通过 includes 包含&#xff0c;redis.conf 可以作为总文件…...

linux磁盘空间满了

登录服务器&#xff0c;通过df -Hl查看 确定告警信息一致&#xff0c;接着是找到占用空间大目录或文件 一种比较笨的方法是&#xff0c;在根目录下&#xff0c;通过du -sh命令&#xff0c;列出各目录所占空间大小 之后再用同样的方法继续到对应目录下去找 再相对高效一点的…...

Ansible项目实战管理/了解项目环境/项目管理

一&#xff0c;项目环境 1.项目基础 项目过程 调研阶段 设计阶段 开发阶段 测试阶段 运营阶段 2.项目环境 个人开发环境 公司开发环境 项目测试环境 项目预发布环境 灰度环境&#xff1a;本身是生产环境&#xff0c;安装项目规划&#xff0c;最终所有的生产环境都发…...

hadoop 学习:mapreduce 入门案例一:WordCount 统计一个文本中单词的个数

一 需求 这个案例的需求很简单 现在这里有一个文本wordcount.txt&#xff0c;内容如下 现要求你使用 mapreduce 框架统计每个单词的出现个数 这样一个案例虽然简单但可以让新学习大数据的同学熟悉 mapreduce 框架 二 准备工作 &#xff08;1&#xff09;创建一个 maven 工…...

vue2项目中el-input单独使用max和maxlength不生效问题

vue2项目中el-input单独使用max和maxlength不生效问题 今天在vue2的项目中使用element中的<el-input>组件&#xff0c;因为没有使用form所以max和maxlength属性没有生效&#xff0c;下面是解决办法 <el-input placeholder"请输入" v-model"holeDat…...

源码角度看待线程池的执行流程

文章目录 前言一、线程池的相关接口和实现类1.Executor接口2.ExecutorService接口3.AbstractExecutorService接口4.ThreadPoolExecutor 实现类 二、ThreadPoolExecutor源码解析1.Worker内部类2.execute()方法3.addWorker()方法 总结 前言 线程池内部维护了若干个线程&#xff…...

我们的第一个 Qt 窗口程序

Qt 入门实战教程&#xff08;目录&#xff09; Windows Qt 5.12.10下载与安装 为何使用Qt Creator开发QT 本文介绍用Qt自带的集成开发工具Qt Creator创建Qt默认的窗口程序。 本文不需要你另外安装Visual Studio 2022这样的集成开发环境&#xff0c;也不需要你再在Visual St…...

Linux 8 下的容器引擎Podman概述

一、前言 最近在进行OS国产化交流中&#xff0c;了解到部分业务迁移到BClinux 8.2或Anolis 8.2时&#xff0c;原有docker业务需要迁移到新的容器平台&#xff1a;Podman&#xff0c;来完成容器的新的管理。Podman&#xff08;全称 Pod Manager&#xff09;是一款用于在 Linux 系…...

C++编辑修改PDF

PDFWriter是一个易于使用的C创建、修改PDF文档的库 1.创建一个PDF文件 #include #include “PDFWriter.h” int main() { std::cout << “Hello World!\n”; PDFWriter pdfWriter; int retpdfWriter.StartPDF(“D:\mytestwriterpdf.pdf”, ePDFVersion13); if (ret eS…...