springboot

Springboot简介

Spring 作为一个轻量级的容器,在 Java EE 开发中得到了广泛的应用,但是 Spring 的配置繁琐臃肿,在和各种第三方框架进行整合时代码量都非常大,并且整合的代码大多是重复的,为了使开发者能够快速上手 Spring,利用 Spring 框架快速搭建 Java EE 项目, Spring Boot 应运而生。

Spring Boot 带来了全新的自动化配置解决方案,使用 Spring Boot 可以快速创建基于 Spring生产级的独立应用程序 Spring Boot 中对一些常用的第三方库提供了默认的自动化配置方案,使得开发者只需要很少的 Spring 配置就能运行一个完整的 Java EE 应用。 Spring Boot 项目以采用传统的方案打成 war 包,然后部署到 Tomcat 中运行。也可以直接打成可执行 jar 包,这样通过 java -jar命令就可以启动 Spring Boot 项目。总体来说,Spring Boot 主要有如下优势

• 提供一个快速的 Spring 项目搭建渠道。

• 开箱即用 ,很少的 Spring配置就能运行一个 Java EE 项目

• 提供了生产级的服务监控方案。

• 内嵌服务器,可以快速部署。

• 提供了一系列非功能性的通用配置。

• 纯java配置,没有代码生成,也不需要XML配置。

springboot官网:Spring Boot

第一个springboot案例

创建springboot项目

创建Greeting类

创建GreetingController类

完成后的项目结构

启动项目

用postman测试

说明

  1. @RestController注解是@Controller和@ResponseBody两个注解的简化写法。
  2. hello方法的返回值类型可以是自定义类型的原因是springboot内置了jackson,

jackson可以将实体类自动转换成json格式。

  1. 启动类上的注解@SpringBootApplication是一个综合注解相当于同时添加了以下三个注解:

@SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan。

@SpringBootConfiguration注解:表明这是一个配置类,开发者可以在这个类中配置bean。从这个角度讲,这个类所扮演的角色有点类似于Spring中applicationContext.xml文件的角色。

@EnableAutoConfiguration注解:启用自动配置注解,Spring Boot 中的自动化配置非侵入式的,在任意时刻,开发者都可以使用自定义配置代替自动化配置中的某个配置。

@ComponentScan注解:组件扫描注解,完成包扫描,也是spring中的功能。由于@ComponentScan注解默认扫描的类都位于当前类所在包的下面,因此建议在实际项目开发中把项目启动类放根包中。

springboot项目进行到第二步卡住了怎么办?

原因:网卡不给了,或者是链接不到https://start.spring.io.

解决办法:https://start.aliyun.com/   用阿里的服务器

把项目打包成jar包在命令行执行

双击Maven Projects中的package打包成jar包

在项目的target目录中可以找到打包后的jar包

执行命令:java -jar springboot-helloworld-0.0.1-SNAPSHOT.jar

启动后用postman测试

把项目打包成war包发布到tomcat容器中(不推荐)

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#deployment-install

这个位置讲解怎么打包成一个war包

The first step in producing a deployable war file is to provide a SpringBootServletInitializer subclass and override its configure method. Doing so makes use of Spring Framework’s Servlet 3.0 support and lets you configure your application when it is launched by the servlet container. Typically, you should update your application’s main class to extend SpringBootServletInitializer, as shown in the following example:

第一步:把原来的启动类删掉,重新创建一个启动类

The next step is to update your build configuration such that your project produces a war file rather than a jar file. If you use Maven and spring-boot-starter-parent (which configures Maven’s war plugin for you), all you need to do is to modify pom.xmlto change the packaging to war, as follows:

第二步:在pom.xml文件中添加<packaging>war</packaging>

The final step in the process is to ensure that the embedded servlet container does not interfere with the servlet container to which the war file is deployed. To do so, you need to mark the embedded servlet container dependency as being provided.

If you use Maven, the following example marks the servlet container (Tomcat, in this case) as being provided:

第三步:禁用内嵌的tomcat容器,添加依赖即可

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

第四步:打包

打包成功后可以在项目的target目录中找到war包

将war包布署到tomcat中即可,访问地址有所不同,

变为http://localhost:8080/springboot-helloworld-0.0.1-SNAPSHOT/hello,

也就是说要加上webapp中的项目文件夹名

定制banner

Springboot项目在项目启动时会打印一个banner,如图

