🎉 3.0.0.RELEASE 架构升级适配 SpringCloud 2020

This commit is contained in:
smallchill 2021-01-06 15:56:41 +08:00
parent 231fbd653f
commit 17d8fce5f7
79 changed files with 799 additions and 788 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.1-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的部署架构
* 项目分包明确,规范微服务的开发模式,使包与包之间的分工清晰。
## 架构图

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.8.0</version>
<version>3.0.0</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

@ -33,7 +33,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@MapperScan("org.springblade.**.mapper.**")
public class MybatisPlusConfiguration {

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

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

@ -97,7 +97,7 @@ mybatis-plus:
swagger:
title: SpringBlade 接口文档系统
description: SpringBlade 接口文档系统
version: 2.8.0
version: 3.0.0
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.0</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>

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,54 @@
/**
* 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 org.springblade.core.cloud.feign.BladeFeignSentinel;
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() {
return BladeFeignSentinel.builder();
}
@Bean
@ConditionalOnMissingBean
public BlockExceptionHandler blockExceptionHandler() {
return new BladeBlockExceptionHandler();
}
}

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,136 @@
/*
* 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.FeignContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
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) {
Object feignClientFactoryBean = Builder.this.applicationContext.getBean("&" + target.type().getName());
Class fallback = (Class) getFieldValue(feignClientFactoryBean, "fallback");
Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean, "fallbackFactory");
String name = (String) getFieldValue(feignClientFactoryBean, "name");
Object fallbackInstance;
FallbackFactory fallbackFactoryInstance;
// 判断fallback类型
if (void.class != fallback) {
fallbackInstance = getFromContext(name, "fallback", fallback, target.type());
return new BladeSentinelInvocationHandler(target, dispatch, new FallbackFactory.Default(fallbackInstance));
}
if (void.class != fallbackFactory) {
fallbackFactoryInstance = (FallbackFactory) getFromContext(name, "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();
}
private Object getFieldValue(Object instance, String fieldName) {
Field field = ReflectionUtils.findField(instance.getClass(), fieldName);
field.setAccessible(true);
try {
return field.get(instance);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
@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;
/**
* 重写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.0</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.0</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.0";
/**
* 基础包

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.0</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.0</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.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

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

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.8.0</version>
<version>3.0.0</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.0</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

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.8.0</version>
<version>3.0.0</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.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -41,7 +41,7 @@ import java.util.List;
*
* @author Chill
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableSwagger
@EnableConfigurationProperties(SwaggerProperties.class)
@Import(BeanValidatorPluginsConfiguration.class)

View File

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

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.8.0</version>
<version>3.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -1,30 +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.test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
/**
* boot test 基类
*
* @author L.cm
*/
@RunWith(BladeSpringRunner.class)
public abstract class BladeBaseTest extends AbstractJUnit4SpringContextTests {
}

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.0</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

@ -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.0</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 {
}

21
pom.xml
View File

@ -5,7 +5,7 @@
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.8.0</version>
<version>3.0.0</version>
<packaging>pom</packaging>
<name>blade-tool</name>
<description>
@ -36,24 +36,24 @@
</scm>
<properties>
<blade.tool.version>2.8.0</blade.tool.version>
<blade.tool.version>3.0.0</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.1</mybatis.plus.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.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.1</spring.boot.version>
<spring.cloud.version>2020.0.0</spring.cloud.version>
<spring.platform.version>Cairo-SR8</spring.platform.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -141,6 +141,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>