程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

Spring Boot -- Spring Boot之热部署、性能优化、打包

一、热部署

所谓的热部署:比如项目的热部署,就是在应用程序在不停止的情况下,实现新的部署。

1.1、热部署原理

原理: 使用类加载器(classloader重新读取字节码文件到jvm内存)

如何纯手写一个热部署功能:

  • 监听 class文件是否发生改变  版本号、修改时间  作对比;
  • 如果发生改变就用classloader进行重新读取;

热部署可以用于在生产环境?

  • 理论上可以(不推荐),热部署要是用在生产环境,性能很差了,不安全;
  • 本地开发(eclipse、idea)、用来提高效率,不需要重启服务器;

1.2、devtools依赖

要想实现热部署,我们可以利用Spring Boot为我们提供了一个非常方便的工具spring-boot-devtools。

添加依赖:

<!--   热部署     -->
<dependency>
     <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
       <optional>true</optional><!-- optional=true,依赖不会传递 -->
</dependency>

1.3、IDEA设置

如果使用idea,必须需要确保开启运行时编译才行IDEA Settings->Build,Execution,Deployment->Compiler  开启Build project automatically:

快捷键 ctrl+shift+a,输入Registry,找到下面这行,开启:

以上即可实现热部署,前后台代码修改都可不用再次部署运行。

1.4、devtools原理

springboot-devtools模块能够实现热部署,添加类.添加方法,修改配置文件,修改页面等,都能实现热部署。

原理就是重启项目,但比手动重启快多了,其深层原理是使用了两个ClassLoder:

  • 一个ClassLoader加载哪些不会改变的类(第三方jar包);
  • 另一个ClassLoader加载会更改的类,称之为restart ClassLoader;
  • 这样在有代码更改的时候,原来的restart Classloader被丢弃,重新创建一个restart ClassLoader,由于需要加载的类相比较少,所以实现了较快的重启时间(5秒以内);

二、性能优化

2.1、组件自动扫描带来的问题

默认情况下,我们会使用 @SpringBootApplication 注解来自动获取应用的配置信息,我们首先来看一下这个注解源代码:

复制代码
/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.repository.Repository;

/**
 * Indicates a {@link Configuration configuration} class that declares one or more
 * {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
 * auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
 * annotation that is equivalent to declaring {@code @Configuration},
 * {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
 *
 * @author Phillip Webb
 * @author Stephane Nicoll
 * @author Andy Wilkinson
 * @since 1.2.0
 */
@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 {

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    /**
     * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
     * for a type-safe alternative to String-based package names.
     * <p>
     * <strong>Note:</strong> this setting is an alias for
     * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity}
     * scanning or Spring Data {@link Repository} scanning. For those you should add
     * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and
     * {@code @Enable...Repositories} annotations.
     * @return base packages to scan
     * @since 1.3.0
     */
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    /**
     * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
     * scan for annotated components. The package of each class specified will be scanned.
     * <p>
     * Consider creating a special no-op marker class or interface in each package that
     * serves no purpose other than being referenced by this attribute.
     * <p>
     * <strong>Note:</strong> this setting is an alias for
     * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity}
     * scanning or Spring Data {@link Repository} scanning. For those you should add
     * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and
     * {@code @Enable...Repositories} annotations.
     * @return base packages to scan
     * @since 1.3.0
     */
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

    /**
     * Specify whether {@link Bean @Bean} methods should get proxied in order to enforce
     * bean lifecycle behavior, e.g. to return shared singleton bean instances even in
     * case of direct {@code @Bean} method calls in user code. This feature requires
     * method interception, implemented through a runtime-generated CGLIB subclass which
     * comes with limitations such as the configuration class and its methods not being
     * allowed to declare {@code final}.
     * <p>
     * The default is {@code true}, allowing for 'inter-bean references' within the
     * configuration class as well as for external calls to this configuration's
     * {@code @Bean} methods, e.g. from another configuration class. If this is not needed
     * since each of this particular configuration's {@code @Bean} methods is
     * self-contained and designed as a plain factory method for container use, switch
     * this flag to {@code false} in order to avoid CGLIB subclass processing.
     * <p>
     * Turning off bean method interception effectively processes {@code @Bean} methods
     * individually like when declared on non-{@code @Configuration} classes, a.k.a.
     * "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore behaviorally
     * equivalent to removing the {@code @Configuration} stereotype.
     * @since 2.2
     * @return whether to proxy {@code @Bean} methods
     */
    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;

}
View Code
复制代码

使用这个注解后,会触发自动配置( auto-configuration )和 组件扫描 ( component scanning ),这跟使用 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 三个注解的作用是一样的。但是由于@SpringBootApplication会扫描当前、以及子包下的所有文件,导致做给开发带来方便的同时,也会有三方面的影响:

  • 会导致项目启动时间变长,当启动一个大的应用程序,或将做大量的集成测试启动应用程序时,影响会特别明显;
  • 会加载一些不需要的多余的实例(beans);
  • 会增加 CPU 消耗;