这个banner是可以定制的,在resources目录下创建一个banner.txt文件,在这个文件中写入的文本将在项目启动时打印出来。如果想将txt文本设置成艺术字体,可以使用下面网站:

http://www.network-science.de/ascii/  

http://patorjk.com/software/taag

也可以关闭banner,在配置文件 application.propertites中添加以下代码

spring.main.banner-mode = off

SpringBoot 项目实现热启动

  1. 引入依赖。在pom.xml 文件 <dependencies></ dependencies> 添加如下内容

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

  1. 引入插件。在pom.xml 文件 <plugins></ plugins> 添加如下内容

<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <!--如果没有fork配置,devtools不起作用,即项目修改后不会重新启动 -->
            <fork>true</fork>
        </configuration>
    </plugin>
</plugins>

3.修改配置。打开settings。File=>settings

4.修改settings

Web容器配置-Tomcat 配置

在Springboot项目中,可以内置Tomcat、Jetty、netty等容器。当开发者添加了spring-boot-starter-web依赖之后,默认使用Tomcat作为web容器。如果需要对Tomcat做进一步的配置,可以在applicaiton.properties中进行配置。比如修改tomcat端口号

不同环境切换-Profile

开发者在项目发布之前,一般需要频繁地在开发环境、测试环境及生产环境之间进行换,这个候大量配置需要频繁更改,比如数据库配置、 redis 配置、 mongodb 配置等。频繁修改带来了巨大的工作量,Spring Boot提供了简洁的解决方案,Spring Boot中约定的不同环境下配置文件名称规则为application-{profile} .properties, profile 占位符表示当前环境的名称,具体配置步骤如下:

  1. 创建配置文件

首先在 resources 目录下创建两个配置文件application-dev.properties和application-prod.properties, 分别表示开发环境中的配置和生产环境中的配置。其中application-dev.properties文件的内容如下:

server.port=8888

application-prod.properties文件的内容如下:

server.port=9999

  1. 配置application.properties

application.properties文件的内容如下:

spring.profiles.active=dev

这个表示使用application-dev.properties配置文件启动项目,若将dev改为prod,则表示使用application-prod.properties配置文件启动项目。

返回JSON数据

JSON是目前主流的前后端数据传输方式,当开发者创建一个springboot项目,添加web依赖后,默认加入jackson-databind 作为JSON处理器,此时不需要添加额外的JSON处理器就能返回一段JSON了。

创建学生类

                                                                                              

控制器代码

postman测试,可以看到返回json数据

静态资源访问

在springmvc中,对于静态资源都需要开发者手动配置静态资源过滤。回想一下,看看如下配置:

springboot中对此提供了自动化配置,可以简化静态资源过滤配置。springboot默认会过滤所有的静态资源,而静态资源的位置一共有5个,分别是

“classpath:/META-INF/resources/”、

“classpath:/resources/”、

“classpath:/static/”、

“classpath:/public/”、

“/”、

开发者可以将静态资源放到这5个位置的任意一个。注意,按照定义的顺序,5个静态资源位置的优先级依次降低。

在resources的static添加一个图片luobo1.jpg,使用浏览器访问:http://localhost:8080/luobo1.jpg

就可以访问到luobo1.jpg。

文件上传

springmvc对文件上传做了简化,在springboot中对此做了更进一步的简化,使文件上传更加方便。

java中的文件上传一共涉及两个组件,一个是CommonsMultipartResolver,另一个是StandardServletMultipartResolver,而StandardServletMultipartResolver是基于Servlet3.0来处理multipart请求的,因此若使用StandardServletMultipartResolver,则不需要添加额外的jar包。springboot提供的文件上传自动化配置类MultipartAutoConfiguration中,默认采用StandardServletMultipartResolver。

因此,在springboot中上传文件可以不添加任何jar包,也可以不添加任何配置。

控制器代码:

upload文件夹的路径 webapp/upload  没有webapp,就新建一个

postman测试

也可以在application.properties中配置上传文件的最大值等

##上传单个文件的最大值
spring.servlet.multipart.max-file-size=100MB
##多个文件上传时文件的总大小
spring.servlet.multipart.max-request-size=200MB

全局异常处理

如果用户上传的文件超过了限制大小,就会抛出异常,此时可以通过@ControllerAdvice结合@ExceptionHandler定义全局异常捕获机制

@ControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public void uploadException(HttpServletResponse response) throws IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write("上传文件大小超出了限制");
        out.flush();
        out.close();
    }
}

