SpringBoot整合

springboot配置Druid

pom.xml

注:springboot与druid的版本兼容问题(Druid 1.1.8以上兼容springboot2.0)

<!-- druid连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.9</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

application.yml
这里写图片描述

Druid 1.1.9 jar包里含有自动配置类,所以默认注册了 DruidStatView、druidWebStatFilter。

此处可以自定义设置 DruidStatView、druidWebStatFilter的属性

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/***?userUnicode=true&characterEncoding=UTF8&useSSL=false
    username: ****
    password: ****
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      stat-view-servlet:
        enabled: true
        urlPattern: 
        allow: 
        deny: 
        loginUsername: root
        loginPassword: root
        resetEnable: 
      web-stat-filter:
        enabled: true
        urlPattern: 
        exclusions: 
        sessionStatMaxCount: 
        sessionStatEnable: 
        principalSessionName: 
        principalCookieName: 
        profileEnable: 

Druid配置参数(Druid)

配置 缺省值 说明
name 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-“ + System.identityHashCode(this)
jdbcUrl 连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 连接数据库的用户名
password 连接数据库的密码。如果你不希望密码直接写在配置文件中, 可以使用ConfigFilter。详细看这里:(Druid ConfigFilter)
driverClassName 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降, 如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存reparedStatement,就是PSCache。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 在mysql5.5以下的版本中没有PSCache功能,建议关闭掉。作者在5.5版本中使用PSCache,通过监控界面发现PSCache有缓存命中率记录, 该应该是支持PSCache。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时, poolPreparedStatements自动触发修改为true。 在Druid中,不会存在Oracle下PSCache占用内存过多的问题, 可以把这个数值配置大一些,比如说100
validationQuery 用来检测连接是否有效的sql,要求是一个查询语句。 如果validationQuery为null,testOnBorrow、testOnReturn、 testWhileIdle都不会其作用。
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。 申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis, 执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis 有两个含义:
(1) Destroy线程会检测连接的间隔时间
(2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun 不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis 连接保持空闲而不被驱逐的最长时间
connectionInitSqls 物理连接初始化的时候执行的sql
exceptionSorter 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters 属性类型是字符串,通过别名的方式配置扩展插件, 常用的插件有:
监控统计用的filter:stat
日志用的filter:log4j
防御sql注入的filter:wall
proxyFilters 类型是List<com.alibaba.druid.filter.Filter>
如果同时配置了filters和proxyFilters, 是组合关系,并非替换关系

springboot整合mybatis并打印sql至日志

logging:
  level:
    root: info
    #打印sql 前缀为mapper的全路径
    com.example.mapper: debug
  # 不指定路径在当前项目下生成springboot.log日志
  file: log.log

整合JavaMail发送邮件

添加对应依赖

pom.xml

<dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4</version>
        </dependency>

编写发送邮件的类

package com.example.JavaMail;

import org.springframework.stereotype.Component;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.List;
import java.util.Properties;

/**
 * @author yanzt
 * @date 2018/6/29 15:33
 * @description
 */
@Component
public class MailSender {

    //SMTP发送邮件,是否进行身份验证
    private static final String smtpAuth = "true";
    //SMTP服务器
    private static final String smtpHost = "smtp.qq.com";
    //端口号 QQ邮箱有两个端口号:465/587
    private static final String smtpPort = "587";
    //发送邮箱
    private static final String fromMailAddress = "2507967396@qq.com";
    //发送邮箱的16位SMTP口令
    private static final String fromMailStmpPwd = "kziktiflnzjiecbc";

    /**
     * @param content 邮件内容
     * @param title 邮件标题
     * @param internerAddress 收件人地址集合
     * 发送邮件
     * */
    public static void send(String title, String content, List<String> internerAddress){
        Properties prop = new Properties();
        prop.put("mail.smtp.auth", smtpAuth);
        prop.put("mail.smtp.host",smtpHost);
        prop.put("mail.smtp.port",smtpPort);
        prop.put("mail.user",fromMailAddress);
        prop.put("mail.password",fromMailStmpPwd);
        Authenticator authenticator = new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(fromMailAddress,fromMailStmpPwd);
            }
        };
        Session mailSession = Session.getInstance(prop,authenticator);
        Message message = new MimeMessage(mailSession);
        try {
            message.setFrom(new InternetAddress(fromMailAddress));
            message.setSubject(title);
            /**text文本格式发送邮件*/
            message.setText(content);
            /**html格式发送邮件*/
//            message.setContent(content,"text/html; charset=UTF-8");
        } catch (MessagingException e) {
            e.printStackTrace();
        }
        for(int i=0,size=internerAddress.size();i<size;i++){
            try {
                message.setRecipient(Message.RecipientType.TO, new InternetAddress(internerAddress.get(i)));
                Transport.send(message);
            } catch (MessagingException e) {
//                e.printStackTrace();
                continue;
            }
        }
    }

}

