Spring Boot使用AOP实现REST接口简易灵活的安全认证的方法

本文将通过AOP的方式实现一个相对更加简易灵活的API安全认证服务。

我们先看实现,然后介绍和分析AOP基本原理和常用术语。

一、Authorized实现

1、定义注解

package com.power.demo.common;

import java.lang.annotation.*;

/*
 * 安全认证
 * */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Authorized {

  String value() default "";

}

这个注解看上去什么都没有,仅仅是一个占位符,用于标志是否需要安全认证。

2、表现层使用注解

@Authorized
  @RequestMapping(value = "/getinfobyid", method = RequestMethod.POST)
  @ApiOperation("根据商品Id查询商品信息")
  @ApiImplicitParams({
      @ApiImplicitParam(paramType = "header", name = "authtoken", required = true, value = "authtoken", dataType =
          "String"),
  })
  public GetGoodsByGoodsIdResponse getGoodsByGoodsId(@RequestHeader String authtoken, @RequestBody GetGoodsByGoodsIdRequest request) {

    return _goodsApiService.getGoodsByGoodsId(request);

  }

看上去就是在一个方法上加了Authorized注解,其实它也可以作用于类上,也可以类和方法混合使用。

3、请求认证切面

下面的代码是实现灵活的安全认证的关键:

package com.power.demo.controller.tool;

import com.power.demo.common.AppConst;
import com.power.demo.common.Authorized;
import com.power.demo.common.BizResult;
import com.power.demo.service.contract.AuthTokenService;
import com.power.demo.util.PowerLogger;
import com.power.demo.util.SerializeUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;

/**
 * 请求认证切面,验证自定义请求header的authtoken是否合法
 **/
@Aspect
@Component
public class AuthorizedAspect {

  @Autowired
  private AuthTokenService authTokenService;

  @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
  public void requestMapping() {
  }

  @Pointcut("execution(* com.power.demo.controller.*Controller.*(..))")
  public void methodPointCut() {
  }

  /**
   * 某个方法执行前进行请求合法性认证 注入Authorized注解 (先)
   */
  @Before("requestMapping() && methodPointCut()&&@annotation(authorized)")
  public void doBefore(JoinPoint joinPoint, Authorized authorized) throws Exception {

    PowerLogger.info("方法认证开始...");

    Class type = joinPoint.getSignature().getDeclaringType();

    Annotation[] annotations = type.getAnnotationsByType(Authorized.class);

    if (annotations != null && annotations.length > 0) {
      PowerLogger.info("直接类认证");
      return;
    }

    //获取当前http请求
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();

    String token = request.getHeader(AppConst.AUTH_TOKEN);

    BizResult<String> bizResult = authTokenService.powerCheck(token);

    System.out.println(SerializeUtil.Serialize(bizResult));

    if (bizResult.getIsOK() == true) {
      PowerLogger.info("方法认证通过");
    } else {
      throw new Exception(bizResult.getMessage());
    }
  }

  /**
   * 类下面的所有方法执行前进行请求合法性认证 (后)
   */
  @Before("requestMapping() && methodPointCut()")
  public void doBefore(JoinPoint joinPoint) throws Exception {

    PowerLogger.info("类认证开始...");

    Annotation[] annotations = joinPoint.getSignature().getDeclaringType().getAnnotationsByType(Authorized.class);

    if (annotations == null || annotations.length == 0) {
      PowerLogger.info("类不需要认证");
      return;
    }

    //获取当前http请求
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();

    String token = request.getHeader(AppConst.AUTH_TOKEN);

    BizResult<String> bizResult = authTokenService.powerCheck(token);

    System.out.println(SerializeUtil.Serialize(bizResult));

    if (bizResult.getIsOK() == true) {
      PowerLogger.info("类认证通过");
    } else {
      throw new Exception(bizResult.getMessage());
    }
  }

}

需要注意的是,对类和方法上的Authorized处理,定义了重载的处理方法doBefore。AuthTokenService和上文介绍的处理逻辑一样,如果安全认证不通过,则抛出异常。

如果我们在类上或者方法上都加了Authorized注解,不会进行重复安全认证,请放心使用。

4、统一异常处理

上文已经提到过,对所有发生异常的API,都返回统一格式的报文至调用方。主要代码大致如下:

package com.power.demo.controller.exhandling;

import com.power.demo.common.ErrorInfo;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * 全局统一异常处理增强
 **/
@ControllerAdvice
public class GlobalExceptionHandler {

