IDEA教程

链接: IntelliJ IDEA 简体中文专题教程.

链接: Java 单体服务开发指南.

创建基础项目

1.创建项目

在这里插入图片描述
在这里插入图片描述
一路next,选择项目存放路径后点击finish完成,等待依赖下载完成
在这里插入图片描述
删除没必要的文件(选中的红色)
在这里插入图片描述

项目结构

在这里插入图片描述

导入web依赖

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

yml配置

删除 application.properties
新建 application.yml
server:
  port: 8080

编写TestController测试项目

package com.zm.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("hello")
    public String helloGet() {
        return "get hello world!";
    }

    @PostMapping("hello")
    public String helloPost() {
        return "post hello world!";
    }

}

在这里插入图片描述
在这里插入图片描述

统一参数回调

package com.zm.demo.common;

import java.io.Serializable;

public class ApiResult<T> implements Serializable {

    private T data;

    private Integer code;

    private String msg;
    
    /**
     * 请求成功回调
     */
    public static ApiResult successMsg() {
        return new ApiResult().setCode(200).setMsg("ok");
    }

    /**
     * 请求成功回调
     * @param Object 对象参数
     */
    public static ApiResult successMsg(Object Object) {
        return new ApiResult().setCode(200).setMsg("ok").setData(Object);
    }

    /**
     * 请求失败回调
     * @param code 状态码
     * @param msg 描述信息
     */
    public static ApiResult errorMsg(Integer code, String msg) {
        return new ApiResult().setCode(code).setMsg(msg);
    }

    /**
     * 请求失败回调
     *  @param msg 描述信息
     */
    public static ApiResult errorMsg(String msg) {
        return new ApiResult().setCode(500).setMsg(msg);
    }

    public T getData() {
        return data;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public ApiResult<T> setData(T data) {
        this.data = data;
        return this;
    }

    public ApiResult<T> setCode(Integer code) {
        this.code = code;
        return this;
    }

    public ApiResult<T> setMsg(String msg) {
        this.msg = msg;
        return this;
    }
    
}

整合Swagger-knife4j接口文档

pom.xml配置依赖

	<dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.49</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.5.22</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>2.0.4</version>
        </dependency>
    </dependencies>
package com.zm.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket docket(Environment environment) {
        return docket("测试demo", "com.zm.demo.controller",environment);
    }

