1.Social包在SpringBoot2.x移除问题

spring-boot-autoconfigure1.5x版本中支持facebook,领英和推特
官方文档:https://docs.spring.io/spring-boot/docs/1.5.18.RELEASE/api/

 
image.png

spring-boot-autoconfigure2.x中版本找不到了
官方文档:https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/api/

 
image.png

问题:遇到SocialAutoConfigurerAdapterSocialPropertiesSocialWebAutoConfigurerAdapter类不存在

解决方法:

不想引入1.5版本的springboot的话只能自己按照源码重写(复制粘贴)
官方Github也是这样写的:https://github.com/spring-projects/spring-social
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包

 
image.png

SpringBoot2.x源码中Jdbc包

 
image.png

遇到的问题:DataSourceBuilder在SpringBoot2.x不存在

解决方法:

引入spring-boot-starter-jdbc依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

源码对比

SpringBoot1.5DataSourceBuilder 源码

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.xspring-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.xspring-boot-starter-data-jpa依赖中的pom.xmlspring-data-jpa2.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.5spring-boot-starter-data-jpa依赖中的pom.xmlspring-data-jpa1.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);
}
}

在聚合查询中SpringBoot1.5用Terms.Order.count()是没问题的,在SpringBoot2.x中需要改成BucketOrder.count()

5.与Thymeleaf集成时SpringWebContext方法不存在

为了优化访问速度,应对高并发,把页面信息全部获取出来存到redis缓存中,需要用thymeleafViewResolver.getTemplateEngine().process("goodslist.html",ctx);实现

ctx参数在SpringBoot1.5中使用的是SpringWebContext

SpringWebContext源码

public class SpringWebContext extends WebContext {
public static final String BEANS_VARIABLE_NAME = "beans";
private static final ConcurrentHashMap<ApplicationContext, HashMap<String, Object>> variableMapPrototypes = new ConcurrentHashMap();
private final ApplicationContext applicationContext; public SpringWebContext(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, Locale locale, Map<String, ?> variables, ApplicationContext appctx) {
super(request, response, servletContext, locale, addSpringSpecificVariables(variables, appctx));
this.applicationContext = appctx;
} private static Map<String, Object> addSpringSpecificVariables(Map<String, ?> variables, ApplicationContext appctx) {
HashMap<String, Object> variableMapPrototype = (HashMap)variableMapPrototypes.get(appctx);
if (variableMapPrototype == null) {
variableMapPrototype = new HashMap(20, 1.0F);
Beans beans = new Beans(appctx);
variableMapPrototype.put("beans", beans);
variableMapPrototypes.put(appctx, variableMapPrototype);
}
Map newVariables;
synchronized(variableMapPrototype) {
newVariables = (Map)variableMapPrototype.clone();
}
if (variables != null) {
newVariables.putAll(variables);
}
return newVariables;
} public ApplicationContext getApplicationContext() {
return this.applicationContext;
}
}
ctx参数在SpringBoot2.x时用的是WebContext,官方已经把大部分的功能移到了IWebContext接口下,用于区分边界。

WebContext源码

public final class WebContext extends AbstractContext implements IWebContext {
private final HttpServletRequest request;
private final HttpServletResponse response;
private final ServletContext servletContext; public WebContext(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) {
this.request = request;
this.response = response;
this.servletContext = servletContext;
} public WebContext(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, Locale locale) {
super(locale);
this.request = request;
this.response = response;
this.servletContext = servletContext;
} public WebContext(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, Locale locale, Map<String, Object> variables) {
super(locale, variables);
this.request = request;
this.response = response;
this.servletContext = servletContext;
} public HttpServletRequest getRequest() {
return this.request;
} public HttpSession getSession() {
return this.request.getSession(false);
} public HttpServletResponse getResponse() {
return this.response;
} public ServletContext getServletContext() {
return this.servletContext;
}
}

其实区别就是在构造方法,SpringBoot2.x中剔除了ApplicationContext过多的依赖,现在thymeleaf渲染不再过多依赖spring容器

解决方法:

SpringWebContext换成WebContext,构造参数中删除ApplicationContext对象

6.Security中Md5PasswordEncoder废弃处理

SpringBoot1.5中经常用到SecurityMd5PasswordEncoder进行密码MD5加密和验证

Md5PasswordEncoder源码

public class Md5PasswordEncoder extends MessageDigestPasswordEncoder {
public Md5PasswordEncoder() {
super("MD5");
}
}

源码很简单,主要用到父类的方法
继承于MessageDigestPasswordEncoder