开启POP3/SMTP

​ 第一步:进入QQ邮箱点击“设置”

​ 第二步:点击“账户”后往下拉找到POP3/IMAP/SMTP…

​ 第三步:请点击“开启”并且按照提示步骤完成获取“授权码”即可,授权码就是发送邮件参数的SMTP口令(fromMailStmpPwd)。

测试发送邮件

@ResponseBody
@RequestMapping("/sendMail")
public void sendMail(){
    List<String> internerAddress = new ArrayList<>();
    internerAddress.add("2975804955@qq.com");
    MailSender.send("测试邮件","测试邮件的内容啊!",internerAddress);
}

springboot集成spring cache

pom.xml

<!--添加缓存依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

在程序的入口中加入@ EnableCaching开启缓存技术:

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@SpringBootApplication
@EnableScheduling
@EnableCaching
public class RestSpringbootDemoApplication {

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

    /**
     * 使用 websockt注解的时候,使用@EnableScheduling注解
     * 启动的时候一直报错,增加这个bean 则报错解决。
     * 报错信息:  Unexpected use of scheduler.
     *https://stackoverflow.com/questions/49343692/websocketconfigurer-and-scheduled-are-not-work-well-in-an-application
     *
     * @return
     */
    @Bean
    public TaskScheduler taskScheduler(){
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.initialize();
        return taskScheduler;
    }

}

在需要缓存的地方加入@Cacheable注解,比如在getUser()方法上加入@Cacheable(“userInf”),这个方法就开启了缓存策略,当缓存有这个数据的时候,会直接返回数据,不会等待去查询数据库。

/**
     *
     * 如果设置sync=true,
     *  如果缓存中没有数据,多个线程同时访问这个方法,则只有一个方法会执行到方法,其它方法需要等待
     *  如果缓存中已经有数据,则多个线程可以同时从缓存中获取数据
     *
     * */
@Cacheable(value = "userInf",sync = false)
    @RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = "application/json")
    public UserInf getUser(@PathVariable("id") int id) {
        UserInf user = (UserInf) userInfService.getUserInf(id);
        log.info("查询用户 :" + user);
        return user;
    }

springboot集成RabbitMQ

详见《RabbitMQ消息队列》中 springboot集成RabbitMQ

springboot整合MongoDB

详见《MongoDB》中 springboot集成MongoDB

pom.xml

<!--  Spring Boot MongoDB -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
    <version>2.0.2.RELEASE</version>
</dependency>

application.yml

data:
    mongodb:
      host: 127.0.0.1
      port: 27017
      username: root
      password: root
      authentication-database: admin
      database: test
      #uri方式会报错

model类

package com.example.mongodb.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import java.util.Date;
import java.util.List;

@Document(collection = "article_info")
public class Article {

    @Id
    private String id;

    @Field("title")
    private String title;

    @Field("url")
    private String url;

    @Field("author")
    private String author;

    @Field("tags")
    private List<String> tags;

    @Field("visit_count")
    private Long visitCount;

    @Field("add_time")
    private Date addTime;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public List<String> getTags() {
        return tags;
    }

    public void setTags(List<String> tags) {
        this.tags = tags;
    }

    public Long getVisitCount() {
        return visitCount;
    }

    public void setVisitCount(Long visitCount) {
        this.visitCount = visitCount;
    }

    public Date getAddTime() {
        return addTime;
    }

    public void setAddTime(Date addTime) {
        this.addTime = addTime;
    }

    @Override
    public String toString() {
        return "article{" +
                "id='" + id + '\'' +
                ", title='" + title + '\'' +
                ", url='" + url + '\'' +
                ", author='" + author + '\'' +
                ", tags=" + tags +
                ", visitCount=" + visitCount +
                ", addTime=" + addTime +
                '}';
    }
}

继承MongoRepository接口

package com.example.mongodb.repository;

import com.example.mongodb.model.Article;
import org.springframework.data.mongodb.repository.MongoRepository;

/**
 * @author yanzt
 * @date 2018/6/27 14:30
 * @description
 */
public interface ArticleRepository extends MongoRepository<Article,String> {
}

测试

package com.example.mongodb.controller;

import com.example.annotation.IgnoreSecurity;
import com.example.mongodb.model.Article;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * MongoDB数据库的基本操作
 * */