  /**
   * API统一异常处理
   **/
  @ExceptionHandler(value = Exception.class)
  @ResponseBody
  public ErrorInfo<Exception> jsonApiErrorHandler(HttpServletRequest request, Exception e) {
    ErrorInfo<Exception> errorInfo = new ErrorInfo<>();
    try {
      System.out.println("统一异常处理...");
      e.printStackTrace();

      Throwable innerEx = e.getCause();
      while (innerEx != null) {
        //innerEx.printStackTrace();
        if (innerEx.getCause() == null) {
          break;
        }
        innerEx = innerEx.getCause();
      }

      if (innerEx == null) {
        errorInfo.setMessage(e.getMessage());
        errorInfo.setError(e.toString());
      } else {
        errorInfo.setMessage(innerEx.getMessage());
        errorInfo.setError(innerEx.toString());
      }

      errorInfo.setData(e);
      errorInfo.setTimestamp(new Date());
      errorInfo.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());//500错误
      errorInfo.setUrl(request.getRequestURL().toString());
      errorInfo.setPath(request.getServletPath());

    } catch (Exception ex) {
      ex.printStackTrace();

      errorInfo.setMessage(ex.getMessage());
      errorInfo.setError(ex.toString());
    }

    return errorInfo;
  }

}

认证不通过的API调用结果如下:

异常的整个堆栈可以非常非常方便地帮助我们排查到问题。

我们再结合上文来看安全认证的时间先后,根据理论分析和实践发现,过滤器Filter先于拦截器Interceptor先于自定义Authorized方法认证先于Authorized类认证。

到这里,我们发现通过AOP框架AspectJ,一个@Aspect注解外加几个方法几十行业务代码,就可以轻松实现对REST API的拦截处理。

那么为什么会有@Pointcut,既然有@Before,是否有@After?

其实上述简易安全认证功能实现的过程主要利用了Spring的AOP特性。

下面再简单介绍下AOP常见概念(主要参考Spring实战),加深理解。AOP概念较多而且比较乏味,经验丰富的老鸟到此就可以忽略这一段了。

二、AOP

1、概述

AOP(Aspect Oriented Programming),即面向切面编程,可以处理很多事情,常见的功能比如日志记录,性能统计,安全控制,事务处理,异常处理等。

AOP可以认为是一种更高级的“复用”技术,它是OOP(Object Oriented Programming,面向对象编程)的补充和完善。AOP的理念,就是将分散在各个业务逻辑代码中相同的代码通过横向切割的方式抽取到一个独立的模块中。将相同逻辑的重复代码横向抽取出来,使用动态代理技术将这些重复代码织入到目标对象方法中,实现和原来一样的功能。这样一来,我们在写业务逻辑时就只关心业务代码。

OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。

所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。

业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,它们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

2、AOP术语

深刻理解AOP,要掌握的术语可真不少。

Target:目标类,需要被代理的类,如:UserService

Advice:通知,所要增强或增加的功能,定义了切面的“什么”和“何时”,模式有Before、After、After-returning,、After-throwing和Around

Join Point:连接点,应用执行过程中,能够插入切面的所有“点”(时机)

Pointcut:切点,实际运行中,选择插入切面的连接点,即定义了哪些点得到了增强。切点定义了切面的“何处”。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。

Aspect:切面,把横切关注点模块化为特殊的类,这些类称为切面,切面是通知和切点的结合。通知和切点共同定义了切面的全部内容:它是什么,在何时和何处完成其功能

Introduction:引入,允许我们向现有的类添加新方法或属性

Weaving:织入,把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:编译期、类加载期、运行期

下面参考自网上图片,可以比较直观地理解上述这几个AOP术语和流转过程。

3、AOP实现

(1)动态代理

使用动态代理可以为一个或多个接口在运行期动态生成实现对象,生成的对象中实现接口的方法时可以添加增强代码,从而实现AOP:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理类
 */
public class DynamicProxy implements InvocationHandler {

  /**
   * 需要代理的目标类
   */
  private Object target;

  /**
   * 写法固定,aop专用:绑定委托对象并返回一个代理类
   *
   * @param target
   * @return
   */
  public Object bind(Object target) {
    this.target = target;
    return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
  }