public class MessageDigestPasswordEncoder extends BaseDigestPasswordEncoder {
private final String algorithm;
private int iterations; public MessageDigestPasswordEncoder(String algorithm) {
this(algorithm, false);
} public MessageDigestPasswordEncoder(String algorithm, boolean encodeHashAsBase64) throws IllegalArgumentException {
this.iterations = 1;
this.algorithm = algorithm;
this.setEncodeHashAsBase64(encodeHashAsBase64);
this.getMessageDigest();
} public String encodePassword(String rawPass, Object salt) {
String saltedPass = this.mergePasswordAndSalt(rawPass, salt, false);
MessageDigest messageDigest = this.getMessageDigest();
byte[] digest = messageDigest.digest(Utf8.encode(saltedPass)); for(int i = 1; i < this.iterations; ++i) {
digest = messageDigest.digest(digest);
} return this.getEncodeHashAsBase64() ? Utf8.decode(Base64.encode(digest)) : new String(Hex.encode(digest));
} protected final MessageDigest getMessageDigest() throws IllegalArgumentException {
try {
return MessageDigest.getInstance(this.algorithm);
} catch (NoSuchAlgorithmException var2) {
throw new IllegalArgumentException("No such algorithm [" + this.algorithm + "]");
}
} public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
String pass1 = "" + encPass;
String pass2 = this.encodePassword(rawPass, salt);
return PasswordEncoderUtils.equals(pass1, pass2);
} public String getAlgorithm() {
return this.algorithm;
} public void setIterations(int iterations) {
Assert.isTrue(iterations > 0, "Iterations value must be greater than zero");
this.iterations = iterations;
}
}

继承关系如下

public class Md5PasswordEncoder extends MessageDigestPasswordEncoder
public class MessageDigestPasswordEncoder extends BaseDigestPasswordEncoder
public abstract class BaseDigestPasswordEncoder extends BasePasswordEncoder
public abstract class BasePasswordEncoder implements PasswordEncoder

PasswordEncoder接口

public interface PasswordEncoder {
String encodePassword(String var1, Object var2);
boolean isPasswordValid(String var1, String var2, Object var3);
}

encodePassword()是对原始密码进行加密,采用hash+salt方式,在方法中应用系统得提供盐值(salt)。
isPasswordValid()是用来验证密码是否正确的,得提供三个参数,加密后的密码、原始密码以及盐值(salt)。缺点就是每次加密和解密都得提供盐值,加密后的密码是固定的,而且接口实现复杂,存在多继承和实现。

SpringBoot2.x删除了MD5,因为它不再足够安全,应该使用Bcrypt

BCryptPasswordEncoder方法采用SHA-256 +随机盐+密钥对密码进行加密。SHA系列是Hash算法,不是加密算法,使用加密算法意味着可以解密(这个与编码/解码一样),但是采用Hash处理,其过程是不可逆的
BCryptPasswordEncoder源码

public class BCryptPasswordEncoder implements PasswordEncoder {
private Pattern BCRYPT_PATTERN;
private final Log logger;
private final int strength;
private final SecureRandom random; public BCryptPasswordEncoder() {
this(-1);
} public BCryptPasswordEncoder(int strength) {
this(strength, (SecureRandom)null);
} public BCryptPasswordEncoder(int strength, SecureRandom random) {
this.BCRYPT_PATTERN = Pattern.compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}");
this.logger = LogFactory.getLog(this.getClass());
if (strength == -1 || strength >= 4 && strength <= 31) {
this.strength = strength;
this.random = random;
} else {
throw new IllegalArgumentException("Bad strength");
}
} public String encode(CharSequence rawPassword) {
String salt;
if (this.strength > 0) {
if (this.random != null) {
salt = BCrypt.gensalt(this.strength, this.random);
} else {
salt = BCrypt.gensalt(this.strength);
}
} else {
salt = BCrypt.gensalt();
} return BCrypt.hashpw(rawPassword.toString(), salt);
} public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword != null && encodedPassword.length() != 0) {
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
this.logger.warn("Encoded password does not look like BCrypt");
return false;
} else {
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
} else {
this.logger.warn("Empty encoded password");
return false;
}
}
}

继承关系如下

public class BCryptPasswordEncoder implements PasswordEncoder

PasswordEncoder接口源码也有改变

public interface PasswordEncoder {
String encode(CharSequence var1);
boolean matches(CharSequence var1, String var2);
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}

encode(CharSequence rawPassword)是对方法加密,入参只有原始密码,而且每次获取的加密后的密码不一样
matches(CharSequence rawPassword, String encodedPassword) 前一个参数为前端传来的值,后一个为数据库中需要对比的值(已加密存入数据库的密码)。是用来验证密码和加密后密码是否一致的,如果一致则返回true。优点盐值不用用户提供,每次随机生成,多重加密——迭代SHA-256算法+密钥+随机盐来对密码加密,大大增加密码破解难度,而且接口实现简单,不存在多继承。

7.Spring Boot异常处理相关类缺失问题

SpringBoot 1.5的org.springframework.boot.autoconfigure.web包中
 
image.png
SpringBoot 2.x的org.springframework.boot.autoconfigure.web包中
 
image.png

其中的ErrorAttributesErrorControllerDefaultErrorAttributes在SpringBoot 2.x的时候都转到org.springframework.boot.web.servlet.error包中

ErrorAttributes接口源码对比
SpringBoot 1.5
public interface ErrorAttributes {
Map<String, Object> getErrorAttributes(RequestAttributes var1, boolean var2);
Throwable getError(RequestAttributes var1);
}
SpringBoot2.x
public interface ErrorAttributes {
Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace);
Throwable getError(WebRequest webRequest);
}
ErrorController接口源码对比
SpringBoot 1.5
public interface ErrorController {
String getErrorPath();
}
SpringBoot2.x
@FunctionalInterface
public interface ErrorController {
String getErrorPath();
}
DefaultErrorAttributes类源码对比
SpringBoot 1.5
@Order(-2147483648)
public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {
private static final String ERROR_ATTRIBUTE = DefaultErrorAttributes.class.getName() + ".ERROR"; public DefaultErrorAttributes() {
} public int getOrder() {
return -2147483648;
} public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
this.storeErrorAttributes(request, ex);
return null;
} private void storeErrorAttributes(HttpServletRequest request, Exception ex) {
request.setAttribute(ERROR_ATTRIBUTE, ex);
} public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap();
errorAttributes.put("timestamp", new Date());
this.addStatus(errorAttributes, requestAttributes);
this.addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
this.addPath(errorAttributes, requestAttributes);
return errorAttributes;
} private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
Integer status = (Integer)this.getAttribute(requestAttributes, "javax.servlet.error.status_code");
if (status == null) {
errorAttributes.put("status", 999);
errorAttributes.put("error", "None");
} else {
errorAttributes.put("status", status); try {
errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
} catch (Exception var5) {
errorAttributes.put("error", "Http Status " + status);
} }
} private void addErrorDetails(Map<String, Object> errorAttributes, RequestAttributes requestAttributes, boolean includeStackTrace) {
Throwable error = this.getError(requestAttributes);
if (error != null) {
while(true) {
if (!(error instanceof ServletException) || error.getCause() == null) {
errorAttributes.put("exception", error.getClass().getName());
this.addErrorMessage(errorAttributes, error);
if (includeStackTrace) {
this.addStackTrace(errorAttributes, error);
}
break;
} error = ((ServletException)error).getCause();
}
} Object message = this.getAttribute(requestAttributes, "javax.servlet.error.message");
if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null) && !(error instanceof BindingResult)) {
errorAttributes.put("message", StringUtils.isEmpty(message) ? "No message available" : message);
} } private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
BindingResult result = this.extractBindingResult(error);
if (result == null) {
errorAttributes.put("message", error.getMessage());
} else {
if (result.getErrorCount() > 0) {
errorAttributes.put("errors", result.getAllErrors());
errorAttributes.put("message", "Validation failed for object='" + result.getObjectName() + "'. Error count: " + result.getErrorCount());
} else {
errorAttributes.put("message", "No errors");
} }
} private BindingResult extractBindingResult(Throwable error) {
if (error instanceof BindingResult) {
return (BindingResult)error;
} else {
return error instanceof MethodArgumentNotValidException ? ((MethodArgumentNotValidException)error).getBindingResult() : null;
}
} private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) {
StringWriter stackTrace = new StringWriter();
error.printStackTrace(new PrintWriter(stackTrace));
stackTrace.flush();
errorAttributes.put("trace", stackTrace.toString());
} private void addPath(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
String path = (String)this.getAttribute(requestAttributes, "javax.servlet.error.request_uri");
if (path != null) {
errorAttributes.put("path", path);
} } public Throwable getError(RequestAttributes requestAttributes) {
Throwable exception = (Throwable)this.getAttribute(requestAttributes, ERROR_ATTRIBUTE);
if (exception == null) {
exception = (Throwable)this.getAttribute(requestAttributes, "javax.servlet.error.exception");
} return exception;
} private <T> T getAttribute(RequestAttributes requestAttributes, String name) {
return requestAttributes.getAttribute(name, 0);
}
}
SpringBoot2.x
@Order(-2147483648)
public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {
private static final String ERROR_ATTRIBUTE = DefaultErrorAttributes.class.getName() + ".ERROR";
private final boolean includeException; public DefaultErrorAttributes() {
this(false);
} public DefaultErrorAttributes(boolean includeException) {
this.includeException = includeException;
} public int getOrder() {
return -2147483648;
} public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
this.storeErrorAttributes(request, ex);
return null;
} private void storeErrorAttributes(HttpServletRequest request, Exception ex) {
request.setAttribute(ERROR_ATTRIBUTE, ex);
} public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap();
errorAttributes.put("timestamp", new Date());
this.addStatus(errorAttributes, webRequest);
this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);
this.addPath(errorAttributes, webRequest);
return errorAttributes;
} private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
Integer status = (Integer)this.getAttribute(requestAttributes, "javax.servlet.error.status_code");
if (status == null) {
errorAttributes.put("status", 999);
errorAttributes.put("error", "None");
} else {
errorAttributes.put("status", status); try {
errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
} catch (Exception var5) {
errorAttributes.put("error", "Http Status " + status);
} }
} private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest, boolean includeStackTrace) {
Throwable error = this.getError(webRequest);
if (error != null) {
while(true) {
if (!(error instanceof ServletException) || error.getCause() == null) {
if (this.includeException) {
errorAttributes.put("exception", error.getClass().getName());
} this.addErrorMessage(errorAttributes, error);
if (includeStackTrace) {
this.addStackTrace(errorAttributes, error);
}
break;
} error = ((ServletException)error).getCause();
}
} Object message = this.getAttribute(webRequest, "javax.servlet.error.message");
if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null) && !(error instanceof BindingResult)) {
errorAttributes.put("message", StringUtils.isEmpty(message) ? "No message available" : message);
} } private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
BindingResult result = this.extractBindingResult(error);
if (result == null) {
errorAttributes.put("message", error.getMessage());
} else {
if (result.hasErrors()) {
errorAttributes.put("errors", result.getAllErrors());
errorAttributes.put("message", "Validation failed for object='" + result.getObjectName() + "'. Error count: " + result.getErrorCount());
} else {
errorAttributes.put("message", "No errors");
} }
} private BindingResult extractBindingResult(Throwable error) {
if (error instanceof BindingResult) {
return (BindingResult)error;
} else {
return error instanceof MethodArgumentNotValidException ? ((MethodArgumentNotValidException)error).getBindingResult() : null;
}
} private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) {
StringWriter stackTrace = new StringWriter();
error.printStackTrace(new PrintWriter(stackTrace));
stackTrace.flush();
errorAttributes.put("trace", stackTrace.toString());
} private void addPath(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
String path = (String)this.getAttribute(requestAttributes, "javax.servlet.error.request_uri");
if (path != null) {
errorAttributes.put("path", path);
} } public Throwable getError(WebRequest webRequest) {
Throwable exception = (Throwable)this.getAttribute(webRequest, ERROR_ATTRIBUTE);
if (exception == null) {
exception = (Throwable)this.getAttribute(webRequest, "javax.servlet.error.exception");
} return exception;
} private <T> T getAttribute(RequestAttributes requestAttributes, String name) {
return requestAttributes.getAttribute(name, 0);
}
}

