SpringBoot的pom文件、容器、组件使用及说明
作者:JunSouth
一、pom文件、配置文件
1、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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- pom模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 项目信息 -->
<groupId>demo</groupId><!-- 项目唯一标识 -->
<artifactId>springboot</artifactId><!-- 项目名 -->
<version>0.0.1-SNAPSHOT</version><!-- 版本 -->
<packaging>jar</packaging><!-- 打包方式 (pom,war,jar) -->
<name>springboot</name><!-- 项目的名称, Maven 产生的文档用 -->
<description>Demo project for Spring Boot</description><!-- 项目的描述, Maven 产生的文档用 -->
<!-- 父级项目 -->
<parent>
<artifactId>spring-boot-starter-parent</artifactId> <!-- 被继承的父项目的构件标识符 -->
<groupId>org.springframework.boot</groupId> <!-- 被继承的父项目的全球唯一标识符 -->
<version>1.5.7.RELEASE</version> <!-- 被继承的父项目的版本 -->
<!-- 父项目的pom.xml文件的相对路径。相对路径允许你选择一个不同的路径。默认值是../pom.xml。
Maven首先在构建当前项目的地方寻找父项目的pom,其次在文件系统的这个位置(relativePath位置),
然后在本地仓库,最后在远程仓库寻找父项目的pom。 -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- 模块(有时称作子项目) 被构建成项目的一部分。列出的每个模块元素是指向该模块的目录的相对路径 -->
<modules>
<!-- 子项目相对路径 -->
<module></module>
</modules>
<!-- 属性设置 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!-- 编译字符编码为utf-8 -->
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><!-- 输出字符编码为UTF-8 -->
<java.version>1.8</java.version><!-- jdK版本 -->
</properties>
<!-- 依赖关系 -->
<dependencies>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mysql(数据库) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<!-- 编译 -->
<build>
<!-- 插件 -->
<plugins>
<!-- maven插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>2、配置文件
SpringBoot 支持以下几种类型的配置文件:
- application.properties:基于属性键值对的配置文件,使用简单的 key=value 格式,可读性较高。
- application.yml:基于 YAML 格式的配置文件,使用缩进和冒号表示属性的层次结构,可读性更好。
- application.yaml:与 application.yml 相同,只是文件扩展名不同。
优先级从高到低
properties -> yml -> yaml
bootstrap.yml配置文件
在 SpringCloud 的项目中常用到 bootstrap.yml配置文件,用于应用程序上下文的引导阶段,在 application.yml 之前加载。
二、Spring的流程
1、Spring的启动

2、Spring中bean的生命周期
(1).Spring对bean进行实例化;
(2).Spring将值和bean的引用注入到bean对应的属性中;
(3).bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;
(4).bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
(5).bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;
(6).bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization()方法;
(7).bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地,
(8).bean使用initmethod声明了初始化方法,该方法也会被调用;
(9).bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法;
(10).bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
(11).bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。
三、内置容器
SpringBoot提供了四种Web容器,分别为Tomcat,Jetty,Undertow,Netty。
1、Tomcat(默认)
Tomcat在8.0之前默认采⽤的I/O⽅式为BIO,之后改为NIO,适合处理少数非常繁忙的链接。

1.Tomcat组成、架构
(1)Tomcat中只有一个Server,一个Server可以有多个Service,一个Service可以有多个 Connector(链接) 和一个 Container(容器);
(2)Server 掌管着整个Tomcat的生死大权;
(4)Service 是对外提供服务的;
(5)Connector 用于接受请求并将请求封装成Request和Response来具体处理;
(6)Container 用于封装和管理Servlet,以及具体处理request请求;

