Dubbo SpringBoot集成MyBatis

版权声明: https://blog.csdn.net/typ1805/article/details/84838926

一、Dubbo Spring Boot

       Apache Dubbo(孵化) Spring Boot Project 使用Dubbo作为RPC Framework 轻松创建Spring Boot应用程序。更重要的是,它也提供了

Apache Dubbo(孵化)是一个由阿里巴巴开源的基于Java的高性能RPC框架。与许多RPC系统一样,dubbo基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。在服务器端,服务器实现此接口并运行dubbo服务器来处理客户端调用。在客户端,客户端有一个存根,它提供与服务器相同的方法。

二、 相关集成

1、Dubbo Spring Boot与MyBatis的集成,在这里不做更多的赘述,详情见源码:

https://download.csdn.net/download/typ1805/10830730

开发版本:

  • springboot-2.0.5.RELEASE
  • dubbo-0.2.0
  • mybatis-1.3.0

2、服务跟踪,traceId透传

      在微服务的趋势下,一次调用产生的日志分布在不同的机器上,虽然可以使用ELK的技术,将所有服务的日志灌入es中,但是如何将这写日志“穿起来”是一个关键问题。

   一般的做法是在系统的边界生成一个traceId,向调用链上的后继服务传递traceId,后继服务使用traceId 打印相应日志,并再向后继服务传递traceId。简称“traceId透传”。

自定义拦截器实现日志透传,每次请求时生成唯一的TraceID(使用UUID生成的),方便问题定位:

public class TraceIDUtils {

	private static final ThreadLocal<String> traceid = new ThreadLocal<String>();

	public static String getTraceId() {
		return traceid.get();
	}

	public static void setTraceId(String traceId) {
		traceid.set(traceId);
	}

	public static void clear(){
		traceid.remove();
	}
}
Activate()
public class TraceIDFilter implements Filter {

	private static final String TRACE_ID = "TRACE_ID";

    public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
		Result result = null;
    	try{
			if(inv.getAttachment(TRACE_ID)!=null){
				TraceIDUtils.setTraceId(inv.getAttachment(TRACE_ID));
				String mdcData = String.format("[TraceID:%s]", inv.getAttachment(TRACE_ID));
				MDC.put("mdcData", mdcData);
			}else if(TraceIDUtils.getTraceId()!=null){
				inv.getAttachments().put("TRACE_ID", TraceIDUtils.getTraceId());
			}
			result = invoker.invoke(inv);
		}catch (RpcException e) {
    		e.printStackTrace();
		}finally {
			inv.getAttachments().clear();
			TraceIDUtils.clear();
		}
        return result;
    }
}

这里需要在resource目录下, 添加META-INF/dubbo目录, 继而添加com.alibaba.dubbo.rpc.Filter文件:

编辑(com.alibaba.dubbo.rpc.Filter文件)内容如下:

traceIdFilter=com.example.demo.dubbo.filter.TraceIDFilter

每次请求时使用UUID生成唯一的traceID,这里顺便把跨域请求也做了处理:

@Slf4j
@Component
public class TraceLoggingFilter extends OncePerRequestFilter implements Ordered {

    private int order = Ordered.HIGHEST_PRECEDENCE;

    @Override
    public int getOrder() {
        return this.order;
    }

