关于Springboot 应配置外移和Maven个性化打包一些做法

期望达到的效果是每次更新服务器端应用只需要更新主程序jar 依赖jar单独分离。配置文件独立存放于文件夹内,更新程序并不会覆盖已有的配置信息。

一、配置外移

1、开发环境外移

做法:在项目同级或者上级创建config文件夹放置配置文件,具体module内部application.properties通过spring.profiles.include引用外部配置。利用的是springboot加载配置顺序原理,优先同级config文件夹,然后是同级properties,继而是jar包内部的properties。

在这里插入图片描述
但是如果是用test测试用例则无法配置外移需要将配置放于test/resources
在这里插入图片描述
这里附带一下springboot 测试用例写法

import com.health.AdminSrvApp;
import com.health.bean.po.UserInfo;
import com.health.main.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;/*** @author katasea* 2020/7/14 10:27*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AdminSrvApp.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource("classpath:application.yml")
@Slf4j
public class UserInfoServiceTest {@ResourceUserInfoService userInfoService;@Testpublic void testSaveOrUpdate(){log.info("无法获取登录用户信息,模拟返回用户:test,2,test 要修改检索:LoginHandlerMethodArgumentResolver.java");UserInfo userInfo = new UserInfo();userInfo.setUserId("test");userInfo.setUserType(2);userInfo.setUserName("test2");userInfo.setIsAdmin(1);userInfo.setPhone(18650093759L);userInfo.setSex(1);userInfoService.mySaveOrUpdate(userInfo);}
}

也顺便附带一下logback配置文件内容

<?xml version="1.0" encoding="UTF-8"?>
<configuration><contextName>admin-srv</contextName><property name="LOG_PATH" value="log" /><!--<property name="CONSOLE_LOG_PATTERN"--><!--value="%boldRed(%date{yyyy-MM-dd HH:mm:ss}) | %boldYellow(%thread) | %msg%n"/>--><!-- 日志记录器,日期滚动记录 --><appender name="FILEERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${LOG_PATH}/admin-srv_log_error.log</file><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 --><fileNamePattern>${LOG_PATH}/error/admin-srv_log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern><!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,命名日志文件,例如log-error-2013-12-21.0.log --><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>10MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><!-- 追加方式记录日志 --><append>true</append><!-- 日志文件的格式 --><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %thread | MDC[%X{requestId}] | %logger :%-3L| %msg%n</pattern><!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>--><charset>utf-8</charset></encoder><!-- 此日志文件只记录info级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>error</level><!--<onMatch>ACCEPT</onMatch>--><!--<onMismatch>DENY</onMismatch>--></filter></appender><!-- 日志记录器,日期滚动记录 --><appender name="FILEDEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${LOG_PATH}/admin-srv_log_debug.log</file><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/info/admin-srv_log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>10MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><!-- 追加方式记录日志 --><append>true</append><!-- 日志文件的格式 --><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %thread | MDC[%X{requestId}] | %logger | %msg%n</pattern><charset>utf-8</charset></encoder><!-- 此日志文件只记录debug级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>debug</level><!--<onMatch>ACCEPT</onMatch>--><!--<onMismatch>DENY</onMismatch>--></filter></appender><appender name="ASYNC_FILEDEBUG" class="ch.qos.logback.classic.AsyncAppender"><!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --><discardingThreshold>0</discardingThreshold><!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --><queueSize>10000</queueSize><!-- 添加附加的appender,最多只能添加一个 --><appender-ref ref="FILEDEBUG"/></appender><appender name="ASYNC_FILEERROR" class="ch.qos.logback.classic.AsyncAppender"><!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --><discardingThreshold>0</discardingThreshold><!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --><queueSize>10000</queueSize><!-- 添加附加的appender,最多只能添加一个 --><appender-ref ref="FILEERROR"/></appender><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><!--encoder 默认配置为PatternLayoutEncoder--><encoder><!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %thread | MDC[%X{requestId}] | %msg%n</pattern><!--<pattern>${CONSOLE_LOG_PATTERN}</pattern>--><!--<charset>gbk</charset>--></encoder><!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息--><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>debug</level></filter></appender><logger name="org.springframework" level="WARN" /><logger name="com.health.main.mapper" level="DEBUG" /><!-- 生产环境下,将此级别配置为适合的级别,以免日志文件太多或影响程序性能 --><root level="INFO"><appender-ref ref="ASYNC_FILEERROR" /><!--<appender-ref ref="FILEWARN" />--><appender-ref ref="ASYNC_FILEDEBUG" /><!-- 生产环境将请stdout,testfile去掉 --><appender-ref ref="STDOUT" /></root>
</configuration>
2、生产环境部署分离

这里按开发环境一样将配置文件放config文件夹与module同级,并在同级下编写启动脚本。
在这里插入图片描述
文件夹内部如下图则是lib与主jar分离的形式。具体maven如何编写下面会介绍。
在这里插入图片描述
这里附带脚本写法

title 应用名称:8503/dubbo:20890
java -Xmx256M -Xms256M -jar -Dfile.encoding=gbk -Dloader.path=./admin-srv/lib,./config  ./admin-srv/admin-srv-v1.0.jar.original

配置文件夹需要与启动脚本同级,这里脚本加了限制启动内存,jar包路径等根据实际情况自己修改。

二、MAVEN打包

1、普通lib包分离

maven关键配置如下:

<build><resources><resource><directory>src/main/resources</directory><includes><include>**/*</include></includes></resource><resource><directory>src/main/resources</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/resources/mapping/*</directory><includes><include>**/*.xml</include></includes></resource></resources><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><configuration><!-- jdk 版本和编译等级 --><source>${java.version}</source><target>${java.version}</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><!--这里如果是true 打包的jar 无法用rar重新放入class文件--><excludeDevtools>false</excludeDevtools></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>2.4</version><configuration><archive><!--不打包依赖的jar,把依赖的jar copy到lib目录,和生成的jar放在同一级目录下--><manifest><addClasspath>true</addClasspath><classpathPrefix>lib/</classpathPrefix><mainClass>com.health.AdminSrvApp</mainClass></manifest></archive><alias>abc</alias></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><executions><execution><id>copy-dependencies</id><phase>prepare-package</phase><goals><goal>copy-dependencies</goal></goals><configuration><outputDirectory>target/lib</outputDirectory><overWriteReleases>false</overWriteReleases><overWriteSnapshots>false</overWriteSnapshots><overWriteIfNewer>true</overWriteIfNewer></configuration></execution></executions></plugin> </plugins></build>