相关例子

相关代码
public Object errorApiHandler(HttpServletRequest request,boolean includeStackTrace) {
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
Map<String, Object> attr = this.errorAttributes.getErrorAttributes((WebRequest)requestAttributes,includeStackTrace);
......
}
这样写虽然不会报语法错误,但是在SpringBoot2.x运行时会报
Caused by: java.lang.ClassCastException: org.springframework.web.context.request.ServletRequestAttributes
cannot be cast to org.springframework.web.context.request.WebRequest

也就是类型转换异常,ServletRequestAttributes 不能强转为WebRequest

解决方法

代码修改为

public Object errorApiHandler(HttpServletRequest request,boolean includeStackTrace) {
WebRequest webRequest=new ServletWebRequest(request);
Map<String, Object> attr = this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
......
}

源码分析关于SpringBoot2.x版本与1.5版本之间的问题的更多相关文章

  1. STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort

    最近在看 侯捷的 STL源码分析,发现了以下的这个list 排序算法,乍眼看去,实在难以看出它是归并排序. 平常大家写归并排序,通常写的是 递归版本..为了效率的考虑,STL库 给出了如下的 归并排序 ...

  2. Android源码分析(七)-----如何解决java编译版本问题

    一 : 问题描述 Your version is: java version "1.6.0_31" Java(TM) SE Runtime Environment (build 1 ...

  3. [Abp 源码分析]零、文章目录

    0.系列文章目录 一.Abp 框架启动流程分析 二.模块系统 三.依赖注入 四.模块配置 五.系统设置 六.工作单元的实现 七.仓储与 Entity Framework Core 八.缓存管理 九.事 ...

  4. epoll(2) 使用及源码分析的引子

    epoll(2) 使用及源码分析的引子 本文代码取自内核版本 4.17 epoll(2) - I/O 事件通知设施. epoll 是内核在2.6版本后实现的,是对 select(2)/poll(2) ...

  5. Android源码分析(九)-----如何修改Android系统默认时间

    一 : 修改Android系统默认时间 源码路径:frameworks/base/services/java/com/android/server/SystemServer.java 主要变量EARL ...

  6. Android源码分析(八)-----系统启动流程&IPC简述

    一 :系统启动流程图 从下往上依次启动linux kernel -->zygote-->SystemServer-->NativeService-->AndroidServic ...

  7. DolphinScheduler源码分析

    DolphinScheduler源码分析 本博客是基于1.2.0版本进行分析,与最新版本的实现有一些出入,还请读者辩证的看待本源码分析.具体细节可能描述的不是很准确,仅供参考 源码版本 1.2.0 技 ...

  8. 【JDK】JDK源码分析-Semaphore

    概述 Semaphore 是并发包中的一个工具类,可理解为信号量.通常可以作为限流器使用,即限制访问某个资源的线程个数,比如用于限制连接池的连接数. 打个通俗的比方,可以把 Semaphore 理解为 ...

  9. 微前端框架 之 qiankun 从入门到源码分析

    封面 简介 从 single-spa 的缺陷讲起 -> qiankun 是如何从框架层面解决 single-spa 存在的问题 -> qiankun 源码解读,带你全方位刨析 qianku ...

随机推荐

  1. Vue.js源码中大量采用的ES6新特性介绍:模块、let、const

    1 关于ES6      ECMAScript6(以下简称ES6)是JavaScript语言的最新一代标准,发布于2015年6月,因为ECMA委员会决定从ES6起每年更新一次标准,因此ES6被改名为E ...

  2. 搭建exsi主机6.5版本

    1.服务器读取到镜像,进入此图: 2.回车或者F11安装进行下一步 至此exsi主机安装和配置IP完成(80和443端口的开关会影响远程登录) 在浏览器输入IP登录exsi主机 正常安装centos就 ...

  3. openwrt使用3G拔号的实践笔记

    参照文档: https://soha.moe/post/make-4g-wifi-ap-with-openwrt.html 步骤: 1.安装必要的包: opkg update opkg install ...

  4. 使用Cloudera Manager搭建Hive服务

      使用Cloudera Manager搭建Hive服务 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.安装Hive环境 1>.进入CM服务安装向导 2>.选择需要 ...

  5. Apache的虚拟主机

    一.虚拟主机的分类 基于IP的虚拟主机:一台服务器,多个ip,搭建多个网站 基于端口的虚拟主机:一台服务器,一个ip,利用不同端口,搭建多个网站 基于域名的虚拟主机:一台服务器,一个ip,多个域名,搭 ...

  6. Systemweaver — 电子电气协同设计研发平台

            当前电子电气系统随着功能安全.AutoSAR.车联网.智能驾驶等新要求,导致其复杂性.关联性日益上升.当前,传统基于文档的设计由于其低复用性.无关联性.无协同性等缺点,已经无法适应日益 ...

  7. 51nod 2488 矩形并的面积

    在二维平面上,给定两个矩形,满足矩形的每条边分别和坐标轴平行,求这个两个矩形的并的面积.即它们重叠在一起的总的面积. 收起   输入 8个数,分别表示第一个矩形左下角坐标为(A,B),右上角坐标为(C ...

  8. 苹果cms和海洋cms通用的百度主动推送工具

    百度主动推送的代码,不需要每天手动去添加地址推送,只要浏览器打开推送请求,不要关掉浏览器,程序自动帮你推送.(该插件只推送内容页,支持动态.伪静态.静态页面的推送,但这三种地址规则需要去代码里面自行拼 ...

  9. lstm-bp过程的手工源码实现

    近些年来,随着深度学习的崛起,RNN模型也变得非常热门.如果把RNN模型按照时间轴展开,它也类似其它的深度神经网络模型结构.因此,我们可以参照已有的方法训练RNN模型. 现在最流行的一种RNN模型是L ...

  10. http健康状态检查

    来自为知笔记(Wiz)