  /**
   * 调用 InvocationHandler接口定义方法
   *
   * @param proxy 指被代理的对象。
   * @param method 要调用的方法
   * @param args  方法调用时所需要的参数
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result = null;
    // 切面之前执行
    System.out.println("[动态代理]切面之前执行");

    // 执行业务
    result = method.invoke(target, args);

    // 切面之后执行
    System.out.println("[动态代理]切面之后执行");

    return result;
  }

}

缺点是只能针对接口进行代理,同时由于动态代理是通过反射实现的,有时可能要考虑反射调用的开销,否则很容易引发性能问题。

(2)字节码生成

动态字节码生成技术是指在运行时动态生成指定类的一个子类对象(注意是针对类),并覆盖其中特定方法,覆盖方法时可以添加增强代码,从而实现AOP。

最常用的工具是CGLib:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 使用cglib动态代理
 * <p>
 * JDK中的动态代理使用时,必须有业务接口,而cglib是针对类的
 */
public class CglibProxy implements MethodInterceptor {

  private Object target;

  /**
   * 创建代理对象
   *
   * @param target
   * @return
   */
  public Object getInstance(Object target) {
    this.target = target;
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(this.target.getClass());
    // 回调方法
    enhancer.setCallback(this);
    // 创建代理对象
    return enhancer.create();
  }

  @Override
  public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object result = null;
    System.out.println("[cglib]切面之前执行");

    result = methodProxy.invokeSuper(proxy, args);

    System.out.println("[cglib]切面之后执行");

    return result;
  }

}

(3)定制的类加载器

当需要对类的所有对象都添加增强,动态代理和字节码生成本质上都需要动态构造代理对象,即最终被增强的对象是由AOP框架生成,不是开发者new出来的。

解决的办法就是实现自定义的类加载器,在一个类被加载时对其进行增强。

JBoss就是采用这种方式实现AOP功能。

这种方式目前只是道听途说,本人没有在实际项目中实践过。

(4)代码生成

利用工具在已有代码基础上生成新的代码,其中可以添加任何横切代码来实现AOP。

(5)语言扩展

可以对构造方法和属性的赋值操作进行增强,AspectJ是采用这种方式实现AOP的一个常见的Java语言扩展。

比较:根据日志,上述流程的执行顺序依次为:过滤器、拦截器、AOP方法认证、AOP类认证

附:记录API日志
最后通过记录API日志,记录日志时加入API耗时统计(其实我们在开发.NET应用的过程中通过AOP这种记录日志的方式也已经是标配),加深上述AOP的几个核心概念的理解:

package com.power.demo.controller.tool;

import com.power.demo.apientity.BaseApiRequest;
import com.power.demo.apientity.BaseApiResponse;
import com.power.demo.util.DateTimeUtil;
import com.power.demo.util.SerializeUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

/**
 * 服务日志切面,主要记录接口日志及耗时
 **/
@Aspect
@Component
public class SvcLogAspect {

  @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
  public void requestMapping() {
  }

  @Pointcut("execution(* com.power.demo.controller.*Controller.*(..))")
  public void methodPointCut() {
  }

  @Around("requestMapping() && methodPointCut()")
  public Object around(ProceedingJoinPoint pjd) throws Throwable {

    System.out.println("Spring AOP方式记录服务日志");

    Object response = null;//定义返回信息

    BaseApiRequest baseApiRequest = null;//请求基类

    int index = 0;

    Signature curSignature = pjd.getSignature();

    String className = curSignature.getClass().getName();//类名

    String methodName = curSignature.getName(); //方法名

    Logger logger = LoggerFactory.getLogger(className);//日志

    StopWatch watch = DateTimeUtil.StartNew();//用于统计调用耗时

    // 获取方法参数
    Object[] reqParamArr = pjd.getArgs();
    StringBuffer sb = new StringBuffer();
    //获取请求参数集合并进行遍历拼接
    for (Object reqParam : reqParamArr) {
      if (reqParam == null) {
        index++;
        continue;
      }
      try {
        sb.append(SerializeUtil.Serialize(reqParam));

        //获取继承自BaseApiRequest的请求实体
        if (baseApiRequest == null && reqParam instanceof BaseApiRequest) {
          index++;
          baseApiRequest = (BaseApiRequest) reqParam;
        }

      } catch (Exception e) {
        sb.append(reqParam.toString());
      }
      sb.append(",");
    }

    String strParam = sb.toString();
    if (strParam.length() > 0) {
      strParam = strParam.substring(0, strParam.length() - 1);
    }

    //记录请求
    logger.info(String.format("【%s】类的【%s】方法,请求参数:%s", className, methodName, strParam));

    response = pjd.proceed(); // 执行服务方法

    watch.stop();

    //记录应答
    logger.info(String.format("【%s】类的【%s】方法,应答参数:%s", className, methodName, SerializeUtil.Serialize(response)));

    // 获取执行完的时间
    logger.info(String.format("接口【%s】总耗时(毫秒):%s", methodName, watch.getTotalTimeMillis()));

    //标准请求-应答模型

    if (baseApiRequest == null) {

      return response;
    }

    if ((response != null && response instanceof BaseApiResponse) == false) {

      return response;
    }

    System.out.println("Spring AOP方式记录标准请求-应答模型服务日志");

    Object request = reqParamArr[index];

    BaseApiResponse bizResp = (BaseApiResponse) response;
    //记录日志
    String msg = String.format("请求:%s======应答:%s======总耗时(毫秒):%s", SerializeUtil.Serialize(request),
        SerializeUtil.Serialize(response), watch.getTotalTimeMillis());

    if (bizResp.getIsOK() == true) {
      logger.info(msg);
    } else {
      logger.error(msg);//记录错误日志
    }

    return response;
  }

}