    private Docket docket(String groupName, String basePackage,Environment environment) {
        return new Docket(DocumentationType.SWAGGER_2)
                //enable配置是否启用Swagger,如果是false,在浏览器将无法访问
                .enable(environment.acceptsProfiles(Profiles.of("dev", "test")))
                .groupName(groupName)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage(basePackage))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("title")
                .description("description")
                .license("license")
                .licenseUrl("licenseUrl")
                .termsOfServiceUrl("termsOfServiceUrl")
                .contact(new Contact("name", "url", "email"))
                .version("version")
                .build();
    }

}
package com.zm.demo.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class HttpConverterConfig implements WebMvcConfigurer {

    /**
     * 配置Swagger静态资源访问
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    /**
     * 使用阿里 FastJson 作为JSON MessageConverter
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        List<MediaType> supportedMediaTypes = new ArrayList<>();
        supportedMediaTypes.add(MediaType.APPLICATION_JSON);
        supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
        supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        supportedMediaTypes.add(MediaType.APPLICATION_PDF);
        supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XML);
        supportedMediaTypes.add(MediaType.IMAGE_GIF);
        supportedMediaTypes.add(MediaType.IMAGE_JPEG);
        supportedMediaTypes.add(MediaType.IMAGE_PNG);
        supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
        supportedMediaTypes.add(MediaType.TEXT_HTML);
        supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
        supportedMediaTypes.add(MediaType.TEXT_PLAIN);
        supportedMediaTypes.add(MediaType.TEXT_XML);
        converter.setSupportedMediaTypes(supportedMediaTypes);
        converter.setDefaultCharset(Charset.forName("UTF-8"));
        FastJsonConfig config = new FastJsonConfig();
        JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
        config.setSerializerFeatures(SerializerFeature.WriteDateUseDateFormat);//格式化时间
        converter.setFastJsonConfig(config);
        // 把FastJsonHttpMessageConverter放到Jackson的前面去
        // 这个list是个ArrayList 所以我们要么就放在首位(不建议),
        // 而converters.indexOf() 因为人家是new的 所以肯定是找不到此对象的位置的 所以采用遍历的方式吧
        int jacksonIndex = 0;
        for (int i = 0; i < converters.size(); i++) {
            if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
                jacksonIndex = i;
                break;
            }
        }
        // 添加在前面
        converters.add(jacksonIndex, converter);
    }
}

ApiResult加上Swagger注解

	@ApiModelProperty(value = "响应体")
    private T data;
    
    @ApiModelProperty(value = "状态码")
    private Integer code;
    
    @ApiModelProperty(value = "描述信息")
    private String msg;

多环境yml配置

在这里插入图片描述
application.yml

spring:
  profiles:
    active: dev

application-dev.yml

server:
  port: 8080

application-pro.yml

server:
  port: 8081

启动项目 访问 http://127.0.0.1:8080/doc.html

在这里插入图片描述
在这里插入图片描述

将application.yml中active 改为 pro 启动项目 访问 http://127.0.0.1:8080/doc.html
enable配置是否启用Swagger成功
在这里插入图片描述

全局异常处理

可将项目中需要统一处理的异常进行捕获,统一返回错误信息
package com.zm.demo.common;

/**
 * 全局错误类枚举
 * 错误码为字符串类型,共 5 位,分成两个部分:错误产生来源+四位数字编号
 * 错误码分为一级宏观错误码、二级宏观错误码、三级宏观错误码。
 * 分别是:
 * A0001(用户端错误)
 * B0001(系统执行出错)
 * C0001(调用第三方服务出错)
 *
 */
public enum ErrorCode {

    /**
     *系统异常
     */
    SYSTEM_ERROR("B0001","系统异常"),

    /**
     *用户登录失效
     */
    USER_LOGIN_INVALID("A0001","用户登录失效"),

    ;

    /**
     * 错误码
     */
    private String errorCode;

    /**
     * 错误码描述
     */
    private String errorMsg;

    ErrorCode(String errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
}
package com.zm.demo.common;

/**
 * 自定义业务异常类
 */
public class BusinessRuntimeException extends RuntimeException {

    private static final long serialVersionUID = 7903308178033567233L;

    /**
     * 错误码
     */
    private String errorCode;

    /**
     * 错误码描述
     */
    private String errorMsg;


    public BusinessRuntimeException(String errorMsg) {
        this.errorCode = "500";
        this.errorMsg = errorMsg;
    }

    public BusinessRuntimeException(ErrorCode errorCode) {
        this.errorCode = errorCode.getErrorCode();
        this.errorMsg = errorCode.getErrorMsg();
    }

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
}
package com.zm.demo.config;

import com.zm.demo.common.ApiResult;
import com.zm.demo.common.BusinessRuntimeException;
import com.zm.demo.common.ErrorCode;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局Controller层异常处理类
 * @RestControllerAdvice 和 @ControllerAdvice区别在于@RestControllerAdvice不需要加@ResponseBody
 */
@RestControllerAdvice
public class GlobalExceptionResolver {

    /**
     * 处理所有不可知异常
     * @param e 异常
     * @return json结果
     */
    @ExceptionHandler(Exception.class)
    public ApiResult handleException(Exception e) {
        e.printStackTrace();
        return ApiResult.errorMsg(ErrorCode.SYSTEM_ERROR);
    }

    /**
     * 处理所有业务异常
     * @param e 业务异常
     * @return  json结果
     */
    @ExceptionHandler(BusinessRuntimeException.class)
    public ApiResult handleOpdRuntimeException(BusinessRuntimeException e) {
        return ApiResult.errorMsg(e.getErrorMsg());
    }


}

ApiResult 中增加方法

/**
 * 请求失败回调
 *  @param errorCode 错误枚举
 */
public static ApiResult errorMsg(ErrorCode errorCode) {
    return new ApiResult().setCode(errorCode.getErrorCode()).setMsg(errorCode.getErrorMsg());
}

修改TestController代码,进行测试

@GetMapping("hello")
    public ApiResult helloGet() {
        String msg = "get hello world!";
        if(true){
            throw new BusinessRuntimeException("出现BusinessRuntimeException错误");
        }
        return ApiResult.successMsg(msg);
    }

    @PostMapping("hello")
    public ApiResult helloPost() {
        String msg = "post hello world!";
        if(true){
            throw new BusinessRuntimeException(ErrorCode.SYSTEM_ERROR);
        }
        return ApiResult.errorMsg(msg);
    }

在这里插入图片描述
在这里插入图片描述

参数校验

导入依赖

<!-- hibernate校验依赖包-->
<dependency>
 	<groupId>org.hibernate</groupId>
 	<artifactId>hibernate-validator</artifactId>
	<version>6.1.0.Final</version>
</dependency>
<!-- lombok 需要下载lombok插件 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>
package com.zm.demo.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;

@ApiModel(value = "查询订单测试参数校验")
@Data
public class SelectOrderReq {

     /**
     * required = true 参数为必填
     */
    @ApiModelProperty(value = "订单号",required = true)
    @NotBlank(message = "订单号不能为空")
    private String orderNumber;

    @ApiModelProperty(value = "订单类型")
    @Range(min = 1,max = 3,message = "订单类型错误")
    private Integer orderType;

    @ApiModelProperty(value = "下单时间")
    @Past(message = "查询订单时间需在当前时间之前")
    private Date orderTime;

}

在GlobalExceptionResolver新增参数校验异常处理

/**
 *@RequestParam上校验失败-> 抛出ConstraintViolationException
 */
@ExceptionHandler(value = ConstraintViolationException.class)
public ApiResult error(ConstraintViolationException e) {
    String message = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining());
    return ApiResult.errorMsg(message);
}

/**
 *单独使用@Valid@Validated验证路径中请求实体校验失败后抛出的异常
 */
@ExceptionHandler(BindException.class)
public ApiResult BindExceptionHandler(BindException e) {
    String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
    return ApiResult.errorMsg(message);
}

/**
 *@RequestBody上校验失败后抛出的异常
 */
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResult MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
    String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
    return ApiResult.errorMsg(message);
}
package com.zm.demo.controller;

