一、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