标准的请求-应答模型,我们都会定义请求基类和应答基类,本文示例给到的是BaseApiRequest和BaseApiResponse,搜集日志时,可以对错误日志加以区分特殊处理。

注意上述代码中的@Around环绕通知,参数类型是ProceedingJoinPoint,而前面第一个示例的@Before前置通知,参数类型是JoinPoint。

下面是AspectJ通知和增强的5种模式:

@Before前置通知,在目标方法执行前实施增强,请求参数JoinPoint,用来连接当前连接点的连接细节,一般包括方法名和参数值。在方法执行前进行执行方法体,不能改变方法参数,也不能改变方法执行结果。

@After 后置通知,请求参数JoinPoint,在目标方法执行之后,无论是否发生异常,都进行执行的通知。在后置通知中,不能访问目标方法的执行结果(因为有可能发生异常),不能改变方法执行结果。

@AfterReturning 返回通知,在目标方法执行后实施增强,请求参数JoinPoint,其能访问方法执行结果(因为正常执行)和方法的连接细节,但是不能改变方法执行结果。(注意和后置通知的区别) 

@AfterThrowing 异常通知,在方法抛出异常后实施增强,请求参数JoinPoint,throwing属性代表方法体执行时候抛出的异常,其值一定与方法中Exception的值需要一致。

