遇到的问题:程序运行时,连接数据库报错,如下图,
错误信息意思是访问被拒,先追踪报错信息到DruidDataSource.java这个类的init()这个方法的870行,如下图,
也就是说,在这个try的方法块里抛出了异常,开始从859行断点,进入createPhysicalConnection()方法,
调到这里,瞬间就明白了,密码是未解密的密文,当然连不上数据库了。顺着往下调,刚好找到了解决问题的办法,
Druid可以配置一个叫做passwordCallBack的参数,这个参数的值是一个全限定类名,这个类需要继承DruidPasswordCallback.java类,实现一个setProperties()方法,在这个方法里,可以将加密的密码通过Druid提供的ConfigTools.decrypt(publickey, password)方法进行解密,再通过DruidPasswordCallback.java类的父类PasswordCallback.java的setPassword()的方法赋值解密后的密码,再看上面图中1545行代码,password获得的值就是解密后的密码,请求连接数据库就没问题了。
整个问题处理的思路整理完了,下面把关键代码奉上。
import com.alibaba.druid.filter.config.ConfigTools;
import com.alibaba.druid.util.DruidPasswordCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;
public class DbPasswordCallback extends DruidPasswordCallback {
private static final Logger LOGGER = LoggerFactory.getLogger(DbPasswordCallback.class);
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
String password = (String) properties.get("password");
String publickey = (String) properties.get("publickey");
try {
String dbpassword = ConfigTools.decrypt(publickey, password);
setPassword(dbpassword.toCharArray());
} catch (Exception e) {
LOGGER.error("Druid ConfigTools.decrypt", e);
}
}
}
这个是密码回调类,主要处理解密逻辑。
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.sql.SQLException;
@Configuration
@MapperScan(basePackages = DbConfig.PACKAGE, sqlSessionFactoryRef = "sqlSessionFactory")
public class DbConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(DbConfig.class);
static final String PACKAGE = "com.xxx.xx.dao";
private static final String MAPPER_LOCATION = "classpath*:mapper/*.xml";
private static final String DOMAIN_PACKAGE = "com.xxx.xx.dao.pojo";
@Value("${spring.datasource.druid.url}")
private String dbUrl;
@Value("${spring.datasource.druid.username}")
private String username;
@Value("${spring.datasource.druid.password}")
private String password;
@Value("${spring.datasource.druid.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.type}")
private String dbType;
@Value("${spring.datasource.druid.initial-size}")
private int initialSize;
@Value("${spring.datasource.druid.min-idle}")
private int minIdle;
@Value("${spring.datasource.druid.max-active}")
private int maxActive;
@Value("${spring.datasource.druid.max-wait}")
private int maxWait;
@Value("${spring.datasource.druid.time-between-eviction-runs-millis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.druid.min-evictable-idle-time-millis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.validation-query}")
private String validationQuery;
@Value("${spring.datasource.druid.test-while-idle}")
private boolean testWhileIdle;
@Value("${spring.datasource.druid.test-on-borrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.druid.test-on-return}")
private boolean testOnReturn;
@Value("${spring.datasource.druid.pool-prepared-statements}")
private boolean poolPreparedStatements;
@Value("${spring.datasource.druid.max-pool-prepared-statement-per-connection-size}")
private int maxPoolPreparedStatementPerConnectionSize;
@Value("${spring.datasource.druid.filters}")
private String filters;
@Value("${spring.datasource.druid.connection-properties}")
private String connectionProperties;
@Value("${spring.datasource.druid.password-callback}")
private String passwordCallbackClassName;
@Bean(name = "dataSource")
@Primary
public DataSource dataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
datasource.setConnectionProperties(connectionProperties);
datasource.setDbType(dbType);
try {
datasource.setPasswordCallbackClassName(passwordCallbackClassName);
} catch (Exception e) {
LOGGER.error("druid configuration initialization passwordCallbackClassName", e);
}
try {
datasource.setFilters(filters);
} catch (SQLException e) {
LOGGER.error("druid configuration initialization filter", e);
}
return datasource;
}
@Bean(name = "transactionManager")
@Primary
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean(name = "sqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource)
throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(DbConfig.MAPPER_LOCATION));
sessionFactory.setTypeAliasesPackage(DOMAIN_PACKAGE);
sessionFactory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return sessionFactory.getObject();
}
}
这个是springboot配置Druid时需要显式注入一下数据源配置。
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
spring.datasource.druid.url=
spring.datasource.druid.username=
spring.datasource.druid.password=
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.initial-size=1
spring.datasource.druid.max-active=20
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-wait=60000
spring.datasource.druid.pool-prepared-statements=false
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
spring.datasource.druid.validation-query=SELECT 'x'
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.time-between-eviction-runs-millis=60000
spring.datasource.druid.min-evictable-idle-time-millis=300000
spring.datasource.druid.filters=stat
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.password-callback=com.xxx.xx.dao.util.DbPasswordCallback
spring.datasource.publicKey=
spring.datasource.druid.connection-properties=config.decrypt=true;publickey=${spring.datasource.publicKey};password=${spring.datasource.druid.password}
这个是springboot的application.properties配置文件。
摁,就这么多。