直接用maven打包命令即可: mvn package -Dmaven.test.skip=true
打包后目录如下,拷贝lib文件夹和original轻量包到上面生产环境的具体服务文件夹下即可。
在这里插入图片描述

2、自定义某些lib包不分离合并到主jar一起打包

实际中,我们需要频繁拷贝 api common 等jar来更新各个文件夹内部的lib对应的jar十分繁琐,有没有可能将经常变动的jar也合并打包到轻量包内,不经常变动的外部引用jar才放置于lib文件夹呢?这样后续更新只要更新original轻量包重新启动脚本即可。无需每次更新都拷贝大量的不变的lib文件夹外部的jar。

为了实现自定义打包则需要使用maven另外一个插件 maven部分代码如下。

<build><resources><resource><directory>src/main/resources</directory><includes><include>**/*</include></includes></resource><resource><directory>src/main/resources</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/resources/mapping/*</directory><includes><include>**/*.xml</include></includes></resource></resources><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><configuration><!-- jdk 版本和编译等级 --><source>${java.version}</source><target>${java.version}</target><encoding>UTF-8</encoding></configuration></plugin><!--            <plugin>--><!--                <groupId>org.springframework.boot</groupId>--><!--                <artifactId>spring-boot-maven-plugin</artifactId>--><!--                <configuration>--><!--                    <excludeDevtools>false</excludeDevtools>--><!--                </configuration>--><!--            </plugin>--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>2.4</version><configuration><archive><!--不打包依赖的jar,把依赖的jar copy到lib目录,和生成的jar放在同一级目录下--><manifest><addClasspath>true</addClasspath><classpathPrefix>lib/</classpathPrefix><mainClass>com.health.AdminSrvApp</mainClass></manifest></archive><alias>abc</alias></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><executions><execution><id>copy-dependencies</id><phase>prepare-package</phase><goals><goal>copy-dependencies</goal></goals><configuration><outputDirectory>target/lib</outputDirectory><overWriteReleases>false</overWriteReleases><overWriteSnapshots>false</overWriteSnapshots><overWriteIfNewer>true</overWriteIfNewer></configuration></execution></executions></plugin><!-- 这里来实现自定义的maven打包 可以指定哪些jar要合并到original --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-antrun-plugin</artifactId><version>1.8</version><executions><execution><phase>package</phase><goals><goal>run</goal></goals><configuration><target><echo>Building 描述信息 START....</echo><!-- 删除目录 --><delete dir="target/temp"/><!-- 删除目录 --><mkdir dir="target/temp"/><mkdir dir="target/temp/main"/><mkdir dir="target/temp/oth"/><!-- 复制目录文件 复制到-todir 要复制的目录-fileset --><echo>Copying jar START....</echo><!--                                <copy tofile="target/${project.artifactId}-${project.version}.jar.original"-->
<!--                                      file="target/${project.artifactId}-${project.version}.jar"-->
<!--                                      failonerror="false" verbose="true" overwrite="true">-->
<!--                                </copy>--><!--个性化制作jar包 --><!-- 第一步 先把常用的jar挪到dir中 --><copy todir="target/temp/oth"failonerror="false" verbose="true" overwrite="true"><!-- TODO 以下就是要合并到original的jar 根据自己情况编辑 还包括下面一段内容 --><fileset dir="target/lib" includes="mall-*.jar"/><fileset dir="target/lib" includes="core-srv-*.jar"/></copy><copy tofile="target/temp/main/${project.artifactId}-${project.version}.jar"file="target/${project.artifactId}-${project.version}.jar"failonerror="false" verbose="true" overwrite="true"></copy><!-- 第二步 删除lib里面的这些常用jar --><delete><!-- TODO 以下就是要合并到original的jar 根据自己情况编辑 应该与上面定义的拷贝内容一致! --><fileset dir="target/lib" includes="mall-*.jar"/><fileset dir="target/lib" includes="core-srv-*.jar"/></delete><!-- 第三步 解压临时文件夹的jar --><unzip dest="target/temp/oth"><fileset dir="target/temp/oth"></fileset></unzip><unzip dest="target/temp/main" overwrite="true"><fileset dir="target/temp/main"></fileset></unzip><!-- 第四步 删除temp里面的其他jar留下 主程序jar--><delete><fileset dir="target/temp/oth" includes="*.jar"/><fileset dir="target/temp/main" includes="*.jar"/></delete><!-- 第六步 删除temp里面的jar待会儿要压缩--><copy todir="target/temp"failonerror="false" verbose="true" overwrite="true"><fileset dir="target/temp/oth"/></copy><copy todir="target/temp"failonerror="false" verbose="true" overwrite="true"><fileset dir="target/temp/main"/></copy><delete dir="target/temp/oth"/><delete dir="target/temp/main"/><!--第七步 重新压缩一个jar --><!--                                <jar--><!--                                        jarfile="target/${project.artifactId}-${project.version}.jar.original2"--><!--                                        basedir="target/temp"--><!--                                >--><!--                                </jar>--><!--destfile	目标文件duplicate	打包方式(一般使用preserve)zipfileset	打包那些文件prefix		增加前缀(使用最佳实践)--><zip destfile="target/${project.artifactId}-${project.version}.jar.original"duplicate="preserve"><zipfileset dir="target/temp" includes="**/*.*" /></zip><echo>Building 描述信息 END</echo></target></configuration></execution></executions></plugin></plugins></build>