@Around 环绕通知,请求参数ProceedingJoinPoint,环绕通知类似于动态代理的全过程,ProceedingJoinPoint类型的参数可以决定是否执行目标方法,而且环绕通知必须有返回值,返回值即为目标方法的返回值。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot使用过滤器和拦截器分别实现REST接口简易安全认证示例代码详解

    本文通过一个简易安全认证示例的开发实践,理解过滤器和拦截器的工作原理. 很多文章都将过滤器(Filter).拦截器(Interceptor)和监听器(Listener)这三者和Spring关联起来讲解,并认为过滤器(Filter).拦截器(Interceptor)和监听器(Listener)是Spring提供的应用广泛的组件功能. 但是严格来说,过滤器和监听器属于Servlet范畴的API,和Spring没什么关系. 因为过滤器继承自javax.servlet.Filter接口,监听器继承自ja

  • 详解Spring Boot实战之Rest接口开发及数据库基本操作

    本文介绍了Spring Boot实战之Rest接口开发及数据库基本操作,分享给大家 1.修改pom.xml,添加依赖库,本文使用的是mysql <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <

  • 浅谈Spring Boot 开发REST接口最佳实践

    本文介绍了Spring Boot 开发REST接口最佳实践,分享给大家,具体如下: HTTP动词与SQL命令对应 GET 从服务器获取资源,可一个或者多个,对应SQL命令中的SELECT GET /users 获取服务器上的所有的用户信息 GET /users/ID 获取指定ID的用户信息 POST 在服务器上创建一个新资源,对应SQL命令中的CREATE POST /users 创建一个新的用户 PUT 在服务器上更新一个资源,客户端提供改变后的完整资源,对应SQL命令中的UPDATE PUT

  • Spring Boot使用AOP实现REST接口简易灵活的安全认证的方法

    本文将通过AOP的方式实现一个相对更加简易灵活的API安全认证服务. 我们先看实现,然后介绍和分析AOP基本原理和常用术语. 一.Authorized实现 1.定义注解 package com.power.demo.common; import java.lang.annotation.*; /* * 安全认证 * */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Doc

  • 详解使用Spring Boot的AOP处理自定义注解

    上一篇文章Java 注解介绍讲解了下Java注解的基本使用方式,并且通过自定义注解实现了一个简单的测试工具:本篇文章将介绍如何使用Spring Boot的AOP来简化处理自定义注解,并将通过实现一个简单的方法执行时间统计工具为样例来讲解这些内容. AOP概念 面向侧面的程序设计(aspect-oriented programming,AOP,又译作面向方面的程序设计.观点导向编程.剖面导向程序设计)是计算机科学中的一个术语,指一种程序设计范型.该范型以一种称为侧面(aspect,又译作方面)的语

  • Spring Boot 通过AOP和自定义注解实现权限控制的方法

    本文介绍了Spring Boot 通过AOP和自定义注解实现权限控制,分享给大家,具体如下: 源码:https://github.com/yulc-coding/java-note/tree/master/aop 思路 自定义权限注解 在需要验证的接口上加上注解,并设置具体权限值 数据库权限表中加入对应接口需要的权限 用户登录时,获取当前用户的所有权限列表放入Redis缓存中 定义AOP,将切入点设置为自定义的权限 AOP中获取接口注解的权限值,和Redis中的数据校验用户是否存在该权限,如果R

  • spring boot集成smart-doc自动生成接口文档详解

    目录 前言 功能特性 1 项目中创建 /src/main/resources/smart-doc.json配置文件 2 配置内容如下(指定文档的输出路径) 3 pom.xml下添加配置 4 运行插件 5 找到存放路径浏览器打开 6 测试结果 前言 smart-doc 是一款同时支持 java restful api 和 Apache Dubbo rpc 接口文档生成的工具,smart-doc 颠覆了传统类似 swagger 这种大量采用注解侵入来生成文档的实现方法. smart-doc 完全基于

  • Spring Boot 实例代码之通过接口安全退出

    1.在pom.xml中引入actuator, security依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot&l

  • spring boot+自定义 AOP 实现全局校验的实例代码

    最近公司重构项目,重构为最热的微服务框架 spring boot, 重构的时候遇到几个可以统一处理的问题,也是项目中经常遇到,列如:统一校验参数,统一捕获异常... 仅凭代码 去控制参数的校验,有时候是冗余的,但通过框架支持的 去控制参数的校验,是对于开发者很友好,先看下面的例子 @NotEmpty(message="手机号不能为空") @Size(min=11,max=11,message="手机号码长度不正确") @Pattern(regexp=StringUt

  • Spring boot通过AOP防止API重复请求代码实例

    这篇文章主要介绍了Spring boot通过AOP防止API重复请求代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 实现思路 基于Spring Boot 2.x 自定义注解,用来标记是哪些API是需要监控是否重复请求 通过Spring AOP来切入到Controller层,进行监控 检验重复请求的Key:Token + ServletPath + SHA1RequestParas Token:用户登录时,生成的Token Servlet

  • Spring Boot 使用 Swagger 构建 RestAPI 接口文档

    源码地址:https://github.com/laolunsi/spring-boot-examples 目前SpringBoot常被用于开发Java Web应用,特别是前后端分离项目.为方便前后端开发人员进行沟通,我们在SpringBoot引入了Swagger. Swagger作用于接口,让接口数据可视化,尤其适用于Restful APi 本节分两部分介绍,第一部分是SpringBoot引入Swagger的两种方式,第二部分是详细介绍在Web接口上应用Swagger的注解. 本篇文章使用Sp

  • Spring Boot 结合 aop 实现读写分离

    前言 入职新公司到现在也有一个月了,完成了手头的工作,前几天终于有时间研究下公司旧项目的代码.在研究代码的过程中,发现项目里用到了Spring Aop来实现数据库的读写分离,本着自己爱学习(我自己都不信-)的性格,决定写个实例工程来实现spring aop读写分离的效果. 环境部署 数据库:MySql 库数量:2个,一主一从 关于mysql的主从环境部署,可以参考: https://www.jb51.net/article/184698.htm 开始项目 首先,毫无疑问,先开始搭建一个Sprin

  • 使用Spring Boot实现操作数据库的接口的过程

    一.Spring Boot原理 用户从页面前端,也就是我们所说的 view 层进行查询访问,进入到 controller 层找到对应的接口,接 着 controller 进行对 service 层进行业务功能的调用,service 要进入 dao 层查询数据,dao 层调用 mapper.xml 文件生成 sql 语句到数据库中进行查询 二.实现过程 2.1.准备数据库user表插入四条数据 2.2.model下创建一个User类 与数据库的字段一一对应 @Getter @Setter publ

随机推荐