SpringBoot教程(未写完)
一、Spring Boot 是什么
1、Spring Boot 的特点
1.独立运行的 Spring 项目
2.内嵌Servlet容器
3.提供starter简化Maven配置
4.提供了大量的自动配置
5.自带应用监控
6.无代码生成和xml配置
二、创建Spring Boot项目
使用脚手架:
三、Spring Boot starter入门
Spring Boot 将日常企业应用研发中的各种场景都抽取出来,做成一个个的 starter(启动器),starter 中整合了该场景下各种可能用到的依赖,用户只需要在 Maven 中引入 starter 依赖,SpringBoot 就能自动扫描到要加载的信息并启动相应的默认配置。starter 提供了大量的自动配置,让用户摆脱了处理各种依赖和配置的困扰。所有这些 starter 都遵循着约定成俗的默认配置,并允许用户调整这些配置,即遵循“约定大于配置”的原则。
spring-boot-starter-parent 是所有 Spring Boot 项目的父级依赖,它被称为 Spring Boot 的版本仲裁中心,可以对项目内的部分常用依赖进行统一管理。
spring-boot-starter-parent底层代码有一个父级依赖 spring-boot-dependencies,里面控制了所有版本信息。
部分元素说明如下:
- dependencyManagement :负责管理依赖;
- pluginManagement:负责管理插件;
- properties:负责定义依赖或插件的版本号。
四、YAML入门教程
1、YAML简介
YAML 全称 YAML Ain't Markup Language,它是一种以数据为中心的标记语言,比 XML 和 JSON 更适合作为配置文件。
2、YAML语法
缩进时不允许使用 Tab 键,只允许使用空格。
缩进的空格数不重要,但同级元素必须左侧对齐。
大小写敏感,使用缩进表示层级关系
3、YAML字面量写法
字面量是指单个的,不可拆分的值,例如:数字、字符串、布尔值、以及日期等。
字面量直接写在键值对的“value”
name: bianchengbang
4、YAML对象写法
在 YAML 中,对象可能包含多个属性,每一个属性都是一对键值对。
YAML 为对象提供了 2 种写法:
普通写法,使用缩进表示对象与属性的层级关系。
website:
name: bianchengbang
url: www.biancheng.net
行内写法:
website: {name: bianchengbang,url: www.biancheng.net}
5、YAML数组写法
YAML使用 “-”表示数组中的元素,普通写法如下:
pets:
-dog
-cat
行内写法:
pets: [dog,pig]
6、YAML组织结构
一个 YAML 文件可以由一个或多个文档组成,文档之间使用“---”作为分 隔符,且个文档相互独立,互不干扰。如果 YAML 文件只包含一个文档, 则“---”分隔符可以省略。
五、Spring Boot配置绑定
所谓“配置绑定”就是把配置文件中的值与 JavaBean 中对应的属性进行绑定。
1、@ConfigurationProperties
通过 Spring Boot 提供的 @ConfigurationProperties 注解,可以将全局 配置文件中的配置数据绑定到 JavaBean 中。
-
在 helloworld 的全局配置文件 appilcation.yml 中添加以下自定义属性。
person: all-name: 邹仁波 sex: 男 age: 19 money: 1000.999 -
在 helloworld 项目的 net.biancheng.www.bean 中创建一个名为 Person 的实体类,并将配置文件中的属性映射到这个实体类上,代码如下。
@Data
@NoArgsConstructor
/**
* 将配置文件中配置的每一个属性的值,映射到这个组件中
*
* @ConfigurationProperties:告诉 SpringBoot 将本类中的所有属性和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中哪个下面的所有属性进行一一映射
*
* 只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能;
*/
@PropertySource(value = "classpath:person.yml",encoding = "utf-8",factory = YamlPropertySourceFactory.class)
@Component
@ConfigurationProperties(prefix="person")
public class Person {
// @Value("${person.name}")
private String allname;
private String sex;
//SpEL表达式
// @Value("#{10*2}")
private int age;
private double money;
}
3. 修改 HelloController 的代码,在浏览器中展示配置文件中各个属性值,代码如下。
@Controller
@Data
@ConfigurationProperties(prefix="envir")
public class HelloController {
@Autowired
private Person person;
private String intro;
@RequestMapping("/")
@ResponseBody
public String intro(){
return intro;
}
@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "HelloWorld!";
}
@RequestMapping("/person")
@ResponseBody
public Person person(){
return person;
}
}
2、@Value
当我们只需要读取配置文件中的某一个配置时,可以通过 @Value 注解获取。
1. 以 Spring Boot 项目 helloworld 为例,修改实体类 Person 中的代码,使用 @Value 注解进行配置绑定,代码如下。
@Data
@NoArgsConstructor
@PropertySource(value = "classpath:person.yml",encoding = "utf-8",factory = YamlPropertySourceFactory.class)
@Component
//@ConfigurationProperties(prefix="person")
public class Person {
@Value("${person.name}")
private String allname;
private String sex;
//SpEL表达式
@Value("#{10*2}")
private int age;
private double money;
}
3、@Value与@ConfigurationProperties对比
@Value 和 @ConfigurationProperties 注解都能读取配置文件中的属性值并绑定到 JavaBean 中,但两者存在以下不同。
-
使用位置不同
- @ConfigurationProperties:标注在 JavaBean 的类名上;
- @Value:标注在 JavaBean 的属性上。
-
功能不同
- @ConfigurationProperties:用于批量绑定配置文件中的配置;
- @Value:只能一个一个的指定需要绑定的配置。
-
松散绑定支持不同
@ConfigurationProperties:支持松散绑定(松散语法),例如实体类 Person 中有一个属性为 lastName,那么配置文件中的属性名支持以下写法:
- person.firstName
- person.first-name
- person.first_name
- PERSON_FIRST_NAME
@Vaule:不支持松散绑定。
-
SpEL 支持不同
- @ConfigurationProperties:不支持 SpEL 表达式;
- @Value:支持 SpEL 表达式。
5. 复杂类型封装
- @ConfigurationProperties:支持所有类型数据的封装,例如 Map、List、Set、以及对象等;
- @Value:只支持基本数据类型的封装,例如字符串、布尔值、整数等类型。
6. 应用场景不同
@Value 和 @ConfigurationProperties 两个注解之间,并没有明显的优劣之分,它们只是适合的应用场景不同而已。
- 若只是获取配置文件中的某项值,则推荐使用 @Value 注解;
- 若专门编写了一个 JavaBean 来和配置文件进行映射,则建议使用 @ConfigurationProperties 注解。
我们在选用时,根据实际应用场景选择合适的注解能达到事半功倍的效果。
4、PropertySource
如果将所有的配置都集中到 application.properties 或 application.yml 中,那么这个配置文件会十分的臃肿且难以维护,因此我们通常会将与 Spring Boot 无关的配置(例如自定义配置)提取出来,写在一个单独的配置文件中,并在对应的 JavaBean 上使用 @PropertySource 注解指向该配置文件。
例:在 Person 使用 @PropertySource 注解指向 person.properties,代码如下。
@Data
@NoArgsConstructor
//指向对应的配置文件
//encoding设置读取的编码类型
//如读取的是yml文件,则需改变factory,详见下一小节
@PropertySource(value = "classpath:person.yml",encoding = "utf-8",factory = YamlPropertySourceFactory.class)
@Component
//@ConfigurationProperties(prefix="person")
public class Person {
@Value("${person.name}")
private String allname;
private String sex;
//SpEL表达式
@Value("#{10*2}")
private int age;
private double money;
}
PropertySource读取yml文件,需要自定义一个Factory,代码如下:
/**
* 用于@PropertySource读取yml文件
*/
public class YamlPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<!--?--> createPropertySource(String name, EncodedResource resource) throws IOException {
Properties propertiesFromYaml = loadYamlIntoProperties(resource);
String sourceName = name != null ? name : resource.getResource().getFilename();
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
try {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
} catch (IllegalStateException e) {
// for ignoreResourceNotFound
Throwable cause = e.getCause();
if (cause instanceof FileNotFoundException)
throw (FileNotFoundException) e.getCause();
throw e;
}
}
}
六、Spring Boot导入Spring配置
Spring Boot 为了我们提供了以下 2 种方式来导入 Spring 配置:
1、使用 @ImportResource 注解加载 Spring 配置文件
1. 以 helloworld 为例,在 net.biancheng.www.service 包下创建一个名为 PersonService 的接口,代码如下。
package net.biancheng.www.service;
import net.biancheng.www.bean.Person;
public interface PersonService {
public Person getPersonInfo();
}
- 在 net.biancheng.www.service.impl 包下创建一个名为 PersonServiceImpl 的类,并实现 PersonService 接口,代码如下。
package net.biancheng.www.service.impl;
import net.biancheng.www.bean.Person;
import net.biancheng.www.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
public class PersonServiceImpl implements PersonService {
@Autowired
private Person person;
@Override
public Person getPersonInfo() {
return person;
}
}
- 在该项目的 resources 下添加一个名为 beans.xml 的 Spring 配置文件,配置代码如下。
<!--?xml version="1.0" encoding="UTF-8"?-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="persionService" class="net.biancheng.www.service.impl.PersonServiceImpl"></bean>
</beans>
- 修改该项目的单元测试类 HelloworldApplicationTests ,校验 IOC 容器是否已经 personService,代码如下。
package net.biancheng.www;
import net.biancheng.www.bean.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
@SpringBootTest
class HelloworldApplicationTests {
@Autowired
Person person;
//IOC 容器
@Autowired
ApplicationContext ioc;
@Test
public void testHelloService() {
//校验 IOC 容器中是否包含组件 personService
boolean b = ioc.containsBean("personService");
if (b) {
System.out.println("personService 已经添加到 IOC 容器中");
} else {
System.out.println("personService 没添加到 IOC 容器中");
}
}
@Test
void contextLoads() {
System.out.println(person);
}
}
5.运行测试类,发现组件添加失败。
6.在主启动程序类上使用 @ImportResource 注解,将 Spring 配置文件 beans.xml 加载到项目中,代码如下。
//将 beans.xml 加载到项目中
@ImportResource(locations = {"classpath:/beans.xml"})
@SpringBootApplication
public class HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloworldApplication.class, args);
}
}
7.再次运行,组件添加成功。
2、使用全注解方式加载 Spring 配置
Spring Boot 推荐我们使用全注解的方式加载 Spring 配置,其实现方式如下:
- 使用 @Configuration 注解定义配置类,替换 Spring 的配置文件;
- 配置类内部可以包含有一个或多个被 @Bean 注解的方法,这些方法会被 AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext 类扫描,构建 bean 定义(相当于 Spring 配置文件中的
标签),方法的返回值会以组件的形式添加到容器中,组件的 id 就是方法名。
七、Spring Boot Profile(多环境配置)
多Profile文件方式
Spring Boot 的配置文件共有两种形式:.properties 文件和 .yml 文件,不管哪种形式,它们都能通过文件名的命名形式区分出不同的环境的配置,文件命名格式为:
application-{profile}.properties/yml
其中,{profile} 一般为各个环境的名称或简称,例如 dev、test 和 prod 等等。
例:
在 helloworld 的 src/main/resources 下添加 4 个配置文件:
- application.properties:主配置文件
- application-dev.properties:开发环境配置文件
- application-test.properties:测试环境配置文件
- application.prod-properties:生产环境配置文件
在 applcation.properties 文件中,指定默认服务器端口号为 8080,并通过以下配置激活生产环境(prod)的 profile。
#默认端口号
server.port=8080
#激活指定的profile
spring.profiles.active=prod
在 application-dev.properties 中,指定开发环境端口号为 8081,配置如下
开发环境server.port=8081
在 application-test.properties 中,指定测试环境端口号为 8082,配置如下。
测试环境server.port=8082
在 application-prod.properties 中,指定生产环境端口号为 8083,配置如下。
生产环境server.port=8083
#激活指定的profile
spring.profiles.active=prod
此时,生产模式(prod)生效.
多 Profile 文档块模式
在 YAML 配置文件中,可以使用“---”把配置文件分割成了多个文档块,因此我们可以在不同的文档块中针对不同的环境进行不同的配置,并在第一个文档块内对配置进行切换。
server:
port: 8080
envir:
intro: 哈哈哈
#切换配置
#spring:
# profiles:
# active: prod
---
#开发环境
server:
port: 8081
spring:
config:
activate:
on-profile: dev
envir:
intro: 当前是开发环境
---
#测试环境
server:
port: 8082
spring:
config:
activate:
on-profile: test
envir:
intro: 当前是测试环境
---
运行JAR文件时,激活各种环境配置
java -jar helloworld.jar --spring.profiles.active=dev
or
java -Dspring.profiles.active=prod -jar helloworld.jar
八、Spring Boot默认配置文件
Spring Boot 启动时会扫描以下 5 个位置的 application.properties 或 apllication.yml 文件,并将它们作为 Spring boot 的默认配置文件。
- file:./config/
- file:./config/*/
- file:./
- classpath:/config/
- classpath:/
注:file: 指当前项目根目录;classpath: 指当前项目的类路径,即 resources 目录。
以上所有位置的配置文件都会被加载,且它们优先级依次降低,序号越小优先级越高。其次,位于相同位置的 application.properties 的优先级高于 application.yml。
九、Spring Boot外部配置文件
除了默认配置文件,Spring Boot 还可以加载一些位于项目外部的配置文件。我们可以通过如下 2 个参数,指定外部配置文件的路径:
-
spring.config.location
需要注意的是,使用该参数指定配置文件后,会使项目默认配置文件(application.properties 或 application.yml )失效,Spring Boot 将只加载指定的外部配置文件。
java -jar springbootdemo.jar --spring.config.location=D:\my-app.yml
-
spring.config.additional-location
与 --spring.config.location 不同,--spring.config.additional-location 不会使项目默认的配置文件失效,使用该命令行参数添加的外部配置文件会与项目默认的配置文件共同生效,形成互补配置,且其优先级是最高的,比所有默认配置文件的优先级都高。
java -jar springbootdemo.jar --spring.config.additional-location=D:\my-app.yml
十、Spring Boot配置加载顺序
Spring Boot 配置优先级
以下是常用的 Spring Boot 配置形式及其加载顺序(优先级由高到低):
- 命令行参数
- 来自 java:comp/env 的 JNDI 属性
- Java 系统属性(System.getProperties())
- 操作系统环境变量
- RandomValuePropertySource 配置的 random.* 属性值
- 配置文件(YAML 文件、Properties 文件)
- @Configuration 注解类上的 @PropertySource 指定的配置文件
- 通过 SpringApplication.setDefaultProperties 指定的默认属性
以上所有形式的配置都会被加载,当存在相同配置内容时,高优先级的配置会覆盖低优先级的配置;存在不同的配置内容时,高优先级和低优先级的配置内容取并集,共同生效,形成互补配置。
命令行参数
Spring Boot 中的所有配置,都可以通过命令行参数进行指定,其配置形式如下。
java -jar {Jar文件名} --{参数1}={参数值1} --{参数2}={参数值2}
示例 1
- 在 springbootdemo 项目启动时,使用以下命令。
java -jar springbootdemo-0.0.1-SNAPSHOT.jar --server.port=8081 --server.servlet.context-path=/bcb
十一、Spring Boot自动配置原理
Spring Factories 机制
Spring Boot 的自动配置是基于 Spring Factories 机制实现的。
Spring Factories 机制是 Spring Boot 中的一种服务发现机制,这种扩展机制与 Java SPI 机制十分相似。Spring Boot 会自动扫描所有 Jar 包类路径下 META-INF/spring.factories 文件,并读取其中的内容,进行实例化,这种机制也是 Spring Boot Starter 的基础。
十二、Spring Boot统一日志框架
日志框架的选择
市面上常见的日志框架有很多,它们可以被分为两类:日志门面(日志抽象层)和日志实现,如下表。
| 日志分类 | 描述 | 举例 |
|---|---|---|
| 日志门面(日志抽象层) | 为 Java 日志访问提供一套标准和规范的 API 框架,其主要意义在于提供接口。 | JCL(Jakarta Commons Logging)、SLF4j(Simple Logging Facade for Java)、jboss-logging |
| 日志实现 | 日志门面的具体的实现 | Log4j、JUL(java.util.logging)、Log4j2、Logback |
通常情况下,日志由一个日志门面与一个日志实现组合搭建而成,Spring Boot 选用 SLF4J + Logback 的组合来搭建日志系统。
SLF4J 是目前市面上最流行的日志门面,使用 Slf4j 可以很灵活的使用占位符进行参数占位,简化代码,拥有更好的可读性。
Logback 是 Slf4j 的原生实现框架,它与 Log4j 出自一个人之手,但拥有比 log4j 更多的优点、特性和更做强的性能,现在基本都用来代替 log4j 成为主流。
SLF4J 的使用
在项目开发中,记录日志时不应该直接调用日志实现层的方法,而应该调用日志门面(日志抽象层)的方法。
在使用 SLF4J 记录日志时,我们需要在应用中导入 SLF4J 及日志实现,并在记录日志时调用 SLF4J 的方法,例如:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
//调用 sl4j 的 info() 方法,而非调用 logback 的方法
logger.info("Hello World");
}
}
统一日志框架(通用)
统一日志框架一共需要以下 3 步 :
- 排除应用中的原来的日志框架;
- 引入替换包替换被排除的日志框架;
- 导入 SLF4J 实现。
SpringBoot 底层使用 slf4j+logback 的方式记录日志,当我们引入了依赖了其他日志框架的第三方框架(例如 Hibernate)时,只需要把这个框架所依赖的日志框架排除,即可实现日志框架的统一,示例代码如下:
<dependency>
<groupid>org.apache.activemq</groupid>
<artifactid>activemq-console</artifactid>
<version>${activemq.version}</version>
<exclusions>
<exclusion>
<groupid>commons-logging</groupid>
<artifactid>commons-logging</artifactid>
</exclusion>
</exclusions>
</dependency>
十三、Spring Boot日志配置及输出
默认配置
常见的日志级别如下(优先级依次升高)。
| 序号 | 日志级别 | 说明 |
|---|---|---|
| 1 | trace | 追踪,指明程序运行轨迹。 |
| 2 | debug | 调试,实际应用中一般将其作为最低级别,而 trace 则很少使用。 |
| 3 | info | 输出重要的信息,使用较多。 |
| 4 | warn | 警告,使用较多。 |
| 5 | error | 错误信息,使用较多。 |
常用日志参数:
| 序号 | 输出格式 | 说明 |
|---|---|---|
| 1 | %d | 日志生产时间,输出到毫秒的时间 |
| 2 | %-5level | 输出日志级别,-5 表示左对齐并且固定输出 5 个字符,如果不足在右边补 0 |
| 3 | %logger 或 %c | logger 的名称 |
| 4 | %thread 或 %t | 输出当前线程名称 |
| 5 | %p | 日志输出格式 |
| 6 | %message 或 %msg 或 %m | 日志内容,即 logger.info("message") |
| 7 | %n | 换行符 |
| 8 | %class 或 %C | 输出 Java 类名 |
| 9 | %file 或 %F | 输出文件名 |
| 10 | %L | 输出错误行号 |
| 11 | %method 或 %M | 输出方法名 |
| 12 | %l | 输出语句所在的行数, 包括类名、方法名、文件名、行数 |
| 13 | hostName | 本地机器名 |
| 14 | hostAddress | 本地 ip 地址 |
Springboot默认输出级别为info,输出内容包括:
- 时间日期
- 日志级别
- 进程 ID
- 分隔符:---
- 线程名:方括号括起来(可能会截断控制台输出)
- Logger 名称
- 日志内容
修改默认日志配置
在 application.properties 中,修改 Spring Boot 日志的默认配置,代码如下。
#日志级别
logging.level.net.biancheng.www=trace
#使用相对路径的方式设置日志输出的位置(项目根目录目录\my-log\mylog\spring.log)
#logging.file.path=my-log/myLog
#绝对路径方式将日志文件输出到 【项目所在磁盘根目录\springboot\logging\my\spring.log】
logging.file.path=/spring-boot/logging
#控制台日志输出格式
logging.pattern.console=%d{yyyy-MM-dd hh:mm:ss} [%thread] %-5level %logger{50} - %msg%n
#日志文件输出格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} === - %msg%n
自定义日志配置(略……)
Spring Boot日志配置及输出 (biancheng.net)
十四、spring-boot-starter-web
Spring Boot Web 快速开发
spring Boot 为 Spring MVC 提供了自动配置,并在 Spring MVC 默认功能的基础上添加了以下特性:
- 引入了 ContentNegotiatingViewResolver 和 BeanNameViewResolver(视图解析器)
- 对包括 WebJars 在内的静态资源的支持
- 自动注册 Converter、GenericConverter 和 Formatter (转换器和格式化器)
- 对 HttpMessageConverters 的支持(Spring MVC 中用于转换 HTTP 请求和响应的消息转换器)
- 自动注册 MessageCodesResolver(用于定义错误代码生成规则)
- 支持对静态首页(index.html)的访问
- 自动使用 ConfigurableWebBindingInitializer
静态资源映射
WebJars 映射
示例 1
- 在 Spring Boot 项目 spring-boot-springmvc-demo1 的 pom.xml 中添加以下依赖,将 jquery 引入到该项目中。
<dependency>
<groupid>org.webjars</groupid>
<artifactid>jquery</artifactid>
<version>3.6.0</version>
</dependency>
“http://localhost:8080/webjars/jquery/3.6.0/jquery.js”访问 jquery.js
静态首页(欢迎页)映射
静态资源文件夹下的所有 index.html 被称为静态首页或者欢迎页,它们会被被 /** 映射,换句话说就是,当我们访问“/”或者“/index.html”时,都会跳转到静态首页(欢迎页)。
十五、Spring Boot定制Spring MVC
SpringBoot 1.5 及以前是通过继承 WebMvcConfigurerAdapter 抽象类来定制 Spring MVC 配置的,但在 SpringBoot 2.0 后,WebMvcConfigurerAdapter 抽象类就被弃用了,改为实现 WebMvcConfigurer 接口来定制 Spring MVC 配置。
扩展 Spring MVC
如果 Spring Boot 对 Spring MVC 的自动配置不能满足我们的需要,我们还可以通过自定义一个 WebMvcConfigurer 类型(实现 WebMvcConfigurer 接口)的配置类(标注 @Configuration,但不标注 @EnableWebMvc 注解的类),来扩展 Spring MVC。这样不但能够保留 Spring Boot 对 Spring MVC 的自动配置,享受 Spring Boot 自动配置带来的便利,还能额外增加自定义的 Spring MVC 配置。
创建一个名为 MyMvcConfig 的配置类并实现 WebMvcConfigurer 接口,重写 addViewControllers() 方法,代码如下。
//实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能
//@EnableWebMvc 不要接管SpringMVC
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//当访问 “/” 或 “/index.html” 时,都直接跳转到登陆页面
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
}
}
全面接管 Spring MVC
在类上标注 @EnableWebMvc 注解,来实现完全接管 Spring MVC。 完全接管 Spring MVC 后,Spring Boot 对 Spring MVC 的自动配置将全部失效。

浙公网安备 33010602011771号