使用总结-
2022-01-05
测试时动态设置property/env
@DynamicPropertySource
@SpringJUnitConfig(...)
@Testcontainers
class ExampleIntegrationTests {
@Container
static RedisContainer redis = new RedisContainer();
// ...
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("redis.host", redis::getContainerIpAddress);
registry.add("redis.port", redis::getMappedPort);
}
}
2021-11-30
springboot command line arguments
https://www.baeldung.com/spring-boot-command-line-arguments
2021-10-06
打包信息(可依赖注入)
BuildProperties buildProperties;
自定义json格式
- 在字段上添加注解
@JsonFormat(shape = JsonFormat.Shape.STRING)
private BigDecimal decimal = BigDecimal.valueOf(100.01);
- 使用全局配置
@Configuration
public class JacksonConfiguration {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder.modules(modules())
// 自定义过滤器的方式,需要在类上添加注解@JsonFilter("myFilter")
.filters(new SimpleFilterProvider().addFilter("myFilter", new CurrencyPropertyFilter()))
.failOnUnknownProperties(false)
.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
.serializationInclusion(JsonInclude.Include.NON_EMPTY)
.featuresToEnable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)
;
}
public List<Module> modules() {
// 改写数据格式的方式
SimpleModule bigDecimalModule = new SimpleModule(PackageVersion.VERSION){
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.configOverride(BigDecimal.class)
.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
}
};
// 自定义序列化器的方式
JavaTimeModule timeModule = new JavaTimeModule();
timeModule.addDeserializer(LocalDateTime.class, new ZLocalDateTimeDeserializer());
timeModule.addSerializer(LocalDateTime.class, new ZLocalDateTimeSerializer());
return Lists.newArrayList(bigDecimalModule, timeModule);
}
}
- 自定义过滤器的方式拦截处理
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class CurrencyPropertyFilter extends SimpleBeanPropertyFilter {
private static final DecimalFormat FORMAT = new DecimalFormat("0.00000000");
@Override
public void serializeAsField(Object pojo, JsonGenerator jgen,
SerializerProvider provider, PropertyWriter writer) throws Exception {
CurrencyJson currencyJson = writer.findAnnotation(CurrencyJson.class);
if (currencyJson != null) {
Currency currency = null;
// retrieve currency
String unitCode = currencyJson.unitCode();
if (StringUtils.isNotBlank(unitCode)) {
currency = Currency.resolve(unitCode);
} else {
String unitFieldName = currencyJson.unitFieldName();
Field currencyField = ReflectionUtils.findField(pojo.getClass(), unitFieldName);
if (currencyField != null) {
currencyField.setAccessible(true);
currency = (Currency) currencyField.get(pojo);
}
}
if (currency != null) {
String writerFieldName = writer.getName();
Field writerField = ReflectionUtils.findField(pojo.getClass(), writerFieldName);
if (writerField != null) {
writerField.setAccessible(true);
BigDecimal amount = (BigDecimal) writerField.get(pojo);
if (currency == VirtualCurrency.DIAMOND || currency == VirtualCurrency.XP) {
jgen.writeNumberField(writerFieldName, amount.intValue());
} else {
jgen.writeStringField(writerFieldName, FORMAT.format(amount));
}
return;
}
}
}
super.serializeAsField(pojo, jgen, provider, writer);
}
}
2021-09-07
spring泛型解析(工具)
public interface AppRepository extends JpaRepository<App, UUID>, JpaSpecificationExecutor<App> {}
@Nullable
private ResolvableType[] getRequiredTypeInfo(Class<?> converterClass, Class<?> genericIfc) {
ResolvableType resolvableType = ResolvableType.forClass(converterClass).as(genericIfc);
ResolvableType[] generics = resolvableType.getGenerics();
if (generics.length < 2) {
return null;
}
Class<?> sourceType = generics[0].resolve();
Class<?> targetType = generics[1].resolve();
if (sourceType == null || targetType == null) {
return null;
}
return generics;
}
public static void main(String[] args) {
ResolvableType resolvableType = ResolvableType.forClass(AppRepository.class).as(JpaRepository.class);
ResolvableType[] generics = resolvableType.getGenerics();
if (generics.length < 2) {
// return null;
}
Class<?> sourceType = generics[0].resolve();
Class<?> targetType = generics[1].resolve();
// if (sourceType == null || targetType == null) {
// return null;
// }
// return generics;
System.out.println(sourceType);
}
2021-09-02
Cache
https://www.baeldung.com/spring-cache-tutorial
https://www.baeldung.com/spring-boot-evict-cache
2021-07-23
@Profile
- 支持简单的 &(与) |(或) i(非) 运算
- 不要让逻辑运算结果不明确,(尴尬的没记录,印象中是逻辑运算与括号混合使用情况下容易出现)
- 底层原理如下代码所示,主要是依赖Predicate的test方法,每次传入表达式的部分进行比较
public static void main(String[] args) {
Profiles profiles = Profiles.of("(spring | framework)");
boolean res = profiles.matches(activeProfiles("test"));
System.out.println("haha" + res);
}
private static Predicate<String> activeProfiles(String... profiles) {
return new MockActiveProfiles(profiles);
}
private static class MockActiveProfiles implements Predicate<String> {
private final List<String> activeProfiles;
MockActiveProfiles(String[] activeProfiles) {
this.activeProfiles = Arrays.asList(activeProfiles);
}
@Override
public boolean test(String profile) {
// The following if-condition (which basically mimics
// AbstractEnvironment#validateProfile(String)) is necessary in order
// to ensure that the Profiles implementation returned by Profiles.of()
// never passes an invalid (parsed) profile name to the active profiles
// predicate supplied to Profiles#matches(Predicate<String>).
if (!StringUtils.hasText(profile) || profile.charAt(0) == '!') {
throw new IllegalArgumentException("Invalid profile [" + profile + "]");
}
return this.activeProfiles.contains(profile);
}
}
@Conditional
-
@ConditionalOnProperty
判断environment中是否有指定属性变量,
在springboot中大量使用
-
@ConditionalOnExpression
根据SpEL表达式计算条件是否满足
表达式的返回值必须时 TRUE 或者 FALSE
@ConditionalOnExpression("!'${flyway.locations}'.isEmpty()")