mirror of
https://github.com/chillzhuang/blade-tool
synced 2024-11-16 23:49:34 +08:00
commit
226e2d751a
@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/license-LGPL%20v3-blue.svg" alt="Build Status">
|
||||
<img src="https://img.shields.io/badge/Spring%20Cloud-Hoxton.SR8-blue.svg" alt="Coverage Status">
|
||||
<img src="https://img.shields.io/badge/Spring%20Boot-2.2.11.RELEASE-blue.svg" alt="Downloads">
|
||||
<img src="https://img.shields.io/badge/Spring%20Cloud-2020-blue.svg" alt="Coverage Status">
|
||||
<img src="https://img.shields.io/badge/Spring%20Boot-2.4.4-blue.svg" alt="Downloads">
|
||||
</p>
|
||||
|
||||
## SpringBlade微服务开发平台
|
||||
@ -14,7 +14,7 @@
|
||||
* 极简封装了多租户底层,用更少的代码换来拓展性更强的SaaS多租户系统。
|
||||
* 借鉴OAuth2,实现了多终端认证系统,可控制子系统的token权限互相隔离。
|
||||
* 借鉴Security,封装了Secure模块,采用JWT做Token认证,可拓展集成Redis等细颗粒度控制方案。
|
||||
* 稳定生产了一年,经历了从Camden -> Greenwich的技术架构,也经历了从fat jar -> docker -> k8s + jenkins的部署架构
|
||||
* 稳定生产了一年,经历了从 Camden -> Hoxton -> 2020 的技术架构,也经历了从fat jar -> docker -> k8s + jenkins的部署架构
|
||||
* 项目分包明确,规范微服务的开发模式,使包与包之间的分工清晰。
|
||||
|
||||
## 架构图
|
||||
@ -55,7 +55,7 @@ blade-tool
|
||||
## 项目地址
|
||||
* 后端Gitee地址:[https://gitee.com/smallc/SpringBlade](https://gitee.com/smallc/SpringBlade)
|
||||
* 后端Github地址:[https://github.com/chillzhuang/SpringBlade](https://github.com/chillzhuang/SpringBlade)
|
||||
* 后端SpringBoot版:[https://gitee.com/smallc/SpringBlade/tree/2.0-boot/](https://gitee.com/smallc/SpringBlade/tree/2.0-boot/)
|
||||
* 后端SpringBoot版:[https://gitee.com/smallc/SpringBlade/tree/boot/](https://gitee.com/smallc/SpringBlade/tree/boot/)
|
||||
* 前端框架Sword(基于React):[https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword)
|
||||
* 前端框架Saber(基于Vue):[https://gitee.com/smallc/Saber](https://gitee.com/smallc/Saber)
|
||||
* 核心框架项目地址:[https://github.com/chillzhuang/blade-tool](https://github.com/chillzhuang/blade-tool)
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -30,7 +30,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties({
|
||||
BladeProperties.class
|
||||
})
|
||||
|
@ -31,7 +31,7 @@ import java.util.List;
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableCaching
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class BladeWebMvcConfiguration implements WebMvcConfigurer {
|
||||
|
@ -22,9 +22,11 @@ import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerIntercept
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springblade.core.boot.props.MybatisPlusProperties;
|
||||
import org.springblade.core.mp.plugins.SqlLogInterceptor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@ -33,9 +35,10 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@MapperScan("org.springblade.**.mapper.**")
|
||||
@EnableConfigurationProperties(MybatisPlusProperties.class)
|
||||
public class MybatisPlusConfiguration {
|
||||
|
||||
private final TenantLineHandler tenantLineHandler;
|
||||
@ -45,12 +48,15 @@ public class MybatisPlusConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MybatisPlusInterceptor.class)
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor(MybatisPlusProperties mybatisPlusProperties) {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 配置租户拦截器
|
||||
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler));
|
||||
// 配置分页拦截器
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
||||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
|
||||
paginationInnerInterceptor.setMaxLimit(mybatisPlusProperties.getPageLimit());
|
||||
paginationInnerInterceptor.setOverflow(mybatisPlusProperties.getOverflow());
|
||||
interceptor.addInnerInterceptor(paginationInnerInterceptor);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ import org.springframework.retry.interceptor.RetryOperationsInterceptor;
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class RetryConfiguration {
|
||||
|
||||
@Bean
|
||||
|
@ -36,7 +36,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile({AppConstant.DEV_CODE, AppConstant.TEST_CODE})
|
||||
public class RequestLogAspect {
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -13,18 +13,28 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.boot.props;
|
||||
|
||||
package org.springblade.core.test;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* boot test 基类
|
||||
* MybatisPlus配置类
|
||||
*
|
||||
* @author L.cm
|
||||
* @author Chill
|
||||
*/
|
||||
@RunWith(BladeSpringRunner.class)
|
||||
public abstract class BladeBaseTest extends AbstractJUnit4SpringContextTests {
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "blade.mybatis-plus")
|
||||
public class MybatisPlusProperties {
|
||||
|
||||
/**
|
||||
* 分页最大数
|
||||
*/
|
||||
private Long pageLimit = 500L;
|
||||
|
||||
/**
|
||||
* 溢出总页数后是否进行处理
|
||||
*/
|
||||
protected Boolean overflow = false;
|
||||
|
||||
}
|
@ -29,7 +29,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@AutoConfigureBefore(MybatisPlusConfiguration.class)
|
||||
@EnableConfigurationProperties(BladeTenantProperties.class)
|
||||
|
@ -1,32 +1,36 @@
|
||||
#服务器配置
|
||||
server:
|
||||
undertow:
|
||||
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
|
||||
io-threads: 4
|
||||
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
|
||||
worker-threads: 20
|
||||
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
|
||||
buffer-size: 1024
|
||||
# 是否分配的直接内存
|
||||
direct-buffers: true
|
||||
# 线程配置
|
||||
threads:
|
||||
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
|
||||
io: 16
|
||||
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
|
||||
worker: 400
|
||||
servlet:
|
||||
# 编码配置
|
||||
encoding:
|
||||
charset: UTF-8
|
||||
force: true
|
||||
|
||||
#spring配置
|
||||
spring:
|
||||
cache:
|
||||
ehcache:
|
||||
config: classpath:config/ehcache.xml
|
||||
http:
|
||||
encoding:
|
||||
charset: UTF-8
|
||||
force: true
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 256MB
|
||||
max-request-size: 1024MB
|
||||
web:
|
||||
resources:
|
||||
add-mappings: false
|
||||
mvc:
|
||||
throw-exception-if-no-handler-found: true
|
||||
resources:
|
||||
add-mappings: false
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
@ -97,7 +101,7 @@ mybatis-plus:
|
||||
swagger:
|
||||
title: SpringBlade 接口文档系统
|
||||
description: SpringBlade 接口文档系统
|
||||
version: 2.8.0
|
||||
version: 3.0.2
|
||||
license: Powered By SpringBlade
|
||||
licenseUrl: https://bladex.vip
|
||||
terms-of-service-url: https://bladex.vip
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -22,42 +22,22 @@
|
||||
<artifactId>blade-core-secure</artifactId>
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<!--Spring-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
</dependency>
|
||||
<!--Feign-->
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-okhttp</artifactId>
|
||||
<version>10.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!--Hystrix-->
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-okhttp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
<!-- Actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
<!-- Admin -->
|
||||
<dependency>
|
||||
@ -69,13 +49,30 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<version>${alibaba.cloud.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<version>${alibaba.cloud.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>${alibaba.nacos.version}</version>
|
||||
</dependency>
|
||||
<!-- Sentinel -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
@ -86,7 +83,7 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.74</version>
|
||||
<version>1.2.75</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.client;
|
||||
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Cloud启动注解配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@EnableDiscoveryClient
|
||||
@EnableCircuitBreaker
|
||||
@EnableFeignClients(AppConstant.BASE_PACKAGES)
|
||||
@SpringBootApplication(exclude = RibbonAutoConfiguration.class)
|
||||
public @interface BladeCloudApplication {
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.config;
|
||||
|
||||
import com.alibaba.cloud.sentinel.feign.SentinelFeignAutoConfiguration;
|
||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
|
||||
import feign.Feign;
|
||||
import feign.RequestInterceptor;
|
||||
import org.springblade.core.cloud.feign.BladeFeignSentinel;
|
||||
import org.springblade.core.cloud.header.BladeFeignRequestHeaderInterceptor;
|
||||
import org.springblade.core.cloud.sentinel.BladeBlockExceptionHandler;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
|
||||
/**
|
||||
* blade cloud 增强配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureBefore(SentinelFeignAutoConfiguration.class)
|
||||
public class BladeCloudAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "feign.sentinel.enabled")
|
||||
public Feign.Builder feignSentinelBuilder(RequestInterceptor requestInterceptor) {
|
||||
return BladeFeignSentinel.builder().requestInterceptor(requestInterceptor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public BlockExceptionHandler blockExceptionHandler() {
|
||||
return new BladeBlockExceptionHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RequestInterceptor requestInterceptor() {
|
||||
return new BladeFeignRequestHeaderInterceptor();
|
||||
}
|
||||
|
||||
}
|
@ -16,9 +16,9 @@
|
||||
package org.springblade.core.cloud.feign;
|
||||
|
||||
import feign.Target;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
import org.springframework.cloud.openfeign.FallbackFactory;
|
||||
|
||||
/**
|
||||
* 默认 Fallback,避免写过多fallback类
|
||||
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springblade.core.cloud.feign;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
import org.springblade.core.tool.convert.EnumToStringConverter;
|
||||
import org.springblade.core.tool.convert.StringToEnumConverter;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.openfeign.BladeFeignClientsRegistrar;
|
||||
import org.springframework.cloud.openfeign.BladeHystrixTargeter;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.Targeter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* blade feign 增强配置
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@Import(BladeFeignClientsRegistrar.class)
|
||||
@AutoConfigureAfter(EnableFeignClients.class)
|
||||
public class BladeFeignAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Targeter.class)
|
||||
public Targeter bladeFeignTargeter() {
|
||||
return new BladeHystrixTargeter();
|
||||
}
|
||||
|
||||
@Configuration("hystrixFeignConfiguration")
|
||||
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
|
||||
protected static class HystrixFeignConfiguration {
|
||||
@Bean
|
||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
@ConditionalOnProperty("feign.hystrix.enabled")
|
||||
public Feign.Builder feignHystrixBuilder(
|
||||
RequestInterceptor requestInterceptor, Contract feignContract) {
|
||||
return HystrixFeign.builder()
|
||||
.contract(feignContract)
|
||||
.decode404()
|
||||
.requestInterceptor(requestInterceptor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RequestInterceptor requestInterceptor() {
|
||||
return new BladeFeignRequestHeaderInterceptor();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* blade enum 《-》 String 转换配置
|
||||
* @param conversionService ConversionService
|
||||
* @return SpringMvcContract
|
||||
*/
|
||||
@Bean
|
||||
public Contract feignContract(@Qualifier("mvcConversionService") ConversionService conversionService) {
|
||||
ConverterRegistry converterRegistry = ((ConverterRegistry) conversionService);
|
||||
converterRegistry.addConverter(new EnumToStringConverter());
|
||||
converterRegistry.addConverter(new StringToEnumConverter());
|
||||
return new BladeSpringMvcContract(new ArrayList<>(), conversionService);
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ public class BladeFeignFallback<T> implements MethodInterceptor {
|
||||
private final Class<T> targetType;
|
||||
private final String targetName;
|
||||
private final Throwable cause;
|
||||
private final String code = "code";
|
||||
private final static String CODE = "code";
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@ -66,7 +66,7 @@ public class BladeFeignFallback<T> implements MethodInterceptor {
|
||||
// 转换成 jsonNode 读取,因为直接转换,可能 对方放回的并 不是 R 的格式。
|
||||
JsonNode resultNode = JsonUtil.readTree(content);
|
||||
// 判断是否 R 格式 返回体
|
||||
if (resultNode.has(code)) {
|
||||
if (resultNode.has(CODE)) {
|
||||
return JsonUtil.getInstance().convertValue(resultNode, R.class);
|
||||
}
|
||||
return R.fail(resultNode.toString());
|
||||
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.feign;
|
||||
|
||||
import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.InvocationHandlerFactory;
|
||||
import feign.Target;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springblade.core.cloud.sentinel.BladeSentinelInvocationHandler;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.cloud.openfeign.FallbackFactory;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.FeignContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* feign集成sentinel自动配置
|
||||
* 重写 {@link com.alibaba.cloud.sentinel.feign.SentinelFeign} 适配最新API
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class BladeFeignSentinel {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder extends Feign.Builder implements ApplicationContextAware {
|
||||
private Contract contract = new Contract.Default();
|
||||
private ApplicationContext applicationContext;
|
||||
private FeignContext feignContext;
|
||||
|
||||
@Override
|
||||
public Feign.Builder invocationHandlerFactory(
|
||||
InvocationHandlerFactory invocationHandlerFactory) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder contract(Contract contract) {
|
||||
this.contract = contract;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Feign build() {
|
||||
super.invocationHandlerFactory(new InvocationHandlerFactory() {
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
|
||||
// 注解取值以避免循环依赖的问题
|
||||
FeignClient feignClient = AnnotationUtils.findAnnotation(target.type(), FeignClient.class);
|
||||
Class fallback = feignClient.fallback();
|
||||
Class fallbackFactory = feignClient.fallbackFactory();
|
||||
String contextId = feignClient.contextId();
|
||||
|
||||
if (!StringUtils.hasText(contextId)) {
|
||||
contextId = feignClient.name();
|
||||
}
|
||||
|
||||
Object fallbackInstance;
|
||||
FallbackFactory fallbackFactoryInstance;
|
||||
// 判断fallback类型
|
||||
if (void.class != fallback) {
|
||||
fallbackInstance = getFromContext(contextId, "fallback", fallback, target.type());
|
||||
return new BladeSentinelInvocationHandler(target, dispatch, new FallbackFactory.Default(fallbackInstance));
|
||||
}
|
||||
if (void.class != fallbackFactory) {
|
||||
fallbackFactoryInstance = (FallbackFactory) getFromContext(contextId, "fallbackFactory", fallbackFactory, FallbackFactory.class);
|
||||
return new BladeSentinelInvocationHandler(target, dispatch, fallbackFactoryInstance);
|
||||
}
|
||||
// 默认fallbackFactory
|
||||
BladeFallbackFactory bladeFallbackFactory = new BladeFallbackFactory(target);
|
||||
return new BladeSentinelInvocationHandler(target, dispatch, bladeFallbackFactory);
|
||||
}
|
||||
|
||||
private Object getFromContext(String name, String type, Class fallbackType, Class targetType) {
|
||||
Object fallbackInstance = feignContext.getInstance(name, fallbackType);
|
||||
if (fallbackInstance == null) {
|
||||
throw new IllegalStateException(
|
||||
String.format("No %s instance of type %s found for feign client %s",
|
||||
type, fallbackType, name)
|
||||
);
|
||||
}
|
||||
|
||||
if (!targetType.isAssignableFrom(fallbackType)) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
|
||||
type, fallbackType, targetType, name)
|
||||
);
|
||||
}
|
||||
return fallbackInstance;
|
||||
}
|
||||
});
|
||||
super.contract(new SentinelContractHolder(contract));
|
||||
return super.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
feignContext = this.applicationContext.getBean(FeignContext.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,8 @@ package org.springblade.core.cloud.feign;
|
||||
|
||||
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
@ -31,6 +33,7 @@ import java.lang.annotation.*;
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@EnableFeignClients(AppConstant.BASE_PACKAGES)
|
||||
@EnableAutoConfiguration(exclude = RibbonAutoConfiguration.class)
|
||||
public @interface EnableBladeFeign {
|
||||
/**
|
||||
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.hystrix;
|
||||
package org.springblade.core.cloud.header;
|
||||
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.secure.utils.SecureUtil;
|
||||
@ -27,7 +27,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class BladeAccountGetter implements BladeHystrixAccountGetter {
|
||||
public class BladeAccountGetter implements BladeFeignAccountGetter {
|
||||
|
||||
@Override
|
||||
public String get(HttpServletRequest request) {
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.hystrix;
|
||||
package org.springblade.core.cloud.header;
|
||||
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
@ -25,7 +25,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public interface BladeHystrixAccountGetter {
|
||||
public interface BladeFeignAccountGetter {
|
||||
|
||||
/**
|
||||
* 账号信息获取器
|
@ -13,11 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.feign;
|
||||
package org.springblade.core.cloud.header;
|
||||
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import org.springblade.core.cloud.hystrix.BladeHttpHeadersContextHolder;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
/**
|
@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.hystrix;
|
||||
package org.springblade.core.cloud.header;
|
||||
|
||||
import org.springblade.core.cloud.props.BladeHystrixHeadersProperties;
|
||||
import org.springblade.core.cloud.props.BladeFeignHeadersProperties;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@ -33,8 +33,8 @@ public class BladeHttpHeadersCallable<V> implements Callable<V> {
|
||||
private HttpHeaders httpHeaders;
|
||||
|
||||
public BladeHttpHeadersCallable(Callable<V> delegate,
|
||||
@Nullable BladeHystrixAccountGetter accountGetter,
|
||||
BladeHystrixHeadersProperties properties) {
|
||||
@Nullable BladeFeignAccountGetter accountGetter,
|
||||
BladeFeignHeadersProperties properties) {
|
||||
this.delegate = delegate;
|
||||
this.httpHeaders = BladeHttpHeadersContextHolder.toHeaders(accountGetter, properties);
|
||||
}
|
@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.hystrix;
|
||||
package org.springblade.core.cloud.header;
|
||||
|
||||
import org.springblade.core.cloud.props.BladeHystrixHeadersProperties;
|
||||
import org.springblade.core.cloud.props.BladeFeignHeadersProperties;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
@ -35,7 +35,7 @@ import java.util.List;
|
||||
* @author L.cm
|
||||
*/
|
||||
public class BladeHttpHeadersContextHolder {
|
||||
private static final ThreadLocal<HttpHeaders> HTTP_HEADERS_HOLDER = new NamedThreadLocal<>("Blade hystrix HttpHeaders");
|
||||
private static final ThreadLocal<HttpHeaders> HTTP_HEADERS_HOLDER = new NamedThreadLocal<>("Blade Feign HttpHeaders");
|
||||
|
||||
/**
|
||||
* 请求和转发的ip
|
||||
@ -59,8 +59,8 @@ public class BladeHttpHeadersContextHolder {
|
||||
|
||||
@Nullable
|
||||
public static HttpHeaders toHeaders(
|
||||
@Nullable BladeHystrixAccountGetter accountGetter,
|
||||
BladeHystrixHeadersProperties properties) {
|
||||
@Nullable BladeFeignAccountGetter accountGetter,
|
||||
BladeFeignHeadersProperties properties) {
|
||||
HttpServletRequest request = WebUtil.getRequest();
|
||||
if (request == null) {
|
||||
return null;
|
@ -18,12 +18,13 @@ package org.springblade.core.cloud.http;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.cloud.hystrix.BladeHystrixAccountGetter;
|
||||
import org.springblade.core.cloud.props.BladeHystrixHeadersProperties;
|
||||
import org.springblade.core.cloud.header.BladeFeignAccountGetter;
|
||||
import org.springblade.core.cloud.props.BladeFeignHeadersProperties;
|
||||
import org.springblade.core.tool.utils.Charsets;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
|
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
||||
@ -47,9 +48,10 @@ import java.util.concurrent.TimeUnit;
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(okhttp3.OkHttpClient.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnClass(okhttp3.OkHttpClient.class)
|
||||
@EnableConfigurationProperties(BladeFeignHeadersProperties.class)
|
||||
public class RestTemplateConfiguration {
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@ -135,8 +137,8 @@ public class RestTemplateConfiguration {
|
||||
|
||||
@Bean
|
||||
public RestTemplateHeaderInterceptor requestHeaderInterceptor(
|
||||
@Autowired(required = false) @Nullable BladeHystrixAccountGetter accountGetter,
|
||||
BladeHystrixHeadersProperties properties) {
|
||||
@Autowired(required = false) @Nullable BladeFeignAccountGetter accountGetter,
|
||||
BladeFeignHeadersProperties properties) {
|
||||
return new RestTemplateHeaderInterceptor(accountGetter,properties);
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,9 @@
|
||||
package org.springblade.core.cloud.http;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.cloud.hystrix.BladeHttpHeadersContextHolder;
|
||||
import org.springblade.core.cloud.hystrix.BladeHystrixAccountGetter;
|
||||
import org.springblade.core.cloud.props.BladeHystrixHeadersProperties;
|
||||
import org.springblade.core.cloud.header.BladeHttpHeadersContextHolder;
|
||||
import org.springblade.core.cloud.header.BladeFeignAccountGetter;
|
||||
import org.springblade.core.cloud.props.BladeFeignHeadersProperties;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
@ -36,8 +36,8 @@ import java.io.IOException;
|
||||
@AllArgsConstructor
|
||||
public class RestTemplateHeaderInterceptor implements ClientHttpRequestInterceptor {
|
||||
@Nullable
|
||||
private final BladeHystrixAccountGetter accountGetter;
|
||||
private final BladeHystrixHeadersProperties properties;
|
||||
private final BladeFeignAccountGetter accountGetter;
|
||||
private final BladeFeignHeadersProperties properties;
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(
|
||||
|
@ -1,71 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.hystrix;
|
||||
|
||||
import com.netflix.hystrix.Hystrix;
|
||||
import com.netflix.hystrix.strategy.HystrixPlugins;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
|
||||
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
|
||||
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
|
||||
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
|
||||
import org.springblade.core.cloud.props.BladeHystrixHeadersProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* Hystrix 配置
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(Hystrix.class)
|
||||
@EnableConfigurationProperties(BladeHystrixHeadersProperties.class)
|
||||
public class BladeHystrixAutoConfiguration {
|
||||
@Nullable
|
||||
@Autowired(required = false)
|
||||
private HystrixConcurrencyStrategy existingConcurrencyStrategy;
|
||||
@Nullable
|
||||
@Autowired(required = false)
|
||||
private BladeHystrixAccountGetter accountGetter;
|
||||
@Autowired
|
||||
private BladeHystrixHeadersProperties properties;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// Keeps references of existing Hystrix plugins.
|
||||
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
|
||||
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
|
||||
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
|
||||
HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();
|
||||
|
||||
HystrixPlugins.reset();
|
||||
|
||||
// Registers existing plugins excepts the Concurrent Strategy plugin.
|
||||
HystrixConcurrencyStrategy strategy = new BladeHystrixConcurrencyStrategy(existingConcurrencyStrategy, accountGetter, properties);
|
||||
HystrixPlugins.getInstance().registerConcurrencyStrategy(strategy);
|
||||
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
|
||||
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
|
||||
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
|
||||
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
|
||||
}
|
||||
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.hystrix;
|
||||
|
||||
import com.netflix.hystrix.HystrixThreadPoolKey;
|
||||
import com.netflix.hystrix.HystrixThreadPoolProperties;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
|
||||
import com.netflix.hystrix.strategy.properties.HystrixProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.cloud.props.BladeHystrixHeadersProperties;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Hystrix传递ThreaLocal中的一些变量
|
||||
*
|
||||
* <p>
|
||||
* https://github.com/Netflix/Hystrix/issues/92#issuecomment-260548068
|
||||
* https://github.com/spring-cloud/spring-cloud-sleuth/issues/39
|
||||
* https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/security
|
||||
* https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java
|
||||
* </p>
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class BladeHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
|
||||
@Nullable
|
||||
private final HystrixConcurrencyStrategy existingConcurrencyStrategy;
|
||||
@Nullable
|
||||
private final BladeHystrixAccountGetter accountGetter;
|
||||
private final BladeHystrixHeadersProperties properties;
|
||||
|
||||
@Override
|
||||
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
|
||||
return existingConcurrencyStrategy != null
|
||||
? existingConcurrencyStrategy.getBlockingQueue(maxQueueSize)
|
||||
: super.getBlockingQueue(maxQueueSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> HystrixRequestVariable<T> getRequestVariable(
|
||||
HystrixRequestVariableLifecycle<T> rv) {
|
||||
return existingConcurrencyStrategy != null
|
||||
? existingConcurrencyStrategy.getRequestVariable(rv)
|
||||
: super.getRequestVariable(rv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
|
||||
HystrixProperty<Integer> corePoolSize,
|
||||
HystrixProperty<Integer> maximumPoolSize,
|
||||
HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
|
||||
BlockingQueue<Runnable> workQueue) {
|
||||
return existingConcurrencyStrategy != null
|
||||
? existingConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
|
||||
: super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) {
|
||||
return existingConcurrencyStrategy != null
|
||||
? existingConcurrencyStrategy.getThreadPool(threadPoolKey, threadPoolProperties)
|
||||
: super.getThreadPool(threadPoolKey, threadPoolProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Callable<T> wrapCallable(Callable<T> callable) {
|
||||
Callable<T> wrapCallable = new BladeHttpHeadersCallable<>(callable, accountGetter, properties);
|
||||
return existingConcurrencyStrategy != null
|
||||
? existingConcurrencyStrategy.wrapCallable(wrapCallable)
|
||||
: super.wrapCallable(wrapCallable);
|
||||
}
|
||||
}
|
@ -32,8 +32,8 @@ import java.util.List;
|
||||
@Getter
|
||||
@Setter
|
||||
@RefreshScope
|
||||
@ConfigurationProperties("blade.hystrix.headers")
|
||||
public class BladeHystrixHeadersProperties {
|
||||
@ConfigurationProperties("blade.feign.headers")
|
||||
public class BladeFeignHeadersProperties {
|
||||
|
||||
/**
|
||||
* 用于 聚合层 向调用层传递用户信息 的请求头,默认:x-blade-account
|
@ -0,0 +1,26 @@
|
||||
package org.springblade.core.cloud.sentinel;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Sentinel统一限流策略
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class BladeBlockExceptionHandler implements BlockExceptionHandler {
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
|
||||
// Return 429 (Too Many Requests) by default.
|
||||
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.getWriter().print(JsonUtil.toJson(R.fail(e.getMessage())));
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.sentinel;
|
||||
|
||||
import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.Tracer;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import feign.Feign;
|
||||
import feign.InvocationHandlerFactory;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Target;
|
||||
import org.springframework.cloud.openfeign.FallbackFactory;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static feign.Util.checkNotNull;
|
||||
|
||||
/**
|
||||
* 重写 {@link com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler} 适配最新API
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class BladeSentinelInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Target<?> target;
|
||||
|
||||
private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
|
||||
|
||||
private FallbackFactory fallbackFactory;
|
||||
|
||||
private Map<Method, Method> fallbackMethodMap;
|
||||
|
||||
public BladeSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch,
|
||||
FallbackFactory fallbackFactory) {
|
||||
this.target = checkNotNull(target, "target");
|
||||
this.dispatch = checkNotNull(dispatch, "dispatch");
|
||||
this.fallbackFactory = fallbackFactory;
|
||||
this.fallbackMethodMap = toFallbackMethod(dispatch);
|
||||
}
|
||||
|
||||
public BladeSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
|
||||
this.target = checkNotNull(target, "target");
|
||||
this.dispatch = checkNotNull(dispatch, "dispatch");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(final Object proxy, final Method method, final Object[] args)
|
||||
throws Throwable {
|
||||
if ("equals".equals(method.getName())) {
|
||||
try {
|
||||
Object otherHandler = args.length > 0 && args[0] != null
|
||||
? Proxy.getInvocationHandler(args[0]) : null;
|
||||
return equals(otherHandler);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
} else if ("hashCode".equals(method.getName())) {
|
||||
return hashCode();
|
||||
} else if ("toString".equals(method.getName())) {
|
||||
return toString();
|
||||
}
|
||||
|
||||
Object result;
|
||||
InvocationHandlerFactory.MethodHandler methodHandler = this.dispatch.get(method);
|
||||
// only handle by HardCodedTarget
|
||||
if (target instanceof Target.HardCodedTarget) {
|
||||
Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
|
||||
MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP
|
||||
.get(hardCodedTarget.type().getName()
|
||||
+ Feign.configKey(hardCodedTarget.type(), method));
|
||||
// resource default is HttpMethod:protocol://url
|
||||
if (methodMetadata == null) {
|
||||
result = methodHandler.invoke(args);
|
||||
} else {
|
||||
String resourceName = methodMetadata.template().method().toUpperCase()
|
||||
+ ":" + hardCodedTarget.url() + methodMetadata.template().path();
|
||||
Entry entry = null;
|
||||
try {
|
||||
ContextUtil.enter(resourceName);
|
||||
entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
|
||||
result = methodHandler.invoke(args);
|
||||
} catch (Throwable ex) {
|
||||
// fallback handle
|
||||
if (!BlockException.isBlockException(ex)) {
|
||||
Tracer.trace(ex);
|
||||
}
|
||||
if (fallbackFactory != null) {
|
||||
try {
|
||||
Object fallbackResult = fallbackMethodMap.get(method)
|
||||
.invoke(fallbackFactory.create(ex), args);
|
||||
return fallbackResult;
|
||||
} catch (IllegalAccessException e) {
|
||||
// shouldn't happen as method is public due to being an
|
||||
// interface
|
||||
throw new AssertionError(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new AssertionError(e.getCause());
|
||||
}
|
||||
} else {
|
||||
// throw exception if fallbackFactory is null
|
||||
throw ex;
|
||||
}
|
||||
} finally {
|
||||
if (entry != null) {
|
||||
entry.exit(1, args);
|
||||
}
|
||||
ContextUtil.exit();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// other target type using default strategy
|
||||
result = methodHandler.invoke(args);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof BladeSentinelInvocationHandler) {
|
||||
BladeSentinelInvocationHandler other = (BladeSentinelInvocationHandler) obj;
|
||||
return target.equals(other.target);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return target.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return target.toString();
|
||||
}
|
||||
|
||||
static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
|
||||
Map<Method, Method> result = new LinkedHashMap<>();
|
||||
for (Method method : dispatch.keySet()) {
|
||||
method.setAccessible(true);
|
||||
result.put(method, method);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -13,12 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.cloud.feign;
|
||||
package org.springblade.core.cloud.version;
|
||||
|
||||
import feign.MethodMetadata;
|
||||
import org.springblade.core.cloud.annotation.ApiVersion;
|
||||
import org.springblade.core.cloud.annotation.UrlVersion;
|
||||
import org.springblade.core.cloud.version.BladeMediaType;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
@ -27,7 +27,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication
|
||||
public class VersionMappingAutoConfiguration {
|
||||
@Bean
|
||||
|
@ -1,230 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springblade.core.cloud.feign.BladeFeignAutoConfiguration;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* feign 自动配置
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
public class BladeFeignClientsRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware, EnvironmentAware {
|
||||
@Getter
|
||||
private ClassLoader beanClassLoader;
|
||||
@Getter
|
||||
private Environment environment;
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
|
||||
registerFeignClients(metadata, registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
private void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
|
||||
List<String> feignClients = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
|
||||
// 如果 spring.factories 里为空
|
||||
if (feignClients.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (String className : feignClients) {
|
||||
try {
|
||||
Class<?> clazz = beanClassLoader.loadClass(className);
|
||||
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(clazz, FeignClient.class);
|
||||
if (attributes == null) {
|
||||
continue;
|
||||
}
|
||||
// 如果已经存在该 bean,支持原生的 Feign
|
||||
if (registry.containsBeanDefinition(className)) {
|
||||
continue;
|
||||
}
|
||||
registerClientConfiguration(registry, getClientName(attributes), attributes.get("configuration"));
|
||||
|
||||
validate(attributes);
|
||||
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
|
||||
definition.addPropertyValue("url", getUrl(attributes));
|
||||
definition.addPropertyValue("path", getPath(attributes));
|
||||
String name = getName(attributes);
|
||||
definition.addPropertyValue("name", name);
|
||||
|
||||
// 兼容最新版本的 spring-cloud-openfeign,尚未发布
|
||||
StringBuilder aliasBuilder = new StringBuilder(18);
|
||||
if (attributes.containsKey("contextId")) {
|
||||
String contextId = getContextId(attributes);
|
||||
aliasBuilder.append(contextId);
|
||||
definition.addPropertyValue("contextId", contextId);
|
||||
} else {
|
||||
aliasBuilder.append(name);
|
||||
}
|
||||
|
||||
definition.addPropertyValue("type", className);
|
||||
definition.addPropertyValue("decode404", attributes.get("decode404"));
|
||||
definition.addPropertyValue("fallback", attributes.get("fallback"));
|
||||
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
|
||||
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
|
||||
|
||||
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
|
||||
|
||||
// alias
|
||||
String alias = aliasBuilder.append("FeignClient").toString();
|
||||
|
||||
// has a default, won't be null
|
||||
boolean primary = (Boolean)attributes.get("primary");
|
||||
|
||||
beanDefinition.setPrimary(primary);
|
||||
|
||||
String qualifier = getQualifier(attributes);
|
||||
if (StringUtils.hasText(qualifier)) {
|
||||
alias = qualifier;
|
||||
}
|
||||
|
||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the class used by {@link SpringFactoriesLoader} to load configuration
|
||||
* candidates.
|
||||
* @return the factory class
|
||||
*/
|
||||
private Class<?> getSpringFactoriesLoaderFactoryClass() {
|
||||
return BladeFeignAutoConfiguration.class;
|
||||
}
|
||||
|
||||
private void validate(Map<String, Object> attributes) {
|
||||
AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes);
|
||||
// This blows up if an aliased property is overspecified
|
||||
// FIXME annotation.getAliasedString("name", FeignClient.class, null);
|
||||
FeignClientsRegistrar.validateFallback(annotation.getClass("fallback"));
|
||||
FeignClientsRegistrar.validateFallbackFactory(annotation.getClass("fallbackFactory"));
|
||||
}
|
||||
|
||||
private String getName(Map<String, Object> attributes) {
|
||||
String name = (String) attributes.get("serviceId");
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = (String) attributes.get("name");
|
||||
}
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = (String) attributes.get("value");
|
||||
}
|
||||
name = resolve(name);
|
||||
return FeignClientsRegistrar.getName(name);
|
||||
}
|
||||
|
||||
private String getContextId(Map<String, Object> attributes) {
|
||||
String contextId = (String) attributes.get("contextId");
|
||||
if (!StringUtils.hasText(contextId)) {
|
||||
return getName(attributes);
|
||||
}
|
||||
|
||||
contextId = resolve(contextId);
|
||||
return FeignClientsRegistrar.getName(contextId);
|
||||
}
|
||||
|
||||
private String resolve(String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
return this.environment.resolvePlaceholders(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String getUrl(Map<String, Object> attributes) {
|
||||
String url = resolve((String) attributes.get("url"));
|
||||
return FeignClientsRegistrar.getUrl(url);
|
||||
}
|
||||
|
||||
private String getPath(Map<String, Object> attributes) {
|
||||
String path = resolve((String) attributes.get("path"));
|
||||
return FeignClientsRegistrar.getPath(path);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getQualifier(@Nullable Map<String, Object> client) {
|
||||
if (client == null) {
|
||||
return null;
|
||||
}
|
||||
String qualifier = (String) client.get("qualifier");
|
||||
if (StringUtils.hasText(qualifier)) {
|
||||
return qualifier;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getClientName(@Nullable Map<String, Object> client) {
|
||||
if (client == null) {
|
||||
return null;
|
||||
}
|
||||
String value = (String) client.get("contextId");
|
||||
if (!StringUtils.hasText(value)) {
|
||||
value = (String) client.get("value");
|
||||
}
|
||||
if (!StringUtils.hasText(value)) {
|
||||
value = (String) client.get("name");
|
||||
}
|
||||
if (!StringUtils.hasText(value)) {
|
||||
value = (String) client.get("serviceId");
|
||||
}
|
||||
if (StringUtils.hasText(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName());
|
||||
}
|
||||
|
||||
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
|
||||
builder.addConstructorArgValue(name);
|
||||
builder.addConstructorArgValue(configuration);
|
||||
registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.Target;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
import feign.hystrix.SetterFactory;
|
||||
import org.springblade.core.cloud.feign.BladeFallbackFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* 添加 blade 默认的 fallbackFactory L.cm 2019.01.19
|
||||
*
|
||||
* @author L.cm
|
||||
* @author Spencer Gibb
|
||||
* @author Erik Kringen
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class BladeHystrixTargeter implements Targeter {
|
||||
|
||||
@Override
|
||||
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
|
||||
Target.HardCodedTarget<T> target) {
|
||||
if (!(feign instanceof HystrixFeign.Builder)) {
|
||||
return feign.target(target);
|
||||
}
|
||||
HystrixFeign.Builder builder = (HystrixFeign.Builder) feign;
|
||||
SetterFactory setterFactory = getOptional(factory.getName(), context, SetterFactory.class);
|
||||
if (setterFactory != null) {
|
||||
builder.setterFactory(setterFactory);
|
||||
}
|
||||
Class<?> fallback = factory.getFallback();
|
||||
if (fallback != void.class) {
|
||||
return targetWithFallback(factory.getName(), context, target, builder, fallback);
|
||||
}
|
||||
Class<?> fallbackFactory = factory.getFallbackFactory();
|
||||
if (fallbackFactory != void.class) {
|
||||
return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
|
||||
}
|
||||
// blade 默认的 fallbackFactory
|
||||
BladeFallbackFactory bladeFallbackFactory = new BladeFallbackFactory(target);
|
||||
return (T) builder.target(target, bladeFallbackFactory);
|
||||
}
|
||||
|
||||
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
|
||||
Target.HardCodedTarget<T> target,
|
||||
HystrixFeign.Builder builder,
|
||||
Class<?> fallbackFactoryClass) {
|
||||
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>)
|
||||
getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
|
||||
return builder.target(target, fallbackFactory);
|
||||
}
|
||||
|
||||
|
||||
private <T> T targetWithFallback(String feignClientName, FeignContext context,
|
||||
Target.HardCodedTarget<T> target,
|
||||
HystrixFeign.Builder builder, Class<?> fallback) {
|
||||
T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
|
||||
return builder.target(target, fallbackInstance);
|
||||
}
|
||||
|
||||
private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context, Class<?> beanType,
|
||||
Class<T> targetType) {
|
||||
Object fallbackInstance = context.getInstance(feignClientName, beanType);
|
||||
if (fallbackInstance == null) {
|
||||
throw new IllegalStateException(String.format("No " + fallbackMechanism +
|
||||
" instance of type %s found for feign client %s", beanType, feignClientName));
|
||||
}
|
||||
|
||||
if (!targetType.isAssignableFrom(beanType)) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of " +
|
||||
"type %s is not assignable to %s for feign client %s", beanType, targetType, feignClientName));
|
||||
}
|
||||
return (T) fallbackInstance;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> T getOptional(String feignClientName, FeignContext context, Class<T> beanType) {
|
||||
return context.getInstance(feignClientName, beanType);
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.Target;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface Targeter {
|
||||
/**
|
||||
* target
|
||||
*
|
||||
* @param factory
|
||||
* @param feign
|
||||
* @param context
|
||||
* @param target
|
||||
* @param <T>
|
||||
* @return T
|
||||
*/
|
||||
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
|
||||
Target.HardCodedTarget<T> target);
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -25,6 +25,17 @@
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-generator</artifactId>
|
||||
<version>${mybatis.plus.generator.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
<version>${mybatis.plus.version}</version>
|
||||
</dependency>
|
||||
<!--Velocity-->
|
||||
|
@ -25,6 +25,7 @@ import com.baomidou.mybatisplus.generator.config.*;
|
||||
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
|
||||
import com.baomidou.mybatisplus.generator.config.converts.OracleTypeConvert;
|
||||
import com.baomidou.mybatisplus.generator.config.converts.PostgreSqlTypeConvert;
|
||||
import com.baomidou.mybatisplus.generator.config.converts.SqlServerTypeConvert;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
||||
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
|
||||
import lombok.Data;
|
||||
@ -150,6 +151,9 @@ public class BladeCodeGenerator {
|
||||
} else if (StringUtil.containsAny(driverName, DbType.POSTGRE_SQL.getDb())) {
|
||||
dsc.setDbType(DbType.POSTGRE_SQL);
|
||||
dsc.setTypeConvert(new PostgreSqlTypeConvert());
|
||||
} else if (StringUtil.containsAny(driverName, DbType.SQL_SERVER.getDb())) {
|
||||
dsc.setDbType(DbType.SQL_SERVER);
|
||||
dsc.setTypeConvert(new SqlServerTypeConvert());
|
||||
} else {
|
||||
dsc.setDbType(DbType.ORACLE);
|
||||
dsc.setTypeConvert(new OracleTypeConvert());
|
||||
|
@ -1,11 +1,10 @@
|
||||
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES (0, '$!{cfg.entityKey}', '$!{cfg.codeName}', 'menu', '/$!{cfg.servicePackage}/$!{cfg.entityKey}', NULL, 1, 1, 0, 1, NULL, 0);
|
||||
set @parentid = (SELECT LAST_INSERT_ID());
|
||||
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES (@parentid, '$!{cfg.entityKey}_add', '新增', 'add', '/$!{cfg.servicePackage}/$!{cfg.entityKey}/add', 'plus', 1, 2, 1, 1, NULL, 0);
|
||||
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES (@parentid, '$!{cfg.entityKey}_edit', '修改', 'edit', '/$!{cfg.servicePackage}/$!{cfg.entityKey}/edit', 'form', 2, 2, 1, 2, NULL, 0);
|
||||
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES (@parentid, '$!{cfg.entityKey}_delete', '删除', 'delete', '/api/$!{cfg.serviceName}/$!{cfg.entityKey}/remove', 'delete', 3, 2, 1, 3, NULL, 0);
|
||||
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES (@parentid, '$!{cfg.entityKey}_view', '查看', 'view', '/$!{cfg.servicePackage}/$!{cfg.entityKey}/view', 'file-text', 4, 2, 1, 2, NULL, 0);
|
||||
INSERT INTO `blade_menu`(`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES ('$!{cfg.menuId}', 1123598815738675201, '$!{cfg.entityKey}', '$!{cfg.codeName}', 'menu', '/$!{cfg.servicePackage}/$!{cfg.entityKey}', NULL, 1, 1, 0, 1, NULL, 0);
|
||||
INSERT INTO `blade_menu`(`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES ('$!{cfg.addMenuId}', '$!{cfg.menuId}', '$!{cfg.entityKey}_add', '新增', 'add', '/$!{cfg.servicePackage}/$!{cfg.entityKey}/add', 'plus', 1, 2, 1, 1, NULL, 0);
|
||||
INSERT INTO `blade_menu`(`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES ('$!{cfg.editMenuId}', '$!{cfg.menuId}', '$!{cfg.entityKey}_edit', '修改', 'edit', '/$!{cfg.servicePackage}/$!{cfg.entityKey}/edit', 'form', 2, 2, 2, 1, NULL, 0);
|
||||
INSERT INTO `blade_menu`(`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES ('$!{cfg.removeMenuId}', '$!{cfg.menuId}', '$!{cfg.entityKey}_delete', '删除', 'delete', '/api/$!{cfg.serviceName}/$!{cfg.entityKey}/remove', 'delete', 3, 2, 3, 1, NULL, 0);
|
||||
INSERT INTO `blade_menu`(`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
|
||||
VALUES ('$!{cfg.viewMenuId}', '$!{cfg.menuId}', '$!{cfg.entityKey}_view', '查看', 'view', '/$!{cfg.servicePackage}/$!{cfg.entityKey}/view', 'file-text', 4, 2, 2, 1, NULL, 0);
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -30,7 +30,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class StartEventListener {
|
||||
|
||||
@Async
|
||||
|
@ -27,7 +27,7 @@ import org.springframework.core.annotation.Order;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@EnableConfigurationProperties({
|
||||
|
@ -25,7 +25,7 @@ public interface AppConstant {
|
||||
/**
|
||||
* 应用版本
|
||||
*/
|
||||
String APPLICATION_VERSION = "2.8.0";
|
||||
String APPLICATION_VERSION = "3.0.2";
|
||||
|
||||
/**
|
||||
* 基础包
|
||||
|
@ -28,7 +28,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @author Chill
|
||||
*/
|
||||
@Getter
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class ServerInfo implements SmartInitializingSingleton {
|
||||
private final ServerProperties serverProperties;
|
||||
private String hostName;
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -41,7 +41,7 @@ import javax.servlet.Servlet;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnWebApplication
|
||||
@AutoConfigureBefore(ErrorMvcAutoConfiguration.class)
|
||||
|
@ -34,7 +34,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnWebApplication
|
||||
public class BladeLogToolAutoConfiguration {
|
||||
|
@ -18,6 +18,7 @@ package org.springblade.core.log.error;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
|
||||
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -41,12 +42,13 @@ public class BladeErrorController extends BasicErrorController {
|
||||
|
||||
@Override
|
||||
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
|
||||
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
|
||||
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
|
||||
Map<String, Object> body = getErrorAttributes(request, (includeStackTrace) ? ErrorAttributeOptions.of(ErrorAttributeOptions.Include.STACK_TRACE) : ErrorAttributeOptions.defaults());
|
||||
HttpStatus status = getStatus(request);
|
||||
response.setStatus(status.value());
|
||||
MappingJackson2JsonView view = new MappingJackson2JsonView();
|
||||
view.setObjectMapper(JsonUtil.getInstance());
|
||||
view.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
view.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
return new ModelAndView(view, body);
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ import java.util.Set;
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@RestControllerAdvice
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -115,24 +115,11 @@ public class AliossTemplate {
|
||||
return getOssHost(bucketName).concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件对象
|
||||
*
|
||||
* @param file 上传文件类
|
||||
* @return
|
||||
*/
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName 上传文件名
|
||||
* @param file 上传文件类
|
||||
* @return
|
||||
*/
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String fileName, MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), fileName, file);
|
||||
|
@ -33,6 +33,8 @@ import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@ -127,6 +129,31 @@ public class QiniuTemplate {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件公开链接
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 文件公开链接
|
||||
*/
|
||||
public String publicFileLink(String fileName) {
|
||||
return String.format("%s/%s", ossProperties.getEndpoint(), fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件私有链接
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @param expireTime 超时时间
|
||||
* @return 私有文件链接
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String privateFileLink(String fileName, Long expireTime) {
|
||||
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replace("+", "%20");
|
||||
String publicUrl = String.format("%s/%s", ossProperties.getEndpoint(), encodedFileName);
|
||||
return auth.privateDownloadUrl(publicUrl, expireTime);
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file);
|
||||
@ -158,6 +185,8 @@ public class QiniuTemplate {
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile put(String bucketName, InputStream stream, String key, boolean cover) {
|
||||
BladeFile file = new BladeFile();
|
||||
file.setOriginalName(key);
|
||||
makeBucket(bucketName);
|
||||
key = getFileName(key);
|
||||
// 覆盖上传
|
||||
@ -172,7 +201,6 @@ public class QiniuTemplate {
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
BladeFile file = new BladeFile();
|
||||
file.setName(key);
|
||||
file.setLink(fileLink(bucketName, key));
|
||||
return file;
|
||||
|
@ -37,7 +37,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@AutoConfigureAfter(QiniuConfiguration.class)
|
||||
@EnableConfigurationProperties(OssProperties.class)
|
||||
|
@ -36,7 +36,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@EnableConfigurationProperties(OssProperties.class)
|
||||
@ConditionalOnProperty(value = "oss.name", havingValue = "qiniu")
|
||||
@ -52,7 +52,7 @@ public class QiniuConfiguration {
|
||||
|
||||
@Bean
|
||||
public com.qiniu.storage.Configuration qiniuConfiguration() {
|
||||
return new com.qiniu.storage.Configuration(Zone.zone0());
|
||||
return new com.qiniu.storage.Configuration(Zone.autoZone());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ -69,7 +69,7 @@ public class QiniuConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnBean(com.qiniu.storage.Configuration.class)
|
||||
public BucketManager bucketManager(com.qiniu.storage.Configuration cfg) {
|
||||
return new BucketManager(auth(), cfg);
|
||||
return new BucketManager(Auth.create(ossProperties.getAccessKey(), ossProperties.getSecretKey()), cfg);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -40,7 +40,7 @@ import javax.servlet.Servlet;
|
||||
* @author Chill
|
||||
*/
|
||||
@Order
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "report.enabled", havingValue = "true", matchIfMissing = true)
|
||||
@EnableConfigurationProperties({ReportProperties.class, ReportDatabaseProperties.class})
|
||||
@ImportResource("classpath:ureport-console-context.xml")
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -29,7 +29,7 @@ import org.springframework.core.annotation.Order;
|
||||
* @author Chill
|
||||
*/
|
||||
@Order
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureBefore(SecureConfiguration.class)
|
||||
public class RegistryConfiguration {
|
||||
|
||||
|
@ -39,7 +39,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
* @author Chill
|
||||
*/
|
||||
@Order
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@EnableConfigurationProperties({BladeSecureProperties.class})
|
||||
public class SecureConfiguration implements WebMvcConfigurer {
|
||||
|
@ -26,7 +26,7 @@ import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@ -40,7 +40,7 @@ import java.util.Objects;
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class ClientInterceptor extends HandlerInterceptorAdapter {
|
||||
public class ClientInterceptor implements AsyncHandlerInterceptor {
|
||||
|
||||
private final String clientId;
|
||||
|
||||
|
@ -24,7 +24,7 @@ import org.springblade.core.tool.constant.BladeConstant;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@ -38,7 +38,7 @@ import java.util.Objects;
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class SecureInterceptor extends HandlerInterceptorAdapter {
|
||||
public class SecureInterceptor implements AsyncHandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -29,7 +29,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(SocialProperties.class)
|
||||
public class SocialConfiguration {
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -16,7 +16,10 @@
|
||||
package org.springblade.core.swagger;
|
||||
|
||||
|
||||
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.launch.props.BladeProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -41,16 +44,26 @@ import java.util.List;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableSwagger
|
||||
@EnableConfigurationProperties(SwaggerProperties.class)
|
||||
@Import(BeanValidatorPluginsConfiguration.class)
|
||||
@AllArgsConstructor
|
||||
public class SwaggerAutoConfiguration {
|
||||
|
||||
private static final String DEFAULT_MAPPING_PATH = "/";
|
||||
private static final String DEFAULT_BASE_PATH = "/**";
|
||||
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");
|
||||
|
||||
/**
|
||||
* 引入Knife4j扩展类
|
||||
*/
|
||||
private final OpenApiExtensionResolver openApiExtensionResolver;
|
||||
|
||||
/**
|
||||
* 引入Blade环境变量
|
||||
*/
|
||||
private final BladeProperties bladeProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SwaggerProperties swaggerProperties() {
|
||||
@ -77,11 +90,11 @@ public class SwaggerAutoConfiguration {
|
||||
swaggerProperties.getExcludePath().forEach(p -> apis.paths(PathSelectors.ant(p).negate()));
|
||||
|
||||
return apis.build()
|
||||
.securitySchemes(Collections.singletonList(securitySchema()))
|
||||
.securityContexts(Collections.singletonList(securityContext()))
|
||||
.securityContexts(Lists.newArrayList(securityContext()))
|
||||
.securitySchemes(Collections.singletonList(securitySchema()))
|
||||
.pathMapping(DEFAULT_MAPPING_PATH);
|
||||
.securitySchemes(Collections.singletonList(securitySchema(swaggerProperties)))
|
||||
.securityContexts(Collections.singletonList(securityContext(swaggerProperties)))
|
||||
.securityContexts(Lists.newArrayList(securityContext(swaggerProperties)))
|
||||
.securitySchemes(Collections.singletonList(securitySchema(swaggerProperties)))
|
||||
.extensions(openApiExtensionResolver.buildExtensions(bladeProperties.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,10 +102,10 @@ public class SwaggerAutoConfiguration {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private SecurityContext securityContext() {
|
||||
private SecurityContext securityContext(SwaggerProperties swaggerProperties) {
|
||||
return SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex()))
|
||||
.securityReferences(defaultAuth(swaggerProperties))
|
||||
.forPaths(PathSelectors.regex(swaggerProperties.getAuthorization().getAuthRegex()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -101,23 +114,23 @@ public class SwaggerAutoConfiguration {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private List<SecurityReference> defaultAuth() {
|
||||
private List<SecurityReference> defaultAuth(SwaggerProperties swaggerProperties) {
|
||||
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
|
||||
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
|
||||
swaggerProperties.getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];
|
||||
return Collections.singletonList(SecurityReference.builder()
|
||||
.reference(swaggerProperties().getAuthorization().getName())
|
||||
.reference(swaggerProperties.getAuthorization().getName())
|
||||
.scopes(authorizationScopeList.toArray(authorizationScopes))
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
private OAuth securitySchema() {
|
||||
private OAuth securitySchema(SwaggerProperties swaggerProperties) {
|
||||
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
|
||||
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
|
||||
swaggerProperties.getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
|
||||
ArrayList<GrantType> grantTypes = new ArrayList<>();
|
||||
swaggerProperties().getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
|
||||
return new OAuth(swaggerProperties().getAuthorization().getName(), authorizationScopeList, grantTypes);
|
||||
swaggerProperties.getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
|
||||
return new OAuth(swaggerProperties.getAuthorization().getName(), authorizationScopeList, grantTypes);
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
|
||||
|
@ -55,7 +55,7 @@ public class SwaggerProperties {
|
||||
/**
|
||||
* 版本
|
||||
**/
|
||||
private String version = "2.8.0";
|
||||
private String version = "3.0.2";
|
||||
/**
|
||||
* 许可证
|
||||
**/
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package org.springblade.core.test;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
@ -31,6 +32,7 @@ import java.lang.annotation.*;
|
||||
@Documented
|
||||
@Inherited
|
||||
@SpringBootTest
|
||||
@ExtendWith(BladeSpringExtension.class)
|
||||
public @interface BladeBootTest {
|
||||
/**
|
||||
* 服务名:appName
|
||||
|
@ -17,7 +17,7 @@
|
||||
package org.springblade.core.test;
|
||||
|
||||
|
||||
import org.junit.runners.model.InitializationError;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.springblade.core.launch.BladeApplication;
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
import org.springblade.core.launch.constant.NacosConstant;
|
||||
@ -25,7 +25,8 @@ import org.springblade.core.launch.constant.SentinelConstant;
|
||||
import org.springblade.core.launch.service.LauncherService;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@ -35,14 +36,16 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class BladeSpringRunner extends SpringJUnit4ClassRunner {
|
||||
public class BladeSpringExtension extends SpringExtension {
|
||||
|
||||
public BladeSpringRunner(Class<?> clazz) throws InitializationError {
|
||||
super(clazz);
|
||||
setUpTestClass(clazz);
|
||||
@Override
|
||||
public void beforeAll(@NonNull ExtensionContext context) throws Exception {
|
||||
super.beforeAll(context);
|
||||
setUpTestClass(context);
|
||||
}
|
||||
|
||||
private void setUpTestClass(Class<?> clazz) {
|
||||
private void setUpTestClass(ExtensionContext context) {
|
||||
Class<?> clazz = context.getRequiredTestClass();
|
||||
BladeBootTest bladeBootTest = AnnotationUtils.getAnnotation(clazz, BladeBootTest.class);
|
||||
if (bladeBootTest == null) {
|
||||
throw new BladeBootTestException(String.format("%s must be @BladeBootTest .", clazz));
|
||||
@ -74,7 +77,7 @@ public class BladeSpringRunner extends SpringJUnit4ClassRunner {
|
||||
launcherList.stream().sorted(Comparator.comparing(LauncherService::getOrder)).collect(Collectors.toList())
|
||||
.forEach(launcherService -> launcherService.launcher(builder, appName, profile));
|
||||
}
|
||||
System.err.println(String.format("---[junit.test]:[%s]---启动中,读取到的环境变量:[%s]", appName, profile));
|
||||
System.err.printf("---[junit.test]:[%s]---启动中,读取到的环境变量:[%s]%n", appName, profile);
|
||||
}
|
||||
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -41,7 +41,7 @@ import java.util.TimeZone;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
@AutoConfigureBefore(JacksonAutoConfiguration.class)
|
||||
|
@ -34,7 +34,7 @@ import java.util.List;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class MessageConfiguration implements WebMvcConfigurer {
|
||||
|
@ -41,7 +41,7 @@ import java.time.Duration;
|
||||
* @author Chill
|
||||
*/
|
||||
@EnableCaching
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureBefore(RedisAutoConfiguration.class)
|
||||
public class RedisTemplateConfiguration {
|
||||
|
||||
|
@ -28,7 +28,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class ToolConfiguration implements WebMvcConfigurer {
|
||||
|
||||
|
@ -33,7 +33,7 @@ import javax.servlet.DispatcherType;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnProperty(value = "blade.xss.enabled", havingValue = "true")
|
||||
@EnableConfigurationProperties({XssProperties.class, XssUrlProperties.class})
|
||||
|
@ -82,7 +82,7 @@ public abstract class AbstractReadWriteJackson2HttpMessageConverter extends Abst
|
||||
return false;
|
||||
}
|
||||
AtomicReference<Throwable> causeRef = new AtomicReference<>();
|
||||
if (this.objectMapper.canSerialize(clazz, causeRef)) {
|
||||
if (this.defaultObjectMapper.canSerialize(clazz, causeRef)) {
|
||||
return true;
|
||||
}
|
||||
logWarningIfNecessary(clazz, causeRef.get());
|
||||
|
@ -16,7 +16,7 @@
|
||||
package org.springblade.core.tool.support.xss;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@ -30,8 +30,9 @@ import java.io.IOException;
|
||||
@AllArgsConstructor
|
||||
public class XssFilter implements Filter {
|
||||
|
||||
private XssProperties xssProperties;
|
||||
private XssUrlProperties xssUrlProperties;
|
||||
private final XssProperties xssProperties;
|
||||
private final XssUrlProperties xssUrlProperties;
|
||||
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig config) {
|
||||
@ -50,8 +51,8 @@ public class XssFilter implements Filter {
|
||||
}
|
||||
|
||||
private boolean isSkip(String path) {
|
||||
return (xssUrlProperties.getExcludePatterns().stream().anyMatch(path::startsWith))
|
||||
|| (xssProperties.getSkipUrl().stream().map(url -> url.replace("/**", StringPool.EMPTY)).anyMatch(path::startsWith));
|
||||
return (xssUrlProperties.getExcludePatterns().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)))
|
||||
|| (xssProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3,6 +3,7 @@ package org.springblade.core.tool.utils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -69,13 +70,12 @@ public class RedisUtil {
|
||||
*
|
||||
* @param key 可以传一个值 或多个
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void del(String... key) {
|
||||
if (key != null && key.length > 0) {
|
||||
if (key.length == 1) {
|
||||
redisTemplate.delete(key[0]);
|
||||
} else {
|
||||
redisTemplate.delete(CollectionUtil.arrayToList(key));
|
||||
redisTemplate.delete(Arrays.asList(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ public class WebUtil extends org.springframework.web.util.WebUtils {
|
||||
* @param result 结果对象
|
||||
*/
|
||||
public static void renderJson(HttpServletResponse response, Object result) {
|
||||
renderJson(response, result, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
renderJson(response, result, MediaType.APPLICATION_JSON_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -15,11 +15,17 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!-- Blade-->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-mybatis</artifactId>
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-cloud</artifactId>
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<!-- Cloud-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
|
@ -0,0 +1,33 @@
|
||||
package com.alibaba.cloud.seata.feign;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 重写SeataFeignBlockingLoadBalancerClient以适配最新API
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class SeataFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient {
|
||||
|
||||
public SeataFeignBlockingLoadBalancerClient(Client delegate,
|
||||
BlockingLoadBalancerClient loadBalancerClient,
|
||||
SeataFeignObjectWrapper seataFeignObjectWrapper,
|
||||
LoadBalancerProperties properties,
|
||||
LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||
super((Client) seataFeignObjectWrapper.wrap(delegate), loadBalancerClient, properties, loadBalancerClientFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
return super.execute(request, options);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.cloud.seata.feign;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
import feign.Retryer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.cloud.feign.BladeFeignSentinel;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
/**
|
||||
* 重写SeataFeignClientAutoConfiguration以适配最新API
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Client.class)
|
||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||
public class SeataFeignClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnClass(name = "com.alibaba.csp.sentinel.SphU")
|
||||
@ConditionalOnProperty(name = "feign.sentinel.enabled", havingValue = "true")
|
||||
Feign.Builder feignSentinelBuilder(BeanFactory beanFactory) {
|
||||
return BladeFeignSentinel.builder().retryer(Retryer.NEVER_RETRY)
|
||||
.client(new SeataFeignClient(beanFactory));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Scope("prototype")
|
||||
Feign.Builder feignBuilder(BeanFactory beanFactory) {
|
||||
return SeataFeignBuilder.builder(beanFactory);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@RequiredArgsConstructor
|
||||
protected static class FeignBeanPostProcessorConfiguration {
|
||||
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
|
||||
private final LoadBalancerProperties properties;
|
||||
private final LoadBalancerClientFactory loadBalancerClientFactory;
|
||||
|
||||
@Bean
|
||||
SeataBeanPostProcessor seataBeanPostProcessor(SeataFeignObjectWrapper seataFeignObjectWrapper) {
|
||||
return new SeataBeanPostProcessor(seataFeignObjectWrapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
SeataContextBeanPostProcessor seataContextBeanPostProcessor(BeanFactory beanFactory) {
|
||||
return new SeataContextBeanPostProcessor(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
SeataFeignObjectWrapper seataFeignObjectWrapper(BeanFactory beanFactory) {
|
||||
return new SeataFeignObjectWrapper(beanFactory, loadBalancedRetryFactory, properties, loadBalancerClientFactory);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.cloud.seata.feign;
|
||||
|
||||
import feign.Client;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.openfeign.loadbalancer.RetryableFeignBlockingLoadBalancerClient;
|
||||
|
||||
/**
|
||||
* 重写SeataFeignObjectWrapper以适配最新API
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class SeataFeignObjectWrapper {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
|
||||
private final LoadBalancerProperties properties;
|
||||
private final LoadBalancerClientFactory loadBalancerClientFactory;
|
||||
|
||||
private SpringClientFactory springClientFactory;
|
||||
|
||||
SeataFeignObjectWrapper(BeanFactory beanFactory, LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
|
||||
this.properties = properties;
|
||||
this.loadBalancerClientFactory = loadBalancerClientFactory;
|
||||
}
|
||||
|
||||
Object wrap(Object bean) {
|
||||
if (bean instanceof Client && !(bean instanceof SeataFeignClient)) {
|
||||
if (bean instanceof FeignBlockingLoadBalancerClient) {
|
||||
FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) bean;
|
||||
return new SeataFeignBlockingLoadBalancerClient(client.getDelegate(),
|
||||
beanFactory.getBean(BlockingLoadBalancerClient.class), this, properties, loadBalancerClientFactory);
|
||||
}
|
||||
if (bean instanceof RetryableFeignBlockingLoadBalancerClient) {
|
||||
RetryableFeignBlockingLoadBalancerClient client = (RetryableFeignBlockingLoadBalancerClient) bean;
|
||||
return new SeataRetryableFeignBlockingLoadBalancerClient(client.getDelegate(),
|
||||
beanFactory.getBean(BlockingLoadBalancerClient.class), this, loadBalancedRetryFactory, properties, loadBalancerClientFactory);
|
||||
}
|
||||
return new SeataFeignClient(this.beanFactory, (Client) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
SpringClientFactory clientFactory() {
|
||||
if (this.springClientFactory == null) {
|
||||
this.springClientFactory = this.beanFactory
|
||||
.getBean(SpringClientFactory.class);
|
||||
}
|
||||
return this.springClientFactory;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.cloud.seata.feign;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.cloud.openfeign.loadbalancer.RetryableFeignBlockingLoadBalancerClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 拓展SeataRetryableFeignBlockingLoadBalancerClient以适配最新API
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class SeataRetryableFeignBlockingLoadBalancerClient extends RetryableFeignBlockingLoadBalancerClient {
|
||||
|
||||
public SeataRetryableFeignBlockingLoadBalancerClient(Client delegate,
|
||||
BlockingLoadBalancerClient loadBalancerClient,
|
||||
SeataFeignObjectWrapper seataFeignObjectWrapper,
|
||||
LoadBalancedRetryFactory loadBalancedRetryFactory,
|
||||
LoadBalancerProperties properties,
|
||||
LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||
super((Client) seataFeignObjectWrapper.wrap(delegate), loadBalancerClient, loadBalancedRetryFactory, properties, loadBalancerClientFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
return super.execute(request, options);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, lengleng (wangiegie@gmail.com).
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,6 +19,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@ -33,7 +34,7 @@ import java.lang.annotation.*;
|
||||
@Inherited
|
||||
@EnableDiscoveryClient
|
||||
@EnableCircuitBreaker
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, RibbonAutoConfiguration.class})
|
||||
public @interface SeataCloudApplication {
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, lengleng (wangiegie@gmail.com).
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,7 +22,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class DataSourceConfiguration {
|
||||
|
||||
}
|
||||
|
37
pom.xml
37
pom.xml
@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<version>3.0.2</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>blade-tool</name>
|
||||
<description>
|
||||
@ -36,24 +36,26 @@
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<blade.tool.version>2.8.0</blade.tool.version>
|
||||
<blade.tool.version>3.0.2</blade.tool.version>
|
||||
|
||||
<java.version>1.8</java.version>
|
||||
<maven.plugin.version>3.8.0</maven.plugin.version>
|
||||
<swagger.version>2.10.5</swagger.version>
|
||||
<swagger.models.version>1.6.2</swagger.models.version>
|
||||
<knife4j.version>2.0.6</knife4j.version>
|
||||
<mybatis.plus.version>3.4.0</mybatis.plus.version>
|
||||
<knife4j.version>2.0.8</knife4j.version>
|
||||
<mybatis.plus.version>3.4.2</mybatis.plus.version>
|
||||
<mybatis.plus.generator.version>3.4.1</mybatis.plus.generator.version>
|
||||
<protostuff.version>1.6.0</protostuff.version>
|
||||
<disruptor.version>3.4.2</disruptor.version>
|
||||
<spring.boot.admin.version>2.3.0</spring.boot.admin.version>
|
||||
<spring.boot.admin.version>2.3.1</spring.boot.admin.version>
|
||||
<mica.auto.version>1.2.5</mica.auto.version>
|
||||
<alibaba.cloud.version>2.2.3.RELEASE</alibaba.cloud.version>
|
||||
<alibaba.seata.version>1.3.0</alibaba.seata.version>
|
||||
<alibaba.cloud.version>2.2.5.RELEASE</alibaba.cloud.version>
|
||||
<alibaba.nacos.version>2.0.0</alibaba.nacos.version>
|
||||
<alibaba.seata.version>1.4.1</alibaba.seata.version>
|
||||
<spring.plugin.version>2.0.0.RELEASE</spring.plugin.version>
|
||||
|
||||
<spring.boot.version>2.2.11.RELEASE</spring.boot.version>
|
||||
<spring.cloud.version>Hoxton.SR8</spring.cloud.version>
|
||||
<spring.boot.version>2.4.4</spring.boot.version>
|
||||
<spring.cloud.version>2020.0.2</spring.cloud.version>
|
||||
<spring.platform.version>Cairo-SR8</spring.platform.version>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@ -141,6 +143,11 @@
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>6.2.0.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-auto</artifactId>
|
||||
@ -270,6 +277,18 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Nexus -->
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.8</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>oss</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
|
Loading…
Reference in New Issue
Block a user