本篇用以记录开发中遇到的各类问题,有些是功力不够,有些是经验不足,有些是机制不了解,特整理如下,备忘以用。
1.关于微信公众号程序的 js脚本 变更不生效问题
变更微信公众号所用的js脚本,不生效。后在引用处添加 js版本号,可以正常变更。疑微信存在 js的缓存机制,但过期时间不了解。
具体修改如下:
在网页引用js处添加版本号 xxxxxxxx.js?v=20190609
建议: 如果变更所体现的结果不明显,加入一段易于查看是否发生了文件更新的标识为好。
2.开发环境正常,而测试环境不符合开发环境预期的问题排查
主要从以下几个角度:
1)二者文件是否一致
建议: 如果变更所体现的结果不明显,加入一段易于查看是否发生了文件更新的标识为好。
2)二者运行环境是否一致(包括但不限于jdk版本,服务器编码)
如:UTF-8 汉字占3个或4个字节,但 GBK编码占2个字节,如果报文按照长度截取,若编码格式不正确,可能会造成报文不完整
建议:linux使用echo $LANG 或 locale 查看编码
3)数据库配置是否一致
如:系统参数,连接参数等是否缺失,是否符合该环境要求
4)个别插件造成的影响
如:密码框等
3.java.net.UnkownHostException: ibatis.apache.org (或java.net.UnkownHostException: mybatis.org)
排查时可全局搜索 ibatis.apache.org ,可将问题定位在配置文件引用dtd约束处。如果主机没有联网,那么需要将约束改为本地目录。dtd约束文件可在 jar包 /builder/xml/ 文件夹下找到
4.CannotAcquireResourceException
com.mchange.v2.resourcepool.CannotAcquireResourceException: A ResourcePool could not acquire a resource from its primary factory or source. 查看是否缺少数据库连接驱动包,如 ojdbc6 / mysql-connector-java
5.查看jar包版本号
jar包根目录\META-INF\MANIFEST.MF
6.Resource路径下资源文件未找到
排查:1.Resource文件是否编译到项目空间 classes 文件夹下。如未编译,在项目的构建路径中,检查源代码页签下,resource文件夹的“排除”(Excluded)是否为 **,如是,将其改为 “无”(None)
2.资源文件路径书写是否正确
7.Junit测试只能有一个无参构造(可省略不写,有默认),但不能多
8.关于静态变量的依赖注入
直接对静态变量采用常规@Autowired方式不能完成注入。
解决办法:生成这一变量的setter方法,去掉setter的static属性,将@Autowired加在setter方法上。此种方式简单高效,不做他解。
private static Person person;
@Autowired
public void setPerson(Person person) {
MyThread.person = person;
}
9.Junit测试多线程程序的简易方式
Junit不支持多线程测试,@Test方法体执行结束后,程序即终止,子线程死活不论。
解决办法:打开调试配置,勾选最下方的 “在测试后保持Junit运行”即可,程序无需改动,简单高效。
10. Spring不能管理多线程运行类中的Bean注入
有A,B两个类,现要在A中以多线程方式运行多个B,B中需要注入MessageDao。
Class A{
public void method{
new Thread(new B()).start();
}
}
Class B implements Runnable{
@Autowired
private MessageDao messageDao;
}
messageDao不能够正常注入。
我个人的解决方案:新建一个类C,作为B的父类,将MessageDao 作为父类静态变量声明并注入(参见第8条)。注意:将变量声明为B类的静态变量,或其父类C的非静态变量均不可。
11.在第10条的条件下,使用ThreadLocal做线程间的数据隔离。创建子线程运行B时需要传递参数完成B的构造,但是在子线程运行时拿不到A类(主线程)构建B时传递的参数。
Class A{
public void method{
String param = "参数";
new Thread(new B(param)).start();
}
}
Class B implements Runnable{
private static ThreadLocal localParam = new ThreadLocal();
public B(){
}
public B(String param){
localParam.set(param);
}
}
调试时发现,main线程运行A,各个子线程运行B。ThreadLocal以 线程-值 的形式将数据保存在ThreadLocalMap中,由于main调用的构造器,导致子线程并不能以各自名称取得对应的构造数据。解决办法:使用InheritableThreadLocal,实现主线程和子线程的数据传递。
private static InheritableThreadLocal localParam = new InheritableThreadLocal();
12.线程池的优势和简单使用
线程池的好处:1.减小创建销毁线程的开销 2.任务到来即可取用线程,无需等待 3.可以管理线程相关的参数(比如线程数(并发数)),调优
简单使用:Executors静态工厂方法 newFixedThreadPool 或 newCachedThreadPool 创建 ExectorService对象。
ExectorService exectorService = Executors.newFixedThreadPool(10);
exectorService.execute(new Thread());
exectorService.shutdown();
newFixedThreadPool 是定容的线程池,便于控制线程数(并发数)(理解成出水管),高于并发数的会在队列中等待(进水管),如果并发处理较慢(出水慢),而请求激增(进水快),队列无限增长,可能导致OOM
newCachedThreadPool 自动扩容(自动收缩)的线程池,并发可无限增长,很容易OOM
二者底层使用
new ThreadPoolExecutor(corePoolSize, maximumPoolSize,keepAliveTime, milliseconds,runnableTaskQueue, threadFactory,handler);
调优可自定义参数。
13.线程池和ThreadLocal组合使用时,由于线程复用导致脏数据问题
由于线程池的线程复用,比如newFixedThreadPool (size:10)处理13条数据,那么势必有3个线程实现复用,而第一次初始化线程时数据已经绑定完毕了,复用时不重新绑定,导致第11,12,13条数据丢失。
使用Ali开源包 transmittable-thread-local-2.6.1.jar 。
简单使用:
Class A{
public void method{
String param = "参数";
ExectorService exectorService = Executors.newFixedThreadPool(10);
exectorService.execute(TtlRunnable.get(new B(param)));
exectorService.shutdown();
}
}
Class B implements Runnable{
private static InheritableThreadLocal localParam = new TransmittableThreadLocal();
public B(){
}
public B(String param){
localParam.set(param);
}
}
14. Mybatis报sql未正确结束
检查mapper.xml中sql语句结尾是否有 分号
15.服务器报错但是由于某些原因无法查看报错信息,可用Chrome的开发者工具,Network中查看响应信息,可能有报错信息
16.百度搜 “我的ip” 即可查看手机或PC端IP ,可能有助于运维查询日志
17.调试时改变变量值,可能导致Mybatis调用数据库时不能正确获取mapper中的sql语句
18.JUnit测试所用注解
@ContextConfiguration(location="classpath:applicationContext.xml")
@Runwith(SpringJUnit4ClassRunner.class)
publice class A{
@Test
public void method(){
}
}
19.使用maven的clean命令,可能会导致jdk根目录下lib目录中文件丢失
20.SpringMVC的@requestParam注解只能获取get请求拼接的参数或表单形式提交的post请求参数
21.vue打包时会将标签的src属性当做静态资源处理,添加静态资源目录前缀
22.如Base64Encoder类不能导入,可在jre构建路径中删除jre环境重新添加
23.Mapped Statements collection does not contain value for ...(某sql的id) 原因:
1.未在Mybatis的mapper配置文件中引入该sql所在的xml配置文件
2.mapper中的xml文件名或路径不对,导致不能正常引入xml
3.该sql的id是否存在,是否正确
4.多个xml配置文件的命名空间是否重复
24.在页面加载函数中调用微信扫一扫功能未能实现,为页面按钮添加绑定事件,在事件中可以调起。
25.在Listener中无法注入@Service的问题。
Listener的生命周期由servlet容器管理,Listener由servletContext实例化并调用其contextInitialized方法初始化,但是Service由Spring容器(WebApplicationContext)管理,在Spring容器外无法直接通过依赖注入得到Spring容器管理的bean实例。可以使用Spring提供的工具类WebApplicationContextUtils获得bean。
Service service= WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()).getBean(ServiceImpl.class);
Spring上下文绑定在容器上下文ServletContext上,使用工具类WebApplicationContextUtils从ServletContext上获取Spring的上下文WebApplicationContext,再调用getBean()方法获取以完成初始化的bean实例。
注:ContextLoaderListener必须先于自定义的Listener初始化(web.xml中的顺序),保证Spring容器初始化和bean初始化的正确进行。