Commit 26528d96 by qiuweili123

1.0.8.RELEASE

parents 6cf6964d c3cc61fa
......@@ -18,25 +18,27 @@
+-- dubbo-starter
+-- web-starter
```
重要版本说明
说明:
* common-\*:通用依赖
* \*-starter:组件依赖
版本升级须知
当前开发版本 1.0.6-DEV-SNAPSHOT
1、rocketmq-starter 兼容Apollo
2、新增rocketmq 发送/接收,消息监听
正式版本
1.0.5.RELEASE
1.0.4.RELEASE
1、增加logstater模块
2、修正多语言环境下zh的无法取到错误信息
3、增加rocketmq-starter组件
1.0.4.RELEASE
1、update spring boot to 2.1.6
1.0.3.RELEASE
1、增加monitor traceId监控模块
2、增加防灾冗余
3、优化文件结构
4、增加对controller返回null响应增强
1.0.3.RELEASE
1、增加monitor traceId监控模块
......@@ -46,6 +48,6 @@
1.0.2.RELEASE
init code
......@@ -5,7 +5,7 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -44,18 +44,6 @@
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
......
......@@ -4,6 +4,7 @@ import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
......@@ -14,6 +15,7 @@ import javax.annotation.Resource;
@Aspect
@Component
@ConditionalOnProperty(value = "transaction.support.enabled",havingValue = "true")
public class AopTransaction {
@Resource
private PlatformTransactionManager transactionManager;
......
......@@ -5,7 +5,7 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,7 +5,7 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -19,7 +19,6 @@
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
......
......@@ -26,6 +26,8 @@ public class ApolloContextInitializer implements ApplicationContextInitializer<C
if (!StringUtils.isEmpty(appId)) {
System.setProperty("app.id", appId);
System.setProperty("apollo.cacheDir", cacheDir);
System.setProperty("apollo.bootstrap.enabled","true");
System.setProperty("apollo.bootstrap.eagerLoad.enabled","true");
}
}
......
package com.secoo.mall.dubbo.annotation;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface DubboService {
@AliasFor(
annotation = Service.class
)
String value() default "";
}
......@@ -5,7 +5,7 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,12 +5,16 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>logger-starter</artifactId>
<packaging>pom</packaging>
<modules>
<module>secoo-log</module>
<module>secoo-log-starter</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<logback.version>1.2.3</logback.version>
......@@ -18,39 +22,43 @@
<janino.version>3.0.6</janino.version>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- 桥接slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 支持logback condition表达式 -->
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>${janino.version}</version>
</dependency>
<!-- 打印skywalking tid插件-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>6.2.1-beta.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- 桥接slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 支持logback condition表达式 -->
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>${janino.version}</version>
</dependency>
<!-- 打印skywalking tid插件-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>6.2.1-beta.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
\ No newline at end of file
......@@ -3,23 +3,34 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>matrix</artifactId>
<artifactId>logger-starter</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>web-starter</artifactId>
<modelVersion>4.0.0</modelVersion>
<artifactId>secoo-log-starter</artifactId>
<name>${project.artifactId}</name>
<dependencies>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>common-util</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 打印skywalking tid插件-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.secoo.mall.logs.config;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Set;
/**
* @author qianglu
*/
@Configuration
@ConditionalOnClass(ApolloAutoConfiguration.class)
public class MatrixLogListenerConfiguration {
private static final Logger logger = LoggerFactory.getLogger(MatrixLogListenerConfiguration.class);
private static final String LOGGER_TAG = "matrix.loggers.";
@Resource
private LoggingSystem loggingSystem;
@ApolloConfig
private Config config;
@ApolloConfigChangeListener
private void onChange(ConfigChangeEvent changeEvent) {
refreshLoggingLevels();
}
@PostConstruct
private void refreshLoggingLevels() {
Set<String> keyNames = config.getPropertyNames();
for (String key : keyNames) {
if (containsIgnoreCase(key, LOGGER_TAG)) {
String strLevel = config.getProperty(key, "info");
LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level);
logger.info("update class {} logger level to {}", key, strLevel);
}
}
}
private static boolean containsIgnoreCase(String str, String searchStr) {
if (StringUtils.isEmpty(str) || StringUtils.isEmpty(searchStr)) {
return false;
}
int len = searchStr.length();
int max = str.length() - len;
for (int i = 0; i <= max; i++) {
if (str.regionMatches(true, i, searchStr, 0, len)) {
return true;
}
}
return false;
}
}
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.secoo.mall.logs.config.MatrixLogListenerConfiguration
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!-- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/2002/xmlspec/dtd/2.10/xmlspec.dtd"> -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<springProperty scope="context" name="logName" source="spring.application.name" defaultValue="app"/>
<springProperty scope="context" name="logLevel" source="log.level" defaultValue="info"/>
<springProperty scope="context" name="LOG_NAME" source="spring.application.name" defaultValue="app"/>
<springProperty scope="context" name="LOG_LEVEL" source="log.level" defaultValue="info"/>
<springProperty scope="context" name="LOG_PATH" source="log.path" defaultValue="/data/logs"/>
<springProperty scope="context" name="MAX_FILE_SIZE" source="log.max_file_size" defaultValue="128MB"/>
<springProperty scope="context" name="MAX_HISTORY" source="log.max_history" defaultValue="7"/>
<springProperty scope="context" name="PATTERN" source="log.pattern"
defaultValue="-|%d{yyyy-MM-dd HH:mm:ss.SSS}|%-5level|%X{tid}|%thread|%logger{36}.%M:%L-%msg%n"/>
<springProperty scope="context" name="PATTERN" source="log.pattern" defaultValue="-|%d{yyyy-MM-dd HH:mm:ss.SSS}|%-5level|%X{tid}|%thread|%logger{36}.%M:%L-%msg%n"/>
<property name="LOG_DIR" value="${LOG_PATH}/${logName}/%d{yyyyMMdd}"/>
<property name="LOG_DIR" value="${LOG_PATH}/${LOG_NAME}/%d{yyyyMMdd}"/>
<property name="CHARSET" value="UTF-8"/>
<jmxConfigurator/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
......@@ -32,9 +30,9 @@
<onMismatch>DENY</onMismatch>
</filter>
<!-- 定义文件的名称 -->
<file>${LOG_PATH}/${logName}/error.log</file>
<file>${LOG_PATH}/${LOG_NAME}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_DIR}/err_${logName}%i.log</FileNamePattern>
<FileNamePattern>${LOG_DIR}/err_${LOG_NAME}%i.log</FileNamePattern>
<MaxHistory>${MAX_HISTORY}</MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
......@@ -47,9 +45,9 @@
<appender name="APP" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 定义文件的名称 -->
<file>${LOG_PATH}/${logName}/app.log</file>
<file>${LOG_PATH}/${LOG_NAME}/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_DIR}/all_${logName}%i.log</FileNamePattern>
<FileNamePattern>${LOG_DIR}/all_${LOG_NAME}%i.log</FileNamePattern>
<MaxHistory>${MAX_HISTORY}</MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
......@@ -81,22 +79,20 @@
</appender>
<logger name="app" level="${logLevel}">
<logger name="app" level="${LOG_LEVEL}">
<appender-ref ref="ASYN_APP"/>
</logger>
<logger name="error" level="${logLevel}">
<logger name="error" level="${LOG_LEVEL}">
<appender-ref ref="ASYN_ERROR"/>
</logger>
<!-- 日志排除 -->
<logger name="org.logicalcobwebs.proxool" level="error"/>
<!--统一日志输出级别,其他appender中如果有高于此处等级设置的也会被输出 -->
<root level="${logLevel}">
<root level="${LOG_LEVEL}">
<springProfile name="test,dev,local">
<appender-ref ref="ASYN_STDOUT"/>
</springProfile>
<appender-ref ref="ASYN_APP"/>
<appender-ref ref="ASYN_ERROR"/>
</root>
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>logger-starter</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>secoo-log</artifactId>
<name>${project.artifactId}</name>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<!-- 桥接slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<!-- 打印skywalking tid插件-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -3,7 +3,7 @@
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property resource="log.properties"/>
<property name="LOG_PATH" value="/data/logs"/>
<property name="LOG_PATH" value="/data/logs" />
<property name="LOG_DIR" value="${LOG_PATH}/${logName}/%d{yyyyMMdd}"/>
<property name="MAX_FILE_SIZE" value="${logFileSize}"/>
<property name="MAX_HISTORY" value="${logMaxHistory}"/>
......
......@@ -5,7 +5,7 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,7 +5,7 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,7 +5,7 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,7 +5,7 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
package com.secoo.mall.openfeign.config;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
// TODO: 2019/5/8 实现enable package自动加载 实现灰度发布 client请求日志拦截
@EnableFeignClients("com.secoo.mall")
public class MatrixFeignAutoConfiguration {
}
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.secoo.mall.openfeign.config.MatrixFeignAutoConfiguration
\ No newline at end of file
......@@ -6,7 +6,7 @@
<groupId>com.secoo.mall</groupId>
<artifactId>matrix</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
<packaging>pom</packaging>
......@@ -17,13 +17,12 @@
<module>mybatis-starter</module>
<module>mongodb-starter</module>
<module>elasticsearch-starter</module>
<module>dubbo-starter</module>
<module>web-starter</module>
<module>openfeign-starter</module>
<module>rocketmq-starter</module>
<module>monitor-starter</module>
<module>config-starter</module>
<module>logger-starter</module>
<module>protocol-starter</module>
</modules>
<parent>
......@@ -35,131 +34,78 @@
<properties>
<java.version>1.8</java.version>
<dubbo.version>2.7.1</dubbo.version>
<dubbo-starter.version>2.7.1</dubbo-starter.version>
<dubbo.version>2.7.2</dubbo.version>
<matrix.version>1.0.1-DEV-SNAPSHOT</matrix.version>
</properties>
<profiles>
<profile>
<id>dev</id>
<properties>
<ver_type>-DEV-SNAPSHOT</ver_type>
<repo_id>secoo-dev</repo_id>
<repo_name>secoo-dev-repository</repo_name>
<repo_url>nexus.secoo.com:8081/nexus/content/repositories/secoo-hosted-dev/</repo_url>
</properties>
<distributionManagement>
<repository>
<id>${repo_id}</id>
<name>${repo_name}</name>
<url>http://secoo-dev:secoo-dev@${repo_url}</url>
</repository>
</distributionManagement>
</profile>
<profile>
<id>test</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<ver_type>-SNAPSHOT</ver_type>
<repo_id>secoo-test</repo_id>
<repo_name>secoo-test-repository</repo_name>
<repo_url>nexus.secoo.com:8081/nexus/content/repositories/secoo-hosted-test/</repo_url>
</properties>
<distributionManagement>
<repository>
<id>${repo_id}</id>
<name>${repo_name}</name>
<url>http://secoo-test:secoo-test@${repo_url}</url>
</repository>
</distributionManagement>
</profile>
<profile>
<id>pro</id>
<properties>
<ver_type>.RELEASE</ver_type>
<repo_id>secoo-pro</repo_id>
<repo_name>secoo-pro-repository</repo_name>
<repo_url>nexus.secoo.com:8081/nexus/content/repositories/secoo-hosted-pro/</repo_url>
</properties>
<distributionManagement>
<repository>
<id>${repo_id}</id>
<name>${repo_name}</name>
<url>http://secoo-pro:secoo-PRO@${repo_url}</url>
</repository>
</distributionManagement>
</profile>
</profiles>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>secoo-log-starter</artifactId>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>secoo-log</artifactId>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>monitor-starter</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>common-core</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>config-starter</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>common-util</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>redis-starter</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>mybatis-starter</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>mongodb-starter</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>elasticsearch-starter</artifactId>
<version>1.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>dubbo-starter</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>web-starter</artifactId>
<version>1.0.5.RELEASE</version>
<artifactId>protocol-starter</artifactId>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>rocketmq-starter</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>openfeign-starter</artifactId>
<version>1.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>logger-starter</artifactId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</dependency>
<dependency>
......@@ -224,11 +170,26 @@
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<!--spring boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Aapche Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<artifactId>dubbo-dependencies-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-starter.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
......@@ -281,11 +242,7 @@
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>swagger-dubbo</artifactId>
<version>2.0.1</version>
</dependency>
<!--spring cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
......@@ -294,8 +251,15 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<!--appollo-->
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
......@@ -324,7 +288,20 @@
</plugin>
</plugins>
</pluginManagement>
</build>
<distributionManagement>
<snapshotRepository>
<id>secoo-dev</id>
<name>secoo-dev-repository</name>
<url>http://secoo-dev:secoo-dev@nexus.secoo.com:8081/nexus/content/repositories/secoo-hosted-dev/</url>
</snapshotRepository>
<repository>
<id>secoo-pro</id>
<name>secoo-pro-repository</name>
<url>http://secoo-pro:secoo-PRO@nexus.secoo.com:8081/nexus/content/repositories/secoo-hosted-pro/</url>
</repository>
</distributionManagement>
</project>
\ No newline at end of file
......@@ -5,16 +5,22 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-starter</artifactId>
<artifactId>protocol-starter</artifactId>
<dependencies>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>common-core</artifactId>
<artifactId>common-util</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--dubbo-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
......@@ -23,10 +29,26 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!--swagger2-->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>swagger-dubbo</artifactId>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
</dependencies>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-core</artifactId>
<version>1.5.22</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.secoo.mall.web.config;
package com.secoo.mall.common.config;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.secoo.mall.common.core.condition.ProdEnvCondition;
import com.secoo.mall.dubbo.swagger.annotations.EnableDubboSwagger;
import com.secoo.mall.web.annotation.ApiController;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
......@@ -15,11 +17,14 @@ import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
;
@Configuration
@EnableSwagger2
@EnableSwaggerBootstrapUI
@Conditional(ProdEnvCondition.class)
public class SwaggerConfig {
@EnableDubboSwagger
@ConditionalOnClass(ProdEnvCondition.class)
public class MatrixSwaggerAutoConfiguration {
@Value("${spring.application.name}")
private String appName;
......@@ -27,16 +32,27 @@ public class SwaggerConfig {
private String appVersion;
@Bean
public Docket createRestApi() {
public Docket createWebApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("SecooMall")
.groupName("web")
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(ApiController.class))
.paths(PathSelectors.any())
.build();
}
public Docket createDubboApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("dubbo")
.select()
.apis(RequestHandlerSelectors.basePackage("com.deepoove.dubbo.provider.springboot"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(appName + " RESTful APIs")
......@@ -44,4 +60,5 @@ public class SwaggerConfig {
.description("更多内容请关注:http://apims.siku.cn")
.build();
}
}
\ No newline at end of file
package com.secoo.mall.web.advice;
package com.secoo.mall.common.handler;
import com.secoo.mall.common.core.errorcode.CommonErrorCode;
import com.secoo.mall.common.core.exception.BusinessException;
......@@ -6,24 +6,14 @@ import com.secoo.mall.common.core.exception.ParameterException;
import com.secoo.mall.common.core.exception.SystemInternalException;
import com.secoo.mall.common.util.log.LoggerUtil;
import com.secoo.mall.common.util.response.ResponseUtil;
import com.secoo.mall.web.annotation.ApiController;
import com.secoo.mall.web.annotation.ApiIgnoreJson;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.annotation.Resource;
@RestControllerAdvice(annotations = ApiController.class)
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
public class ProtocolExceptionHandler {
@Resource
private MessageSource messageSource;
......@@ -46,17 +36,11 @@ public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
return ResponseUtil.getFailResponse(CommonErrorCode.SYSTEM_INTERNAL_EXCEPTION.getCode(), CommonErrorCode.SYSTEM_INTERNAL_EXCEPTION.getMsg());
}
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return AnnotationUtils.findAnnotation(methodParameter.getMethod(), ApiIgnoreJson.class) == null;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
return ResponseUtil.getSuccessResponse(o);
}
private String getMsg(BusinessException e) {
if (messageSource == null) {
return e.getMsg();
}
return messageSource.getMessage(e.getMsg(), e.getArgs(), LocaleContextHolder.getLocale());
}
}
package com.secoo.mall.common.provider;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.swagger.web.InMemorySwaggerResourcesProvider;
import springfox.documentation.swagger.web.SwaggerResource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@Primary
@Component
public class DefaultSwaggerResourcesProvider extends InMemorySwaggerResourcesProvider {
public DefaultSwaggerResourcesProvider(Environment environment, DocumentationCache documentationCache) {
super(environment, documentationCache);
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> swaggerResources = super.get();
swaggerResources.forEach(swaggerResource -> {
String name = swaggerResource.getName();
if (SwaggerConfigEnum.SwaggerConfigMap.containsKey(name)) {
swaggerResource.setLocation(SwaggerConfigEnum.SwaggerConfigMap.get(name).getLocation());
}
}
);
return swaggerResources;
}
private enum SwaggerConfigEnum {
DUBBO("dubbo", "/swagger-dubbo/api-docs");
private String name;
private String location;
private final static Map<String, SwaggerConfigEnum> SwaggerConfigMap = Arrays.stream(SwaggerConfigEnum.values()).collect(Collectors.toMap(SwaggerConfigEnum::getName, Function.identity()));
SwaggerConfigEnum(String name, String location) {
this.name = name;
this.location = location;
}
public String getName() {
return name;
}
public String getLocation() {
return location;
}
}
}
package com.secoo.mall.dubbo.filter;
import com.secoo.mall.common.core.exception.BusinessException;
import com.secoo.mall.common.core.exception.ParameterException;
import com.secoo.mall.common.core.exception.SystemInternalException;
import com.secoo.mall.common.handler.ProtocolExceptionHandler;
import com.secoo.mall.common.util.response.ResponseUtil;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.filter.ExceptionFilter;
import org.apache.dubbo.rpc.service.GenericService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
import java.lang.reflect.Method;
@Activate(
group = {CommonConstants.PROVIDER}, order = 1000
)
public class DefaultExceptionFilter extends ExceptionFilter implements Filter {
private Logger log = LoggerFactory.getLogger(DefaultExceptionFilter.class);
public DefaultExceptionFilter() {
super.listener = new DefaultExceptionFilter.ExceptionListener();
}
static class ExceptionListener extends ProtocolExceptionHandler implements Listener {
private Logger logger = LoggerFactory.getLogger(ExceptionListener.class);
private ExceptionHandlerMethodResolver resolver;
public ExceptionListener() {
resolver = new ExceptionHandlerMethodResolver(this.getClass());
}
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
if (appResponse.hasException() && GenericService.class != invoker.getInterface()) {
Exception exception = getException(appResponse.getException());
Method method = resolver.resolveMethod(exception);
try {
Object realResult = method.invoke(this, exception);
appResponse.setValue(realResult);
} catch (Exception e) {
appResponse.setValue(ResponseUtil.getFailResponse(new SystemInternalException()));
}
appResponse.setException(null);
}
}
@Override
public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) {
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
}
private Exception getException(Throwable throwable) {
Exception exception = null;
String className = throwable.getClass().getSimpleName();
switch (className) {
case "BusinessException":
exception = (BusinessException) throwable;
break;
case "SystemInternalException":
exception = (SystemInternalException) throwable;
break;
case "ParameterException":
exception = (ParameterException) throwable;
break;
default:
exception = (Exception) throwable;
}
return exception;
}
}
}
package com.secoo.mall.dubbo.swagger.annotations;
import org.springframework.context.annotation.ComponentScan;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = {java.lang.annotation.ElementType.TYPE})
@Documented
@ComponentScan(basePackages = {"com.secoo.mall.dubbo.swagger.config",
"com.secoo.mall.dubbo.swagger.controller", "com.secoo.mall.common.provider"})
public @interface EnableDubboSwagger {
}
package com.secoo.mall.dubbo.swagger.config;
import com.secoo.mall.dubbo.swagger.http.ReferenceManager;
import io.swagger.config.SwaggerConfig;
import io.swagger.models.Contact;
import io.swagger.models.Info;
import io.swagger.models.Swagger;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.ApplicationConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.ServletContext;
import java.text.MessageFormat;
@Component
public class DubboPropertyConfig implements SwaggerConfig {
@Value("${swagger.dubbo.application.version:}")
private String version;
@Value("${swagger.dubbo.application.groupId:}")
private String groupId;
@Value("${swagger.dubbo.application.artifactId:}")
private String artifactId;
@Autowired
private ServletContext servletContext;
private static String mavenDependency = "&lt;dependency&gt;<br/>"
+ "&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;{0}&lt;/groupId&gt;<br/>"
+ "&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;{1}&lt;/artifactId&gt;<br/>"
+ "&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;{2}&lt;/version&gt;<br/>"
+ "&lt;/dependency&gt;<br/>";
@Override
public Swagger configure(Swagger swagger) {
ApplicationConfig application = ReferenceManager.getInstance().getApplication();
if (null != application) {
Info info = swagger.getInfo();
if (info == null) {
info = new Info();
swagger.setInfo(info);
}
info.setTitle(application.getName());
version = StringUtils.isNotBlank(version) ? version : application.getVersion();
if (StringUtils.isNotBlank(groupId)
&& StringUtils.isNotBlank(artifactId)
&& StringUtils.isNotBlank(version)) {
info.setDescription(MessageFormat.format(mavenDependency, groupId, artifactId, version));
}
info.setVersion(StringUtils.isNotBlank(version) ? version : "");
Contact contact = new Contact();
info.setContact(contact);
contact.setName(application.getOwner());
}
setBashPath(swagger);
return swagger;
}
private void setBashPath(Swagger swagger) {
if (StringUtils.isEmpty(swagger.getBasePath())) {
swagger.setBasePath(StringUtils.isEmpty(servletContext.getContextPath()) ? "/" : servletContext.getContextPath());
}
}
@Override
public String getFilterClass() {
return null;
}
}
package com.secoo.mall.dubbo.swagger.config;
import com.secoo.mall.dubbo.swagger.http.ReferenceManager;
import io.swagger.config.Scanner;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Set;
@Component
public class DubboServiceScanner implements Scanner {
@Override
public Set<Class<?>> classes() {
return interfaceMapRef().keySet();
}
public Map<Class<?>, Object> interfaceMapRef() {
return ReferenceManager.getInstance().getInterfaceMapRef();
}
@Override
public boolean getPrettyPrint() {
return false;
}
@Override
public void setPrettyPrint(boolean shouldPrettyPrint) {
}
}
package com.secoo.mall.dubbo.swagger.config;
import io.swagger.models.Swagger;
import org.springframework.stereotype.Component;
@Component
public class SwaggerDocCache {
private Swagger swagger;
public Swagger getSwagger() {
return swagger;
}
public void setSwagger(Swagger swagger) {
this.swagger = swagger;
}
}
package com.secoo.mall.dubbo.swagger.controller;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.secoo.mall.dubbo.swagger.http.HttpMatch;
import com.secoo.mall.dubbo.swagger.http.ReferenceManager;
import com.secoo.mall.dubbo.swagger.reader.NameDiscover;
import io.swagger.annotations.Api;
import io.swagger.util.Json;
import io.swagger.util.PrimitiveType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map.Entry;
@Controller
@RequestMapping("${swagger.dubbo.http:h}")
@Api(hidden = true)
public class DubboHttpController {
private static Logger logger = LoggerFactory.getLogger(DubboHttpController.class);
private static final String CLUSTER_RPC = "rpc";
@Value("${swagger.dubbo.enable:true}")
private boolean enable = true;
@Value("${swagger.dubbo.cluster:rpc}")
private String cluster = CLUSTER_RPC;
@RequestMapping(value = "/{interfaceClass}/{methodName}", produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseEntity<String> invokeDubbo(@PathVariable("interfaceClass") String interfaceClass,
@PathVariable("methodName") String methodName, HttpServletRequest request,
HttpServletResponse response) throws Exception {
return invokeDubbo(interfaceClass, methodName, null, request, response);
}
@RequestMapping(value = "/{interfaceClass}/{methodName}/{operationId}", produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseEntity<String> invokeDubbo(@PathVariable("interfaceClass") String interfaceClass,
@PathVariable("methodName") String methodName,
@PathVariable("operationId") String operationId, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (!enable) {
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
Object ref = null;
Method method = null;
Object result = null;
Entry<Class<?>, Object> entry = ReferenceManager.getInstance().getRef(interfaceClass);
if (null == entry) {
logger.info("No Ref Service FOUND.");
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
ref = entry.getValue();
HttpMatch httpMatch = new HttpMatch(entry.getKey(), ref.getClass());
Method[] interfaceMethods = httpMatch.findInterfaceMethods(methodName);
if (null != interfaceMethods && interfaceMethods.length > 0) {
Method[] refMethods = httpMatch.findRefMethods(interfaceMethods, operationId,
request.getMethod());
method = httpMatch.matchRefMethod(refMethods, methodName, request.getParameterMap().keySet());
}
if (null == method) {
logger.info("No Service Method FOUND.");
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
String[] parameterNames = NameDiscover.parameterNameDiscover.getParameterNames(method);
logger.info("[Swagger-dubbo] Invoke by " + cluster);
if (CLUSTER_RPC.equals(cluster)) {
ref = ReferenceManager.getInstance().getProxy(interfaceClass);
if (null == ref) {
logger.info("No Ref Proxy Service FOUND.");
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
method = ref.getClass().getMethod(method.getName(), method.getParameterTypes());
if (null == method) {
logger.info("No Proxy Service Method FOUND.");
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
}
logger.debug("[Swagger-dubbo] Invoke dubbo service method:{},parameter:{}", method, Json.pretty(request.getParameterMap()));
if (null == parameterNames || parameterNames.length == 0) {
result = method.invoke(ref);
} else {
Object[] args = new Object[parameterNames.length];
Type[] parameterTypes = method.getGenericParameterTypes();
Class<?>[] parameterClazz = method.getParameterTypes();
for (int i = 0; i < parameterNames.length; i++) {
Object suggestPrameterValue = suggestPrameterValue(parameterTypes[i],
parameterClazz[i], request.getParameter(parameterNames[i]));
args[i] = suggestPrameterValue;
}
result = method.invoke(ref, args);
}
return ResponseEntity.ok(Json.mapper().writeValueAsString(result));
}
private Object suggestPrameterValue(Type type, Class<?> cls, String parameter)
throws JsonParseException, JsonMappingException, IOException {
PrimitiveType fromType = PrimitiveType.fromType(type);
if (null != fromType) {
DefaultConversionService service = new DefaultConversionService();
boolean actual = service.canConvert(String.class, cls);
if (actual) {
return service.convert(parameter, cls);
}
} else {
if (null == parameter) return null;
try {
return Json.mapper().readValue(parameter, cls);
} catch (Exception e) {
throw new IllegalArgumentException("The parameter value [" + parameter + "] should be json of [" + cls.getName() + "] Type.", e);
}
}
try {
return Class.forName(cls.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package com.secoo.mall.dubbo.swagger.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.secoo.mall.dubbo.swagger.config.DubboPropertyConfig;
import com.secoo.mall.dubbo.swagger.config.DubboServiceScanner;
import com.secoo.mall.dubbo.swagger.config.SwaggerDocCache;
import com.secoo.mall.dubbo.swagger.reader.Reader;
import io.swagger.annotations.Api;
import io.swagger.config.SwaggerConfig;
import io.swagger.models.Swagger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import springfox.documentation.spring.web.json.Json;
import java.util.Map;
@Controller
@RequestMapping("${swagger.dubbo.doc:swagger-dubbo}")
@Api(hidden = true)
public class SwaggerDubboController {
public static final String DEFAULT_URL = "/api-docs";
private static final String HAL_MEDIA_TYPE = "application/hal+json";
@Autowired
private DubboServiceScanner dubboServiceScanner;
@Autowired
private DubboPropertyConfig dubboPropertyConfig;
@Autowired
private SwaggerDocCache swaggerDocCache;
@Value("${swagger.dubbo.http:h}")
private String httpContext;
@Value("${swagger.dubbo.enable:true}")
private boolean enable = true;
@RequestMapping(value = DEFAULT_URL,
method = RequestMethod.GET,
produces = {"application/json; charset=utf-8", HAL_MEDIA_TYPE})
@ResponseBody
public ResponseEntity<Json> getApiList() throws JsonProcessingException {
if (!enable) {
return new ResponseEntity<Json>(HttpStatus.NOT_FOUND);
}
Swagger swagger = swaggerDocCache.getSwagger();
if (null != swagger) {
return new ResponseEntity<Json>(new Json(io.swagger.util.Json.mapper().writeValueAsString(swagger)), HttpStatus.OK);
} else {
swagger = new Swagger();
}
final SwaggerConfig configurator = dubboPropertyConfig;
if (configurator != null) {
configurator.configure(swagger);
}
Map<Class<?>, Object> interfaceMapRef = dubboServiceScanner.interfaceMapRef();
if (null != interfaceMapRef) {
Reader.read(swagger, interfaceMapRef, httpContext);
}
swaggerDocCache.setSwagger(swagger);
return new ResponseEntity<Json>(new Json(io.swagger.util.Json.mapper().writeValueAsString(swagger)), HttpStatus.OK);
}
}
package com.secoo.mall.dubbo.swagger.http;
import com.secoo.mall.dubbo.swagger.reader.NameDiscover;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.HttpMethod;
import io.swagger.util.ReflectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.*;
public class HttpMatch {
private static Logger logger = LoggerFactory.getLogger(HttpMatch.class);
private Class<?> interfaceClass;
private Class<?> refClass;
public HttpMatch(Class<?> interfaceClass, Class<?> refClass) {
this.interfaceClass = interfaceClass;
this.refClass = refClass;
}
public Method[] findInterfaceMethods(String methodName) {
Method[] methods = interfaceClass.getMethods();
List<Method> ret = new ArrayList<Method>();
for (Method method : methods) {
if (method.getName().equals(methodName)) ret.add(method);
}
return ret.toArray(new Method[]{});
}
public Method[] findRefMethods(Method[] interfaceMethods, String operationId,
String requestMethod) {
List<Method> ret = new ArrayList<Method>();
for (Method method : interfaceMethods) {
Method m;
try {
m = refClass.getMethod(method.getName(), method.getParameterTypes());
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(m,
ApiOperation.class);
String nickname = null == apiOperation ? null : apiOperation.nickname();
if (operationId != null) {
if (!operationId.equals(nickname)) continue;
} else {
if (StringUtils.isNotBlank(nickname)) continue;
}
if (requestMethod != null) {
String httpMethod = null == apiOperation ? null : apiOperation.httpMethod();
if (StringUtils.isNotBlank(httpMethod) && !requestMethod.equals(httpMethod))
continue;
if (StringUtils.isBlank(httpMethod)
&& !requestMethod.equalsIgnoreCase(HttpMethod.POST.name()))
continue;
}
ret.add(m);
} catch (NoSuchMethodException e) {
logger.error("NoSuchMethodException", e);
} catch (SecurityException e) {
logger.error("SecurityException", e);
}
}
return ret.toArray(new Method[]{});
}
public Method matchRefMethod(Method[] refMethods, String methodName, Set<String> keySet) {
if (refMethods.length == 0) {
return null;
}
if (refMethods.length == 1) {
return refMethods[0];
}
List<RateMethod> rateMethods = new ArrayList<RateMethod>();
for (Method method : refMethods) {
String[] parameterNames = NameDiscover.parameterNameDiscover
.getParameterNames(method);
if (parameterNames == null) return method;
float correctRate = 0.0f;
int hit = 0;
int error = 0;
for (String paramName : parameterNames) {
if (keySet.contains(paramName)) hit++;
else error++;
}
correctRate = error / (float) hit;
rateMethods.add(new RateMethod(method, (int) correctRate * 100));
}
if (rateMethods.isEmpty()) return null;
Collections.sort(rateMethods, new Comparator<RateMethod>() {
@Override
public int compare(RateMethod o1, RateMethod o2) {
return o2.getRate() - o1.getRate();
}
});
return rateMethods.get(0).getMethod();
}
class RateMethod {
private Method method;
private int rate;
public RateMethod(Method method, int rate) {
this.method = method;
this.rate = rate;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public int getRate() {
return rate;
}
public void setRate(int rate) {
this.rate = rate;
}
}
}
package com.secoo.mall.dubbo.swagger.http;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.extension.SpringExtensionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class ReferenceManager {
private static Logger logger = LoggerFactory.getLogger(ReferenceManager.class);
@SuppressWarnings("rawtypes")
private static Collection<ServiceBean> services;
private static Map<Class<?>, Object> interfaceMapProxy = new ConcurrentHashMap<Class<?>, Object>();
private static Map<Class<?>, Object> interfaceMapRef = new ConcurrentHashMap<Class<?>, Object>();
private static ReferenceManager instance;
private static ApplicationConfig application;
private ReferenceManager() {
}
@SuppressWarnings({"rawtypes", "unchecked"})
public synchronized static ReferenceManager getInstance() {
if (null != instance) return instance;
instance = new ReferenceManager();
services = new HashSet<ServiceBean>();
try {
/* Field field = SpringExtensionFactory.class.getDeclaredField("contexts");
field.setAccessible(true);*/
Set<ApplicationContext> contexts = SpringExtensionFactory.getContexts();
for (ApplicationContext context : contexts) {
services.addAll(context.getBeansOfType(ServiceBean.class).values());
}
} catch (Exception e) {
logger.error("Get All Dubbo Service Error", e);
return instance;
}
for (ServiceBean<?> bean : services) {
interfaceMapRef.putIfAbsent(bean.getInterfaceClass(), bean.getRef());
}
//
if (!services.isEmpty()) {
ServiceBean<?> bean = services.toArray(new ServiceBean[]{})[0];
application = bean.getApplication();
}
return instance;
}
public Object getProxy(String interfaceClass) {
Set<Entry<Class<?>, Object>> entrySet = interfaceMapProxy.entrySet();
for (Entry<Class<?>, Object> entry : entrySet) {
if (entry.getKey().getName().equals(interfaceClass)) {
return entry.getValue();
}
}
for (ServiceBean<?> service : services) {
if (interfaceClass.equals(service.getInterfaceClass().getName())) {
ReferenceConfig<Object> reference = new ReferenceConfig<Object>();
reference.setApplication(service.getApplication());
reference.setRegistry(service.getRegistry());
reference.setRegistries(service.getRegistries());
reference.setInterface(service.getInterfaceClass());
reference.setVersion(service.getVersion());
interfaceMapProxy.put(service.getInterfaceClass(), reference.get());
return reference.get();
}
}
return null;
}
public Entry<Class<?>, Object> getRef(String interfaceClass) {
Set<Entry<Class<?>, Object>> entrySet = interfaceMapRef.entrySet();
for (Entry<Class<?>, Object> entry : entrySet) {
if (entry.getKey().getName().equals(interfaceClass)) {
return entry;
}
}
return null;
}
@SuppressWarnings("rawtypes")
public Collection<ServiceBean> getServices() {
return services;
}
public ApplicationConfig getApplication() {
return application;
}
public Map<Class<?>, Object> getInterfaceMapRef() {
return interfaceMapRef;
}
}
package com.secoo.mall.dubbo.swagger.reader;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.collect.Collections2;
import io.swagger.annotations.*;
import io.swagger.converter.ModelConverters;
import io.swagger.models.*;
import io.swagger.models.parameters.*;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.RefProperty;
import io.swagger.util.*;
import org.apache.commons.lang3.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;
public class DubboReaderExtension implements ReaderExtension {
@Override
public int getPriority() {
return 0;
}
@Override
public boolean isReadable(ReaderContext context) {
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
// read do not have Api annotation cls, skip hidden cls
return apiAnnotation == null || (context.isReadHidden() || !apiAnnotation.hidden());
}
@Override
public void applyConsumes(ReaderContext context, Operation operation, Method method) {
final List<String> consumes = new ArrayList<String>();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null) {
consumes.addAll(parseStringValues(apiOperation.consumes()));
}
if (consumes.isEmpty()) {
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiAnnotation != null) {
consumes.addAll(parseStringValues(apiAnnotation.consumes()));
}
consumes.addAll(context.getParentConsumes());
}
for (String consume : consumes) {
operation.consumes(consume);
}
}
private static List<String> parseStringValues(String str) {
return parseAnnotationValues(str, new Function<String, String>() {
@Override
public String apply(String value) {
return value;
}
});
}
private static <T> List<T> parseAnnotationValues(String str, Function<String, T> processor) {
final List<T> result = new ArrayList<T>();
for (String item : Splitter.on(",").trimResults().omitEmptyStrings().split(str)) {
result.add(processor.apply(item));
}
return result;
}
@Override
public void applyProduces(ReaderContext context, Operation operation, Method method) {
final List<String> produces = new ArrayList<String>();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null) {
produces.addAll(parseStringValues(apiOperation.produces()));
}
if (produces.isEmpty()) {
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiAnnotation != null) {
produces.addAll(parseStringValues(apiAnnotation.produces()));
}
produces.addAll(context.getParentProduces());
}
for (String produce : produces) {
operation.produces(produce);
}
}
@Override
public String getHttpMethod(ReaderContext context, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
return apiOperation == null || StringUtils.isEmpty(apiOperation.httpMethod())
? HttpMethod.POST.name() : apiOperation.httpMethod();
}
@Override
public String getPath(ReaderContext context, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
String operationId = null == apiOperation ? ""
: StringUtils.isBlank(apiOperation.nickname()) ? null : apiOperation.nickname();
return PathUtils.collectPath(context.getParentPath(), context.getInterfaceCls().getName(),
method.getName(), operationId);
}
@Override
public void applyOperationId(Operation operation, Method method) {
operation.operationId(method.getName());
}
@Override
public void applySummary(Operation operation, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && StringUtils.isNotBlank(apiOperation.value())) {
operation.summary(apiOperation.value());
}
}
@Override
public void applyDescription(Operation operation, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && StringUtils.isNotBlank(apiOperation.notes())) {
operation.description(apiOperation.notes());
}
operation.description(operation.getDescription() == null ? outputMethod(method)
: (outputMethod(method) + operation.getDescription()));
}
private String outputMethod(Method method) {
try {
Class<?>[] types = method.getParameterTypes();
Class<?>[] exceptionTypes = method.getExceptionTypes();
StringBuilder sb = new StringBuilder();
sb.append(method.getReturnType().getSimpleName()).append(" ").append(method.getName());
sb.append('(');
for (int j = 0; j < types.length; j++) {
sb.append(types[j].getName());
if (j < (types.length - 1)) sb.append(",");
}
sb.append(')');
if (exceptionTypes.length > 0) {
sb.append(" throws ");
for (int j = 0; j < exceptionTypes.length; j++) {
sb.append(exceptionTypes[j].getSimpleName());
if (j < (exceptionTypes.length - 1)) sb.append(",");
}
}
return sb.toString();
} catch (Exception e) {
return "<" + e + ">";
}
}
@Override
public void applySchemes(ReaderContext context, Operation operation, Method method) {
final List<Scheme> schemes = new ArrayList<Scheme>();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiOperation != null) {
schemes.addAll(parseSchemes(apiOperation.protocols()));
}
if (schemes.isEmpty() && apiAnnotation != null) {
schemes.addAll(parseSchemes(apiAnnotation.protocols()));
}
for (Scheme scheme : schemes) {
operation.scheme(scheme);
}
}
private static List<Scheme> parseSchemes(String schemes) {
final List<Scheme> result = new ArrayList<Scheme>();
for (String item : StringUtils.trimToEmpty(schemes).split(",")) {
final Scheme scheme = Scheme.forValue(StringUtils.trimToNull(item));
if (scheme != null && !result.contains(scheme)) {
result.add(scheme);
}
}
return result;
}
@Override
public void setDeprecated(Operation operation, Method method) {
operation.deprecated(ReflectionUtils.getAnnotation(method, Deprecated.class) != null);
}
@Override
public void applySecurityRequirements(ReaderContext context, Operation operation,
Method method) {
}
// @Override
// public void applyTags(ReaderContext context) {
// final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
// if (apiAnnotation != null) {
// Tag tag = new
// Tag().name(context.getInterfaceCls().getSimpleName()).description(apiAnnotation.value());
// }
// }
@Override
public void applyTags(ReaderContext context, Operation operation, Method method) {
final List<String> tags = new ArrayList<String>();
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiAnnotation != null) {
Collection<String> filter = Collections2.filter(Arrays.asList(apiAnnotation.tags()),
new Predicate<String>() {
@Override
public boolean apply(String input) {
return StringUtils.isNotBlank(input);
}
});
if (filter.isEmpty()) {
tags.add(context.getInterfaceCls().getSimpleName());
} else {
tags.addAll(filter);
}
} else {
tags.add(context.getInterfaceCls().getSimpleName());
}
tags.addAll(context.getParentTags());
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null) {
tags.addAll(Collections2.filter(Arrays.asList(apiOperation.tags()),
new Predicate<String>() {
@Override
public boolean apply(String input) {
return StringUtils.isNotBlank(input);
}
}));
}
for (String tag : tags) {
operation.tag(tag);
}
}
private static final String SUCCESSFUL_OPERATION = "";
private static boolean isValidResponse(Type type) {
final JavaType javaType = TypeFactory.defaultInstance().constructType(type);
return !ReflectionUtils.isVoid(javaType);
}
private static Map<String, Property> parseResponseHeaders(ReaderContext context,
ResponseHeader[] headers) {
Map<String, Property> responseHeaders = null;
for (ResponseHeader header : headers) {
final String name = header.name();
if (StringUtils.isNotEmpty(name)) {
if (responseHeaders == null) {
responseHeaders = new HashMap<String, Property>();
}
final Class<?> cls = header.response();
if (!ReflectionUtils.isVoid(cls)) {
final Property property = ModelConverters.getInstance().readAsProperty(cls);
if (property != null) {
final Property responseProperty = ContainerWrapper.wrapContainer(
header.responseContainer(), property, ContainerWrapper.ARRAY,
ContainerWrapper.LIST, ContainerWrapper.SET);
responseProperty.setDescription(header.description());
responseHeaders.put(name, responseProperty);
appendModels(context.getSwagger(), cls);
}
}
}
}
return responseHeaders;
}
private static void appendModels(Swagger swagger, Type type) {
final Map<String, Model> models = ModelConverters.getInstance().readAll(type);
for (Map.Entry<String, Model> entry : models.entrySet()) {
swagger.model(entry.getKey(), entry.getValue());
}
}
private static Type getResponseType(Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && !ReflectionUtils.isVoid(apiOperation.response())) {
return apiOperation.response();
} else {
return method.getGenericReturnType();
}
}
private static String getResponseContainer(ApiOperation apiOperation) {
return apiOperation == null ? null
: StringUtils.defaultIfBlank(apiOperation.responseContainer(), null);
}
@Override
public void applyResponses(ReaderContext context, Operation operation, Method method) {
final Map<Integer, Response> result = new HashMap<Integer, Response>();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && StringUtils.isNotBlank(apiOperation.responseReference())) {
final Response response = new Response().description(SUCCESSFUL_OPERATION);
response.schema(new RefProperty(apiOperation.responseReference()));
result.put(apiOperation.code(), response);
}
final Type responseType = getResponseType(method);
if (isValidResponse(responseType)) {
final Property property = ModelConverters.getInstance().readAsProperty(responseType);
if (property != null) {
final Property responseProperty = ContainerWrapper
.wrapContainer(getResponseContainer(apiOperation), property);
final int responseCode = apiOperation == null ? 200 : apiOperation.code();
final Map<String, Property> defaultResponseHeaders = apiOperation == null
? Collections.<String, Property>emptyMap()
: parseResponseHeaders(context, apiOperation.responseHeaders());
final Response response = new Response().description(SUCCESSFUL_OPERATION)
.schema(responseProperty).headers(defaultResponseHeaders);
result.put(responseCode, response);
appendModels(context.getSwagger(), responseType);
}
}
final ApiResponses responseAnnotation = ReflectionUtils.getAnnotation(method,
ApiResponses.class);
if (responseAnnotation != null) {
for (ApiResponse apiResponse : responseAnnotation.value()) {
final Map<String, Property> responseHeaders = parseResponseHeaders(context,
apiResponse.responseHeaders());
final Response response = new Response().description(apiResponse.message())
.headers(responseHeaders);
if (StringUtils.isNotEmpty(apiResponse.reference())) {
response.schema(new RefProperty(apiResponse.reference()));
} else if (!ReflectionUtils.isVoid(apiResponse.response())) {
final Type type = apiResponse.response();
final Property property = ModelConverters.getInstance().readAsProperty(type);
if (property != null) {
response.schema(ContainerWrapper
.wrapContainer(apiResponse.responseContainer(), property));
appendModels(context.getSwagger(), type);
}
}
result.put(apiResponse.code(), response);
}
}
for (Map.Entry<Integer, Response> responseEntry : result.entrySet()) {
if (responseEntry.getKey() == 0) {
operation.defaultResponse(responseEntry.getValue());
} else {
operation.response(responseEntry.getKey(), responseEntry.getValue());
}
}
}
@Override
public void applyParameters(ReaderContext context, Operation operation, Type type,
Annotation[] annotations) {
}
private void applyParametersV2(ReaderContext context, Operation operation, String name,
Type type, Class<?> cls, Annotation[] annotations, Annotation[] interfaceParamAnnotations) {
Annotation apiParam = null;
if (annotations != null) {
for (Annotation annotation : interfaceParamAnnotations) {
if (annotation instanceof ApiParam) {
apiParam = annotation;
break;
}
}
if (null == apiParam) {
for (Annotation annotation : annotations) {
if (annotation instanceof ApiParam) {
apiParam = annotation;
break;
}
}
}
}
final Parameter parameter = readParam(context.getSwagger(), type, cls,
null == apiParam ? null : (ApiParam) apiParam);
if (parameter != null) {
parameter.setName(null == name ? parameter.getName() : name);
operation.parameter(parameter);
}
}
private Parameter readParam(Swagger swagger, Type type, Class<?> cls, ApiParam param) {
PrimitiveType fromType = PrimitiveType.fromType(type);
final Parameter para = null == fromType ? new BodyParameter() : new QueryParameter();
Parameter parameter = ParameterProcessor.applyAnnotations(swagger, para,
type == null ? String.class : type, null == param ? new ArrayList<Annotation>()
: Collections.<Annotation>singletonList(param));
if (parameter instanceof AbstractSerializableParameter) {
final AbstractSerializableParameter<?> p = (AbstractSerializableParameter<?>) parameter;
if (p.getType() == null) p.setType(null == fromType ? "string" : fromType.getCommonName());
p.setRequired(p.getRequired() == true ? true : cls.isPrimitive());
} else {
//hack: Get the from data model paramter from BodyParameter
BodyParameter bp = (BodyParameter) parameter;
bp.setIn("formData");
}
return parameter;
}
@Override
public void applyParameters(ReaderContext context, Operation operation, Method method,
Method interfaceMethod) {
try {
String[] parameterNames = NameDiscover.parameterNameDiscover.getParameterNames(method);
Type[] genericParameterTypes = method.getGenericParameterTypes();
Class<?>[] parameterTypes = method.getParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Annotation[][] interfaceParamAnnotations = interfaceMethod.getParameterAnnotations();
for (int i = 0; i < genericParameterTypes.length; i++) {
applyParametersV2(context, operation,
null == parameterNames ? null : parameterNames[i], genericParameterTypes[i], parameterTypes[i],
parameterAnnotations[i], interfaceParamAnnotations[i]);
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
@Override
public void applyImplicitParameters(ReaderContext context, Operation operation, Method method) {
final ApiImplicitParams implicitParams = method.getAnnotation(ApiImplicitParams.class);
if (implicitParams != null && implicitParams.value().length > 0) {
for (ApiImplicitParam param : implicitParams.value()) {
final Parameter p = readImplicitParam(context.getSwagger(), param);
if (p != null) {
operation.parameter(p);
}
}
}
}
private Parameter readImplicitParam(Swagger swagger, ApiImplicitParam param) {
PrimitiveType fromType = PrimitiveType.fromName(param.paramType());
final Parameter p = null == fromType ? new FormParameter() : new QueryParameter();
final Type type = ReflectionUtils.typeFromString(param.dataType());
return ParameterProcessor.applyAnnotations(swagger, p, type == null ? String.class : type,
Collections.<Annotation>singletonList(param));
}
@Override
public void applyExtensions(ReaderContext context, Operation operation, Method method) {
final ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
if (apiOperation != null) {
operation.getVendorExtensions()
.putAll(BaseReaderUtils.parseExtensions(apiOperation.extensions()));
}
}
enum ContainerWrapper {
LIST("list") {
@Override
protected Property doWrap(Property property) {
return new ArrayProperty(property);
}
},
ARRAY("array") {
@Override
protected Property doWrap(Property property) {
return new ArrayProperty(property);
}
},
MAP("map") {
@Override
protected Property doWrap(Property property) {
return new MapProperty(property);
}
},
SET("set") {
@Override
protected Property doWrap(Property property) {
ArrayProperty arrayProperty = new ArrayProperty(property);
arrayProperty.setUniqueItems(true);
return arrayProperty;
}
};
private final String container;
ContainerWrapper(String container) {
this.container = container;
}
public static Property wrapContainer(String container, Property property,
ContainerWrapper... allowed) {
final Set<ContainerWrapper> tmp = allowed.length > 0
? EnumSet.copyOf(Arrays.asList(allowed))
: EnumSet.allOf(ContainerWrapper.class);
for (ContainerWrapper wrapper : tmp) {
final Property prop = wrapper.wrap(container, property);
if (prop != null) {
return prop;
}
}
return property;
}
public Property wrap(String container, Property property) {
if (this.container.equalsIgnoreCase(container)) {
return doWrap(property);
}
return null;
}
protected abstract Property doWrap(Property property);
}
}
package com.secoo.mall.dubbo.swagger.reader;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.PrioritizedParameterNameDiscoverer;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
public final class NameDiscover {
public static final ParameterNameDiscoverer parameterNameDiscover;
static {
parameterNameDiscover = new PrioritizedParameterNameDiscoverer();
((PrioritizedParameterNameDiscoverer) parameterNameDiscover)
.addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
((PrioritizedParameterNameDiscoverer) parameterNameDiscover)
.addDiscoverer(new StandardReflectionParameterNameDiscoverer());
}
}
package com.secoo.mall.dubbo.swagger.reader;
import io.swagger.annotations.Info;
import io.swagger.annotations.*;
import io.swagger.models.Contact;
import io.swagger.models.ExternalDocs;
import io.swagger.models.License;
import io.swagger.models.Tag;
import io.swagger.models.*;
import io.swagger.models.auth.In;
import io.swagger.models.parameters.Parameter;
import io.swagger.util.BaseReaderUtils;
import io.swagger.util.PathUtils;
import io.swagger.util.ReflectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;
/**
* The <code>Reader</code> class scans classes for Swagger annotations.
*/
public class Reader {
private final Swagger swagger;
private Reader(Swagger swagger) {
this.swagger = swagger;
}
/**
* Scans a set of classes for Swagger annotations.
*
* @param swagger is the Swagger instance
* @param classes are a set of classes to scan
*/
public static void read(Swagger swagger, Set<Class<?>> classes) {
final Reader reader = new Reader(swagger);
for (Class<?> cls : classes) {
final ReaderContext context = new ReaderContext(swagger, cls, cls, "", null, false,
new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(),
new ArrayList<Parameter>());
reader.read(context);
}
}
public static void read(Swagger swagger, Map<Class<?>, Object> interfaceMapRef,
String httpContext) {
final Reader reader = new Reader(swagger);
List<Entry<Class<?>, Object>> arrayList = new ArrayList<Entry<Class<?>, Object>>(interfaceMapRef.entrySet());
Collections.sort(arrayList, new Comparator<Entry<Class<?>, Object>>() {
@Override
public int compare(Entry<Class<?>, Object> o1, Entry<Class<?>, Object> o2) {
return o1.getKey().getSimpleName().compareTo(o2.getKey().getSimpleName());
}
});
for (Entry<Class<?>, Object> entry : arrayList) {
final ReaderContext context = new ReaderContext(swagger,
entry.getValue().getClass(), entry.getKey(), httpContext, null, false,
new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(),
new ArrayList<Parameter>());
reader.read(context);
}
}
private void read(ReaderContext context) {
final SwaggerDefinition swaggerDefinition = context.getCls()
.getAnnotation(SwaggerDefinition.class);
if (swaggerDefinition != null) {
readSwaggerConfig(swaggerDefinition);
}
List<Method> interfaceMethodList = Arrays.asList(context.getInterfaceCls().getMethods());
Collections.sort(interfaceMethodList, new Comparator<Method>() {
@Override
public int compare(Method o1, Method o2) {
return o1.getName().compareTo(o2.getName());
}
});
Map<Method, Method> serviceMethods = new LinkedHashMap<Method, Method>();
for (Method method : interfaceMethodList) {
if (!ReflectionUtils.isOverriddenMethod(method, context.getCls())) {
serviceMethods.put(method, getRefMethod(context, method));
}
}
for (Entry<Method, Method> entry : serviceMethods.entrySet()) {
Method method = entry.getValue();
Method interfaceMethod = entry.getKey();
final Operation operation = new Operation();
String operationPath = null;
String httpMethod = null;
ReaderExtension extension = new DubboReaderExtension();
if (operationPath == null) {
operationPath = extension.getPath(context, method);
}
if (httpMethod == null) {
httpMethod = extension.getHttpMethod(context, method);
}
if (operationPath == null || httpMethod == null) {
continue;
}
if (extension.isReadable(context)) {
extension.setDeprecated(operation, method);
extension.applyConsumes(context, operation, method);
extension.applyProduces(context, operation, method);
extension.applyOperationId(operation, method);
extension.applySummary(operation, method);
extension.applyDescription(operation, method);
extension.applySchemes(context, operation, method);
extension.applySecurityRequirements(context, operation, method);
extension.applyTags(context, operation, method);
extension.applyResponses(context, operation, method);
extension.applyImplicitParameters(context, operation, method);
extension.applyParameters(context, operation, method, interfaceMethod);
extension.applyExtensions(context, operation, method);
}
if (httpMethod != null) {
if (operation.getResponses() == null) {
operation.defaultResponse(new Response().description("successful operation"));
}
final Map<String, String> regexMap = new HashMap<String, String>();
final String parsedPath = PathUtils.parsePath(operationPath, regexMap);
Path path = swagger.getPath(parsedPath);
if (path == null) {
path = new Path();
swagger.path(parsedPath, path);
}
path.set(httpMethod.toLowerCase(), operation);
}
}
}
private Method getRefMethod(ReaderContext context, Method method) {
try {
return context.getCls().getMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return method;
}
@SuppressWarnings("deprecation")
private void readSwaggerConfig(SwaggerDefinition config) {
readInfoConfig(config);
if (StringUtils.isNotBlank(config.basePath())) {
swagger.setBasePath(config.basePath());
}
if (StringUtils.isNotBlank(config.host())) {
swagger.setHost(config.host());
}
for (String consume : config.consumes()) {
if (StringUtils.isNotBlank(consume)) {
swagger.addConsumes(consume);
}
}
for (String produce : config.produces()) {
if (StringUtils.isNotBlank(produce)) {
swagger.addProduces(produce);
}
}
if (StringUtils.isNotBlank(config.externalDocs().value())) {
ExternalDocs externalDocs = swagger.getExternalDocs();
if (externalDocs == null) {
externalDocs = new ExternalDocs();
swagger.setExternalDocs(externalDocs);
}
externalDocs.setDescription(config.externalDocs().value());
if (StringUtils.isNotBlank(config.externalDocs().url())) {
externalDocs.setUrl(config.externalDocs().url());
}
}
for (OAuth2Definition oAuth2Config : config.securityDefinition().oAuth2Definitions()) {
io.swagger.models.auth.OAuth2Definition oAuth2Definition = new io.swagger.models.auth.OAuth2Definition();
OAuth2Definition.Flow flow = oAuth2Config.flow();
if (flow.equals(OAuth2Definition.Flow.ACCESS_CODE)) {
oAuth2Definition = oAuth2Definition.accessCode(oAuth2Config.authorizationUrl(),
oAuth2Config.tokenUrl());
} else if (flow.equals(OAuth2Definition.Flow.APPLICATION)) {
oAuth2Definition = oAuth2Definition.application(oAuth2Config.tokenUrl());
} else if (flow.equals(OAuth2Definition.Flow.IMPLICIT)) {
oAuth2Definition = oAuth2Definition.implicit(oAuth2Config.authorizationUrl());
} else {
oAuth2Definition = oAuth2Definition.password(oAuth2Config.tokenUrl());
}
for (Scope scope : oAuth2Config.scopes()) {
oAuth2Definition.addScope(scope.name(), scope.description());
}
oAuth2Definition.setDescription(oAuth2Config.description());
swagger.addSecurityDefinition(oAuth2Config.key(), oAuth2Definition);
}
for (ApiKeyAuthDefinition[] apiKeyAuthConfigs : new ApiKeyAuthDefinition[][]{
config.securityDefinition().apiKeyAuthDefintions(),
config.securityDefinition().apiKeyAuthDefinitions()}) {
for (ApiKeyAuthDefinition apiKeyAuthConfig : apiKeyAuthConfigs) {
io.swagger.models.auth.ApiKeyAuthDefinition apiKeyAuthDefinition = new io.swagger.models.auth.ApiKeyAuthDefinition();
apiKeyAuthDefinition.setName(apiKeyAuthConfig.name());
apiKeyAuthDefinition.setIn(In.forValue(apiKeyAuthConfig.in().toValue()));
apiKeyAuthDefinition.setDescription(apiKeyAuthConfig.description());
swagger.addSecurityDefinition(apiKeyAuthConfig.key(), apiKeyAuthDefinition);
}
}
for (BasicAuthDefinition[] basicAuthConfigs : new BasicAuthDefinition[][]{
config.securityDefinition().basicAuthDefinions(),
config.securityDefinition().basicAuthDefinitions()}) {
for (BasicAuthDefinition basicAuthConfig : basicAuthConfigs) {
io.swagger.models.auth.BasicAuthDefinition basicAuthDefinition = new io.swagger.models.auth.BasicAuthDefinition();
basicAuthDefinition.setDescription(basicAuthConfig.description());
swagger.addSecurityDefinition(basicAuthConfig.key(), basicAuthDefinition);
}
}
for (io.swagger.annotations.Tag tagConfig : config.tags()) {
if (StringUtils.isNotBlank(tagConfig.name())) {
final Tag tag = new Tag();
tag.setName(tagConfig.name());
tag.setDescription(tagConfig.description());
if (StringUtils.isNotBlank(tagConfig.externalDocs().value())) {
tag.setExternalDocs(new ExternalDocs(tagConfig.externalDocs().value(),
tagConfig.externalDocs().url()));
}
tag.getVendorExtensions()
.putAll(BaseReaderUtils.parseExtensions(tagConfig.extensions()));
swagger.addTag(tag);
}
}
for (SwaggerDefinition.Scheme scheme : config.schemes()) {
if (scheme != SwaggerDefinition.Scheme.DEFAULT) {
swagger.addScheme(Scheme.forValue(scheme.name()));
}
}
}
private void readInfoConfig(SwaggerDefinition config) {
final Info infoConfig = config.info();
io.swagger.models.Info info = swagger.getInfo();
if (info == null) {
info = new io.swagger.models.Info();
swagger.setInfo(info);
}
if (StringUtils.isNotBlank(infoConfig.description())) {
info.setDescription(infoConfig.description());
}
if (StringUtils.isNotBlank(infoConfig.termsOfService())) {
info.setTermsOfService(infoConfig.termsOfService());
}
if (StringUtils.isNotBlank(infoConfig.title())) {
info.setTitle(infoConfig.title());
}
if (StringUtils.isNotBlank(infoConfig.version())) {
info.setVersion(infoConfig.version());
}
if (StringUtils.isNotBlank(infoConfig.contact().name())) {
Contact contact = info.getContact();
if (contact == null) {
contact = new Contact();
info.setContact(contact);
}
contact.setName(infoConfig.contact().name());
if (StringUtils.isNotBlank(infoConfig.contact().email())) {
contact.setEmail(infoConfig.contact().email());
}
if (StringUtils.isNotBlank(infoConfig.contact().url())) {
contact.setUrl(infoConfig.contact().url());
}
}
if (StringUtils.isNotBlank(infoConfig.license().name())) {
License license = info.getLicense();
if (license == null) {
license = new License();
info.setLicense(license);
}
license.setName(infoConfig.license().name());
if (StringUtils.isNotBlank(infoConfig.license().url())) {
license.setUrl(infoConfig.license().url());
}
}
info.getVendorExtensions().putAll(BaseReaderUtils.parseExtensions(infoConfig.extensions()));
}
}
package com.secoo.mall.dubbo.swagger.reader;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.Parameter;
import java.util.List;
/**
* The <code>ReaderContext</code> class is wrapper for the <code>Reader</code>
* parameters.
*/
public class ReaderContext {
private Swagger swagger;
private Class<?> refCls;
private Class<?> interfaceCls;
private String parentPath;
private String parentHttpMethod;
private boolean readHidden;
private List<String> parentConsumes;
private List<String> parentProduces;
private List<String> parentTags;
private List<Parameter> parentParameters;
public ReaderContext(Swagger swagger, Class<?> refCls, Class<?> interfaceCls, String parentPath,
String parentHttpMethod, boolean readHidden, List<String> parentConsumes,
List<String> parentProduces, List<String> parentTags,
List<Parameter> parentParameters) {
setSwagger(swagger);
setRefCls(refCls);
setInterfaceCls(interfaceCls);
setParentPath(parentPath);
setParentHttpMethod(parentHttpMethod);
setReadHidden(readHidden);
setParentConsumes(parentConsumes);
setParentProduces(parentProduces);
setParentTags(parentTags);
setParentParameters(parentParameters);
}
public Swagger getSwagger() {
return swagger;
}
public void setSwagger(Swagger swagger) {
this.swagger = swagger;
}
public Class<?> getRefCls() {
return refCls;
}
public void setRefCls(Class<?> cls) {
this.refCls = cls;
}
public Class<?> getInterfaceCls() {
return interfaceCls;
}
public Class<?> getCls() {
return refCls;
}
public void setInterfaceCls(Class<?> interfaceCls) {
this.interfaceCls = interfaceCls;
}
public String getParentPath() {
return parentPath;
}
public void setParentPath(String parentPath) {
this.parentPath = parentPath;
}
public String getParentHttpMethod() {
return parentHttpMethod;
}
public void setParentHttpMethod(String parentHttpMethod) {
this.parentHttpMethod = parentHttpMethod;
}
public boolean isReadHidden() {
return readHidden;
}
public void setReadHidden(boolean readHidden) {
this.readHidden = readHidden;
}
public List<String> getParentConsumes() {
return parentConsumes;
}
public void setParentConsumes(List<String> parentConsumes) {
this.parentConsumes = parentConsumes;
}
public List<String> getParentProduces() {
return parentProduces;
}
public void setParentProduces(List<String> parentProduces) {
this.parentProduces = parentProduces;
}
public List<String> getParentTags() {
return parentTags;
}
public void setParentTags(List<String> parentTags) {
this.parentTags = parentTags;
}
public List<Parameter> getParentParameters() {
return parentParameters;
}
public void setParentParameters(List<Parameter> parentParameters) {
this.parentParameters = parentParameters;
}
}
package com.secoo.mall.dubbo.swagger.reader;
import io.swagger.models.Operation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
/**
* The <code>ReaderExtension</code> interface defines methods for scans resources for Swagger.
*/
public interface ReaderExtension {
/**
* Returns this extension's priority.
* Note: Extension will be executed first with lowest priority.
*
* @return this extension's priority
*/
int getPriority();
/**
* Checks that a resource should be scanned.
*
* @param context is the resource context
* @return true if the resource needs to be scanned, otherwise false
*/
boolean isReadable(ReaderContext context);
/**
* Reads the consumes from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyConsumes(ReaderContext context, Operation operation, Method method);
/**
* Reads the produces from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyProduces(ReaderContext context, Operation operation, Method method);
/**
* Returns http method.
*
* @param context is the resource context
* @param method is the method for reading annotations
* @return http method
*/
String getHttpMethod(ReaderContext context, Method method);
/**
* Returns operation's path.
*
* @param context is the resource context
* @param method is the method for reading annotations
* @return operation's path
*/
String getPath(ReaderContext context, Method method);
/**
* Reads the operation id from the method's annotations and applies it to the operation.
*
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyOperationId(Operation operation, Method method);
/**
* Reads the summary from the method's annotations and applies it to the operation.
*
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applySummary(Operation operation, Method method);
/**
* Reads the description from the method's annotations and applies it to the operation.
*
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyDescription(Operation operation, Method method);
/**
* Reads the schemes from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applySchemes(ReaderContext context, Operation operation, Method method);
/**
* Sets the deprecated flag to the operation.
*
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void setDeprecated(Operation operation, Method method);
/**
* Reads the security requirement from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applySecurityRequirements(ReaderContext context, Operation operation, Method method);
/**
* Reads the tags from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyTags(ReaderContext context, Operation operation, Method method);
/**
* Reads the responses from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyResponses(ReaderContext context, Operation operation, Method method);
/**
* Reads the parameters from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param type is the type of parameter
* @param annotations are the method's annotations
*/
void applyParameters(ReaderContext context, Operation operation, Type type, Annotation[] annotations);
/**
* Reads the implicit parameters from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyImplicitParameters(ReaderContext context, Operation operation, Method method);
/**
* Reads the extensions from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyExtensions(ReaderContext context, Operation operation, Method method);
void applyParameters(ReaderContext context, Operation operation, Method method,
Method interfaceMethod);
}
package com.secoo.mall.web.advice;
import com.secoo.mall.common.handler.ProtocolExceptionHandler;
import com.secoo.mall.common.util.response.ResponseUtil;
import com.secoo.mall.web.annotation.ApiController;
import com.secoo.mall.web.annotation.ApiIgnoreJson;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice(annotations = ApiController.class)
public class ControllerResponseAdvice extends ProtocolExceptionHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return AnnotationUtils.findAnnotation(methodParameter.getMethod(), ApiIgnoreJson.class) == null;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
return ResponseUtil.getSuccessResponse(o);
}
}
exception=com.secoo.mall.dubbo.filter.DefaultExceptionFilter
\ No newline at end of file
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.secoo.mall.common.config.MatrixSwaggerAutoConfiguration
\ No newline at end of file
......@@ -5,24 +5,30 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>redis-starter</artifactId>
<dependencies>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>common-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.secoo.mall.redis.config;
import com.secoo.mall.redis.utils.MatrixRedisClusterUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Created by QIANG
*
* @author QIANG
*/
@Configuration
public class MatrixeRedisAutoConfiguration {
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisSerializer stringSerializer = new StringRedisSerializer();
RedisTemplate<String, String> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
return redisTemplate;
}
@Bean
@ConditionalOnMissingBean(MatrixRedisClusterUtils.class)
public MatrixRedisClusterUtils jedisClusterUtils() {
return new MatrixRedisClusterUtils();
}
}
package com.secoo.mall.redis.utils;
import com.alibaba.fastjson.JSON;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @author QIANG
*/
public class MatrixRedisClusterUtils {
private static MatrixRedisClusterUtils cacheUtils;
@Resource
private RedisTemplate<String, String> redisTemplate;
public static RedisTemplate<String, String> getRedisTemplate() {
return cacheUtils.redisTemplate;
}
public static ValueOperations<String, String> opsForValue() {
return cacheUtils.redisTemplate.opsForValue();
}
public static String getAndSet(String key, String value) {
return cacheUtils.redisTemplate.opsForValue().getAndSet(key, value);
}
public static void set(String key, String val, long timeout, TimeUnit unit) {
cacheUtils.redisTemplate.opsForValue().set(key, val, timeout, unit);
}
public static ListOperations<String, String> opsForList() {
return cacheUtils.redisTemplate.opsForList();
}
public static ZSetOperations<String, String> opsForZSet() {
return cacheUtils.redisTemplate.opsForZSet();
}
public static HashOperations<String, Object, Object> opsForHash() {
return cacheUtils.redisTemplate.opsForHash();
}
public static ClusterOperations<String, String> opsForCluster() {
return cacheUtils.redisTemplate.opsForCluster();
}
public static GeoOperations<String, String> opsForGeo() {
return cacheUtils.redisTemplate.opsForGeo();
}
public static HyperLogLogOperations<String, String> opsForHyperLogLog() {
return cacheUtils.redisTemplate.opsForHyperLogLog();
}
public static SetOperations<String, String> opsForSet() {
return cacheUtils.redisTemplate.opsForSet();
}
/**
* 设置超时时间
*
* @param key
*/
public static boolean expire(String key, long timeout, TimeUnit unit) {
return cacheUtils.redisTemplate.expire(key, timeout, unit);
}
/**
* 将数据存入缓存
*
* @param key
* @param val
* @return
*/
public static void set(String key, String val) {
cacheUtils.redisTemplate.opsForValue().set(key, val);
}
/**
* setNx
*
* @param key
* @param val
* @return
*/
public static boolean setIfAbsent(String key, String val) {
return cacheUtils.redisTemplate.opsForValue().setIfAbsent(key, val);
}
/**
* 保存复杂类型数据到缓存
*
* @param key
* @param obj
* @return
*/
public static void set(String key, Object obj) {
cacheUtils.redisTemplate.opsForValue().set(key, JSON.toJSONString(obj));
}
/**
* @param key 缓存Key
* @return keyValue
*/
public static String opsForSet(String key) {
return cacheUtils.redisTemplate.opsForSet().pop(key);
}
/**
* 将数据存入缓存的集合中
*
* @param key
* @param val
* @return
*/
public static void add(String key, String val) {
cacheUtils.redisTemplate.opsForSet().add(key, val);
}
public static RedisConnectionFactory getConnectionFactory() {
return cacheUtils.redisTemplate.getConnectionFactory();
}
/**
* 保存到hash集合中
*
* @param hName 集合名
* @param key
*/
public static void put(String hName, String key, String value) {
cacheUtils.redisTemplate.opsForHash().put(hName, key, value);
}
/**
* 存储多维数据
*
* @param key
* @param data
*/
public static void putAll(String key, Map<String, String> data) {
cacheUtils.redisTemplate.opsForHash().putAll(key, data);
}
/**
* 为哈希表 key 中的域 field 的值加上增量 increment
*
* @param hName
* @param key
* @param value
*/
public static Long increment(String hName, String key, long value) {
return cacheUtils.redisTemplate.opsForHash().increment(hName, key, value);
}
/**
* 根据key获取所以值
*
* @param key
* @return
*/
public static Map<Object, Object> entries(String key) {
return cacheUtils.redisTemplate.opsForHash().entries(key);
}
/**
* 从缓存中取得字符串数据
*
* @param key
* @return 数据
*/
public static String get(String key) {
return cacheUtils.redisTemplate.opsForValue().get(key);
}
/**
* 获取set集合交集opsForSet
*
* @param key
* @param skey
* @return
*/
public static Set<String> intersect(String key, String skey) {
return cacheUtils.redisTemplate.opsForSet().intersect(key, skey);
}
/**
* 获取集合元素数量
*
* @return
*/
public static Long scard(String key) {
return cacheUtils.redisTemplate.opsForSet().size(key);
}
/**
* 返回集合 key 中的所有成员
* opsForSet().members(key)
* @param key set名
*/
public static Set<String> members(String key) {
return cacheUtils.redisTemplate.opsForSet().members(key);
}
/**
* 移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
* opsForSet().remove(key,member)
*/
public static Long srem(String key, String... member) {
return cacheUtils.redisTemplate.opsForSet().remove(key,member);
}
@PostConstruct
public void init() {
cacheUtils = this;
cacheUtils.redisTemplate = redisTemplate;
}
}
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.secoo.mall.redis.config.MatrixeRedisAutoConfiguration
\ No newline at end of file
......@@ -5,7 +5,7 @@
<parent>
<artifactId>matrix</artifactId>
<groupId>com.secoo.mall</groupId>
<version>1.0.5.RELEASE</version>
<version>1.0.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
package com.secoo.mall.mq.config;
import com.secoo.mall.mq.hook.MatrixConsumeHook;
import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author qianglu
*/
@Configuration
public class MatrixListenerContainerConfiguration implements BeanPostProcessor {
private final static Logger log = LoggerFactory.getLogger(MatrixListenerContainerConfiguration.class);
@Bean
MatrixConsumeHook matrixConsumeHook(){
return new MatrixConsumeHook();
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof DefaultRocketMQListenerContainer){
DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer)bean;
container.getConsumer().getDefaultMQPushConsumerImpl().registerConsumeMessageHook(matrixConsumeHook());
}
return bean;
}
}
package com.secoo.mall.mq.hook;
import org.apache.rocketmq.client.hook.ConsumeMessageContext;
import org.apache.rocketmq.client.hook.ConsumeMessageHook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author qianglu
*/
public class MatrixConsumeHook implements ConsumeMessageHook {
private final static Logger log = LoggerFactory.getLogger(MatrixConsumeHook.class);
@Override
public String hookName() {
return "MatrixConsumeHook";
}
@Override
public void consumeMessageBefore(ConsumeMessageContext context) {
}
@Override
public void consumeMessageAfter(ConsumeMessageContext context) {
try {
log.info("consumeMessageAfter,Succ:{} Topic:{},TraceContext:{},ConsumerGroup:{}",context.isSuccess(),context.getMq().getTopic(),context.getMqTraceContext(),context.getConsumerGroup());
} catch (Exception e) {//防灾冗余
}
}
}
package com.secoo.mall.mq.hook;
import org.apache.rocketmq.client.hook.SendMessageContext;
import org.apache.rocketmq.client.hook.SendMessageHook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author qianglu
*/
public class MatrixProducerHook implements SendMessageHook {
private final static Logger log = LoggerFactory.getLogger(MatrixProducerHook.class);
@Override
public String hookName() {
return "MatrixProducerHook";
}
@Override
public void sendMessageBefore(SendMessageContext context) {
}
@Override
public void sendMessageAfter(SendMessageContext context) {
try {
log.info("sendMessageAfter,Mid:{},Mode:{},Topic:{},Targs:{},ProducerGroup:{},BrokerAddr:{},Namespace:{},Exception:{}", context.getMessage().getProperties().get("id"), context.getCommunicationMode().name(), context.getMessage().getTopic(), context.getMessage().getTags(), context.getProducerGroup(), context.getBrokerAddr(), context.getNamespace(), context.getException()==null?null:context.getException().getMessage());
} catch (Exception e) {//防灾冗余}
}
}
}
\ No newline at end of file
package com.secoo.mall.mq.producer;
import org.apache.rocketmq.client.hook.SendMessageHook;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.remoting.RPCHook;
/**
* @author qianglu
*/
public class MartixProducer extends DefaultMQProducer {
public MartixProducer(final String producerGroup, RPCHook rpcHook, boolean enableMsgTrace,
final String customizedTraceTopic, SendMessageHook messageHook) {
super(producerGroup, rpcHook, enableMsgTrace, customizedTraceTopic);
this.defaultMQProducerImpl.registerSendMessageHook(messageHook);
}
public MartixProducer(final String producerGroup, boolean enableMsgTrace, final String customizedTraceTopic, SendMessageHook messageHook) {
super(producerGroup, enableMsgTrace, customizedTraceTopic);
this.defaultMQProducerImpl.registerSendMessageHook(messageHook);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment