spring boot需注意点整理

13 篇文章 1 订阅
12 篇文章 0 订阅

目前最流行的框架莫过于spring boot和spring cloud了,相信大家都对这两个多多少少都有了解。

但是在运用过程中难免有些地方会遇到问题,以下简单整理一下我再次学习spring boot的过程中以前没有注意到的或者容易被人忽视的知识点。相信你看了也会有种恍然大悟的感觉。

-------------------------------持续更新----------------------------------------

1、application.properties配置文件里面中文的处理

大家都知道对于该配置文件中的属性使用很简单,直接@Value注入即可,但是如果属性值为中文时程序中注入得到的会是乱码,这里就告诉大家怎么处理。

book.author=罗贯中

book.name=三国演义

book.pinyin=sanguoyanyi

@Value(value = "${book.author}") private String bookAuthor;

 只需要在配置文件中添加以下配置即可。

server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.messages.encoding=UTF-8

 然后 在IntelliJ IDEA中依次点击File -> Settings -> Editor -> File Encodings
将Properties Files (*.properties)下的Default encoding for properties files设置为UTF-8,将Transparent native-to-ascii conversion前的勾选上。eclipse的话Window—>Preferences—>General—>Content Types—>Text—>Java Properties File,将Default encoding中的ISO-8859-1更改为UTF-8后,点击update即可。

2、日志输出

默认情况下Spring Boot使用Logback作为日志框架,当然如果有需要我们可以手动配置日志级别以及日志输出位置,只需要在application.properties中添加如下代码:

logging.file=/home/sang/workspace/log.log

logging.level.org.springframework.web=debug


3、springMVC相关配置

虽然Spring Boot默认的配置很多情况都可以满足我们的项目需求,可是有的时候我们可能还是会需要更加灵活的SpringMVC配置,这个时候我们只需要自定义类继承自WebMvcConfigurerAdapter,然后使用@Configuration和@EnableWebMvc注解,这样我们会完全屏蔽掉Spring Boot的默认配置,但是正常情况下我们可能只是希望在Spring Boot已有默认配置的基础上再添加一些配置即Spring Boot提供的默认配置和我自定义的配置并存的情况,这个也简单,只需要去掉@EnableWebMvc注解就行了. 

@Configuration
//@EnableWebMvc//无需使用该注解,否则会覆盖掉SpringBoot的默认配置值
public class WebMVCConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/hello").setViewName("/hello");
    }

}

4、websocket的理解

WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算,而且这种太占用资源)

WebSocket为浏览器和服务器之间提供了双工异步通信功能,也就是说我们可以利用浏览器给服务器发送消息,服务器也可以给浏览器发送消息。而http只能是浏览器发起请求。

配置WebSocket:

/**
 * 
 * 1.@EnableWebSocketMessageBroker注解表示开启使用STOMP协议来传输基于代理的消息,Broker就是代理的意思。
 * 2.registerStompEndpoints方法表示注册STOMP协议的节点,并指定映射的URL。
 * 3.stompEndpointRegistry.addEndpoint("/endpointSang").withSockJS();这一行代码用来注册STOMP协议节点,同时指定使用SockJS协议。
 * 4.configureMessageBroker方法用来配置消息代理,由于我们是实现推送功能,这里的消息代理是/topic
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/ws/endpointChat").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue","/topic");
    }
}

/**
 * WebSocket 消息处理类
 * 
 * 
 * 首先@Controller注解不必多言,
 * 方法上添加的@MessageMapping注解和我们之前使用的@RequestMapping类似。
 * 第二个方法是广播式 

 * @SendTo注解表示当服务器有消息需要推送的时候,会对订阅了@SendTo中路径的浏览器发送消息。


 * 第一个方法是点对点聊天
 * 1.SimpMessagingTemplate这个类主要是实现向浏览器发送消息的功能。
 * 2.在Spring MVC中,可以直接在参数中获取Principal,Principal中包含有当前用户的用户名。
 * 3.convertAndSendToUser方法是向用户发送一条消息,第一个参数是目标用户用户名,第二个参数是浏览器中订阅消息的地址,第三个参数是消息本身。
 * 
 */
@Controller
public class WsController {
    @Autowired
    SimpMessagingTemplate messagingTemplate;

    @MessageMapping("/ws/chat")
    public void handleChat(Principal principal, String msg) {
        String destUser = msg.substring(msg.lastIndexOf(";") + 1, msg.length());
        String message = msg.substring(0, msg.lastIndexOf(";"));
        messagingTemplate.convertAndSendToUser(destUser, "/queue/chat", new ChatResp(message, principal.getName()));
    }

    @MessageMapping("/ws/nf")
    @SendTo("/topic/nf")
    public String handleNF() {
        return "系统消息";
    }
}

