Merge pull request #4 from chillzhuang/master

fork push
This commit is contained in:
k 2021-04-27 14:58:36 +08:00 committed by GitHub
commit 226e2d751a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 970 additions and 835 deletions

View File

@ -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)

View File

@ -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>

View File

@ -30,7 +30,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
* @author Chill
*/
@Slf4j
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({
BladeProperties.class
})

View File

@ -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 {

View File

@ -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;
}

View File

@ -28,7 +28,7 @@ import org.springframework.retry.interceptor.RetryOperationsInterceptor;
* @author Chill
*/
@Slf4j
@Configuration
@Configuration(proxyBeanMethods = false)
public class RetryConfiguration {
@Bean

View File

@ -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 {

View File

@ -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;
}

View File

@ -29,7 +29,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@AutoConfigureBefore(MybatisPlusConfiguration.class)
@EnableConfigurationProperties(BladeTenantProperties.class)

View File

@ -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

View File

@ -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>

View File

@ -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 {
}

View File

@ -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();
}
}

View File

@ -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类

View File

@ -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);
}
}

View File

@ -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());

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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 {
/**
* 账号信息获取器

View File

@ -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;
/**

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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(

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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())));
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -27,7 +27,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author L.cm
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
public class VersionMappingAutoConfiguration {
@Bean

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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-->

View File

@ -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());

View File

@ -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);

View File

@ -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>

View File

@ -30,7 +30,7 @@ import org.springframework.util.StringUtils;
* @author Chill
*/
@Slf4j
@Configuration
@Configuration(proxyBeanMethods = false)
public class StartEventListener {
@Async

View File

@ -27,7 +27,7 @@ import org.springframework.core.annotation.Order;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
@EnableConfigurationProperties({

View File

@ -25,7 +25,7 @@ public interface AppConstant {
/**
* 应用版本
*/
String APPLICATION_VERSION = "2.8.0";
String APPLICATION_VERSION = "3.0.2";
/**
* 基础包

View File

@ -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;

View File

@ -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>

View File

@ -41,7 +41,7 @@ import javax.servlet.Servlet;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@ConditionalOnWebApplication
@AutoConfigureBefore(ErrorMvcAutoConfiguration.class)

View File

@ -34,7 +34,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@ConditionalOnWebApplication
public class BladeLogToolAutoConfiguration {

View File

@ -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);
}

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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);

View 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;

View File

@ -37,7 +37,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@AutoConfigureAfter(QiniuConfiguration.class)
@EnableConfigurationProperties(OssProperties.class)

View File

@ -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

View File

@ -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>

View File

@ -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")

View File

@ -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>

View File

@ -29,7 +29,7 @@ import org.springframework.core.annotation.Order;
* @author Chill
*/
@Order
@Configuration
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(SecureConfiguration.class)
public class RegistryConfiguration {

View File

@ -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 {

View File

@ -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;

View File

@ -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) {

View File

@ -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>

View File

@ -29,7 +29,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SocialProperties.class)
public class SocialConfiguration {

View File

@ -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>

View File

@ -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) {

View File

@ -55,7 +55,7 @@ public class SwaggerProperties {
/**
* 版本
**/
private String version = "2.8.0";
private String version = "3.0.2";
/**
* 许可证
**/

View File

@ -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>

View File

@ -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

View File

@ -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);
}
}

View File

@ -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>

View File

@ -41,7 +41,7 @@ import java.util.TimeZone;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@ConditionalOnClass(ObjectMapper.class)
@AutoConfigureBefore(JacksonAutoConfiguration.class)

View File

@ -34,7 +34,7 @@ import java.util.List;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MessageConfiguration implements WebMvcConfigurer {

View File

@ -41,7 +41,7 @@ import java.time.Duration;
* @author Chill
*/
@EnableCaching
@Configuration
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisTemplateConfiguration {

View File

@ -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 {

View File

@ -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})

View File

@ -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());

View File

@ -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

View File

@ -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));
}
}
}

View File

@ -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);
}
/**

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 {
}

View File

@ -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
View File

@ -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>