Spring Boot 学习笔记

spring boot学习笔记

官方地址:https://spring.io/projects

1. 从hello world 开始

1.1 maven 依赖


    org.springframework.boot
    spring-boot-starter-parent
    2.0.2.RELEASE


    
    
        org.springframework.boot
        spring-boot-starter-web
    

1.2 编写启动程序

1.2.1 方式一
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
    
/**
 * 

Spring boot "hello word"

* @author vchar fred * @version 1.0 * @date 2018/6/12 15:42 */ @RestController//相当于Controller+ResponseBody;即自动是ajax的请求 @EnableAutoConfiguration//spring 会自动装配相关的配置,这个是必须有的 public class SpringBootHello { @RequestMapping("/") public String home(){ return "hello word!"; } public static void main(String[] args) throws Exception{ SpringApplication.run(SpringBootHello.class, args); } }

运行main方法,在浏览器中访问:http://localhost:8080/ 即可看到hello word
打jar包运行的方法:添加如下maven插件依赖


    
        
        
            org.springframework.boot
            spring-boot-maven-plugin
        
    

运行maven的 package命令;然后运行 java -jar xxx.jar

1.2.2 方式二
@SpringBootApplication//这个注解包含了EnableAutoConfiguration;更多的配置可以查看源码
@RestController//相当于Controller+ResponseBody;即自动是ajax的请求
public class SpringBootHello {
    @RequestMapping("/")
    public String home(){
        return "hello word!";
    }
    public static void main(String[] args) throws Exception{
        SpringApplication.run(SpringBootHello.class, args);
    }
}

@SpringBootApplication 注解源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
public @interface SpringBootApplication {

    //排除自启动项
    Class[] exclude() default {};

    //排除自动启动的beanName
    String[] excludeName() default {};

       //扫描包
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    //扫描类
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class[] scanBasePackageClasses() default {};

}

2. log日志

来源:https://blog.csdn.net/king_is_everyone/article/details/53074006

SpringBoot默认是采用logback进行日志处理、Logback是由log4j创始人设计的又一个开源日志组件
logback当前分成三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块

2.1 logback.xml详情




    
    spring-boot-demo-log

    
    
    

    
    
    
    
        
            %d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg %n
        
    
    
    
    
    
    
    
    
    

    
    
        
            DEBUG
        
        ${LOG_PATH}/${APP_ID}/access.log
        
            ${LOG_PATH}/${APP_ID}/access.log.%d{yyyy-MM-dd}.zip
            10
        
        
            %d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n
        
    
    
        
            DEBUG
            ACCEPT
            DENY
        
        ${LOG_PATH}/${APP_ID}/access_debug.log
        
            ${LOG_PATH}/${APP_ID}/access_debug.log.%d{yyyy-MM-dd}.zip
            10
        
        
            %d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n
        
    
    
        
            INFO
            ACCEPT
            DENY
        
        ${LOG_PATH}/${APP_ID}/access_info.log
        
            ${LOG_PATH}/${APP_ID}/access_info.log.%d{yyyy-MM-dd}.zip
            10
        
        
            %d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n
        
    
    
        
            WARN
            ACCEPT
            DENY
        
        ${LOG_PATH}/${APP_ID}/access_warn.log
        
            ${LOG_PATH}/${APP_ID}/access_warn.log.%d{yyyy-MM-dd}.zip
            10
        
        
            %d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n
        
    
    
        
            ERROR
            ACCEPT
            DENY
        
        ${LOG_PATH}/${APP_ID}/access_error.log
        
            ${LOG_PATH}/${APP_ID}/access_error.log.%d{yyyy-MM-dd}.zip
            10
        
        
            %d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n
        
    

    
    
        
        0
        
        64
        
    
    
        
        0
        
        64
        
    
    
        
        0
        
        64
        
    
    
        
        0
        
        64
        
    
    
        
        0
        
        64
        
    
    
        
        0
        
        64
        
    

    
    
        
        
        
        
        
        
        
    
    
    
    

3.统一异常处理

来源:https://blog.csdn.net/king_is_everyone/article/details/53080851

3.1 spring boot 自带的统一异常处理,重新配置异常地址和页面

SpringBoot在页面发生异常的时候会自动把请求转到/error; SpringBoot内置了一个BasicErrorController对异常进行统一的处理,
这个错误的地址是可以重新配置的。

resources目录下创建一个application.yaml配置文件;写入如下配置