前端页面需要:STOMP协议的客户端脚本stomp.js、SockJS的客户端脚本sock.js以及jQuery 来实现。具体前端代码可以参考一个简单示例:

<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>广播式WebSocket</title>
    <script th:src="@{js/sockjs.min.js}"></script>
    <script th:src="@{js/stomp.js}"></script>
    <script th:src="@{js/jquery-3.1.1.js}"></script>
</head>
<body οnlοad="disconnect()">
<noscript><h2 style="color: #e80b0a;">Sorry,浏览器不支持WebSocket</h2></noscript>
<div>
    <div>
        <button id="connect" οnclick="connect();">连接</button>
        <button id="disconnect" disabled="disabled" οnclick="disconnect();">断开连接</button>
    </div>

    <div id="conversationDiv">
        <label>输入你的名字</label><input type="text" id="name"/>
        <button id="sendName" οnclick="sendName();">发送</button>
        <p id="response"></p>
    </div>
</div>
<script type="text/javascript">
    var stompClient = null;
    function setConnected(connected) {
        document.getElementById("connect").disabled = connected;
        document.getElementById("disconnect").disabled = !connected;
        document.getElementById("conversationDiv").style.visibility = connected ? 'visible' : 'hidden';
//        $("#connect").disabled = connected;
//        $("#disconnect").disabled = !connected;
        $("#response").html();
    }
    function connect() {
        var socket = new SockJS('/endpointSang');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (frame) {
            setConnected(true);
            console.log('Connected:' + frame);
            stompClient.subscribe('/topic/getResponse', function (response) {
                showResponse(JSON.parse(response.body).responseMessage);
            })
        });
    }
    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setConnected(false);
        console.log('Disconnected');
    }
    function sendName() {
        var name = $('#name').val();
        console.log('name:' + name);
        stompClient.send("/welcome", {}, JSON.stringify({'name': name}));
    }
    function showResponse(message) {
        $("#response").html(message);
    }
</script>
</body>
</html>

我们的页面上先有两个按钮,一个是连接,一个是断开连接,两个按钮分别对应不同的点击事件,在这两个按钮下方有一个输入框,就是我们要发送的内容,然后还有一个发送按钮,发送按钮对应了一个发送消息的点击事件。这是整个页面的元素,很简单,我们这里重点来看一下js逻辑代码。connect方法是当我点击连接按钮的时候执行的,var socket = new SockJS('/endpointSang');表示连接的SockJS的endpoint名称为/endpointSang,stompClient = Stomp.over(socket);表示使用STOMP来创建WebSocket客户端。然后调用stompClient中的connect方法来连接服务端,连接成功之后调用setConnected方法,该隐藏的隐藏,该显示的显示。然后再通过调用stompClient中的subscribe方法来订阅/topic/getResponse发送来的消息,也就是我们在Controller中的say方法上添加的@SendTo注解的参数。stompClient中的send方法表示发送一条消息到服务端,其他的都是常规的js用法。

热部署配置(pom.xml):

 <!-- 热部署 -->
    <!-- devtools可以实现页面热部署(即页面修改后会立即生效,
        这个可以直接在application.properties文件中配置spring.thymeleaf.cache=false来实现) -->
    <!-- 实现类文件热部署(类文件修改后不会立即生效),实现对属性文件的热部署。 -->
    <!-- 即devtools会监听classpath下的文件变动,并且会立即重启应用(发生在保存时机),
        注意:因为其采用的虚拟机机制,该项重启是很快的 -->
    <!-- (1)base classloader (Base类加载器):加载不改变的Class,例如:第三方提供的jar包。 -->
    <!-- (2)restart classloader(Restart类加载器):加载正在开发的Class。 -->
    <!-- 为什么重启很快,因为重启的时候只是加载了在开发的Class,没有重新加载第三方的jar包。 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <!-- optional=true, 依赖不会传递, 该项目依赖devtools;之后依赖boot项目的项目如果想要使用devtools, 需要重新引入 -->
        <optional>true</optional>
     </dependency>

打包跳过测试类配置(pom.xml):

<build>
    <finalName>address-statistics</finalName>
    <plugins>
      <!-- 安装时不运行测试类 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
          <skipTests>true</skipTests>
        </configuration>
      </plugin>
    </plugins>
  </build> 

maven打包,父级packging类型为pom,子级为jar(不指定默认为jar),子级非可执行jar包时报错(找不到main函数)解决如下:

此时如果parent中pom添加的build插件,没有使用pluginManagement标签,在打包common模块时就会提示找不到main入口 

<!-- 添加spring-boot的maven插件 -->
<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-info</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <target>${java.version}</target>
                    <source>${java.version}</source>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build> 

restful服务时间类型格式化返回:

由于spring boot 首选和默认的是Jackson,而且Jackson是spring-boot-starter-json依赖中的一部分,spring-boot-starter-web中包含spring-boot-starter-json。便引出以下配置,在返回json的时候省去了对时间类型数据的处理,精简代码。