import com.zm.demo.common.ApiResult;
import com.zm.demo.common.BusinessRuntimeException;
import com.zm.demo.vo.SelectOrderReq;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
@Validated
public class TestController {

    @PostMapping("hello")
    public ApiResult<SelectOrderReq> helloPost(@Valid @RequestBody SelectOrderReq req) {
        System.out.println(req);
        return ApiResult.successMsg(req);
    }

}

使用Swagger进行接口测试 访问 http://127.0.0.1:8080/doc.html

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

更多使用: 统一的全局异常处理和参数校验.

会话认证(拦截器 和 aop 两种实现)

使用aop来实现session认证

链接: Cookie 和Session是什么?为什么要使用它们?.

导入依赖

    <!-- aop依赖 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>
package com.zm.demo.aop;
import lombok.Data;
/**
 * 用户session
 */
@Data
public class UserSession {

    private Integer userId;

    private String username;
}  
package com.zm.demo.aop;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * 统一操作session
 * session默认30分钟未操作则失效
 * session.setMaxInactiveInterval(7200); 
 * 过期参数单位是秒,即在7200秒没有活动后session将失效
 * 设置为-1将永不关闭。 
 */
public class HttpSessionUtil {

    public static final String SESSION = "UserSession";

    /**
     * 保存用户Session
     */
    public static void setUserSession(UserSession userSession) {
        getSession().setAttribute(SESSION,userSession);
    }
    /**
     * 获取用户Session
     */
    public static UserSession getUserSession() {
        return (UserSession)getRequest().getSession().getAttribute(SESSION);
    }

    public static HttpSession getSession() {
        return getRequest().getSession();
    }

    public static HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes.getRequest();
    }

}
package com.zm.demo.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 用户session认证注解
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface SessionAuthentication {
    String description() default "";
}
package com.zm.demo.aop;

import com.zm.demo.common.BusinessRuntimeException;
import com.zm.demo.common.ErrorCode;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

/**
 * 会话认证aop
 * 异常已经统一交由全局处理 这里无需处理异常
 */
@Aspect
@Component
@Order(1)
public class SessionAuthenticationAspect {

    /**
     *注解拦截
     */
    @Pointcut("@annotation(com.zm.demo.aop.SessionAuthentication)")
    public void aspect() {
    }

    /**
     * 前置通知
     */
    @Before("aspect()")
    public void doBefore(JoinPoint joinPoint){
        UserSession user = HttpSessionUtil.getUserSession();
        if (user == null) {
            throw new BusinessRuntimeException(ErrorCode.USER_LOGIN_INVALID);
        }
        System.out.println( user.getUsername()+  "访问接口: " + getControllerMethodDescription2(joinPoint));
    }

