关于SpringBoot2.x版本与1.5版本之间的问题(持续更
1.Social包在SpringBoot2.x移除问题
spring-boot-autoconfigure1.5x版本中支持facebook,领英和推特
官方文档:https://docs.spring.io/spring-boot/docs/1.5.18.RELEASE/api/
spring-boot-autoconfigure2.x中版本找不到了
官方文档:https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/api/
问题:遇到SocialAutoConfigurerAdapter
,SocialProperties
和SocialWebAutoConfigurerAdapter
类不存在
解决方法:
不想引入1.5版本的springboot的话只能自己按照源码重写(复制粘贴)
SocialAutoConfigurerAdapter
源码
public abstract class SocialAutoConfigurerAdapter extends SocialConfigurerAdapter {
public SocialAutoConfigurerAdapter() {
}
public void addConnectionFactories(ConnectionFactoryConfigurer configurer, Environment environment) {
configurer.addConnectionFactory(this.createConnectionFactory());
}
protected abstract ConnectionFactory<?> createConnectionFactory();
}
SocialProperties
源码
public abstract class SocialProperties {
private String appId;
private String appSecret;
public SocialProperties() {
}
public String getAppId() {
return this.appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return this.appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
}
SocialWebAutoConfiguration
源码
@Configuration
@ConditionalOnClass({ConnectController.class, SocialConfigurerAdapter.class})
@ConditionalOnBean({ConnectionFactoryLocator.class, UsersConnectionRepository.class})
@AutoConfigureBefore({ThymeleafAutoConfiguration.class})
@AutoConfigureAfter({WebMvcAutoConfiguration.class})
public class SocialWebAutoConfiguration {
public SocialWebAutoConfiguration() {
}
private static class SecurityContextUserIdSource implements UserIdSource {
private SecurityContextUserIdSource() {
}
public String getUserId() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
Assert.state(authentication != null, "Unable to get a ConnectionRepository: no user signed in");
return authentication.getName();
}
}
@Configuration
@ConditionalOnClass({SpringResourceResourceResolver.class})
protected static class SpringSocialThymeleafConfig {
protected SpringSocialThymeleafConfig() {
}
@Bean
@ConditionalOnMissingBean
public SpringSocialDialect springSocialDialect() {
return new SpringSocialDialect();
}
}
@Configuration
@EnableSocial
@ConditionalOnWebApplication
@ConditionalOnClass({SecurityContextHolder.class})
protected static class AuthenticationUserIdSourceConfig extends SocialConfigurerAdapter {
protected AuthenticationUserIdSourceConfig() {
}
public UserIdSource getUserIdSource() {
return new SocialWebAutoConfiguration.SecurityContextUserIdSource();
}
}
@Configuration
@EnableSocial
@ConditionalOnWebApplication
@ConditionalOnMissingClass({"org.springframework.security.core.context.SecurityContextHolder"})
protected static class AnonymousUserIdSourceConfig extends SocialConfigurerAdapter {
protected AnonymousUserIdSourceConfig() {
}
public UserIdSource getUserIdSource() {
return new UserIdSource() {
public String getUserId() {
return "anonymous";
}
};
}
}
@Configuration
@EnableSocial
@ConditionalOnWebApplication
protected static class SocialAutoConfigurationAdapter extends SocialConfigurerAdapter {
private final List<ConnectInterceptor<?>> connectInterceptors;
private final List<DisconnectInterceptor<?>> disconnectInterceptors;
private final List<ProviderSignInInterceptor<?>> signInInterceptors;
public SocialAutoConfigurationAdapter(ObjectProvider<List<ConnectInterceptor<?>>> connectInterceptorsProvider, ObjectProvider<List<DisconnectInterceptor<?>>> disconnectInterceptorsProvider, ObjectProvider<List<ProviderSignInInterceptor<?>>> signInInterceptorsProvider) {
this.connectInterceptors = (List)connectInterceptorsProvider.getIfAvailable();
this.disconnectInterceptors = (List)disconnectInterceptorsProvider.getIfAvailable();
this.signInInterceptors = (List)signInInterceptorsProvider.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean({ConnectController.class})
public ConnectController connectController(ConnectionFactoryLocator factoryLocator, ConnectionRepository repository) {
ConnectController controller = new ConnectController(factoryLocator, repository);
if (!CollectionUtils.isEmpty(this.connectInterceptors)) {
controller.setConnectInterceptors(this.connectInterceptors);
}
if (!CollectionUtils.isEmpty(this.disconnectInterceptors)) {
controller.setDisconnectInterceptors(this.disconnectInterceptors);
}
return controller;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(
prefix = "spring.social",
name = {"auto-connection-views"}
)
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver viewResolver = new BeanNameViewResolver();
viewResolver.setOrder(-2147483648);
return viewResolver;
}
@Bean
@ConditionalOnBean({SignInAdapter.class})
@ConditionalOnMissingBean
public ProviderSignInController signInController(ConnectionFactoryLocator factoryLocator, UsersConnectionRepository usersRepository, SignInAdapter signInAdapter) {
ProviderSignInController controller = new ProviderSignInController(factoryLocator, usersRepository, signInAdapter);
if (!CollectionUtils.isEmpty(this.signInInterceptors)) {
controller.setSignInInterceptors(this.signInInterceptors);
}
return controller;
}
}
}
2.Jdbc包在SpringBoot1.5和2.x之间的区别
SpringBoot1.5源码中Jdbc包
SpringBoot2.x源码中Jdbc包
遇到的问题:DataSourceBuilder在SpringBoot2.x不存在
解决方法:
引入spring-boot-starter-jdbc
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
源码对比
SpringBoot1.5
中DataSourceBuilder
源码
public class DataSourceBuilder {
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[]{"org.apache.tomcat.jdbc.pool.DataSource", "com.zaxxer.hikari.HikariDataSource", "org.apache.commons.dbcp.BasicDataSource", "org.apache.commons.dbcp2.BasicDataSource"};
private Class<? extends DataSource> type;
private ClassLoader classLoader;
private Map<String, String> properties = new HashMap();
public static DataSourceBuilder create() {
return new DataSourceBuilder((ClassLoader)null);
}
public static DataSourceBuilder create(ClassLoader classLoader) {
return new DataSourceBuilder(classLoader);
}
public DataSourceBuilder(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public DataSource build() {
Class<? extends DataSource> type = this.getType();
DataSource result = (DataSource)BeanUtils.instantiate(type);
this.maybeGetDriverClassName();
this.bind(result);
return result;
}
private void maybeGetDriverClassName() {
if (!this.properties.containsKey("driverClassName") && this.properties.containsKey("url")) {
String url = (String)this.properties.get("url");
String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
this.properties.put("driverClassName", driverClass);
}
}
private void bind(DataSource result) {
MutablePropertyValues properties = new MutablePropertyValues(this.properties);
(new RelaxedDataBinder(result)).withAlias("url", new String[]{"jdbcUrl"}).withAlias("username", new String[]{"user"}).bind(properties);
}
public DataSourceBuilder type(Class<? extends DataSource> type) {
this.type = type;
return this;
}
public DataSourceBuilder url(String url) {
this.properties.put("url", url);
return this;
}
public DataSourceBuilder driverClassName(String driverClassName) {
this.properties.put("driverClassName", driverClassName);
return this;
}
public DataSourceBuilder username(String username) {
this.properties.put("username", username);
return this;
}
public DataSourceBuilder password(String password) {
this.properties.put("password", password);
return this;
}
public Class<? extends DataSource> findType() {
if (this.type != null) {
return this.type;
} else {
String[] var1 = DATA_SOURCE_TYPE_NAMES;
int var2 = var1.length;
int var3 = 0;
while(var3 < var2) {
String name = var1[var3];
try {
return ClassUtils.forName(name, this.classLoader);
} catch (Exception var6) {
++var3;
}
}
return null;
}
}
private Class<? extends DataSource> getType() {
Class<? extends DataSource> type = this.findType();
if (type != null) {
return type;
} else {
throw new IllegalStateException("No supported DataSource type found");
}
}
}
SpringBoot2.x
的spring-boot-starter-jdbc
依赖中DataSourceBuilder
源码
public final class DataSourceBuilder<T extends DataSource> {
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[]{"com.zaxxer.hikari.HikariDataSource", "org.apache.tomcat.jdbc.pool.DataSource", "org.apache.commons.dbcp2.BasicDataSource"};
private Class<? extends DataSource> type;
private ClassLoader classLoader;
private Map<String, String> properties = new HashMap();
public static DataSourceBuilder<?> create() {
return new DataSourceBuilder((ClassLoader)null);
}
public static DataSourceBuilder<?> create(ClassLoader classLoader) {
return new DataSourceBuilder(classLoader);
}
private DataSourceBuilder(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public T build() {
Class<? extends DataSource> type = this.getType();
DataSource result = (DataSource)BeanUtils.instantiateClass(type);
this.maybeGetDriverClassName();
this.bind(result);
return result;
}
private void maybeGetDriverClassName() {
if (!this.properties.containsKey("driverClassName") && this.properties.containsKey("url")) {
String url = (String)this.properties.get("url");
String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
this.properties.put("driverClassName", driverClass);
}
}
private void bind(DataSource result) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(this.properties);
ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
aliases.addAliases("url", new String[]{"jdbc-url"});
aliases.addAliases("username", new String[]{"user"});
Binder binder = new Binder(new ConfigurationPropertySource[]{source.withAliases(aliases)});
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
}
public <D extends DataSource> DataSourceBuilder<D> type(Class<D> type) {
this.type = type;
return this;
}
public DataSourceBuilder<T> url(String url) {
this.properties.put("url", url);
return this;
}
public DataSourceBuilder<T> driverClassName(String driverClassName) {
this.properties.put("driverClassName", driverClassName);
return this;
}
public DataSourceBuilder<T> username(String username) {
this.properties.put("username", username);
return this;
}
public DataSourceBuilder<T> password(String password) {
this.properties.put("password", password);
return this;
}
public static Class<? extends DataSource> findType(ClassLoader classLoader) {
String[] var1 = DATA_SOURCE_TYPE_NAMES;
int var2 = var1.length;
int var3 = 0;
while(var3 < var2) {
String name = var1[var3];
try {
return ClassUtils.forName(name, classLoader);
} catch (Exception var6) {
++var3;
}
}
return null;
}
private Class<? extends DataSource> getType() {
Class<? extends DataSource> type = this.type != null ? this.type : findType(this.classLoader);
if (type != null) {
return type;
} else {
throw new IllegalStateException("No supported DataSource type found");
}
}
}
3.关于SpringDataJpa中findOne()方法报错问题
报错信息Inferred type 'S' for type parameter 'S' is not within its bound;should extends xxxxxx
解决方法:
1.用回SpringBoot1.5
2.findOne()
改为findById().orElse(null)
源码对比
SpringBoot2.x
的spring-boot-starter-data-jpa
依赖中的pom.xml
中spring-data-jpa
是2.x.x
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.3.RELEASE</version>
CrudRepository源码
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S var1);
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
Optional<T> findById(ID var1);
boolean existsById(ID var1);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> var1);
long count();
void deleteById(ID var1);
void delete(T var1);
void deleteAll(Iterable<? extends T> var1);
void deleteAll();
}
SpringBoot1.5
的spring-boot-starter-data-jpa
依赖中的pom.xml
中spring-data-jpa
是1.x.x
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.17.RELEASE</version>
CrudRepository源码
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S var1);
<S extends T> Iterable<S> save(Iterable<S> var1);
T findOne(ID var1);
boolean exists(ID var1);
Iterable<T> findAll();
Iterable<T> findAll(Iterable<ID> var1);
long count();
void delete(ID var1);
void delete(T var1);
void delete(Iterable<? extends T> var1);
void deleteAll();
}
区别:返回值由T变为Optional<T>,
Optional类是Java8新特性类库:
官方介绍:https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
Optional<T>
源码
public final class Optional<T> {
private static final Optional<?> EMPTY = new Optional<>();
private final T value;
private Optional() {
this.value = null;
}
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
public boolean isPresent() {
return value != null;
}
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
public T orElse(T other) {
return value != null ? value : other;
}
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Optional)) {
return false;
}
Optional<?> other = (Optional<?>) obj;
return Objects.equals(value, other.value);
}
@Override
public int hashCode() {
return Objects.hashCode(value);
}
@Override
public String toString() {
return value != null
? String.format("Optional[%s]", value)
: "Optional.empty";
}
}
get()
可以获取到值,但是直接这样写的话如果值不存在就要抛异常。所以要先做判断,值存在再get()
,或者就是写在try-catch
里
orElse(null)
存在就会直接返回值,如果不存在会返回别的值,这里不存在返回的是null
(可以给默认值)
4.Elasticsearch与springboot集成的问题
1.注释@Field
的变化
源码对比
SpringBoot1.5
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
@Inherited
public @interface Field {
FieldType type() default FieldType.Auto;
FieldIndex index() default FieldIndex.analyzed;
DateFormat format() default DateFormat.none;
String pattern() default "";
boolean store() default false;
String searchAnalyzer() default "";
String analyzer() default "";
String[] ignoreFields() default {};
boolean includeInParent() default false;
}
SpringBoot2.x
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
@Inherited
public @interface Field {
FieldType type() default FieldType.Auto;
boolean index() default true;
DateFormat format() default DateFormat.none;
String pattern() default "";
boolean store() default false;
boolean fielddata() default false;
String searchAnalyzer() default "";
String analyzer() default "";
String normalizer() default "";
String[] ignoreFields() default {};
boolean includeInParent() default false;
String[] copyTo() default {};
}
注解@Field
的内置方法index()
返回值由FieldIndex
变为boolean
2.FieldIndex
枚举
源码对比
SpringBoot1.5
public enum FieldIndex {
not_analyzed,
analyzed,
no;
private FieldIndex() {
}
}
not_analyzed
:整个字段存储为关键词,常用于汉字短语、邮箱等复杂的字符串;
analyzed
:通过默认的standard分析器进行分析,详细的分析规则参考这里
no
:无法通过检索查询到该字段;
SpringBoot2.x
public enum FieldType {
Text,
Integer,
Long,
Date,
Float,
Double,
Boolean,
Object,
Auto,
Nested,
Ip,
Attachment,
Keyword;
private FieldType() {
}
}
3.在Elasticsearch与springboot集成中变化较大的还有Terms
接口
源码对比
SpringBoot1.5
public interface Terms extends MultiBucketsAggregation {
List<Terms.Bucket> getBuckets();
Terms.Bucket getBucketByKey(String var1);
long getDocCountError();
long getSumOfOtherDocCounts();
public abstract static class Order implements ToXContent {
public Order() {
}
public static Terms.Order count(boolean asc) {
return asc ? InternalOrder.COUNT_ASC : InternalOrder.COUNT_DESC;
}
public static Terms.Order term(boolean asc) {
return asc ? InternalOrder.TERM_ASC : InternalOrder.TERM_DESC;
}
public static Terms.Order aggregation(String path, boolean asc) {
return new Aggregation(path, asc);
}
public static Terms.Order aggregation(String aggregationName, String metricName, boolean asc) {
return new Aggregation(aggregationName + "." + metricName, asc);
}
public static Terms.Order compound(List<Terms.Order> orders) {
return new CompoundOrder(orders);
}
public static Terms.Order compound(Terms.Order... orders) {
return compound(Arrays.asList(orders));
}
protected abstract Comparator<Terms.Bucket> comparator(Aggregator var1);
abstract byte id();
}
public abstract static class Bucket extends InternalBucket {
public Bucket() {
}
public abstract Number getKeyAsNumber();
abstract int compareTerm(Terms.Bucket var1);
public abstract long getDocCountError();
}
public static enum ValueType {
STRING(org.elasticsearch.search.aggregations.support.ValueType.STRING),
LONG(org.elasticsearch.search.aggregations.support.ValueType.LONG),
DOUBLE(org.elasticsearch.search.aggregations.support.ValueType.DOUBLE);
final org.elasticsearch.search.aggregations.support.ValueType scriptValueType;
private ValueType(org.elasticsearch.search.aggregations.support.ValueType scriptValueType) {
this.scriptValueType = scriptValueType;
}
static Terms.ValueType resolveType(String type) {
if ("string".equals(type)) {
return STRING;
} else if (!"double".equals(type) && !"float".equals(type)) {
return !"long".equals(type) && !"integer".equals(type) && !"short".equals(type) && !"byte".equals(type) ? null : LONG;
} else {
return DOUBLE;
}
}
}
}
SpringBoot2.x
public interface Terms extends MultiBucketsAggregation {
List<? extends Terms.Bucket> getBuckets();
Terms.Bucket getBucketByKey(String var1);
long getDocCountError();
long getSumOfOtherDocCounts();
public interface Bucket extends org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket {
Number getKeyAsNumber();
long getDocCountError();
}
}
可以发现内部类Order
并没有在Terms
中,而是变成了抽象类BucketOrder
public abstract class BucketOrder implements ToXContentObject, Writeable {
public BucketOrder() {
}
public static BucketOrder count(boolean asc) {
return asc ? InternalOrder.COUNT_ASC : InternalOrder.COUNT_DESC;
}
public static BucketOrder key(boolean asc) {
return asc ? InternalOrder.KEY_ASC : InternalOrder.KEY_DESC;
}
public static BucketOrder aggregation(String path, boolean asc) {
return new Aggregation(path, asc);
}
public static BucketOrder aggregation(String path, String metricName, boolean asc) {
return new Aggregation(path + "." + metricName, asc);
}
public static BucketOrder compound(List<BucketOrder> orders) {
return new CompoundOrder(orders);
}
public static BucketOrder compound(BucketOrder... orders) {
return compound(Arrays.asList(orders));
}
public abstract Comparator<Bucket> comparator(Aggregator var1);
abstract byte id();
public abstract int hashCode();
public abstract boolean equals(Object var1);
public void writeTo(StreamOutput out) throws IOException {
Streams.writeOrder(this, out);
}
public String toString() {
return Strings.toString(this);
}
}