@RestController
@RequestMapping("/testMongoDb")
public class testMongoDbController {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 初始化文章信息
     * */
    @RequestMapping("/init")
    @IgnoreSecurity
    public void initArticle(){
        Article article = new Article();

        article.setTitle("MongoTemplate的基本使用");
        article.setAuthor("yinjihuan");
        article.setUrl("http://cxytiandi.com/blog/detail/1");
        article.setTags(Arrays.asList("java", "mongodb", "spring"));
        article.setVisitCount(0L);
        article.setAddTime(new Date());

        mongoTemplate.save(article);


        //批量添加
        List<Article> articles = new ArrayList<>();
        for(int i = 0 ;i < 10 ;i++){
            article = new Article();

            article.setTitle("MongoTemplate的基本使用");
            article.setAuthor("yinjihuan");
            article.setUrl("http://cxytiandi.com/blog/detail/" + i);
            article.setTags(Arrays.asList("java", "mongodb", "spring"));
            article.setVisitCount(0L);
            article.setAddTime(new Date());

            articles.add(article);
        }
        mongoTemplate.insert(articles,Article.class);
    }

    @RequestMapping("/delete")
    @IgnoreSecurity
    public void delete(){
        //查询
        Query query = Query.query(Criteria.where("author").is("yinjihuan"));
        mongoTemplate.findAndRemove(query,Article.class);//删除第一条

//        mongoTemplate.remove(query,Article.class);//删除查询到的数据
//        mongoTemplate.findAllAndRemove(query,Article.class);//同上

//        mongoTemplate.getDb().drop();//删除所有

//        mongoTemplate.dropCollection(Article.class);//删除所有
//        mongoTemplate.dropCollection("article_info");//删除所有
    }

    @RequestMapping("/update")
    @IgnoreSecurity
    public void update(){
        //查询
        Query query = Query.query(Criteria.where("author").is("yinjihuan"));
        Update update = Update.update("title","MongoTemplate").set("visitCount",10);
        mongoTemplate.updateFirst(query,update,Article.class);//更新第一条
        mongoTemplate.updateMulti(query,update,Article.class);

        //query不存在则新增一条记录
        query = Query.query(Criteria.where("author").is("jason"));
        update = Update.update("title", "MongoTemplate").set("visitCount", 10);
        mongoTemplate.upsert(query,update,Article.class);

        //key不存在则新增
        /*update = Update.update("title", "MongoTemplate").set("money", 100);
        mongoTemplate.updateMulti(query, update, Article.class);*/

        //原有基础上做加法
        /*update = Update.update("title", "MongoTemplate").inc("money", 100);
        mongoTemplate.updateMulti(query, update, Article.class);*/

        //修改key
        /*update = Update.update("title", "MongoTemplate").rename("visitCount", "vc");
        mongoTemplate.updateMulti(query, update, Article.class);*/

        //update的pull方法用于删除tags数组中的java
        query = Query.query(Criteria.where("author").is("yinjihuan"));
        update = Update.update("title", "MongoTemplate").pull("tags", "java");
        mongoTemplate.updateMulti(query, update, Article.class);
    }