2.Tomcat性能调优
- namePrefix: 线程前缀
- maxThreads: 最大线程数,默认设置 200,一般建议在 500 ~ 1000,根据硬件设施和业务来判断
- minSpareThreads: 核心线程数,默认设置 25
- prestartminSpareThreads: 在 Tomcat 初始化的时候就初始化核心线程
- maxQueueSize: 最大的等待队列数,超过则拒绝请求 ,默认 Integer.MAX_VALUE
- maxIdleTime: 线程空闲时间,超过该时间,线程会被销毁,单位毫秒。
3.Tomcat热加载实
调用 Context 容器的 reload 方法,先stop Context容器,再start Context容器。具体的实现:
1)停止和销毁 Context 容器及其所有子容器,子容器其实就是 Wrapper,也就是说 Wrapper 里面 Servlet 实例也被销毁了。
2)停止和销毁 Context 容器关联的 Listener 和 Filter。
3)停止和销毁 Context 下的 Pipeline 和各种 Valve。
4)停止和销毁 Context 的类加载器,以及类加载器加载的类文件资源。
5)启动 Context 容器,在这个过程中会重新创建前面四步被销毁的资源。
- Context 容器对应一个类加载器,类加载器在销毁的过程中会把它加载的所有类也全部销毁。
- Context 容器在启动过程中,会创建一个新的类加载器来加载新的类文件。
4.Tomcat热部署
热部署跟热加载的本质区别是,热部署会重新部署 Web 应用,原来的 Context 对象会整个被销毁掉,因此这个 Context 所关联的一切资源都会被销毁,包括 Session。
Host 容器并没有在 backgroundProcess 方法中实现周期性检测的任务,而是通过监听器 HostConfig 来实现的(HostConfig#lifecycleEvent)
HostConfig 会检查 webapps 目录下的所有 Web 应用:如果原来 Web 应用目录被删掉了,就把相应 Context 容器整个销毁掉。是否有新的 Web 应用目录放进来了,或者有新的 WAR 包放进来了,就部署相应的 Web 应用。
因此 HostConfig 做的事情都是比较“宏观”的,它不会去检查具体类文件或者资源文件是否有变化,而是检查 Web 应用目录级别的变化。
2、Jetty
开源的webserver/servlet容器,是基于 NIO模型。
通过Handler实现扩展简单。Jetty和Tomcat性能方面差异不大,Jetty可以同时处理大量连接而且可以长时间保持连接,适合于web聊天应用等。

1.替换默认的Tomcat容器
<dependencies>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
<exclusions>
<!-- 去除Tomcat容器 -->
<exclusion>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-tomcat</artifactid>
</exclusion>
</exclusions>
</dependency>
<!-- 增加Jetty容器 -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-jetty</artifactid>
</dependency>
</dependencies>2.重要参数
是否打开Jetty日志(默认关闭):server.jetty.accesslog.enabled
- 访问日志所在目录:server.jetty.accesslog.dir
- 最大线程数:server.jetty.threads.max
- 最小线程数:server.jetty.threads.min
- 最大队列容量:server.jetty.threads.max-queue-capacity
- 线程最大空闲时间:server.jetty.threads.idle-timeout
3、Undertow
轻量级:Undertow 是非常小的,只有不到1MB。在内嵌模式下,运行时只占heap空间的4MB左右。
支持 Servlet 3.1
Web Socket:支持 Web Socket (包括JSR-356)
长连接:默认情况下,Undertow 通过添加keep-alive 的response header来支持长连接。它通过重用连接信息(connection details)来改善长连接的性能。
1.替换默认的Tomcat容器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 去除 Tomcat 容器 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加 Undertow 容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>2.重要参数
# Undertow 日志存放目录 server.undertow.accesslog.dir= # 是否启动日志 server.undertow.accesslog.enabled=false # 日志格式 server.undertow.accesslog.pattern=common # 日志文件名前缀 server.undertow.accesslog.prefix=access_log # 日志文件名后缀 server.undertow.accesslog.suffix=log # HTTP POST请求最大的大小 server.undertow.max-http-post-size=0 # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 # 不要设置过大,如果过大,启动项目会报错:打开文件数过多 server.undertow.io-threads=12 # 阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线程 # 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8 server.undertow.worker-threads=20 # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 # 每块buffer的空间大小,越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可 server.undertow.buffer-size=1024 # 每个区分配的buffer数量 , 所以pool的大小是buffer-size * buffers-per-region server.undertow.buffers-per-region=1024 # 是否分配的直接内存 server.undertow.direct-buffers=true
4、Netty
1、SpringBoot整合Netty(服务端)
1.配置
<!-- netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.36.Final</version>
</dependency># netty 配置 netty: # boss线程数量 boss: 4 # worker线程数量 worker: 2 # 连接超时时间 timeout: 6000 # 服务器主端口 port: 17000 # 服务器备用端口 portSalve: 18026 # 服务器地址 host: 127.0.0.1
2.编写netty处理器
/**
* Socket拦截器,用于处理客户端的行为
**/
@Slf4j
public class SocketHandler extends ChannelInboundHandlerAdapter {
public static final ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
/**
* 读取到客户端发来的消息
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 由于我们配置的是 字节数组 编解码器,所以这里取到的用户发来的数据是 byte数组
byte[] data = (byte[]) msg;
log.info("收到消息: " + new String(data));
// 给其他人转发消息
for (Channel client : clients) {
if (!client.equals(ctx.channel())) {
client.writeAndFlush(data);
}
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
log.info("新的客户端链接:" + ctx.channel().id().asShortText());
clients.add(ctx.channel());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
clients.remove(ctx.channel());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.channel().close();
clients.remove(ctx.channel());
}
}3.编写netty初始化器
/**
* Socket 初始化器,每一个Channel进来都会调用这里的 InitChannel 方法
**/
@Component
public class SocketInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
// 添加对byte数组的编解码,netty提供了很多编解码器,你们可以根据需要选择
pipeline.addLast(new ByteArrayDecoder());
pipeline.addLast(new ByteArrayEncoder());
// 添加上自己的处理器
pipeline.addLast(new SocketHandler());
}
}4.编写netty服务
@Slf4j
@Component
public class SocketServer {
@Resource
private SocketInitializer socketInitializer;
@Getter
private ServerBootstrap serverBootstrap;
/**
* netty服务监听端口
*/
@Value("${netty.port:17000}")
private int port;
/**
* 主线程组数量
*/
@Value("${netty.boss:4}")
private int bossThread;
/**
* 启动netty服务器
*/
public void start() {
this.init();
this.serverBootstrap.bind(this.port);
log.info("Netty started on port: {} (TCP) with boss thread {}", this.port, this.bossThread);
}
/**
* 初始化netty配置
*/
private void init() {
// 创建两个线程组,bossGroup为接收请求的线程组,一般1-2个就行
NioEventLoopGroup bossGroup = new NioEventLoopGroup(this.bossThread);
// 实际工作的线程组
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
this.serverBootstrap = new ServerBootstrap();
this.serverBootstrap.group(bossGroup, workerGroup) // 两个线程组加入进来
.channel(NioServerSocketChannel.class) // 配置为nio类型
.childHandler(this.socketInitializer); // 加入自己的初始化器
}
}5.启动netty
/**
* 监听Spring容器启动完成,完成后启动Netty服务器
**/
@Component
public class NettyStartListener implements ApplicationRunner {
@Resource
private SocketServer socketServer;
@Override
public void run(ApplicationArguments args) throws Exception {
this.socketServer.start();
}
}2、Netty客户端
客户端用NIO来编写,在实际工作中客户端可能是 WebSocket、Socket,以 Socket 为例。
1.编写客户端线程
public class ClientThread implements Runnable{
private final Selector selector;
public ClientThread(Selector selector) {
this.selector = selector;
}
@Override
public void run() {
try {
for (; ; ) {
int channels = selector.select();
if (channels == 0) {
continue;
}
Set<SelectionKey> selectionKeySet = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectionKeySet.iterator();
while (keyIterator.hasNext()) {
SelectionKey selectionKey = keyIterator.next();
// 移除集合当前得selectionKey,避免重复处理
keyIterator.remove();
if (selectionKey.isReadable()) {
this.handleRead(selector, selectionKey);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 处理可读状态
private void handleRead(Selector selector, SelectionKey selectionKey) throws IOException {
SocketChannel channel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
StringBuilder message = new StringBuilder();
if (channel.read(byteBuffer) > 0) {
byteBuffer.flip();
message.append(StandardCharsets.UTF_8.decode(byteBuffer));
}
// 再次注册到选择器上,继续监听可读状态
channel.register(selector, SelectionKey.OP_READ);
System.out.println(message);
}
}2.客户端逻辑
public class ChatClient {
public void start(String name) throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8088));
socketChannel.configureBlocking(false);
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
// 监听服务端发来得消息
new Thread(new ClientThread(selector)).start();
// 监听用户输入
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String message = scanner.nextLine();
if (StringUtils.hasText(message)) {
socketChannel.write(StandardCharsets.UTF_8.encode(name + ": " + message));
}
}
}
}3.客户端
public class Client1 {
public static void main(String[] args) throws IOException {
new ChatClient().start("李四");
}
}
public class Client2 {
public static void main(String[] args) throws IOException {
new ChatClient().start("张三");
}
}三、重要组件
- Spring Core:Spring的核心组件,提供IOC、AOP等基础功能,是Spring全家桶的基础。
- Spring Boot:一个基于Spring Framework的快速开发框架,可以快速创建独立的、生产级别的Spring应用程序。
- Spring Cloud:一个用于构建分布式应用程序的框架,提供了诸如服务发现、配置管理、负载均衡等功能。
- Spring Data:用于简化数据访问层开发的框架,提供了一系列数据访问模板和持久化技术的集成。
- Spring Security:一个用于处理应用程序安全的框架,提供了认证、授权、安全防护等功能。
- Spring Integration:Spring Integration是一个用于构建企业级集成解决方案的框架,支持将不同的应用程序和服务集成到一起。它提供了许多组件和模式,如消息通道、消息端点、消息路由器、过滤器等。
- Spring Batch:Spring Batch是一个用于处理大量数据和批处理作业的框架。它提供了各种工具和组件,如任务启动器、作业仓库、作业执行器、步骤处理器、读写器等。
- Spring Web Services:Spring Web Services是一个用于构建基于SOAP协议的Web服务的框架。它提供了各种组件和工具,如消息处理器、绑定器、端点等,使得构建Web服务更加容易。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