1、全局配置(properties配置文件配置) :

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

2、时间类型字段添加注解

/**
     * 创建时间
     */
    @ApiModelProperty(value = "创建时间")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    @TableField("CREATETIME")
    private Date createtime;

3、@RequestParam、@RequestBody和@ModelAttribute区别

@RequestParam(org.springframework.web.bind.annotation.RequestParam)用于将指定的请求参数赋值给方法中的形参,post/get均可用

@RequestBody注解处理HttpEntity传递过来的数据,GET请求中,因为没有HttpEntity,不能用于get请求。可以接收json格式的数据,并将其转换成对应的数据类型

@ModelAttribute注解类型将参数绑定到Model对象

当前台界面使用GET或POST方式提交数据时,数据编码格式由请求头的ContentType指定。分为以下几种情况:
1. application/x-www-form-urlencoded,这种情况的数据@RequestParam、@ModelAttribute可以处理,@RequestBody也可以处理。
2. multipart/form-data,@RequestBody不能处理这种格式的数据。(form表单里面有文件上传时,必须要指定enctype属性值为multipart/form-data,意思是以二进制流的形式传输文件。)
3. application/json、application/xml等格式的数据,必须使用@RequestBody来处理 

@PostConstruct 注释

用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化。此方法必须在将类放入服务之前调用。支持依赖关系注入的所有类都必须支持此注释。即使类没有请求注入任何资源,用 PostConstruct 注释的方法也必须被调用。只有一个方法可以用此注释进行注释。应用 PostConstruct 注释的方法必须遵守以下所有标准:该方法不得有任何参数,除非是在 EJB 拦截器 (interceptor) 的情况下,根据 EJB 规范的定义,在这种情况下它将带有一个 InvocationContext 对象 ;该方法的返回类型必须为 void;该方法不得抛出已检查异常;应用 PostConstruct 的方法可以是 public、protected、package private 或 private;除了应用程序客户端之外,该方法不能是 static;该方法可以是 final;如果该方法抛出未检查异常,那么不得将类放入服务中,除非是能够处理异常并可从中恢复的 EJB。 

1.@PostConstruct说明

     被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Serclet的inti()方法。被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行(@Autowired加载之后)。

2.@PreConstruct说明

     被@PreConstruct修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreConstruct修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前。

3、PostConstruct注解不可以有参数的,否则就会报错:java.lang.IllegalStateException: Lifecycle method annotation requires a no-arg method

@Mapper注解和 @MapperScan

1、@Mapper注解:
作用:在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类
添加位置:接口类上面

@Mapper
public interface UserDAO {
   //代码
}

如果想要每个接口都要变成实现类,那么需要在每个接口类上加上@Mapper注解,比较麻烦,解决这个问题用@MapperScan

2、@MapperScan
作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
添加位置:是在Springboot启动类上面添加,

@SpringBootApplication
@MapperScan("com.winter.dao")
public class SpringbootMybatisDemoApplication {

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

添加@MapperScan(“com.winter.dao”)注解以后,com.winter.dao包下面的接口类,在编译之后都会生成相应的实现类

3、使用@MapperScan注解多个包
(实际用的时候根据自己的包路径进行修改)

@SpringBootApplication  
@MapperScan({"com.kfit.demo","com.kfit.user"})  
public class App {  
    public static void main(String[] args) {  
       SpringApplication.run(App.class, args);  
    }  

4、 如果dao接口类没有在Spring Boot主程序可以扫描的包或者子包下面,可以使用如下方式进行配置:
(没验证过,不确定能否使用,或许需要根据自己定义的包名进行修改路径)

@SpringBootApplication  
@MapperScan({"com.kfit.*.mapper","org.kfit.*.mapper"})  
public class App {  
    public static void main(String[] args) {  
       SpringApplication.run(App.class, args);  
    }  
}

 spring boot打开actuator监控

引入依赖spring-boot-starter-actuator

  ----很坑

2.0之前 直接启动
在启动log日志中,可以看到springboot把一些默认的地址都映射好了,其中我们配置的actuator中health和info已经被映射上了,我们打开地址:http://localhost:8080/actuator/info 或者 http://localhost:8080/actuator/health

2.0之后 

management:  #actuator
  server:
    port: 8081
  endpoints:
    web:
#      base-path: / #默认是/actuator 前缀,可以在这里修改
      exposure:
        include: "*"  #打开全部请求端点
#        include: refresh,health,info,bus-refresh #打开部分
然后加注解@RefreshScope

基于注解开启定时任务