    /**查询操作*/
    @RequestMapping("/select")
    @IgnoreSecurity
    public void select(){
        Query query = Query.query(Criteria.where("author").is("yinjihuan"));
        List<Article> articles = mongoTemplate.find(query,Article.class);//查询符合条件的所有记录
        System.out.println("查询符合条件的所有记录:"+articles.toString());

        Article article = mongoTemplate.findOne(query,Article.class);//查询符合条件的第一条记录
        System.out.println("查询符合条件的第一条记录:"+article.toString());

        articles = mongoTemplate.findAll(Article.class);//查询集合中所有记录
        System.out.println("查询集合中所有记录:"+articles.toString());

        try {
            article = mongoTemplate.findById(new ObjectId("5afd4e0120180c05f8f0b93d"),Article.class);
            System.out.println("根据ID查询:"+article.toString());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        //in查询
        List<String> authors = Arrays.asList("yinjihuan", "jason");
        query = Query.query(Criteria.where("author").in(authors));
        articles = mongoTemplate.find(query, Article.class);
        System.out.println("in查询:"+articles.toString());

        //ne(!=)查询
        query = Query.query(Criteria.where("author").ne("jason"));
        articles = mongoTemplate.find(query, Article.class);
        System.out.println("ne(!=)查询:"+articles.toString());

        //lt(<)查询访问量小于10的文章
        query = Query.query(Criteria.where("visitCount").lt(10));
        articles = mongoTemplate.find(query, Article.class);
        System.out.println("lt(<)查询:"+articles.toString());

        //范围查询,大于5小于10
        query = Query.query(Criteria.where("visitCount").gt(5).lt(10));
        articles = mongoTemplate.find(query, Article.class);
        System.out.println("范围查询:"+articles.toString());

        //模糊查询,author中包含a的数据
        query = Query.query(Criteria.where("author").regex("a"));
        articles = mongoTemplate.find(query, Article.class);
        System.out.println("模糊查询:"+articles.toString());

        //数组查询,查询tags里数量为3的数据
        query = Query.query(Criteria.where("tags").size(3));
        articles = mongoTemplate.find(query, Article.class);
        System.out.println("数组查询:"+articles.toString());

        //or查询,查询author=jason的或者visitCount=0的数据
        query = Query.query(Criteria.where("").orOperator( Criteria.where("author").is("jason"), Criteria.where("visitCount").is(0)));
        articles = mongoTemplate.find(query, Article.class);
        System.out.println("or查询:"+articles.toString());

    }
}

springboot整合Actuator

https://docs.spring.io/spring-boot/docs/2.0.3.BUILD-SNAPSHOT/reference/htmlsingle/#production-ready

主要是完成微服务的监控,完成监控治理。可以查看微服务间的数据处理和调用,当它们之间出现了异常,就可以快速定位到出现问题的地方。

依赖

maven 项目 在 pom.xml 文件中加入 actuator 的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

同时注意只有端点/health和/info端点是暴露的。

Property Default
management.endpoints.jmx.exposure.exclude
management.endpoints.jmx.exposure.include *
management.endpoints.web.exposure.exclude
management.endpoints.web.exposure.include info, health
  1. 您可以按如下方式公开所有端点:management.endpoints.web.exposure.include=*
  2. 您可以通过以下方式显式启用/shutdown端点:management.endpoint.shutdown.enabled=true
  3. 要公开所有(已启用)网络端点除env端点之外:
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    shutdown:
      enabled: true

Endpoints

通过执行器端点,您可以监控应用程序并与之交互。Spring Boot包含许多内置端点,允许您添加自己的端点。例如, health端点提供基本的应用程序健康信息。

可以启用或禁用每个单独的端点。它控制是否创建端点并且其bean存在于应用程序上下文中。要远程访问,还必须通过JMX或HTTP公开端点 。大多数应用程序选择HTTP,其中端点的ID以及前缀/actuator 映射到URL。例如,默认情况下,health端点映射到 /actuator/health

ID 描述 默认情况下启用
auditevents 公开当前应用程序的审核事件信息。
beans 显示应用程序中所有Spring bean的完整列表。
conditions 显示在配置和自动配置类上评估的条件以及它们匹配或不匹配的原因。
configprops 显示所有的整理列表@ConfigurationProperties
env 露出Spring的属性ConfigurableEnvironment
flyway 显示已应用的任何Flyway数据库迁移。
health 显示应用健康信息。
httptrace 显示HTTP跟踪信息(默认情况下,最后100个HTTP请求 - 响应交换)。
info 显示任意应用信息。
loggers 显示和修改应用程序中记录器的配置。
liquibase 显示已应用的任何Liquibase数据库迁移。
metrics 显示当前应用程序的“指标”信息。
mappings 显示所有@RequestMapping路径的整理列表。
scheduledtasks 显示应用程序中的计划任务。
sessions Allows retrieval and deletion of user sessions from a Spring Session-backed session store. Not available when using Spring Session’s support for reactive web applications.
shutdown 允许应用程序正常关闭。 没有
threaddump 执行线程转储。

如果您的应用程序是Web应用程序(Spring MVC,Spring WebFlux或Jersey),则可以使用以下附加端点:

ID 描述 默认情况下启用
heapdump 返回GZip压缩hprof堆转储文件。
jolokia 通过HTTP公开JMX bean(当Jolokia在类路径上时,不适用于WebFlux)。
logfile 返回日志文件的内容(如果已设置logging.filelogging.path属性)。支持使用HTTP Range标头来检索部分日志文件的内容。
prometheus 以可以由Prometheus服务器抓取的格式公开指标。

启用端点

默认情况下,shutdown启用除以外的所有端点。要配置端点的启用,请使用其management.endpoint.<id>.enabled属性。以下示例启用shutdown端点:

management.endpoint.shutdown.enabled = true

如果您希望端点启用是选择加入而不是选择退出,请将该management.endpoints.enabled-by-default属性设置 为false并使用各个端点 enabled属性重新加入。以下示例启用info端点并禁用所有其他端点:

management.endpoints.enabled-by-default = false
 management.endpoint.info.enabled = true

   转载规则


《SpringBoot整合》 yywzt 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Maven基本使用 Maven基本使用
Dependency Scope 中还引入了,它主要管理依赖的部署。目前可以使用5个值: compile,缺省值,适用于所有阶段,会随着项目一起发布。 provided,类似compile,期望JDK、容器或使用
2018-08-06
下一篇 
SpringBoot自定义Start SpringBoot自定义Start
自定义starterstarter: ​ 1、这个场景需要使用到的依赖是什么? ​ 2、如何编写自动配置 @Configuration //指定这个类是一个配置类 @ConditionalOnXXX //在指定条件成立的情况
2018-07-09
  目录