    /**
     * 方法名:
     * 功能:设置此过滤器的顺序
     * 描述:order: the order to set
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    public void setOrder(int order) {
        this.order = order;
    }

    /**
     * 方法名:doFilterInternal
     * 功能:拦截请求,生成traceID和跨域请求处理
     * 描述:
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
        try {
            // 跨域请求处理
            String reqHead = request.getHeader("Origin");
            response.setContentType("application/json;charset=UTF-8");
            response.setHeader("Access-Control-Allow-Origin", reqHead);
            response.addHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type");
            String method = request.getMethod();
            if (method.equals("OPTIONS")) {
                response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization,token");
                response.setStatus(200);
            }

            // 生成traceID
            if (TraceIDUtils.getTraceId() == null) {
                TraceIDUtils.setTraceId(UUID.randomUUID().toString());
                String mdcData = String.format("[TraceID:%s]", TraceIDUtils.getTraceId());
                MDC.put("mdcData", mdcData);
            }
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MDC.remove("mdcData");
        }
    }

}

使用logback进行日志格式化输出及保存:

<?xml version="1.0" encoding="UTF-8" ?>

<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则  根据当前ROOT 级别,日志输出时,级别高于root默认的级别时  会输出 -->
<!-- 以下每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志-->
<!-- 属性描述:
    scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true
    scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
    debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->

<configuration scan="true" dudug="fasle" scanPeriod="5 minutes">

    <!--
     说明:
        1. 文件的命名和加载顺序有关
           logback.xml早于application.yml加载,logback-spring.xml晚于application.yml加载
           如果logback配置需要使用application.yml中的属性,需要命名为logback-spring.xml
        2. logback使用application.yml中的属性
           使用springProperty才可使用application.yml中的值 可以设置默认值
    -->

    <!-- 读取配置中心的属性 -->
    <!--<springProperty scope="context" name="url" source="spring.datasource.url"/>
    <springProperty scope="context" name="username" source="spring.datasource.username"/>
    <springProperty scope="context" name="password" source="spring.datasource.password"/>-->

    <!-- ConsoleAppender 控制台输出日志 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 对日志进行格式化 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d %X{mdcData} [%thread] %-5level %c.%method %line - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 配置myBatis输出SQL语句 -->
    <logger name="dao" level="debug"/>

    <!-- 操作日志存储到指定数据库-->
    <!--<appender name="MYDB" class="com.hcycom.mc.web.OperationLog.MyDBAppender">
        <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
            <dataSource class="com.alibaba.druid.pool.DruidDataSource">
                <driverClassName>com.mysql.jdbc.Driver</driverClassName>
                <url>${url}</url>
                <username>${username}</username>
                <password>${password}</password>
            </dataSource>
        </connectionSource>
    </appender>-->

    <!-- 定义日志的根目录 -->
    <property name="LOG_HOME" value="F:/logs" />

    <!-- ERROR级别日志 -->
    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender-->
    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,只记录WARN级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责出发滚动 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志输出位置  可相对、和绝对路径 -->
            <fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/error-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 除按日志记录之外,还配置了日志文件不能超过5M,若超过5M,日志文件会以索引0开始,命名日志文件,例如log-error-2013-12-21.0.log -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 追加方式记录日志 -->
        <append>true</append>
        <!-- 日志文件的格式 -->
        <encoder>
            <pattern>%d %X{mdcData} [%thread] %-5level %c.%method %line - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>

    <!-- WARN级别日志 appender -->
    <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,只记录WARN级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 按天回滚 daily -->
            <fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/warn-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <append>true</append>
        <encoder>
            <pattern>%d %X{mdcData} [%thread] %-5level %c.%method %line - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>

    <!-- INFO级别日志 appender -->
    <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/info-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <append>true</append>
        <encoder>
            <pattern>%d %X{mdcData} [%thread] %-5level %c.%method %line - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>

    <!-- DEBUG级别日志 appender -->
    <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/debug-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <append>true</append>
        <encoder>
            <pattern>%d %X{mdcData} [%thread] %-5level %c.%method %line - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>

    <!-- TRACE级别日志 appender -->
    <appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>TRACE</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/trace-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <append>true</append>
        <encoder>
            <pattern>%d %X{mdcData} [%thread] %-5level %c.%method %line - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>


    <!-- root级别   DEBUG -->
    <root level="INFO">
        <!-- 控制台输出 -->
        <appender-ref ref="STDOUT" />
        <!-- 日志文件 -->
        <appender-ref ref="ERROR" />
        <appender-ref ref="WARN" />
        <appender-ref ref="INFO" />
        <appender-ref ref="DEBUG" />
        <appender-ref ref="TRACE" />
        <!-- 入MySQL -->
        <!--<appender-ref ref="DB-CLASSIC-MYSQL-POOL" />-->
        <!-- 启动操作日志存储的Appender-->
        <!--<appender-ref ref="MYDB" />-->
    </root>

</configuration>

注意:日志文件中配置的%X{mdcData}为TraceLoggingFilter.doFilterInternal()中起的名字,这个名字可以随便起,只要保持一致就可以。

在消费者(consumer)注入生产者服务是需要添加拦截器配置,这里filter值为com.alibaba.dubbo.rpc.Filter文件的名字

@Reference(filter = "traceIdFilter")
private UserServcie userServcie;

同样这生产者服务注解也需要配置的,这里的Service注解用的dubbo的而不是spring的:

@Service(filter = "traceIdFilter")
public class UserServiceImpl implements UserServcie {

3、全局异常处理

@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {

    /**
     * 方法名:handleMissingServletRequestParameterException
     * 功能:缺少请求参数
     * 描述:400 - Bad Request
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Map<String,Object> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.error("缺少请求参数", e);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",400);
        map.put("message","缺少请求参数!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:handleHttpMessageNotReadableException
     * 功能:参数解析失败
     * 描述:400 - Bad Request
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Map<String,Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("参数解析失败", e);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",400);
        map.put("message","参数解析失败!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:handleMethodArgumentNotValidException
     * 功能:参数验证失败
     * 描述:400 - Bad Request
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String,Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("参数验证失败", e);
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        String field = error.getField();
        String code = error.getDefaultMessage();
        String message = String.format("%s:%s", field, code);
        log.info("参数验证失败:"+message);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",400);
        map.put("message","参数验证失败!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:handleBindException
     * 功能:参数绑定失败
     * 描述: 400 - Bad Request
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public Map<String,Object> handleBindException(BindException e) {
        log.error("参数绑定失败", e);
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        String field = error.getField();
        String code = error.getDefaultMessage();
        log.info("参数绑定失败:"+String.format("%s:%s", field, code));
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",400);
        map.put("message","参数绑定失败!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:handleServiceException
     * 功能:参数验证失败
     * 描述:400 - Bad Request
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public Map<String,Object> handleServiceException(ConstraintViolationException e) {
        log.error("参数验证失败", e);
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        ConstraintViolation<?> violation = violations.iterator().next();
        log.error("参数验证失败"+violation.getMessage());
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",400);
        map.put("message","参数验证失败!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:handleValidationException
     * 功能:参数验证失败
     * 描述:400 - Bad Request
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ValidationException.class)
    public Map<String,Object> handleValidationException(ValidationException e) {
        log.error("参数验证失败", e);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",400);
        map.put("message","参数验证失败!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:noHandlerFoundException
     * 功能:请求的方法找不到
     * 描述:404 - Not Found
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public Map<String,Object> noHandlerFoundException(NoHandlerFoundException e) {
        log.error("Not Found", e);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",404);
        map.put("message","Not Found!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:handleHttpRequestMethodNotSupportedException
     * 功能:不支持当前请求方法
     * 描述:405 - Method Not Allowed
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Map<String,Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error("不支持当前请求方法", e);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",405);
        map.put("message","不支持当前请求方法!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:handleHttpMediaTypeNotSupportedException
     * 功能:不支持当前媒体类型
     * 描述:415 - Unsupported Media Type
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Map<String,Object> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        log.error("不支持当前媒体类型", e);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",415);
        map.put("message","不支持当前媒体类型!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:handleException
     * 功能:操作数据或库出现异常
     * 描述:500-内部服务器错误
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(DataSourceException.class)
    public Map<String,Object> handleException(DataSourceException e) {
        log.error("操作数据库出现异常:", e);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",500);
        map.put("message","服务器异常!");
        map.put("data",null);
        return map;
    }

    /**
     * 方法名:handleCustomException
     * 功能:自己声明异常的情况
     * 描述:
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    /*@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ExceptionUtil.class)
    public Map<String,Object> handleCustomException(ExceptionUtil e) {
        log.error("自定义异常", e);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",e.getResult().getCode());
        map.put("message",e.getResult().getMessage());
        map.put("data",e.getResult().getData());
        return map;
    }*/