 @Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class SaticScheduleTask {
    //3.添加定时任务
    @Scheduled(cron = "0/5 * * * * ?")
    //或直接指定时间间隔,例如:5秒
    //@Scheduled(fixedRate=5000)
    private void configureTasks() {
        System.err.println("执行静态定时任务时间: " + LocalDateTime.now());
    }
}

//@Component注解用于对那些比较中立的类进行注释;
//相对与在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释
@Component
@EnableScheduling   // 1.开启定时任务
@EnableAsync        // 2.开启多线程
public class MultithreadScheduleTask {

        @Async //很关键
        @Scheduled(fixedDelay = 1000)  //间隔1秒
        public void first() throws InterruptedException {
            System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
            System.out.println();
            Thread.sleep(1000 * 10);
        }

        @Async
        @Scheduled(fixedDelay = 2000)
        public void second() {
            System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
            System.out.println();
        }
    }

 cron表达式

             每隔5秒执行一次:*/5 * * * * ?
 
             每隔1分钟执行一次:0 */1 * * * ?
 
             每天23点执行一次:0 0 23 * * ?
 
             每天凌晨1点执行一次:0 0 1 * * ?
 
             每月1号凌晨1点执行一次:0 0 1 1 * ?
 
             每月最后一天23点执行一次:0 0 23 L * ?
 
             每周星期天凌晨1点实行一次:0 0 1 ? * L
 
             在26分、29分、33分执行一次:0 26,29,33 * * * ?
 
             每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?

maven打包jar执行报错:没有主程序清单属性

pom打包方式选择jar,然后添加下面build插件 

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

 

mysql时区处理

 可以修改服务时区或者直接在jdbc连接数据库时设置时区

mysql> set global time_zone='+08:00';
mysql> set time_zone='+08:00';
mysql> flush privileges;
或者

[mysqld]
default-time-zone = '+08:00'
或者

&serverTimezone=GMT%2b8  相差16个时区

&serverTimezone=UTC    相差8个时区

spring boot配置jetCache 

jetcache:
  statIntervalMinutes: 15
  areaInCacheName: false
  hidePackages: com.cn.geostar.md.manage
  local:
    # 默认2小时本地缓存
    default:
      type: caffeine
      keyConvertor: fastjson
      expireAfterWriteInMillis: 3600000
      expireAfterAccessInMillis: 1800000
    # 長時本地緩存,主要用于要求时效一般
    longTime:
      type: caffeine
      keyConvertor: fastjson
      expireAfterWriteInMillis: 300000
      expireAfterAccessInMillis: 180000
    # 短時本地緩存,主要用于要求时效较高的配置
    shortTime:
      type: caffeine
      keyConvertor: fastjson
      expireAfterWriteInMillis: 60000
      expireAfterAccessInMillis: 40000
  remote:
    # 默认2小时的远程缓存
    default:
      type: redis
      expireAfterWriteInMillis: 43200000
      keyConvertor: fastjson
      valueEncoder: kryo
      valueDecoder: kryo
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${REDIS_HOST:172.17.0.104}
      port: ${REDIS_PORT:6400}
      password: redis-123456-redis
      database: 1
    # 长时远程緩存,主要用于要求时效要求一般的集中式缓存
    longTime:
      type: redis
      expireAfterWriteInMillis: 7200000
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${REDIS_HOST:172.17.0.104}
      port: ${REDIS_PORT:6400}
      password: redis-123456-redis
      database: 1
    # 短時远程緩存,主要用于要求时效较高的集中式缓存
    shortTime:
      type: redis
      expireAfterWriteInMillis: 300000
      keyConvertor: fastjson
      valueEncoder: kryo
      valueDecoder: kryo
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${REDIS_HOST:172.17.0.104}
      port: ${REDIS_PORT:6400}
      password: redis-123456-redis
      database: 1

maven打包跳过单元测试

<plugin>

         <groupId>org.apache.maven.plugins</groupId>

         <artifactId>maven-surefire-plugin</artifactId>

         <configuration>

           <skip> true </skip>

         </configuration>

       </plugin>

 

springboot 连接 postgresql 指定模式Schema

一般的连接方式,我们创建数据库之后,在public 的Schema(模式)下建表,这时使用连接方式

jdbc:postgresql://localhost:5432/postgresql
在这种连接方式下,默认连接使用的是postgresql数据库的public 模式
  •  

在业务场景中有时允许多个用户使用一个数据库并且不会互相干扰。这时需要在使用同一个数据库 新建其他模式进行连接。这时在springboot的数据源jdbc配置时注意。

postgresql-> 9.3 及以前的版本指定方式
spring.datasource.url=jdbc:postgresql://localhost:5432/postgresql?searchpath=newschema
  •  
postgresql-> 9.4 及以后的版本指定方式
spring.datasource.url=jdbc:postgresql://localhost:5432/postgresql?currentSchema=newschema

sql语句指定schema在表名前面加上schema.表名就行了 

 

 -------------------------------持续更新----------------------------------------

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值