    /**
     * 后置通知 
     * 可以随时拓展加入用户日志记录
     */
    @After("aspect()")
    public void doAfter(JoinPoint joinPoint){
    }

    /**
     * 获取注解中对方法的描述信息
     */
    public static String getControllerMethodDescription2(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SessionAuthentication controllerLog = method.getAnnotation(SessionAuthentication.class);
        return controllerLog.description();
    }
}    
package com.zm.demo.controller;

import com.zm.demo.aop.HttpSessionUtil;
import com.zm.demo.aop.SessionAuthentication;
import com.zm.demo.aop.UserSession;
import com.zm.demo.common.ApiResult;
import com.zm.demo.vo.SelectOrderReq;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@Validated
public class TestController {

    @GetMapping("login")
    public ApiResult login() {
        System.out.println("login sessionId: " + HttpSessionUtil.getSession().getId());
        // 验证用户名和密码是否正确(省略)
        // 存入session
        UserSession userSession = new UserSession();
        userSession.setUserId(1);
        userSession.setUsername("小周同学~");
        HttpSessionUtil.setUserSession(userSession);
        return ApiResult.successMsg();
    }

    @GetMapping("hello")
    @SessionAuthentication(description = "hello get request")
    public ApiResult helloGet() {
        System.out.println("hello get sessionId: " + HttpSessionUtil.getSession().getId());
        return ApiResult.successMsg();
    }

    @PostMapping("hello")
    @SessionAuthentication(description = "hello post request")
    public ApiResult<SelectOrderReq> helloPost(@Valid @RequestBody SelectOrderReq req) {
        System.out.println("hello post sessionId: " + HttpSessionUtil.getSession().getId());
        return ApiResult.successMsg(req);
    }

}

未登录的情况下访问接口提示用户登录失效

在这里插入图片描述

登录后访问接口,session一致

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

前后端分离跨域设置

链接: springboot跨域请求解决方案.

方式一:在controller上加入@CrossOrigin注解
@CrossOrigin(origins = "http://xxx.com",
		maxAge = 3600,
		methods = {RequestMethod.GET, RequestMethod.POST})
方式二:基于拦截器(推荐使用)
package com.zm.demo.config;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 跨域拦截器
 */
@Component
public class CorsInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setContentType("textml;charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        //Access-Control-Allow-Headers 这里为支持的请求头,如果有自定义的header字段请自己添加
        response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, userId, token");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("XDomainRequestAllowed", "1");
        System.out.println("CorsInterceptor跨域拦截器");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {


    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

在实现WebMvcConfigurer接口的HttpConverterConfig类中注册拦截器

@Autowired
private CorsInterceptor corsInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
    //将CorsInterceptor拦截器添加进来
    registry.addInterceptor(corsInterceptor).addPathPatterns("/**");
}
方式三:在实现WebMvcConfigurer接口的HttpConverterConfig类中重写addCorsMappings(不推荐使用)
@Override
public void addCorsMappings(CorsRegistry registry) {
    final String ORIGINS[] = new String[] { "GET", "POST", "PUT", "DELETE" };
    registry.addMapping("/**") // 所有的当前站点的请求地址,都支持跨域访问。
            // 当前端配置 withCredentials=true时, 后端配置Access-Control-Allow-Origin不能为*, 必须是相应地址
            // 将设置allowedOrigins替换成allowedOriginPatterns
            .allowedOriginPatterns("*")
            .allowCredentials(true) // 是否支持跨域用户凭证
            .allowedMethods(ORIGINS) // 当前站点支持的跨域请求类型是什么
            .maxAge(3600); // 超时时长设置为1小时。 时间单位是秒。
    System.out.println("addCorsMappings");
}

在这里插入图片描述

jwt(json web token) 和 自定义token

链接: springboot整合JWT简单实现认证登陆.

整合spring security 实现用户认证

链接: 整合spring security 实现用户认证.

整合MyBatis-Plus

链接: springboot整合MyBatis-Plus.

整合数据连接池Druid 和 Log4j2日志

链接: springboot 整合 Druid 及 Log4j2.

自定义starter

链接: springboot自定义starter(十二).

图片上传

链接: springboot上传图片后访问图片的几种方式.

定时任务

异步线程池

邮件发送

Http请求客户端

链接: java实现调用Http请求常用的几种方式.

编程规范

关注公众号 阿里技术 下载最新java开发手册
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