1.Social包在SpringBoot2.x移除問題
spring-boot-autoconfigure1.5x版本中支持facebook墩崩,領(lǐng)英和推特
官方文檔: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的話只能自己按照源碼重寫(復(fù)制粘貼)
官方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之間的區(qū)別
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.關(guān)于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();
}
區(qū)別:返回值由T變?yōu)镺ptional<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
(可以給默認(rèn)值)
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
的內(nèi)置方法index()
返回值由FieldIndex
變?yōu)?code>boolean
2.FieldIndex
枚舉
源碼對比
SpringBoot1.5
public enum FieldIndex {
not_analyzed,
analyzed,
no;
private FieldIndex() {
}
}
not_analyzed
:整個字段存儲為關(guān)鍵詞涎永,常用于漢字短語、郵箱等復(fù)雜的字符串鹿响;
analyzed
:通過默認(rèn)的standard分析器進(jìn)行分析羡微,詳細(xì)的分析規(guī)則參考這里
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();
}
}
可以發(fā)現(xiàn)內(nèi)部類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方法不存在
為了優(yōu)化訪問速度,應(yīng)對高并發(fā)绸贡,把頁面信息全部獲取出來存到redis緩存中盯蝴,需要用thymeleafViewResolver.getTemplateEngine().process("goodslist.html",ctx);
實現(xiàn)
ctx參數(shù)在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
參數(shù)在SpringBoot2.x
時用的是WebContext
,官方已經(jīng)把大部分的功能移到了IWebContext
接口下,用于區(qū)分邊界恃轩。
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;
}
}
其實區(qū)別就是在構(gòu)造方法结洼,SpringBoot2.x中剔除了ApplicationContext
過多的依賴,現(xiàn)在thymeleaf
渲染不再過多依賴spring
容器
解決方法:
SpringWebContext
換成WebContext
叉跛,構(gòu)造參數(shù)中刪除ApplicationContext
對象
6.Security中Md5PasswordEncoder廢棄處理
SpringBoot1.5中經(jīng)常用到Security
的Md5PasswordEncoder
進(jìn)行密碼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;
}
}
繼承關(guān)系如下
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()
是對原始密碼進(jìn)行加密,采用hash+salt方式筷厘,在方法中應(yīng)用系統(tǒng)得提供鹽值(salt)鸣峭。
isPasswordValid()
是用來驗證密碼是否正確的宏所,得提供三個參數(shù),加密后的密碼摊溶、原始密碼以及鹽值(salt)爬骤。缺點就是每次加密和解密都得提供鹽值,加密后的密碼是固定的莫换,而且接口實現(xiàn)復(fù)雜霞玄,存在多繼承和實現(xiàn)。
SpringBoot2.x
刪除了MD5
拉岁,因為它不再足夠安全,應(yīng)該使用Bcrypt
BCryptPasswordEncoder
方法采用SHA-256 +隨機鹽+密鑰
對密碼進(jìn)行加密坷剧。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;
}
}
}
繼承關(guān)系如下
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)
是對方法加密陵叽,入?yún)⒅挥性济艽a狞尔,而且每次獲取的加密后的密碼不一樣
matches(CharSequence rawPassword, String encodedPassword)
前一個參數(shù)為前端傳來的值,后一個為數(shù)據(jù)庫中需要對比的值(已加密存入數(shù)據(jù)庫的密碼)巩掺。是用來驗證密碼和加密后密碼是否一致的偏序,如果一致則返回true。優(yōu)點鹽值不用用戶提供锌半,每次隨機生成禽车,多重加密——迭代SHA-256算法+密鑰+隨機鹽來對密碼加密,大大增加密碼破解難度刊殉,而且接口實現(xiàn)簡單,不存在多繼承州胳。
7.Spring Boot異常處理相關(guān)類缺失問題
SpringBoot 1.5的org.springframework.boot.autoconfigure.web
包中
SpringBoot 2.x的org.springframework.boot.autoconfigure.web
包中
其中的ErrorAttributes
记焊,ErrorController
,DefaultErrorAttributes
在SpringBoot 2.x的時候都轉(zhuǎn)到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);
}
}
相關(guān)例子
相關(guān)代碼
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
也就是類型轉(zhuǎn)換異常遍膜,ServletRequestAttributes
不能強轉(zhuǎn)為WebRequest
解決方法
代碼修改為
public Object errorApiHandler(HttpServletRequest request,boolean includeStackTrace) {
WebRequest webRequest=new ServletWebRequest(request);
Map<String, Object> attr = this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
......
}