如果想让该方法处理所有类型的异常,只需将MaxUploadSizeExceededException改成Exception即可。方法的参数可以有异常实例、HttpServletResponse以及HttpServletRequest、Model等,返回值可以是一段JSON、一个ModelAndView、一个逻辑视图名等。

自定义错误页

springboot配置错误页面很简单,只需要在resources/static目录下创建error目录,然后在error目录中创建错误页面即可。错误页面的命名规则有两种:一种是4xx.html、5xx.html;另一种是直接使用响应码命名文件,例如404.html、405.html、500.html。第二种命名方式划分更细,当出错时,不同的错误会展示不同的错误页面。

请求参数预处理

@ControllerAdvice结合@InitBinder能实现请求参数预处理,即将表单中的数据绑定到实体类上时进行一些额外处理。

有两个实体类Student和Teacher

在Controller上需要接收两个实体类的数据

此时在参数传递时,两个实体类中的name属性会混淆,@ControllerAdvice结合@InitBinder可以顺利解决该问题。配置@ControllerAdvice

package com.fengqing.springbootdemo.config;

import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;

@ControllerAdvice
public class GlobalConfig {

    @InitBinder("student")
    public void init(WebDataBinder binder){
        binder.setFieldDefaultPrefix("student.");
    }

    @InitBinder("teacher")
    public void init2(WebDataBinder binder){
        binder.setFieldDefaultPrefix("teacher.");
    }
}

postman发送请求

配置拦截器

springboot使用拦截器非常简单。

第一步:创建拦截器实现HandlerInterceptor接口。

拦截器中的方法将按照preHandle->Controller->postHandle->afterCompletion的顺序执行。注意,只有preHandle方法返回true时后面的方法才会执行。当拦截器链内存在多个拦截器时,所有拦截器返回成功时postHandle方法才会执行。只有preHandle方法返回true时after方法才会被调用。

第二步:配置拦截器。定义配置类进行拦截器配置。

第三步:测试,通过postman请求后台接口观察控制台输出结果

启动项目任务

有些特殊任务需要在项目启动时完成,例如配置文件加载、数据库初始化等操作。我们可以使用ApplicationRunner来完成。创建一个实现类实现ApplicationRunner接口。在项目启动时会自动执行run方法,代码如下:

除去某个自动配置

springboot中提供了大量的自动配置类,例如ErrorMvcAutoConfiguration、ThymeleafConfiguration、MultipartAutoConfiguration等,这些自动配置可以减少相关操作的配置,达到开箱即用的效果。在springboot的入口类上有一个@SpringBootApplication注解。该注解是一个组合注解,由@SpringBootConfiguration、

@EnableAutoConfiguration以及@ComponentScan组成,其中@EnableAutoConfiguration注解开启自动配置,相关的自动配置类就会被使用。如果开发者不想使用某个自动配置,可以使用@EnableAutoConfiguration

注解的exclude属性去除自动配置类。新版本的springboot,exclude属性要加到@SpringBootApplication注解上,比如想关闭ErrorMvcAutoConfiguration自动配置,就可以如下操作:

这时如果在resources/static/error目录下创建4xx.html、5xx.html,访问出错时就不会自动跳转了。

也可以在application.properties中配置实现。

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration

@GetMapping和@PostMapping讲解

@GetMapping注解确保http请求方式为get方式,

等同于@RequestMapping(value = "/hello", method = RequestMethod.GET)

@PostMapping注解确保http请求方式为post方式,

等同于@RequestMapping(value = "/hello", method = RequestMethod.POST)

springboot之@Value用法

springboot之@ConfigurationProperties用法

指定额外配置文件的位置

在启动类上加一个注解

@PropertySource(“classpath:/config/config.properties”)

springboot添加事务管理

springboot整合swagger2

在前后端分离开发中,为了减少与其他团体的沟通成本,一般会构建一份RESTful API文档来描述所有的接口信息,但是这中做法由很大的弊端比如接口众多,编写API文档工作量巨大;维护不方便,一旦接口发生变化,就要修改文档等。

Swagger2是一个开源软件框架,可以帮助开发人员设计、构建、记录和使用RESTful web服务,它将代码和文档融为一体,可以完美解决上述问题,使开发人员将大部分精力集中到业务中,而不是繁杂琐碎的文档中。(注意版本对应,springboot的版本不能太高,太高会包空指针异常,2.3.0.RELEASE版本就可以)

