Mongodb学习笔记(三)
来源:互联网 发布:剑灵女枪手捏脸数据图 编辑:程序博客网 时间:2024/05/17 06:37
这里是ynb中Mongodb的使用:
(1)在pom.xml:
<profiles><!-- ================= Database Profiles ================= --><profile><id>test</id> <activation><activeByDefault>true</activeByDefault></activation><properties><jdbc.url>jdbc:mysql://10.18.96.16:3306/ynb_test?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8&autoReconnect=true</jdbc.url><jdbc.username>njb</jdbc.username> <jdbc.password>njb2015</jdbc.password><mongodb.url>10.18.97.32:27017</mongodb.url><mongodb.dbname>db</mongodb.dbname><mongodb.username/><mongodb.password/><redis.host>10.18.97.32</redis.host><redis.port>6379</redis.port><redis.keyword.ip>10.18.97.32</redis.keyword.ip><redis.keyword.port>6379</redis.keyword.port><zookeeper.ip>10.18.97.32</zookeeper.ip><zookeeper.cliport>2181</zookeeper.cliport>....//省略很多组件</properties></profile></profiles>
<dependencies><dependency><groupId>org.mongodb</groupId><artifactId>mongo-java-driver</artifactId><version>2.12.2</version></dependency><dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> <version>1.3.0.RELEASE</version> </dependency></dependencies>
(2)applicationContext-resources.xml中:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:drools="http://drools.org/schema/drools-spring" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/data/mongohttp://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://drools.org/schema/drools-spring http://drools.org/schema/drools-spring-1.3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- For mail settings and future properties files --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="ignoreUnresolvablePlaceholders" value="true"/> <property name="locations"> <list> <value>classpath:jdbc.properties</value> <value>classpath:hibernate.properties</value> <value>classpath:redis.properties</value> </list> </property> </bean> <!-- JNDI DataSource for J2EE environments --> <!--<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/appfuse"/>--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <!-- <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> --> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="maxActive" value="350"/> <property name="maxWait" value="10000"/> <property name="poolPreparedStatements" value="true"/> <property name="defaultAutoCommit" value="true"/> <property name="validationQuery" value="SELECT 1+1"/> <property name="testOnBorrow" value="true"/> <property name="filters" value="stat" /> <!--申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 --><property name="testWhileIdle" value="true" /><property name="timeBetweenEvictionRunsMillis" value="10000" /> </bean><bean id="jedisPoolConfig" class="com.eshore.framework.util.JedisPoolConfigWrapper"><!--最大连接数 --><property name="maxActive" value="50" /><!--最大空闲连接数 --><property name="maxIdle" value="5" /><!--初始化连接数 --><property name="minIdle" value="1" /><!--最大等待时间 --><property name="maxWait" value="5000" /><!--对拿到的connection进行validateObject校验 --><property name="testOnBorrow" value="true" /><!--在进行returnObject对返回的connection进行validateObject校验 --><property name="testOnReturn" value="true" /><!--定时对线程池中空闲的链接进行validateObject校验 --><property name="testWhileIdle" value="true" /></bean><bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy"> <constructor-arg index="0"> <bean factory-bean="jedisPoolConfig" factory-method="getConfig"/> </constructor-arg> <constructor-arg index="1" value="${redis.host}"/></bean> <!-- <mongo:mongo host="${mongodb.url}" port="${mongodb.port}"/> --><mongo:mongo replica-set="${mongodb.url}"> <mongo:options connections-per-host="800" threads-allowed-to-block-for-connection-multiplier="5" max-wait-time="7000" connect-timeout="7000" write-fsync="true" /></mongo:mongo> <mongo:db-factory dbname="${mongodb.dbname}" username="${mongodb.username}" password="${mongodb.password}" mongo-ref="mongo" /> <bean id="mappingContext" class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" /> <bean id="defaultMongoTypeMapper" class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper"> <constructor-arg name="typeKey"><null /></constructor-arg> </bean> <bean id="mappingMongoConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter"> <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" /> <constructor-arg name="mappingContext" ref="mappingContext" /> <property name="typeMapper" ref="defaultMongoTypeMapper" /> </bean> <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" /> <constructor-arg name="mongoConverter" ref="mappingMongoConverter" /> </bean> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/><bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml" p:shared="true"/> </beans>
(3)MongoDBDaoImpl:
package com.eshore.framework.dao.mongodb;import java.util.ArrayList;import java.util.List;import org.apache.log4j.Logger;import org.bson.types.ObjectId;import org.springframework.data.mongodb.core.MongoTemplate;import com.mongodb.BasicDBObject;import com.mongodb.DB;import com.mongodb.DBCollection;import com.mongodb.DBCursor;import com.mongodb.DBObject;public class MongoDBDaoImpl implements MongoDBDao { Logger log = Logger.getLogger(MongoDBDaoImpl.class); private MongoTemplate mongoTemplate; @Override public List getAll(String collectionName) { DB db = mongoTemplate.getDb(); List list = new ArrayList(); DBCollection coll = db.getCollection(collectionName); DBCursor cursor = coll.find(); try { while (cursor.hasNext()) { DBObject object = cursor.next(); list.add(object); } } finally { cursor.close(); } return list; } @Override public void delete(String collectionName, String id) { DB db = mongoTemplate.getDb(); DBCollection coll = db.getCollection(collectionName); coll.remove(new BasicDBObject("_id", new ObjectId(id))); } @Override public DBObject findOne(String collectionName, String id) { DB db = mongoTemplate.getDb(); DBCollection coll = db.getCollection(collectionName); return coll.findOne(new BasicDBObject("_id", new ObjectId(id))); } public void setMongoTemplate(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } public MongoTemplate getMongoTemplate() { return mongoTemplate; } @Override public List getList(String collectionName, BasicDBObject query, PageInfo pageInfo) { DB db = mongoTemplate.getDb(); DBCollection coll = db.getCollection(collectionName); if (pageInfo.getSortDirection() == pageInfo.SORT_DIRECTION_DESC) { pageInfo.setSortDirection(-1); } DBCursor cursor = coll.find(query).sort( new BasicDBObject(pageInfo.getSortField(), pageInfo .getSortDirection())); List list = new ArrayList(); try { while (cursor.hasNext()) { DBObject object = cursor.next(); list.add(object); } } finally { cursor.close(); } return list; } @Override public List getAll(String collectionName, BasicDBObject query) { DB db = mongoTemplate.getDb(); List list = new ArrayList(); DBCollection coll = db.getCollection(collectionName); DBCursor cursor = coll.find(query); try { while (cursor.hasNext()) { DBObject object = cursor.next(); list.add(object); } } finally { cursor.close(); } return list; }}
(3.5)一个使用了MongoDBDao的例子:
UserLogServiceImpl中:
/** * 查询用户日志 * * @param requestBean * @return */ @Override public Page<AppUserLogVo> queryAppUserLogList(AppUserLogRequestBean requestBean, PageInfo pageInfo) { if (StringUtils.isBlank(pageInfo.getSortField())) { pageInfo.setSortField("operateTime"); pageInfo.setSortDirection(pageInfo.SORT_DIRECTION_DESC); } BasicDBObject query = new BasicDBObject(); List<AppUserLogVo> logVoList = new ArrayList<AppUserLogVo>(); if (requestBean != null && StringUtils.isNotBlank(requestBean.getMobileNo())) { Pattern pattern = Pattern.compile("^.*" + requestBean.getMobileNo() + ".*$", Pattern.CASE_INSENSITIVE); query.put("puppetMobileNo", pattern); } DB db = mongoDBDao.getMongoTemplate().getDb(); DBCollection coll = db.getCollection(COLLECTION_NAME); DBCursor cursor = coll.find(query).skip(pageInfo.getPageNum() * pageInfo.getPageSize()).limit((pageInfo.getPageNum() + 1) * pageInfo.getPageSize()); if (StringUtils.isNotBlank(pageInfo.getSortField())) { cursor.sort(new BasicDBObject(pageInfo.getSortField(), pageInfo.getSortDirection() == pageInfo.SORT_DIRECTION_DESC ? -1 : 1)); } int count = cursor.count(); try { while (cursor.hasNext()) { DBObject object = cursor.next(); AppUserLogVo logListBean = new AppUserLogVo(); logListBean.setOperateClassName(String.valueOf(object.get("operateClassName"))); logListBean.setOperateDesc(String.valueOf(object.get("operateDesc"))); logListBean.setPuppetId(String.valueOf(object.get("puppetId"))); logListBean.setPuppetType(String.valueOf(object.get("puppetType"))); logListBean.setOperateIp(String.valueOf(object.get("operateIp"))); Date d = (Date) object.get("operateTime"); logListBean.setOperateTime(d); logListBean.setOperateAccount(String.valueOf(object.get("operateAccount"))); logListBean.setOperateId(Integer.parseInt(String.valueOf(object.get("operateId")))); logListBean.setOperateData(String.valueOf(object.get("operateData"))); logListBean.setPuppetMobileNo(String.valueOf(object.get("puppetMobileNo"))); if (object.get("device") == null) { logListBean.setDevice("管理后台"); } else { logListBean.setDevice(String.valueOf(object.get("device"))); } logVoList.add(logListBean); } } finally { cursor.close(); } PageImpl<AppUserLogVo> page = new PageImpl<AppUserLogVo>(); page.setResults(logVoList); page.setPageNumber(pageInfo.getPageNum()); page.setPageSize(pageInfo.getPageSize()); page.setTotalResults(count); return page; }
(4)使用GridFS的MongoDBFileDaoImpl:
普通文件系统优点是简单直接,缺点是当规模变大以后难于管理,难以扩展
使用GridFS的话优缺点正好对换,用起来相对复杂,但是当规模变大之后管理和扩展要容易得多
package com.eshore.framework.dao.mongodb;import java.io.IOException;import org.apache.commons.io.IOUtils;import org.springframework.data.mongodb.core.MongoTemplate;import com.mongodb.DB;import com.mongodb.gridfs.GridFS;import com.mongodb.gridfs.GridFSDBFile;import com.mongodb.gridfs.GridFSInputFile;public class MongoDBFileDaoImpl implements MongoDBFileDao { private MongoTemplate mongoTemplate; private static final String COLLECTION_NAME = "uploadFile"; /** * @param filename * 文件名 */ public void removeByFilename(String filename) { this.removeByFilename(COLLECTION_NAME, filename); } /** * @param filename * 文件名 */ public void removeByFilename(String collectionName, String filename) { GridFS gridFS = getGridFS(collectionName); gridFS.remove(filename); } /** * @param filename * 文件名 */ @Override public MongoDBFile findByFilename(String filename) { return this.findByFilename(COLLECTION_NAME, filename); } /** * @param filename * 文件名 * @param collectionName * 集合名 */ @Override public MongoDBFile findByFilename(String collectionName, String filename) { MongoDBFile mongoDBFile = null; GridFS gridFS = getGridFS(collectionName); GridFSDBFile gridFSDBFile = gridFS.findOne(filename); if (gridFSDBFile != null) { mongoDBFile = new MongoDBFile(); mongoDBFile.set_id(gridFSDBFile.getId().toString()); mongoDBFile.setContentType(gridFSDBFile.getContentType()); mongoDBFile.setFilename(gridFSDBFile.getFilename()); try { mongoDBFile.setBytes(IOUtils.toByteArray(gridFSDBFile .getInputStream())); } catch (IOException e) { // TODO Auto-generated catch block //e.printStackTrace(); } // mongoDBFile.setInputStream(gridFSDBFile.getInputStream()); } return mongoDBFile; } /** * 存储文件 使用默认集合名 * * @throws IOException */ @Override public void saveFile(MongoDBFile mongoDBFile) throws IOException { this.saveFile(COLLECTION_NAME, mongoDBFile); } /** * 存储文件 * * @param collectionName * 集合名 默认以IO流方式存入 * @throws IOException */ @Override public void saveFile(String collectionName, MongoDBFile mongoDBFile) throws IOException { GridFS gridFS = getGridFS(collectionName); GridFSInputFile gfs = null; if (mongoDBFile.getBytes() != null) { gfs = gridFS.createFile(mongoDBFile.getBytes()); } else if (mongoDBFile.getInputStream() != null) { gfs = gridFS.createFile(mongoDBFile.getInputStream()); } else { gfs = gridFS.createFile(mongoDBFile.getFile()); } if (mongoDBFile.get_id() != null) { gfs.put("_id", mongoDBFile.get_id()); } gfs.put("filename", mongoDBFile.getFilename()); gfs.put("contentType", mongoDBFile.getContentType()); gfs.save(); } /** * * @param collectionName * 集合名 * @return */ public GridFS getGridFS(String collectionName) { DB db = mongoTemplate.getDb(); GridFS gridFS = new GridFS(db, collectionName); return gridFS; } public MongoTemplate getMongoTemplate() { return mongoTemplate; } public void setMongoTemplate(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; }}
(5)MongoDBFileServiceImpl:
package com.eshore.framework.service.impl;@Service("mongoDBFileService")@Transactional(readOnly = true)public class MongoDBFileServiceImpl implements MongoDBFileService { static Logger log = Logger.getLogger(MongoDBFileServiceImpl.class); @Autowired private MongoDBFileBuilder mongoDBFileBuilder; @Autowired private MongoDBFileDao mongoDBFileDao; @Override @Transactional(readOnly = false) public void removeByFilename(String filename) { mongoDBFileDao.removeByFilename(filename); } @Override public void removeByFilename(String collectionName, String filename) { // TODO Auto-generated method stub } @Override @Transactional(readOnly = false) public MongoDBFileVo findByFilename(String filename) { MongoDBFileVo mongoDBFileVo = null; try { MongoDBFile mongoDBFile = mongoDBFileDao.findByFilename(filename); mongoDBFileVo = mongoDBFileBuilder.buildValueObject(mongoDBFile); } catch (Exception e) { /* * // TODO Auto-generated catch block e.printStackTrace(); */ } return mongoDBFileVo; } @Override public MongoDBFileVo findByFilename(String collectionName, String filename) { // TODO Auto-generated method stub return null; } @Override @Transactional(readOnly = false) public void saveFile(MongoDBFileVo mongoDBFileVo) throws IOException, BeanValidationException { // String startInfo = "调用mongoDBFileService.saveFile开始的时间" + new SimpleDateFormat("yyyyMMddhhmmssms.SSS").format(new Date(System.currentTimeMillis())); // log.error(startInfo); MongoDBFile mongoDBFile = mongoDBFileBuilder.buildEntity(mongoDBFileVo); mongoDBFileDao.saveFile(mongoDBFile); } @Override public void saveFile(String collectionName, MongoDBFileVo mongoDBFileVo) throws IOException, BeanValidationException { MongoDBFile mongoDBFile = mongoDBFileBuilder.buildEntity(mongoDBFileVo); mongoDBFileDao.saveFile(collectionName, mongoDBFile); } @Override @Transactional(readOnly = false) public void saveFile(List<MongoDBFileVo> mongoDBFileVos) throws IOException, BeanValidationException { Iterator<MongoDBFileVo> it = mongoDBFileVos.iterator(); while (it.hasNext()) { MongoDBFileVo mongoDBFileVo = it.next(); saveFile(mongoDBFileVo); } }}(5.5.1)ajax上传用户头像功能(页面代码):
<input type="hidden" id="imgUrl" name="imgUrl" value=""/><img class="normalFace" id="uploadimg" name="uploadimg" src="../images/fileUpload.png" onclick="fileSelect();"/><input id="appLogoFile" runat="server" name="appLogoFile" type="file" class="validate[required]" style="display:none;" onchange="uploadInputChanged();"/>//uploadInputChanged()如下:function uploadInputChanged(){var myfileValue = $("#appLogoFile").val();if(!checkImageStyle("appLogoFile")){alert("仅支持JPG、JPEG、GIF、PNG格式的图片");return;}if(myfileValue&&myfileValue!=""){$.ajaxFileUpload({ url: basepath + '/appManager/uploadIcon.do?ajax=true',//用于文件上传的服务器端请求地址 type:'post', secureuri:false,//一般设置为false dataType: 'json',//返回值类型 一般设置为json fileElementId:'appLogoFile',//文件上传空间的id属性 <input type="file" id="file" name="file" /> success: function (data, status) //服务器成功响应处理函数 { if(data.fail){ alert(data.fail); }else{ $("#imgUrl").attr("value",data.imageurl); //生成预览 <span style="color:#ff0000;">$("#uploadimg").attr("src",contextPath + "/uploadFile/loadImgDataByFileName.do?fileName="+data.imageurl</span>+"&time=" + Date.parse(new Date())); } }, error: function (data, status, e)//服务器响应失败处理函数 { alert(e); }}); }}//其中检测上传图片格式函数:function checkImageStyle(divId){var uploadFile = document.getElementById(divId).value;var subfix = uploadFile.valueOf().toLowerCase();var reg=new RegExp(".jpg$|.jpeg$|gif$|png$");return reg.test(subfix);}(5.5.2)ajax上传用户头像功能(后台代码):
先保存到mongo中,然后返回图片url到前台
@RequestMapping("/uploadIcon")public void uploadIcon(HttpServletRequest request,HttpServletResponse response) {response.setCharacterEncoding("utf-8");response.setContentType("text/html");Date date = new Date();MultipartHttpServletRequest multipartRequest = null;MultipartFile multipartFile = null;try {multipartRequest = (MultipartHttpServletRequest) request;multipartFile = multipartRequest.getFile("appLogoFile");// File newFile = new File(indexbannerpath + "//" +newFilePath);// 如果文件大于400K返回错误信息if (multipartFile.getSize() > 400 * 1024) {JSONObject jsonO = new JSONObject();jsonO.put("fail", "头像文件太大了");response.getWriter().write(jsonO.toString());} else {// multipartFile.transferTo(newFile);AttachmentVo attachmentVo = new AttachmentVo();attachmentVo.setCreateTime(date);String extName = multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf("."));attachmentVo.setExtName(extName);String fileNewName = UUID.randomUUID().toString() + "_"+ new SimpleDateFormat("yyyyMMddhhmmssms").format(date);attachmentVo.setFileName(fileNewName + extName);attachmentVo.setRelativePath(null);attachmentVo.setOldFileName(multipartFile.getOriginalFilename());attachmentVo.setOwnerType("appLogoFile");attachmentVo = attachmentService.add(attachmentVo);MongoDBFileVo mongoDBFileVo = new MongoDBFileVo();mongoDBFileVo.setContentType(attachmentVo.getExtName());mongoDBFileVo.setFilename(attachmentVo.getFileName());mongoDBFileVo.setBytes(multipartFile.getBytes());// mongoDBFileVo.setInputStream(inputStream);<span style="color:#ff0000;">mongoDBFileService</span>.saveFile(mongoDBFileVo);JSONObject jsonO = new JSONObject();jsonO.put(<span style="color:#ff0000;">"imageurl", attachmentVo.getFileName()</span>);<span style="color:#ff0000;">response.getWriter().write(jsonO.toString());</span>}} catch (Exception e) {e.printStackTrace();}}(5.5.3)ajax上传用户头像功能(页面根据url再调用后台方法生成图片):
/** * 用户头像带缓存 * * @param fileName * @param request * @param response * @throws IOException */@RequestMapping("/loadImgDataByFileName")public void loadImgDataByFileName(@RequestParam("fileName") String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {if(fileName==null) {return;} AttachmentVo attachmentVo = attachmentService.getByFileName(fileName); MongoDBFileVo mongoDBFileVo=null; if(attachmentVo!=null && attachmentVo.getFileName()!=null ){ mongoDBFileVo = mongoDBFileService.findByFilename(attachmentVo.getFileName()); }else{ return; } response.setContentType(attachmentVo.getExtName()); response.setHeader("Cache-Control", "public"); // HTTP/1.1 response.setHeader("Pragma", "Pragma"); response.setDateHeader("Expires", System.currentTimeMillis() + 30000000); byte[] buffer = mongoDBFileVo.getBytes(); OutputStream toClient = new BufferedOutputStream(response.getOutputStream()); toClient.write(buffer); toClient.flush(); toClient.close();}
0 0
- MongoDB学习笔记《三》
- MongoDB学习笔记<三>
- Mongodb学习笔记(三)
- mongoDB学习笔记三
- MongoDb学习笔记三 MongoDB and PyMongo
- MongoDB 学习笔记三 JAVA调用MongoDB
- Mongodb学习笔记速记(三)
- MongoDB学习笔记(三)
- MongoDB 学习笔记三 修改器
- Elasticsearch学习笔记(三) mongodb数据同步
- MongoDB学习笔记(三)使用Spring Data操作MongoDB
- MongoDB学习笔记(三) java中如何操作MongoDB
- MongoDB学习笔记三:MongoDB集群环境搭建
- MongoDB学习笔记(三)MongoDB常见操作(续)
- MongoDB学习笔记(三) MongoDB的基本操作
- MongoDB学习笔记(三) MongoDB的基本操作
- MongoDB笔记三
- MongoDB 学习笔记(三):分页、排序与游标
- linux下部署JDK
- python: Fatal IO error 11
- linux中send函数MSG_NOSIGNAL异常消息
- 优秀程序员不得不知道的20个位运算技巧
- ArcGIS教程:路径距离工具的工作原理(一)
- Mongodb学习笔记(三)
- TinyOS的个人见解3之make
- 遇到错误javax.el.PropertyNotFoundException: Property 'start' not found on type java.lang.String
- 把网站从vps转移到虚拟主机后伪静态不能从
- 支持向量机SVM——LIBSVM
- 黑马程序员——Java基础---面向对象
- cf A. Wilbur and Swimming Pool
- Android studio小技巧之xml与Java类快速跳转
- 多线程Runtime.getRuntime().exec常见问题