还是如上执行 mvn package -Dmaven.test.skip=true 打包命令
可以用rar打开 打包后的 original包,可以看到class文件已经合并打包到内部里了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/30900.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云操作系统控制台——解决服务器磁盘I/O故障

目录 引言 需求介绍 操作系统使用实例 获得的帮助与提升 建议 引言 你的云服务器是否遇到过系统响应变慢、服务超时&#xff0c;或者进程卡顿、磁盘空间不足、系统日志频繁告警的问题&#xff1f;这些情况在日常运维中并不少见&#xff0c;尤其是在 高负载或资源紧张时&a…

【英伟达AI论文】多模态大型语言模型的高效长视频理解

摘要&#xff1a;近年来&#xff0c;基于视频的多模态大型语言模型&#xff08;Video-LLMs&#xff09;通过将视频处理为图像帧序列&#xff0c;显著提升了视频理解能力。然而&#xff0c;许多现有方法在视觉主干网络中独立处理各帧&#xff0c;缺乏显式的时序建模&#xff0c;…

蓝桥杯备考:图论初解

1&#xff1a;图的定义 我们学了线性表和树的结构&#xff0c;那什么是图呢&#xff1f; 线性表是一个串一个是一对一的结构 树是一对多的&#xff0c;每个结点可以有多个孩子&#xff0c;但只能有一个父亲 而我们今天学的图&#xff01;就是多对多的结构了 V表示的是图的顶点集…