    /**
     * 方法名:defaultErrorHandler
     * 功能:获取其它异常。包括500
     * 描述:
     * 创建人:typ
     * 创建时间:2018/11/13 18:28:28
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
   @ExceptionHandler(value = Exception.class)
    public Map<String,Object> defaultErrorHandler(Exception e){
        log.error("Exception", e);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",500);
        map.put("message", "服务器异常!");
        map.put("data",null);
        return map;
    }
}

4、myBatis日志输出SQL配置

自定义Sql执行时间记录拦截器,输出SQL语句及执行所耗的时间:

@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
public class SqlCostInterceptor implements Interceptor {

    /**
     * 方法名:intercept
     * 功能:计算SQL执行时间
     * 描述:
     * 创建人:typ
     * 创建时间:2018/11/13
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            long endTime = System.currentTimeMillis();
            long sqlCost = endTime - startTime;
            log.info("==> 执行SQL耗时:" + sqlCost + "ms");
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

mybatis.xml配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true" />
        <!-- 日志输出SQL语句 -->
        <setting name="logPrefix" value="mapper."/>
    </settings>
    <!-- 配置SQL执行时间拦截器 -->
    <plugins>
        <plugin interceptor="com.example.demo.dubbo.mybatis.SqlCostInterceptor"></plugin>
    </plugins>
</configuration>

只在这里配置是不行的,还要在logback.xml文件中配置如下信息:

<!-- 配置myBatis输出SQL语句 -->
<logger name="mapper" level="debug"/>

name必须和mybatis.xml中的 <setting name="logPrefix" value="mapper."/>value值保持一致。

5、生产者服务互调

如果说A服务需要调用B服务中的方法,这时只需要使用@Reference注入就行,列如:

@Slf4j
@Service(filter = "traceIdFilter")
public class AdminServiceImpl implements AdminService {

    @Autowired
    private AdminMapper adminMapper;

    @Reference
    private UserServcie userServcie;

    @Override
    public Admin getById(Integer id) {
        log.info("getById  start id:{}", id);
        User user = userServcie.getById(id);
        log.info("getById  start user:{}", JSON.toJSON(user));
        if (user != null) {
            Admin admin = adminMapper.getById(id);
            log.info("getById  end admin:{}", JSON.toJSON(admin));
            return admin;
        }
        log.info("getById  start user为空!");
        return null;
    }
}

关于Dubbo Spring Boot的更多内容请看官网介绍:

https://github.com/apache/incubator-dubbo-spring-boot-project/blob/master/README_CN.md

猜你喜欢

转载自blog.csdn.net/typ1805/article/details/84838926