针对以上三个情况,我们可以移除 @SpringBootApplication,然后使用 @Configuration、@EnableAutoConfiguration 和 @ComponentScan注解来扫描特定的包。

2.2、Spring Boot JVM参数调优

关于Jvm调优Oracle官网有一份指导说明: Oracle官网对Jvm调优的说明, 有兴趣大家可以去看看。

调优策略:初始化堆内存与最大堆相同,减少垃圾回收次数。

两种方法:内部启动和外部启动

(1)、内部启动

实例参数-XX:+PrintGCDetails -Xmx32M -Xms1M

打印GC日志,设置最大堆内存32M,初始堆内存32M

-Xms :设置Java堆栈的初始化大小

-Xmx :设置最大的java堆大小

测试:

第一步,在项目运行,编辑结构中配置参数:

 运行项目,查看回收次数:

这样配置后,GC回收次数非常多。

更改堆大小后,-XX:+PrintGCDetails -Xmx256M -Xms256M ,GC回收次数减少:

我们可以使用jconsole.exe工具查看堆内存使用情况:

(2)、外部启动

第一步,通过maven 项目打jar包,配置pom.xml:

 

复制代码
    <build>
        <plugins>
            <!-- 指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <!--  使用maven-jar-plugin将指定包目录打成单独的jar包  -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
            </plugin>

            <!--   SpringBoot 项目借由 spring-boot-maven-plugin 插件,通过 Maven 将项目打包成可执行的 JAR(Fat Jar) 或者 WAR,由此插件生成的 Jar 包  -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
复制代码

使用maven打包,如图:

最终生成spring-boot-helloworld-1.0-SNAPSHOT.jar,执行如下命令:

 java -server -Xms32m -Xmx232m -jar spring-boot-helloworld-1.0-SNAPSHOT.jar

2.3、Spring Boot使用undertow代替tomcat

undertow 是基于java nio的web服务器,应用比较广泛,内置提供的PathResourceManager,可以用来直接访问文件系统;如果你有文件需要对外提供访问,除了ftp,nginx等,undertow 也是一个不错的选择,作为java开发,服务搭建非常简便;
spring boot内嵌容器默认为tomcat,想要换成undertow,非常容易,只需修改spring-boot-starter-web依赖,移除tomcat的依赖:
复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!--  为什么不需要版本号,在parent里面已经封装好了版本号 -->
            <exclusions>
                <!--     移除内嵌tomcat    -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
复制代码

然后,添加undertow依赖:

        <!--   引入undertow     -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

下面我们使用jMetter工具来测试udertow和tomcat的吞吐量,jMeter的使用可以参考博客:JMeter性能测试,完整入门篇

服务器名称

第一次运行

第二次运行

第三次运行

平均值

tomcat

8203

8190

7955

8116

undertow

7097

7434

7462

7331

三、发布打包

目前,前后端分离的架构已成主流,因此使用springboot构建应用是非常快速的,项目发布到服务器上的时候,只需要打成一个jar包,然后通过命令 : java -server jar包名称即可启动服务了;

2.3节中我们已经介绍了Jar类型打包方式,这里就不重复介绍了,这里主要介绍一下war类型打包方式。

3.1、修改pom文件

修改默认打包类型:

    <groupId>com.zy.example</groupId>
    <artifactId>springboot-atomikos</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modelVersion>4.0.0</modelVersion>
    <packaging>war</packaging>

添加servlet-api依赖:

        <!--  添加servlet-api的依赖,用来打war包  -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>

3.2、排除Spring Boot内置tomcat

复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!--  为什么不需要版本号,在parent里面已经封装好了版本号 -->
            <!--   排除内置tomcat   -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
复制代码

3.3、改造启动类

修改App.java:

复制代码
package com.zy.example;

import com.zy.example.config.DBConfig1;
import com.zy.example.config.DBConfig2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

/**
 * 启动代码  打包成war、使用外置tomcat启动
 *
 * @author zy
 * @since  2020-2-2
 */

@SpringBootApplication
//开启读取配置文件
@EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class })
public class App extends SpringBootServletInitializer {

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

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(App.class);
    }
}
复制代码

3.4、pom指定maven打包插件(不指定则使用默认的)

复制代码
<build>
        <plugins>
            <!-- 指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <!--  使用maven-war-plugin将指定包目录打成单独的war包  -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
            </plugin>

            <!--   SpringBoot 项目借由 spring-boot-maven-plugin 插件,通过 Maven 将项目打包成可执行的 JAR(Fat Jar) 或者 WAR  -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
复制代码

3.5、打包

在idea环境下,依次执行clean、install:

执行完毕后,可以看到war包已经生成了,默认是在target目录下:

亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。

日期姓名金额
2023-09-06*源19
2023-09-11*朝科88
2023-09-21*号5
2023-09-16*真60
2023-10-26*通9.9
2023-11-04*慎0.66
2023-11-24*恩0.01
2023-12-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
2024-09-06*强8.8
2024-09-09*波1
2024-09-10*口1
2024-09-10*波1
2024-09-12*波10
2024-09-18*明1.68
2024-09-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(3908)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示