记录小白使用 Cursor 开发第一个微信小程序(一):注册账号及下载工具(250308)

文章目录 记录小白使用 Cursor 开发第一个微信小程序&#xff08;一&#xff09;&#xff1a;注册账号及下载工具&#xff08;250308&#xff09;一、微信小程序注册摘要1.1 注册流程要点 二、小程序发布流程三、下载工具 记录小白使用 Cursor 开发第一个微信小程序&#xff08…

【Linux学习笔记】Linux基本指令分析和权限的概念

【Linux学习笔记】Linux基本指令分析和权限的概念 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;Linux学习笔记 文章目录 【Linux学习笔记】Linux基本指令分析和权限的概念前言一. 指令的分析1.1 alias 指令1.2 grep 指令1.3 zip/unzip 指…

【消息队列】数据库的数据管理

1. 数据库的选择 对于当前实现消息队列这样的一个中间件来说&#xff0c;具体要使用哪个数据库&#xff0c;是需要稍作考虑的&#xff0c;如果直接使用 MySQL 数据库也是能实现正常的功能&#xff0c;但是 MySQL 也是一个客户端服务器程序&#xff0c;也就意味着如果想在其他服…

【HarmonyOS Next】鸿蒙加固方案调研和分析

【HarmonyOS Next】鸿蒙加固方案调研和分析 一、前言 根据鸿蒙应用的上架流程&#xff0c;本地构建app文件后&#xff0c;上架到AGC平台&#xff0c;平台会进行解析。根据鸿蒙系统的特殊设置&#xff0c;仿照IOS的生态闭环方案。只能从AGC应用市场下载app进行安装。这样的流程…

nuxt2 打包优化使用“compression-webpack-plugin”插件

在使用 Nuxt.js 构建项目时&#xff0c;为了提高性能&#xff0c;通常会考虑对静态资源进行压缩。compression-webpack-plugin 是一个常用的 Webpack 插件&#xff0c;用于在生产环境中对文件进行 Gzip 压缩。这对于减少网络传输时间和提高页面加载速度非常有帮助。下面是如何在…

不同开发语言之for循环的用法、区别总结

一、Objective-C &#xff08;1&#xff09;标准的c风格 for (int i 0; i < 5; i) {NSLog("i %d", i); } &#xff08;2&#xff09;for in循环。 NSArray *array ["apple", "banana", "orange"]; for (NSString *fruit in …

ctfshow做题笔记—栈溢出—pwn65~pwn68

