最近遇到一个很头疼的问题,就是我把测试环境的项目(测试环境使用win7系统)部署到生产环境时(生产环境使用winserver 2008 r2系统),数据库连接报出如下的错误:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in file [D:\tomcat\webapps\xys-web\WEB-INF\classes\spring\spring-to-dao.xml]: Invocation of init method failed; nested exception is java.sql.SQLException: Access denied for user 'administrator'@'58.32.162.450' (using password: YES)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5118)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5634)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1571)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1561)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.sql.SQLException: Access denied for user 'administrator'@'58.32.162.450'(using password: YES)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1084)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4232)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4164)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:926)
at com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:1748)
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1288)
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2506)
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2539)
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2321)
at com.mysql.jdbc.ConnectionImpl.
at com.mysql.jdbc.JDBC4Connection.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:417)
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:344)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1375)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1431)
at com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource.java:632)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1681)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1620)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)
... 21 more
一开始我以为是我数据库密码错了,然后去检查,后来发现数据库密码没错啊,然后又看了数据库的字符编码,发现编码也没问题呢(UTF-8),然后花了大量时间去排查,都没找出原因,结果在我用度娘搜了大量技术博客之后,尝试各种方法,最终发现了问题所在,下面就让我详细来讲讲:
在这个项目中,我用的是SSH框架(Spring + SpringMVC + Hibernate),然后数据的配置文件如下所示:
首先是数据源,数据源我使用的是阿里大名鼎鼎的开源数据源druid,druid的配置如下所示:
然后是数据库配置文件db.properties(有不少小伙伴这个文件命名为jdbc.properties):
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://102.128.259.121:3306/xyz_music?useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
注:密码可不能起的那么简单123456(手动狗头)
以上是我的数据库连接配置,不管你用的是什么持久化框架,应该都是大同小异的。然后我平时开发、测试的时候都能正常运行,到了上线那天,需要把项目转移到winserver 2008 r2中部署,结果就出问题了。然后我就是各种排查(心急啊有木有),排查了很久都找不出原因,就差重装数据库了(其实从上帝视角来看,就算我重装了数据库也没luan用)。终于上天不负有心人,我在其中的一次排查中,发现了问题所在。
这个数据库连接异常其实是系统引起的,我们在迁移项目的时候,一定要注意系统环境对代码运行的影响。首先大家看看我贴出的druid配置代码,其中有这么一句:
问题就出在"username"这个变量名上,在系统中也有个username属性,在你运行Tomcat的时候,系统变量覆盖了Properties中的值,这时取得username的值为系统的用户名Administrator,然后你链接数据库的时候密码没变,仍是配置文件中的password=123456,但username变了,它的值不再是username="root",而是username="Administrator",所以无论你怎么连都是连不上的。
解决方法(敲敲黑板,划重点啦!):
方法一:别用username来命名就好啦,用account吧,或者别的你喜欢的英文单词,测试一下,能连上就行!(推荐)
方法二:在Spring配置文件中修改成:
然后添加一个system-properties-mode属性
该属性有三个值:FALLBACK --- 默认值,不存在时覆盖
NEVER --- 不覆盖
OVERRIDE --- 覆盖
在实际开发过程中,造成Access denied for user 'administrator'@'58.32.162.450' (using password: YES)异常的原因是很多的,有时间的话我总结一下各种原因。大家迁移项目的时候,要优先考虑系统变化所带来的影响,心里要有个哔数。这次的问题我感觉很隐秘,不多搜搜百度还真不一定能找得到原因。最后祝大家生活愉快!