server:
    #访问端口号
    port:  8082
    error:
        #设置错误路径
        path:  /test/error

开始编写测试程序

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
 * 

测试异常

* * @author vchar fred * @version 1.0 * @date 2018/6/13 11:17 */ @SpringBootApplication @Controller public class TestExceptionController { @RequestMapping(value = "/ajax") @ResponseBody public String ajaxRequestExceptionTest(int nu){ //传入参数为0时抛出异常 int num = 1/nu; return "this ok "+ num; } @RequestMapping(value = "/htmls") public String htmlRequestExceptionTest(int nu){ //传入参数为0时抛出异常 int num = 1/nu; return "index"; } //启动 public static void main(String[] args) throws Exception{ SpringApplication.run(TestExceptionController.class); } }

resources目录下创建templates目录(这个是默认放置模版文件的目录),并分别创建error.ftl和index.ftl 文件。error.ftl文件用于替换spring boot原有的错误页面,index.ftl用于测试页面

由于用到了freemarker,需要添加maven依赖


    org.springframework.boot
    spring-boot-starter-freemarker

启动后开始测试

#测试是否可正常访问
http://127.0.0.1:8082/htmls?nu=1    
#测试异常页面
http://127.0.0.1:8082/htmls?nu=0     
#ajax请求返回的错误提示
{
    "timestamp": "2018-06-13T03:55:51.587+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "/ by zero",
    "path": "/ajax"
}

3.2 通用Exception处理

通过使用@ControllerAdvice来进行统一异常处理,@ExceptionHandler(value = Exception.class)来指定捕获的异常
下面针对两种异常进行了特殊处理分别返回页面和json数据,使用这种方式有个局限,无法根据不同的头部返回不同的数据格式,而且无法针对404、403等多种状态进行处理

//异常处理类
@ControllerAdvice
public class GlobalExceptionHandler {

    public static final String DEFAULT_ERROR_VIEW = "error";
    @ExceptionHandler(value = CustomException.class)
    @ResponseBody
    public ResponseEntity defaultErrorHandler(HttpServletRequest req, CustomException e) throws Exception {
        return ResponseEntity.ok("ok");
    }
    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }
}
//自定义异常
public class CustomException extends Exception {
    CustomException(String errorMsg){
        super(errorMsg);
    }
}

//测试路由
@RequestMapping(value = "/coustom")
public String coustomExceptionTest() throws CustomException {
    customExce();
    return "index";
}

private void customExce() throws CustomException {
    throw new CustomException("自定义异常");
}

3.3 自定义BasicErrorController 错误处理

在初始介绍哪里提到了BasicErrorController,这个是SpringBoot的默认错误处理,也是一种全局处理方式。咱们可以模仿这种处理方式自定义自己的全局错误处理
下面定义了一个自己的BasicErrorController,可以根据自己的需求自定义对应的错误处理。

@ResponseStatus 注解的异常类会被ResponseStatusExceptionResolver 解析

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * 

自定义异常controller

* * @author vchar fred * @version 1.0 * @date 2018/6/13 14:25 */ @Order(Ordered.HIGHEST_PRECEDENCE) @ControllerAdvice @Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController { public BasicErrorController(ErrorAttributes errorAttributes) { super(errorAttributes); } private static final Logger log = LoggerFactory.getLogger(BasicErrorController.class); @Value("${server.error.path:${error.path:/error}}") private static String errorPath = "/error"; /** * 500 错误 * @param request * @param response * @param ex * @return * @throws Exception */ @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(Exception.class) public ModelAndView serverError(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { return handleViewError(ex.getMessage(), "500", "error/500"); } /** * 404错误 * @param request * @param response * @param ex * @return * @throws Exception */ @ResponseStatus(code = HttpStatus.NOT_FOUND) @ExceptionHandler(NoHandlerFoundException.class) public ResponseEntity> notFound(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { Map map = new HashMap<>(); map.put("status", 404); map.put("msg", "the resource is not found"); map.put("exception", ex.getMessage()); return ResponseEntity.ok(map); } /** * 参数不完整错误. * @param req * @param rsp * @param ex * @return * @throws Exception */ @ResponseStatus(code = HttpStatus.BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException.class) public ModelAndView methodArgumentNotValidException(HttpServletRequest req, HttpServletResponse rsp, MethodArgumentNotValidException ex) throws Exception { return handleViewError(ex.getMessage(), "404", "error/404"); } private ModelAndView handleViewError(String errorStack, String errorMessage, String viewName) { ModelAndView mav = new ModelAndView(); mav.addObject("exception", errorStack); mav.addObject("msg", errorMessage); mav.addObject("timestamp", new Date()); mav.setViewName(viewName); return mav; } @Override public String getErrorPath() { return errorPath; } }

定义文件结构如下:

Spring Boot 学习笔记_第1张图片
文件结构

4. spring boot中使用redis

application.yaml配置文件中添加redis如下配置

spring:
    rdeis:
        host: 127.0.0.1
        port: 6379
        timeout: 3000
        pool:
            # 连接池最大连接数(使用负值表示没有限制)
            max-total: 8
            # 连接池最大阻塞等待时间(使用负值表示没有限制)
            max-wait-millis: -1
            # 连接池中的最大空闲连接
            max-idle: 8
            # 连接池中的最小空闲连接
            min-idle: 0

加入需要的依赖包


    org.springframework.boot
    spring-boot-starter-data-redis

注:网上其他有很多教程说引入的是下面这个包


    org.springframework.boot
    spring-boot-starter-redis

但是我在引入时发现当前这个版本中这个包已经无法下载了。

4.1 redis 配置方式1,使用默认的

因为上面依赖了spring-boot-starter-data-redis,可以使用默认的 org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 类加载properties文件的配置。它的源码如下:

@Configuration
protected static class RedisConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate redisTemplate(
            RedisConnectionFactory redisConnectionFactory)
                    throws UnknownHostException {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
                    throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

使用方法

@RestController
public class StartServe {
    @Autowired
    private StringRedisTemplate template;
    @Autowired
    private RedisTemplate redisTemplate;

    @RequestMapping("/redis")
    public String redisTest(){
        //这两个的是不一样的。因为RedisTemplate 默认会将对象使用JdkSerializationRedisSerializer进行序列化
        
        redisTemplate.opsForValue().set("test", "123456");
        template.opsForValue().set("test", "abcdef");
        return template.opsForValue().get("test")+"---|||---"+redisTemplate.opsForValue().get("test");
    }
}

4.2 redis 配置方式2,自己手动配置

4.2.1 redis的单机版连接池配置

需要加入额外的jar包:


    redis.clients
    jedis
    2.9.0

编写相关代码:

  • 单机版

      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
      import org.springframework.data.redis.core.RedisTemplate;
      import org.springframework.data.redis.core.StringRedisTemplate;
      import org.springframework.data.redis.serializer.RedisSerializer;
      import org.springframework.data.redis.serializer.StringRedisSerializer;
      import redis.clients.jedis.JedisPoolConfig;
      
      /**
       * 

    redis 配置

    * * @author vchar fred * @version 1.0 * @date 2018/6/13 18:05 */ @Configuration public class RedisConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.timeout}") private int timeout; //@Value("${spring.redis.password}") //private String password; @Value("${spring.redis.pool.max-idle}") private int maxIdle; @Value("${spring.redis.pool.min-idle}") private int minIdle; @Value("${spring.redis.pool.max-total}") private int maxTotal; @Value("${spring.redis.pool.max-wait-millis}") private int maxWaitMillis; /** * 连接设置 * @return 返回连接工厂 */ @Bean(name = "jedisConnectionFactory") public JedisConnectionFactory getJedisConnectionFactory(JedisPoolConfig jedisPoolConfig){ JedisConnectionFactory factory = new JedisConnectionFactory(); factory.setHostName(host); factory.setPort(port); //factory.setPassword("");//设置认证密码 //factory.setDatabase();//设置库 factory.setTimeout(timeout); factory.setUsePool(true); factory.setPoolConfig(jedisPoolConfig); return factory; } /** * 连接池设置 * @return 返回连接池配置 */ @Bean(name = "jedisPoolConfig") public JedisPoolConfig getJedisPoolConfig(){ JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(maxTotal); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); jedisPoolConfig.setMinIdle(minIdle); return jedisPoolConfig; } /** * RedisTemplate * @param jedisConnectionFactory JedisConnectionFactory * @return */ @Bean public RedisTemplate redisTemplate(JedisConnectionFactory jedisConnectionFactory){ RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(jedisConnectionFactory); //key序列化方式;但是如果方法上有Long等非String类型的话,会报类型转换错误; RedisSerializer redisSerializer = new StringRedisSerializer();//Long类型不可以会出现异常信息; redisTemplate.setKeySerializer(redisSerializer); redisTemplate.setHashKeySerializer(redisSerializer); //key序列化方式;但是如果方法上有Long等非String类型的话,会报类型转换错误; redisTemplate.setValueSerializer(redisSerializer); redisTemplate.setHashValueSerializer(redisSerializer); redisTemplate.afterPropertiesSet(); //查看 StringRedisTemplate 的初始化源码,你会发现其实它和上面一样做了的key和value的序列化设置 return redisTemplate; } /** * StringRedisTemplate * @param jedisConnectionFactory JedisConnectionFactory * @return StringRedisTemplate */ @Bean public StringRedisTemplate stringRedisTemplate(JedisConnectionFactory jedisConnectionFactory){ StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(); stringRedisTemplate.setConnectionFactory(jedisConnectionFactory); return stringRedisTemplate; } }
  • 集群版

      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import redis.clients.jedis.HostAndPort;
      import redis.clients.jedis.JedisCluster;
      
      import java.util.HashSet;
      import java.util.Set;
      
      /**
       * redis集群配置
       */
      @Configuration //相当于以前的配置文件
      public class RedisConfig {
    
          @Value("${spring.redis.cluster.nodes}")
          private static String clusterNodes;
      
          @Bean("jedisCluster")
          public JedisCluster getJedisCluster(){
      
              String[] cNodes = clusterNodes.split(",");
              Set nodes = new HashSet();
              for (String node: cNodes) {
                  String[] hp = node.split(":");
                  nodes.add(new HostAndPort(hp[0],Integer.parseInt(hp[1])));
              }
              //创建redis的集群对象
              return new JedisCluster(nodes);
          }
      }
    

使用方式同上面方式一的,这次你会发现他们的key是一样(因为序列化的方式是一样的了)。

另附一个redis配置比较完整的配置文件
来源:https://www.cnblogs.com/EasonJim/p/7805665.html

  • 单机版

      # REDIS(RedisProperties)
      # (普通集群,不使用则不用开启)在群集中执行命令时要遵循的最大重定向数目。
      # spring.redis.cluster.max-redirects=
      # (普通集群,不使用则不用开启)以逗号分隔的“主机:端口”对列表进行引导。
      # spring.redis.cluster.nodes=
      # 连接工厂使用的数据库索引。
      spring.redis.database=0
      # 连接URL,将覆盖主机,端口和密码(用户将被忽略),例如:redis://user:[email protected]:6379
      spring.redis.url=
      # Redis服务器主机。
      spring.redis.host=localhost
      # 登录redis服务器的密码。
      spring.redis.password=
      # 启用SSL支持。
      spring.redis.ssl=false
      # 池在给定时间可以分配的最大连接数。使用负值无限制。
      spring.redis.pool.max-total=8
      # 池中“空闲”连接的最大数量。使用负值表示无限数量的空闲连接。
      spring.redis.pool.max-idle=8
      # 连接分配在池被耗尽时抛出异常之前应该阻塞的最长时间量(以毫秒为单位)。使用负值可以无限期地阻止。
      spring.redis.pool.max-wait-millis=-1
      # 目标为保持在池中的最小空闲连接数。这个设置只有在正面的情况下才有效果。
      spring.redis.pool.min-idle=0
      # Redis服务器端口。
      spring.redis.port=6379
      # (哨兵模式,不使用则不用开启)Redis服务器的名称。
      # spring.redis.sentinel.master=
      # (哨兵模式,不使用则不用开启)主机:端口对的逗号分隔列表。 
      # spring.redis.sentinel.nodes=
      # 以毫秒为单位的连接超时。
      spring.redis.timeout=0
    
  • 集群版,将下面这2项打开即可。

      # (普通集群,不使用则不用开启)在群集中执行命令时要遵循的最大重定向数目。
      spring.redis.cluster.max-redirects=
      # (普通集群,不使用则不用开启)以逗号分隔的“主机:端口”对列表进行引导。
      spring.redis.cluster.nodes=127.0.0.1:1001,127.0.0.1:1002
    

注意:一旦开启了集群模式,那么基于单机的配置就会覆盖。

使用到的注解说明

注解 说明
@EnableAutoConfiguration spring会自动的猜测你需要的那些配置,智能的帮助你去扫描配置

你可能感兴趣的:(Spring Boot 学习笔记)