目录 前言 一、pwn65(你是一个好人) 二、pwn66(简单的shellcode&#xff1f;不对劲&#xff0c;十分得有十二分的不对劲) 三、pwn67(32bit nop sled)&#xff08;确实不会&#xff09; 四、pwn68(64bit nop sled) 前言 做起来比较吃力哈哈&#xff0c;自己还是太菜了&…

Git基础之工作原理

基础概念 git本地有三个工作区域&#xff0c;工作目录 Working Directory&#xff0c;暂存区Stage/Index和资源区Repository/Git Directory&#xff0c;如果在加上远程的git仓库就是四个工作区域 四个区域与文件交换的命令之间的关系 WorkSpace&#xff1a;工作区&#xff0c;就…

Linux 指定命令行前后添加echo打印内容

目录 一. 前提条件二. 通过sh脚本进行批量修改三. 通过Excel和文本编辑器进行批量转换四. 实际执行效果 一. 前提条件 ⏹项目中有批量检索文件的需求&#xff0c;如下所示需要同时执行500多个find命令 find ./work -type f -name *.java find ./work -type f -name *.html fi…

Immich自托管服务的本地化部署与随时随地安全便捷在线访问数据

文章目录 前言1.关于Immich2.安装Docker3.本地部署Immich4.Immich体验5.安装cpolar内网穿透6.创建远程链接公网地址7.使用固定公网地址远程访问 前言 小伙伴们&#xff0c;你们好呀&#xff01;今天要给大家揭秘一个超炫的技能——如何把自家电脑变成私人云相册&#xff0c;并…

pytorch 50 大模型导出的onnx模型优化尝试

本博文基于Native-LLM-for-Android项目代码实现,具体做了以下操作: 1、尝试并实现将模型结构与权重零散的onnx模型进行合并,通过该操作实现了模型加载速度提升,大约提升了3倍 2、突破了onnxconverter_common 无法将llm模型导出为fp16的操作,基于该操作后将10g的权重降低到…

Training-free Neural Architecture Search for RNNs and Transformers(预览版本)

摘要 神经架构搜索 (NAS) 允许自动创建新的有效神经网络架构&#xff0c;为手动设计复杂架构的繁琐过程提供了替代方案。然而&#xff0c;传统的 NAS 算法速度慢&#xff0c;需要大量的计算能力。最近的研究调查了图像分类架构的无训练 NAS 指标&#xff0c;大大加快了搜索算…

c++_二叉树的介绍

内存模型 一.内存中有代码区&#xff1b;栈区&#xff1b;数据段 堆区 1、栈区存放了函数所有局部变量和形参&#xff1b; 它的局限在于&#xff1a;局部变量和形参的生存期&#xff1b;即函数返回后对象就会被回收 解决方案是&#xff1a;1&#xff09;使用全局变量 &…

②Modbus TCP转Modbus RTU/ASCII网关同步采集无需编程高速轻松组网

Modbus TCP转Modbus RTU/ASCII网关同步采集无需编程高速轻松组网https://item.taobao.com/item.htm?ftt&id784749793551 网关 MS-A1-5081 MS-A1-5081 网关通过 MODBUS TCP 协议与 Modbus RTU/ASCII 协议的相互转换&#xff0c;可以将 Modbus 串口设备接入 MODBUS TCP 网络…

[网络爬虫] 动态网页抓取 — Selenium 元素定位

&#x1f31f;想系统化学习爬虫技术&#xff1f;看看这个&#xff1a;[数据抓取] Python 网络爬虫 - 学习手册-CSDN博客 在使用 Selenium 时&#xff0c;往往需要先定位到指定元素&#xff0c;然后再执行相应的操作。例如&#xff0c;再向文本输入框中输入文字之前&#xff0c;…

vue实现一个pdf在线预览,pdf选择文本并提取复制文字触发弹窗效果

[TOC] 一、文件预览 1、安装依赖包 这里安装了disjs-dist2.16版本&#xff0c;安装过程中报错缺少worker-loader npm i pdfjs-dist2.16.105 worker-loader3.0.8 2、模板部分 <template><div id"pdf-view"><canvas v-for"page in pdfPages&qu…

Java零基础入门笔记:多线程

前言 本笔记是学习狂神的java教程&#xff0c;建议配合视频&#xff0c;学习体验更佳。 【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili 第1-2章&#xff1a;Java零基础入门笔记&#xff1a;(1-2)入门&#xff08;简介、基础知识&#xff09;-CSDN博客 第3章…