在springboot项目中添加swagger2依赖

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

创建swagger2的配置类

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    Docket docket(){
        ApiInfo apiInfo = new ApiInfoBuilder()
                .description("项目接口测试文档") //描述
                .contact(new Contact("风情", "http://www.baidu.com", "626499027@.com"))//联系人信息
                .version("v1.0")//版本
                .title("API测试文档")//标题
                .license("Apache2.0") //许可证
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0")
                .build();
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.fengqing.springbootdemo.controller"))//controller的位置
                .build()
                .apiInfo(apiInfo);
    }
}

用户实体类

@ApiModel(value = "用户实体类", description = "用户信息描述类")
public class User {
    @ApiModelProperty(value = "用户id")
    private Integer id;
    @ApiModelProperty(value = "用户名")
    private String username;
    @ApiModelProperty(value = "用户密码")
    private String password;
    /*getter和setter方法*/

接口的控制器controller

@Api(tags = "用户数据接口")
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @ApiOperation(value = "添加用户", notes = "添加一个用户,传入用户名和密码")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "username", value = "用户名", required = true),
            @ApiImplicitParam(paramType = "query", name = "password", value = "密码", required = true)
    })
    @PostMapping("/addUser")
    public String addUser(String username, String password){
        System.out.println(username);
        System.out.println(password);
        return username + "-" + password;
    }

    @ApiOperation(value = "删除用户", notes = "根据id删除用户")
    @ApiImplicitParam(paramType = "query", name = "id", value = "用户id", required = true)
    @GetMapping("/deleteUser")
    public String deleteUser(Integer id){
        return "id值为"+id+"的用户删除成功";
    }

    @ApiOperation(value = "修改用户", notes = "根据id修改用户的信息")
    @PostMapping("/updateUser")
    public String updateUser(User user){
        System.out.println(user.getId());
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        return "用户修改成功";
    }

    @ApiOperation(value = "查询用户", notes = "根据id查询用户")
    @ApiImplicitParam(paramType = "query", name = "id", value = "用户id", required = true)
    @GetMapping("/getUser")
    public User getUser(@RequestParam("id") Integer id){
        User user = new User();
        user.setId(id);
        user.setUsername("curry");
        user.setPassword("123");
        return user;
    }

    @ApiOperation(value = "获取所有用户", notes = "获取数据库中的所有用户")
    @GetMapping("/getAllUsers")
    public List<User> getAllUsers(){
        List<User> userList = this.userService.getAllUsers();
        return userList;
    }

    /**
     * 根据用户名进行分页查询
     */
    @ApiOperation(value = "根据用户名进行分页查询", notes = "根据用户名进行分页查询")
    @ApiIgnore //表示此接口不生成文档
    @GetMapping("/getUsers")
    public DTO<User> getUsers(@RequestParam("username") String username,
                              @RequestParam("currentPage")int currentPage){
        return null;
    }
}

说明:

@API注解用在类上,用来描述整个Controller信息

@ApiOperation注解用在开发方法上,用来描述一个方法的基本信息,value是对方法作用的一个简短描述,notes用来备注该方法的详细作用。

@ApiImplicitParam注解用在方法上,用来描述方法的参数,paramType是指方法参数的类型,可选值有path(参数获取方式@PathVariable)、query(参数获取方式@RequestParam)、header(参数获取方式@RequestHeader)、body以及form; name表示参数的名称,和参数变量对应;value是参数的描述信息;required表示该字段是否必填;defaultValue表示该字段的默认值。注意,这里的required和defaultValue等字段只是文档上的约束描述,并不能真正约束接口,约束接口还需要在@RequestParam中添加相关属性。如果方法有多个参数,可以将多个参数的@ApiImplicitParam注解放到@ApiImplicitParams注解中

通过@ApiModel注解和@ApiModelProperty注解配置User对象的描述信息。

@ApiIgnore注解表示不对某个接口生成文档。

启动项目,在浏览器中输入http://localhost:8080/swagger-ui.html即可看到接口文档。

springboot和mybatis整合

springboot和mybatis整合-CSDN博客

spring不推荐使用@Autowired,属性注入(Field injection is not recommended)

sringboot和redis整合

springboot和redis整合_sring boot redismanager-CSDN博客

第一步:引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
    <version>1.4.5.RELEASE</version>
