├── pom.xml └── src └── main ├── java └── com │ └── ffy │ └── mqtt │ ├── Application.java │ ├── constant │ └── Constant.java │ ├── model │ └── Message.java │ ├── mqtt │ ├── IMqttSender.java │ ├── MqttReceiverConfig.java │ ├── MqttResHandler.java │ ├── MqttSenderConfig.java │ └── SynMqttSender.java │ ├── util │ └── DefaultFuture.java │ └── web │ └── TestController.java └── resources ├── application-dev.yml ├── application-prod.yml ├── application.yml └── logback-spring.xml /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.ffy 8 | mqtt 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.3.6.RELEASE 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter 20 | 21 | 22 | org.springframework 23 | spring-context 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | cn.hutool 32 | hutool-all 33 | 5.3.7 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-web 38 | 39 | 40 | org.projectlombok 41 | lombok 42 | 1.16.4 43 | 44 | 45 | 46 | org.apache.commons 47 | commons-lang3 48 | 3.2.1 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-integration 53 | 54 | 55 | org.springframework.integration 56 | spring-integration-stream 57 | 58 | 59 | org.springframework.integration 60 | spring-integration-mqtt 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-amqp 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/Application.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt; 2 | 3 | 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | public class Application { 9 | public static void main(String[] args) { 10 | SpringApplication.run(Application.class); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/constant/Constant.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt.constant; 2 | 3 | public interface Constant { 4 | String MQTT_QUEUE="mqtt.queue"; 5 | String MQTT_TOPIC_REQ ="/mqtt/order/request"; 6 | String MQTT_TOPIC_RES ="/mqtt/order/response"; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/model/Message.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt.model; 2 | 3 | import lombok.Data; 4 | 5 | //https://juejin.cn/post/6844904079274180621 6 | @Data 7 | public class Message { 8 | Long messageId; 9 | String playLoad; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/mqtt/IMqttSender.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt.mqtt; 2 | 3 | import org.springframework.integration.annotation.MessagingGateway; 4 | import org.springframework.integration.mqtt.support.MqttHeaders; 5 | import org.springframework.messaging.handler.annotation.Header; 6 | import org.springframework.stereotype.Component; 7 | //https://www.hangge.com/blog/cache/detail_2610.html 8 | 9 | /** 10 | * MQTT生产者消息发送接口 11 | */ 12 | @Component() 13 | @MessagingGateway(defaultRequestChannel = MqttSenderConfig.CHANNEL_NAME_OUT) 14 | public interface IMqttSender { 15 | /** 16 | * 发送信息到MQTT服务器 17 | * 18 | * @param data 发送的文本 19 | */ 20 | void sendToMqtt(String data); 21 | 22 | /** 23 | * 发送信息到MQTT服务器 24 | * 25 | * @param topic 主题 26 | * @param payload 消息主体 27 | */ 28 | void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, 29 | String payload); 30 | 31 | /** 32 | * 发送信息到MQTT服务器 33 | * 34 | * @param topic 主题 35 | * @param qos 对消息处理的几种机制。 36 | * 0 表示的是订阅者没收到消息不会再次发送,消息会丢失。 37 | * 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息。 38 | * 2 多了一次去重的动作,确保订阅者收到的消息有一次。 39 | * @param payload 消息主体 40 | */ 41 | void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, 42 | @Header(MqttHeaders.QOS) int qos, 43 | String payload); 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/mqtt/MqttReceiverConfig.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt.mqtt; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import com.ffy.mqtt.constant.Constant; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.integration.annotation.ServiceActivator; 12 | import org.springframework.integration.channel.DirectChannel; 13 | import org.springframework.integration.core.MessageProducer; 14 | import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; 15 | import org.springframework.integration.mqtt.core.MqttPahoClientFactory; 16 | import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; 17 | import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; 18 | import org.springframework.messaging.Message; 19 | import org.springframework.messaging.MessageChannel; 20 | import org.springframework.messaging.MessageHandler; 21 | import org.springframework.messaging.MessagingException; 22 | import org.apache.commons.lang3.StringUtils; 23 | 24 | /** 25 | * MQTT配置,消费者 26 | */ 27 | @Configuration 28 | @Slf4j 29 | public class MqttReceiverConfig { 30 | /** 31 | * 订阅的bean名称 32 | */ 33 | public static final String CHANNEL_NAME_IN = "mqttInboundChannel"; 34 | 35 | // 客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息 36 | private static final byte[] WILL_DATA; 37 | static { 38 | WILL_DATA = "offline".getBytes(); 39 | } 40 | 41 | @Value("${mqtt.username}") 42 | private String username; 43 | 44 | @Value("${mqtt.password}") 45 | private String password; 46 | 47 | @Value("${mqtt.url}") 48 | private String url; 49 | 50 | @Value("${mqtt.receiver.clientId}") 51 | private String clientId; 52 | 53 | @Value("${mqtt.receiver.defaultTopic}") 54 | private String defaultTopic; 55 | 56 | 57 | 58 | 59 | /** 60 | * MQTT连接器选项 61 | */ 62 | @Bean 63 | public MqttConnectOptions getReceiverMqttConnectOptions(){ 64 | MqttConnectOptions options = new MqttConnectOptions(); 65 | // 设置连接的用户名 66 | if(!username.trim().equals("")){ 67 | options.setUserName(username); 68 | } 69 | // 设置连接的密码 70 | options.setPassword(password.toCharArray()); 71 | // 设置连接的地址 72 | options.setServerURIs(StringUtils.split(url, ",")); 73 | // 设置超时时间 单位为秒 74 | options.setConnectionTimeout(10); 75 | // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线 76 | // 但这个方法并没有重连的机制 77 | options.setKeepAliveInterval(20); 78 | return options; 79 | } 80 | 81 | /** 82 | * MQTT客户端 83 | */ 84 | @Bean 85 | public MqttPahoClientFactory receiverMqttClientFactory() { 86 | DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); 87 | factory.setConnectionOptions(getReceiverMqttConnectOptions()); 88 | return factory; 89 | } 90 | 91 | /** 92 | * MQTT信息通道(消费者) 93 | */ 94 | @Bean(name = CHANNEL_NAME_IN) 95 | public MessageChannel mqttInboundChannel() { 96 | return new DirectChannel(); 97 | } 98 | 99 | /** 100 | * MQTT消息订阅绑定(消费者) 101 | */ 102 | @Bean 103 | public MessageProducer inbound() { 104 | // 可以同时消费(订阅)多个Topic 105 | MqttPahoMessageDrivenChannelAdapter adapter = 106 | new MqttPahoMessageDrivenChannelAdapter( 107 | clientId, receiverMqttClientFactory(), 108 | StringUtils.split(defaultTopic, ",")); 109 | adapter.setCompletionTimeout(5000); 110 | adapter.setConverter(new DefaultPahoMessageConverter()); 111 | adapter.setQos(1); 112 | // 设置订阅通道 113 | adapter.setOutputChannel(mqttInboundChannel()); 114 | return adapter; 115 | } 116 | 117 | @Autowired 118 | MqttResHandler mqttResHandler; 119 | /** 120 | * MQTT消息处理器(消费者) 121 | */ 122 | @Bean 123 | @ServiceActivator(inputChannel = CHANNEL_NAME_IN) 124 | public MessageHandler handler() { 125 | return new MessageHandler() { 126 | @Override 127 | public void handleMessage(Message message) throws MessagingException { 128 | String topic = message.getHeaders().get("mqtt_receivedTopic").toString(); 129 | 130 | if(topic.equals(Constant.MQTT_TOPIC_RES)){ 131 | String msg = message.getPayload().toString(); 132 | mqttResHandler.deal(JSONUtil.toBean(msg,com.ffy.mqtt.model.Message.class)); 133 | } 134 | String msg = message.getPayload().toString(); 135 | log.info("\n--------------------START-------------------\n" + 136 | "接收到订阅消息:\ntopic:" + topic + "\nmessage:" + msg + 137 | "\n---------------------END--------------------"); 138 | } 139 | }; 140 | } 141 | 142 | } 143 | 144 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/mqtt/MqttResHandler.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt.mqtt; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import com.ffy.mqtt.model.Message; 5 | import com.ffy.mqtt.rabbitmq.SimpleSender; 6 | import com.ffy.mqtt.util.DefaultFuture; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | public class MqttResHandler { 13 | 14 | 15 | public void deal(Message msg){ 16 | //根据messageId判断是否是本机发送的消息 17 | Long msgId = msg.getMessageId(); 18 | if(DefaultFuture.contains(msgId)){ 19 | DefaultFuture.received(msg); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/mqtt/MqttSenderConfig.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt.mqtt; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.integration.annotation.IntegrationComponentScan; 9 | import org.springframework.integration.annotation.ServiceActivator; 10 | import org.springframework.integration.channel.DirectChannel; 11 | import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; 12 | import org.springframework.integration.mqtt.core.MqttPahoClientFactory; 13 | import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; 14 | import org.springframework.messaging.MessageChannel; 15 | import org.springframework.messaging.MessageHandler; 16 | 17 | @Configuration 18 | @IntegrationComponentScan 19 | public class MqttSenderConfig { 20 | @Value("${mqtt.username}") 21 | private String username; 22 | 23 | @Value("${mqtt.password}") 24 | private String password; 25 | 26 | @Value("${mqtt.url}") 27 | private String url; 28 | 29 | @Value("${mqtt.clientId}") 30 | private String clientId; 31 | 32 | @Value("${mqtt.defaultTopic}") 33 | private String defaultTopic; 34 | 35 | @Value("${mqtt.completionTimeout}") 36 | private int completionTimeout ; //连接超时 37 | 38 | @Value("${mqtt.willData}") 39 | private String WILL_DATA ; //连接超时 40 | 41 | /** 42 | * 发布的bean名称 43 | */ 44 | public static final String CHANNEL_NAME_OUT = "mqttOutboundChannel"; 45 | 46 | /** 47 | * 发布的bean名称 48 | */ 49 | public static final String CHANNEL_NAME_OUT_2 = "mqttOutboundChannel_2"; 50 | 51 | /** 52 | * MQTT连接器选项 53 | */ 54 | @Bean 55 | public MqttConnectOptions getSenderMqttConnectOptions(){ 56 | MqttConnectOptions options=new MqttConnectOptions(); 57 | // 设置连接的用户名 58 | if(!username.trim().equals("")){ 59 | options.setUserName(username); 60 | } 61 | // 设置连接的密码 62 | options.setPassword(password.toCharArray()); 63 | // 设置连接的地址 64 | options.setServerURIs(StringUtils.split(url, ",")); 65 | // 设置超时时间 单位为秒 66 | options.setConnectionTimeout(10); 67 | // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线 68 | // 但这个方法并没有重连的机制 69 | options.setKeepAliveInterval(20); 70 | // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。 71 | 72 | options.setWill("willTopic", WILL_DATA.getBytes(), 2, false); 73 | 74 | options.setMaxInflight(1000); 75 | return options; 76 | } 77 | /** 78 | * MQTT客户端 79 | */ 80 | @Bean 81 | public MqttPahoClientFactory senderMqttClientFactory() { 82 | DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); 83 | factory.setConnectionOptions(getSenderMqttConnectOptions()); 84 | return factory; 85 | } 86 | 87 | //接收通道 88 | @Bean 89 | public MessageChannel mqttInputChannel() { 90 | return new DirectChannel(); 91 | } 92 | 93 | 94 | 95 | /** 96 | * MQTT信息通道(生产者) 97 | */ 98 | @Bean(name = CHANNEL_NAME_OUT) 99 | public MessageChannel mqttOutboundChannel() { 100 | return new DirectChannel(); 101 | } 102 | 103 | 104 | 105 | 106 | /** 107 | * MQTT消息处理器(生产者) 108 | */ 109 | @Bean 110 | @ServiceActivator(inputChannel = CHANNEL_NAME_OUT) 111 | public MessageHandler mqttOutbound() { 112 | MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler( 113 | clientId, 114 | senderMqttClientFactory()); 115 | messageHandler.setAsync(true); 116 | messageHandler.setDefaultTopic(defaultTopic); 117 | return messageHandler; 118 | } 119 | 120 | 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/mqtt/SynMqttSender.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt.mqtt; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import com.ffy.mqtt.constant.Constant; 5 | import com.ffy.mqtt.model.Message; 6 | import com.ffy.mqtt.util.DefaultFuture; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | 11 | @Component 12 | public class SynMqttSender { 13 | @Autowired 14 | IMqttSender iMqttSender; 15 | public DefaultFuture sendMessage(Message msg) { 16 | Long msgId = DefaultFuture.generateId(); 17 | msg.setMessageId(msgId); 18 | iMqttSender.sendToMqtt(Constant.MQTT_TOPIC_REQ,1, JSONUtil.toJsonStr(msg)); 19 | DefaultFuture future = new DefaultFuture(msg.getMessageId(),30); 20 | return future; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/util/DefaultFuture.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt.util; 2 | 3 | import cn.hutool.core.lang.Snowflake; 4 | import cn.hutool.core.util.IdUtil; 5 | import com.ffy.mqtt.model.Message; 6 | import lombok.Data; 7 | import lombok.SneakyThrows; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.util.Map; 11 | import java.util.concurrent.*; 12 | import java.util.concurrent.atomic.AtomicLong; 13 | import java.util.concurrent.locks.Condition; 14 | import java.util.concurrent.locks.Lock; 15 | import java.util.concurrent.locks.ReentrantLock; 16 | 17 | @Data 18 | @Slf4j 19 | public class DefaultFuture implements Future { 20 | private static final Map FUTURES = new ConcurrentHashMap(); 21 | private final Lock lock = new ReentrantLock(); 22 | private final Condition done = lock.newCondition(); 23 | private int timeout; 24 | private long id; 25 | private final long start = System.currentTimeMillis(); 26 | private boolean cancel = false; 27 | public final static AtomicLong REQUEST_ID_GEN = new AtomicLong(0); 28 | private Message msg; 29 | public DefaultFuture(Long id, int timeout){ 30 | this.id=id; 31 | this.timeout=timeout; 32 | FUTURES.put(id, this); 33 | } 34 | 35 | public DefaultFuture( int timeout){ 36 | this.id=generateId(); 37 | this.timeout=timeout; 38 | FUTURES.put(id, this); 39 | } 40 | 41 | public static Long generateId(){ 42 | Snowflake snowflake = IdUtil.createSnowflake(1, 1); 43 | long id = snowflake.nextId(); 44 | return id; 45 | } 46 | @Override 47 | public boolean cancel(boolean mayInterruptIfRunning) { 48 | FUTURES.remove(id); 49 | this. cancel=true; 50 | return true; 51 | } 52 | 53 | @Override 54 | public boolean isCancelled() { 55 | return cancel; 56 | } 57 | 58 | @Override 59 | public boolean isDone() { 60 | return msg != null; 61 | } 62 | 63 | @SneakyThrows 64 | @Override 65 | public Message get() { 66 | return get(timeout,TimeUnit.SECONDS); 67 | } 68 | 69 | @Override 70 | public Message get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 71 | if (timeout <= 0) { 72 | throw new RuntimeException("参数错误"); 73 | } 74 | if (! isDone()) { 75 | long start = System.currentTimeMillis(); 76 | lock.lock(); 77 | try { 78 | while (! isDone()) { 79 | done.await(timeout, TimeUnit.SECONDS); 80 | if (isDone() || System.currentTimeMillis() - start > timeout) { 81 | break; 82 | } 83 | } 84 | } catch (InterruptedException e) { 85 | throw new RuntimeException(e); 86 | } finally { 87 | lock.unlock(); 88 | } 89 | if (! isDone()) { 90 | throw new RuntimeException("请求超时"); 91 | } 92 | } 93 | return msg; 94 | } 95 | 96 | private long getStartTimestamp() { 97 | return start; 98 | } 99 | public static boolean contains(Long msgId){ 100 | return FUTURES.containsKey(msgId); 101 | } 102 | 103 | public static void received( Message msg) { 104 | DefaultFuture future = FUTURES.remove(msg.getMessageId()); 105 | if (future != null) { 106 | future.doReceived(msg); 107 | } 108 | 109 | } 110 | private void doReceived(Message res) { 111 | lock.lock(); 112 | try { 113 | this.msg = res; 114 | if (done != null) { 115 | done.signal(); 116 | } 117 | } finally { 118 | lock.unlock(); 119 | } 120 | } 121 | 122 | private static class RemotingInvocationTimeoutScan implements Runnable { 123 | 124 | @Override 125 | public void run() { 126 | while (true) { 127 | try { 128 | for (DefaultFuture future : FUTURES.values()) { 129 | if (future == null || future.isDone()) { 130 | continue; 131 | } 132 | if (System.currentTimeMillis() - future.getStartTimestamp() > future.getTimeout()*1000) { 133 | Message timeoutResponse = new Message(); 134 | timeoutResponse.setMessageId(future.getId()); 135 | timeoutResponse.setPlayLoad("超时"); 136 | DefaultFuture.received(timeoutResponse); 137 | } 138 | } 139 | Thread.sleep(30); 140 | } catch (Throwable e) { 141 | log.error("Exception when scan the timeout invocation of remoting.", e); 142 | } 143 | } 144 | } 145 | } 146 | 147 | static { 148 | Thread th = new Thread(new RemotingInvocationTimeoutScan(), "MqttResponseTimeoutScanTimer"); 149 | th.setDaemon(true); 150 | th.start(); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/ffy/mqtt/web/TestController.java: -------------------------------------------------------------------------------- 1 | package com.ffy.mqtt.web; 2 | 3 | import com.ffy.mqtt.model.Message; 4 | import com.ffy.mqtt.mqtt.SynMqttSender; 5 | import com.ffy.mqtt.util.DefaultFuture; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @RestController 13 | @RequestMapping("mqtt") 14 | public class TestController { 15 | @Autowired 16 | SynMqttSender synMqttSender; 17 | @PostMapping("send") 18 | public Message sendMsg(@RequestBody Message message){ 19 | DefaultFuture future = synMqttSender.sendMessage(message); 20 | return future.get(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 3 | mqtt: 4 | username: guest # 账号 5 | password: guest # 密码 6 | url: tcp://127.0.0.1:1883 # mqtt连接tcp地址 7 | clientId: test # 客户端Id,每个启动的id要不同 8 | defaultTopic: test # 默认主题 9 | timeout: 100 # 超时时间 10 | keepalive: 100 # 保持连接数 11 | willData: 100 #遗嘱”消息 12 | completionTimeout: 20 13 | receiver: 14 | clientId: receiver 15 | defaultTopic: /mqtt/# -------------------------------------------------------------------------------- /src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 3 | netty: 4 | port: 16879 5 | spring: 6 | #redis 7 | redis: 8 | database: 0 9 | host: 127.0.0.1 10 | port: 6379 11 | password: 12 | pool: 13 | maxActive: 24 14 | maxWait: -1 15 | maxIdle: 10 16 | minIdle: 0 17 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | # 默认的profile为dev,其他环境通过指定启动参数使用不同的profile,比如: 3 | # 测试环境:java -jar my-spring-boot.jar --spring.profiles.active=test 4 | # 生产环境:java -jar my-spring-boot.jar --spring.profiles.active=prod 5 | profiles: 6 | active: dev 7 | logging: 8 | level: 9 | root: info 10 | -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | %white(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %magenta(%logger) - %cyan(%msg%n) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ${LOG_HOME}/%d{yyyy-MM-dd}.log 23 | 24 | 3 25 | 26 | 27 | 28 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | --------------------------------------------------------------------------------