</dependency>

第二步:在application.properties文件中设置redis的配置信息

##指定使用redis数据库索引(默认为0)
spring.redis.database=0
##指定Redis服务器地址
spring.redis.host=localhost
##指定Redis端口号
spring.redis.port=6379
##指定Redis密码
spring.redis.password=123456

第三步:创建RedisUtil工具类

package com.fengqing.springbootdemo.util;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 添加缓存
     * @param key
     * @param value
     */
    public void set(String key, String value){
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 添加缓存(带超时时间)
     * @param key
     * @param value
     * @param timeout 时间长度
     * @param timeUnit 单位 TimeUnit.MILLISECONDS, TimeUnit.SECONDS, TimeUnit.MINUTES等
     */
    public void setex(String key, String value, long timeout, TimeUnit timeUnit){
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 根据key获取value
     * @param key
     * @return
     */
    public String get(String key){
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 更新缓存(获取旧value)
     * @param key
     * @param value
     * @return
     */
    public String getAndSet(String key, String value){
        return redisTemplate.opsForValue().getAndSet(key, value);
    }

    /**
     * 删除缓存
     * @param key
     */
    public void delete(String key){
        redisTemplate.delete(key);
    }

}

第四步:在需要使用缓存的地方注入RedisUtil工具类

redis实现token机制

springboot+redis实现token机制_springboot redis保存token-CSDN博客

SpringBoot加了拦截器后出现的跨域问题解析

1.背景

起初解决Springboot跨域问题的方法是直接在Controller上添加@CrossOrigin注解,实现了前后端分离中的跨域请求。但随着业务代码的编写,做了token会话保持的检验,添加了拦截器后,再次出现了跨域问题。很纳闷,讲理说后台已经允许了跨域请求,之前的测试也证明了这一点,那为什么又突然出现了跨域拦截问题呢?

2.分析及解决方案

在登录拦截器中作了校验,对于需要登录后才能访问的接口,如果请求头中没有携带token,则是非法请求,直接返回404码。然后由于一直有对拦截到的请求中的请求头中的token做打印,所以出现问题的时候,控制台打印的token值为null,打开浏览器的开发者工具查看请求头也发现没有携带成功。而在没有添加拦截器之前上述问题都是不存在的,都是正常的,所以都不用考虑是前端问题,问题肯定出在后端,准确的说是拦截器。

那么为什么浏览器不能成功发送token呢?根据线索在更详细的查看了CROS的介绍后发现,原来CROS复杂请求时会首先发送一个OPTIONS请求做嗅探,来测试服务器是否支持本次请求,请求成功后才会发送真实的请求;而OPTIONS请求不会携带任何数据,导致这个请求不符合我们拦截器的校验规则被拦截了,直接返回了状态码,响应头中也没携带解决跨域需要的头部信息,进而出现了跨域问题。所以在浏览器调试工具中会发现该次请求没有携带token,后端控制台打印token也为null。

但是,就算这样,为什么会发生在添加跨域相关头部信息前就提前结束请求的这种情况呢?难道自定义的拦截器优先于@CrossOrigin注解执行?

通过查阅资料,解析@CrossOrigin注解的源码得知,如果Controller在类上标了@CrossOrigin或在方法上标了@CrossOrigin注解,则Spring 在记录mapper映射时会记录对应跨域请求映射,将结果返回到AbstractHandlerMethodMapping,当一个跨域请求过来时,Spring在获取handler时会判断这个请求是否是一个跨域请求,如果是,则会返回一个可以处理跨域的handler。总结起来@CrossOrigin的原理相当于和Handler进行强绑定。

于是现在的问题又到了:Handler和拦截器的执行顺序?

DispatchServlet.doDispatch()方法是SpringMVC的核心入口方法,经过分析发现所有的拦截器的preHandle()方法的执行都在实际handler的方法(比如某个API对应的业务方法)之前,其中任意拦截器返回false都会跳过后续所有处理过程。而SpringMVC对预检请求的处理则在PreFlightHandler.handleRequest()中处理,在整个处理链条中出于后置位。由于预检请求中不带数据,因此先被权限拦截器拦截了。

所以每次获取不到token的请求都是OPTIONS请求,那么解决的方法就很明了了:把所有的OPTIONS请求统统放行。

if("OPTIONS".equals(request.getMethod().toUpperCase())) {
    System.out.println("Method:OPTIONS");
    return true;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值