Spring Boot+MyBatis+SQLite配置例子参考下面
分类: Spring
HttpClient利用MultipartEntityBuilder取代MultipartEntity上传图片文件到服务器
注意:在HttpClient4.3之后,原来的上传文件方法MultipartEntity已经不建议使用,现替换成新的httpmime下面的MultipartEntityBuilder。
需要添加httpclient-4.5.jar、httpmime-4.5.jar两个包
maven添加:
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5</version> </dependency> |
下面我们对比一下MultipartEntity和MultipartEntityBuilder的使用区别:
MultipartEntityBuilder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//传入参数可以为file或者filePath,在此处做转换 File file = new File(filePath); CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse httpResponse = null; HttpPost httppost = new HttpPost(url); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); //设置浏览器兼容模式 builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); //设置请求的编码格式 builder.setCharset(Consts.UTF_8); builder.setContentType(ContentType.MULTIPART_FORM_DATA); //添加文件 builder.addBinaryBody("file", file); HttpEntity reqEntity = builder.build(); httppost.setEntity(reqEntity); httpResponse = httpClient.execute(httppost); |
MultipartEntity:
1 2 3 4 5 6 7 8 9 10 |
//传入参数可以为file或者filePath,在此处做转换 File file = new File(filePath); CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse httpResponse = null; HttpPost httppost = new HttpPost(url); FileBody filebody = new FileBody(file); MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8")); entity.addPart("file", filebody); httppost.setEntity(entity); |
直接上替换后的测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
package com.test.util; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.Iterator; import java.util.Map; public class HttpPostUploadUtil { /** * @param args */ public static void main(String[] args) { String filePath = "D:\\Test\\0.jpg"; String url = "http://127.0.0.1:8080/FastFds"; //调用上传方法 String backInfo = uploadPost(url, filePath); if(StringUtils.isNotBlank(backInfo)){ //转json数据 JSONObject json = JSONObject.fromObject(backInfo); if(!json.isEmpty()){ //数据处理 String value = json.getString("test"); System.out.println(value); } } } /** * 上传图片/文件 * @param url * @param filePath * @return String 用于转json或者其他信息 */ public static String uploadPost(String url, String filePath) { String requestJson = ""; //传入参数可以为file或者filePath,在此处做转换 File file = new File(filePath); CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse httpResponse = null; try { HttpPost httppost = new HttpPost(url); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); //设置浏览器兼容模式 builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); //设置请求的编码格式 builder.setCharset(Consts.UTF_8); builder.setContentType(ContentType.MULTIPART_FORM_DATA); //添加文件 builder.addBinaryBody("file", file); HttpEntity reqEntity = builder.build(); httppost.setEntity(reqEntity); httpResponse = httpClient.execute(httppost); int backCode = httpResponse.getStatusLine().getStatusCode(); if(backCode == HttpStatus.SC_OK){ HttpEntity httpEntity = httpResponse.getEntity(); byte[] json= EntityUtils.toByteArray(httpEntity); requestJson = new String(json, "UTF-8"); //关闭流 EntityUtils.consume(httpEntity); return requestJson; } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { //释放资源 try { httpClient.close(); httpResponse.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } } |
继续阅读HttpClient利用MultipartEntityBuilder取代MultipartEntity上传图片文件到服务器
Spring Boot实现异步任务
Spring Boot中的另外一个任务:异步任务。所谓异步任务,其实就是异步执行程序,有些时候遇到一些耗时的的任务,如果一直卡等待,肯定会影响其他程序的执行,所以就让这些程序需要以异步的方式去执行。那么下面就来介绍Spring Boot 如何实现异步任务。
一、使用注解@EnableAsync 开启异步调用方法
在application启动类中,加上@EnableAsync注解,Spring Boot 会自动扫描异步任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.weiz; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import tk.mybatis.spring.annotation.MapperScan; @SpringBootApplication //扫描 mybatis mapper 包路径 @MapperScan(basePackages = "com.weiz.mapper") //扫描 所有需要的包, 包含一些自用的工具类包 所在的路径 @ComponentScan(basePackages = {"com.weiz","org.n3r.idworker"}) //开启定时任务 @EnableScheduling //开启异步调用方法 @EnableAsync public class SpringBootStarterApplication { public static void main(String[] args) { SpringApplication.run(SpringBootStarterApplication.class, args); } } |
二、创建异步执行类,定义@Component及@Async组件
创建com.weiz.tasks包,在tasks包里增加AsyncTask 异步任务类,加上@Component 注解,然后在需要异步执行的方法前面加上@Async注解,这样Spring Boot容器扫描到相关异步方法之后,调用时就会将这些方法异步执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.weiz.tasks; import java.util.concurrent.Future; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; @Component public class AsyncTask { @Async public Future<Boolean> doTask11() throws Exception { long start = System.currentTimeMillis(); Thread.sleep(1000); long end = System.currentTimeMillis(); System.out.println("任务1耗时:" + (end - start) + "毫秒"); return new AsyncResult<>(true); } @Async public Future<Boolean> doTask22() throws Exception { long start = System.currentTimeMillis(); Thread.sleep(700); long end = System.currentTimeMillis(); System.out.println("任务2耗时:" + (end - start) + "毫秒"); return new AsyncResult<>(true); } @Async public Future<Boolean> doTask33() throws Exception { long start = System.currentTimeMillis(); Thread.sleep(600); long end = System.currentTimeMillis(); System.out.println("任务3耗时:" + (end - start) + "毫秒"); return new AsyncResult<>(true); } } |
说明:@Async 加上这个注解,就表示该方法是异步执行方法。
三、调用
创建一个DoTask调用类,我们看看这几个方法,是怎么执行的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.weiz.tasks; import java.util.concurrent.Future; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("tasks") public class DoTask { @Autowired private AsyncTask asyncTask; @RequestMapping("test1") public String test1() throws Exception { long start = System.currentTimeMillis(); Future<Boolean> a = asyncTask.doTask11(); Future<Boolean> b = asyncTask.doTask22(); Future<Boolean> c = asyncTask.doTask33(); while (!a.isDone() || !b.isDone() || !c.isDone()) { if (a.isDone() && b.isDone() && c.isDone()) { break; } } long end = System.currentTimeMillis(); String times = "任务全部完成,总耗时:" + (end - start) + "毫秒"; System.out.println(times); return times; } } |
SpringBoot如何实现一个实时更新的进度条的示例代码
前言
导入excel表格批量修改状态,期间如果发生错误则所有数据不成功,为了防止重复提交,做一个类似进度条的东东。
那么下面我会结合实际业务对这个功能进行分析和记录。
正文
思路
前端使用bootstrap,后端使用SpringBoot分布式到注册中心,原先的想法是导入表格后异步调用修改数据状态的方法,然后每次计算修改的进度然后存放在session中,前台jquery写定时任务访问获取session中的进度,更新进度条进度和百分比。但是这存在session在服务间不共享,跨域问题。那么换一个方式存放,存放在redis中,前台定时任务直接操作获取redis的数据。
实施
进度条
先来看一下bootstrap的进度条
1 2 3 4 5 6 7 |
<div class="progress progress-striped active"> <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 40%;"> 40% </div> </div> |
进度条更新主要更新style="width: 40%;"的值即可,div里面的40%可以省略,无非时看着明确。
可以考虑将进度条放入弹出层。
定时任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
//点击确认导入执行此方法 function bulkImportChanges() { //获取批量操作状态文件 var files = $("#importChanges").prop("files"); var changesFile = files[0]; var formData = new FormData(); formData.append("importFile",changesFile); $.ajax({ type : 'post', url : "/risk/bulk***es", data : formData, processData : false, //文件ajax上传要加这两个的,要不然上传不了 contentType : false, // success : function(obj) { //导入成功 if (obj.rspCode == "00") { //定时任务获取redis导入修改进度 var progress = ""; var timingTask = setInterval(function(){ $.ajax({ type: 'post', url: "/risk/t***k", dataType : 'json', success: function(result) { progress = result.value; if (progress != "error"){ var date = progress.substring(0,6); //这里更新进度条的进度和数据 $(".progress-bar").width(parseFloat(date)+"%"); $(".progress-bar").text(parseFloat(date)+"%"); } } }); //导入修改完成或异常(停止定时任务) if (parseInt(progress)==100 || progress == "error") { //清除定时执行 clearInterval(timingTask); $.ajax({ type: 'post', url: "/risk/de***ess", dataType : 'json', success: function(result) { $("#bulkImportChangesProcessor").hide(); if (parseInt(progress) == 100) { alert("批量导入修改状态成功"); } if (progress == "error") { alert("批量导入修改状态失败"); } //获取最新数据 window.location.href="/risk/re***ByParam" rel="external nofollow" rel="external nofollow" ; } }); } }, 1000) }else { $("#bulkImportChangesProcessor").hide(); alert(obj.rspMsg); window.location.href="/risk/re***ByParam" rel="external nofollow" rel="external nofollow" ; } } }); } |
解释:点击确认导入文件后成功后开启定时任务每一秒(一千毫秒)访问一次后台获取redis存放的进度,返回更新进度条,如果更新完成或者更新失败(根据后台返回的数据决定)则停止定时任务显示相应的信息并刷新页面。获取最新数据。
后台控制层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
/** * 退单管理批量修改状态导入文件 * @param importFile * @return */ @ResponseBody @RequestMapping("/bulk***es") public Map<String,Object> bulk***es(MultipartFile importFile){ log.info("退单管理批量修改状态导入文件,传入参数:"+importFile); Map<String,Object> map = new HashMap<>(); List<Bulk***esEntity> fromExcel = null; try{ //使用工具类导入转成list String[] header = {"sy***um","t***mt","ha***ult","re***nd","sy***nd","r**k"}; fromExcel = importExcelUtil.importDataFromExcel(importFile, header, BulkImportChangesEntity.class); if (fromExcel.size()==0){ map.put("rspCode","99"); map.put("rspMsg","导入数据不能为空"); return map; } }catch (Exception e){ map.put("rspCode","99"); map.put("rspMsg","导入操作表失败,请注意数据列格式"); return map; } try { //这里会对list集合中的数据进行处理 log.info("调用服务开始,参数:"+JSON.toJSONString(fromExcel)); //String url = p4_zuul_url+"/***/ri***eat/bu***nges"; String url = p4_zuul_url+"/***-surpass/ri***eat/bu***nges"; String result = HttpClientUtil.doPost(url,JSON.toJSONString(fromExcel)); log.info("调用服务结束,返回数据:"+result); if (result != null){ map = JSONObject.parseObject(result, Map.class); log.info("批量修改状态导入:"+JSON.toJSONString(map)); } }catch (Exception e){ map.put("rspCode","99"); map.put("rspMsg","导入操作表失败"); log.info("bu***es exception",e); return map; } return map; } /** * 获取退单管理批量修改状态导入文件进度条进度 * @return */ @ResponseBody @RequestMapping("/t***sk") public Map<String,Object> t***sk(){ Map<String,Object> map = new HashMap<>(); //获取redis值 String progress = HttpClientUtil.doGet( p4_zuul_url + "/" + p4_redis + "/redis***ler/get?key=progressSchedule"); if (progress != null){ map = JSONObject.parseObject(progress, Map.class); log.info("进度条进度:"+JSON.toJSONString(map)); map.put("progressSchedule",progress); }else { HttpClientUtil.doGet( p4_zuul_url + "/" + p4_redis + "/redis***ler/del?key=progressSchedule"); } return map; } /** * 清除redis进度条进度 * @return */ @ResponseBody @RequestMapping("/de***ess") public Map<String,Object> de***ess(){ Map<String,Object> map = new HashMap<>(); String progress = HttpClientUtil.doGet( p4_zuul_url + "/" + p4_redis + "/redis***ler/del?key=progressSchedule"); if (progress != null){ map = JSONObject.parseObject(progress, Map.class); log.info("返回数据:"+JSON.toJSONString(map)); } return map; } |
导入时调用第一个bulk***es方法,定时任务调用t***sk方法,导入完成或发生错误调用de***ess方法删除redis数据,避免占用资源。
服务层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
@Async//开启异步 @Transactional(rollbackFor = Exception.class)//事务回滚级别 @Override public void bulkImportChanges(List<BulkImportChangesParam> list) { //初始化进度 Double progressBarSchedule = 0.0; redisClient.set("progressSchedule", progressBarSchedule + "");//存redis try { for (int i = 1; i <= list.size(); i++) { RiskRetreatEntity entity = riskRetreatMapper.selectRetreatListBySysRefNum(list.get(i-1).getSysRefNum()); if (entity == null){ //查询结果为空直接进行下次循环不抛出 continue; } //实体封装 ··· //更新 riskRetreatMapper.updateRetreatByImport(entity); //计算修改进度并存放redis保存(1.0 / list.size())为一条数据进度 progressBarSchedule = (1.0 / list.size()) * i*100; redisClient.set("progressSchedule", progressBarSchedule+""); if (i==list.size()){ redisClient.set("progressSchedule", "100"); } } }catch (Exception e){ //当发生错误则清除缓存直接抛出回滚 redisClient.set("progressSchedule","error"); log.info("导入更新错误,回滚"); log.info("bulkImportChanges exception:",e); throw e; } } |
每更新一条数据存放进度,当发生错误则进行回滚。如果开启异步则需要在启动类添加注解@EnableAsync。
1 2 3 4 5 6 7 |
@EnableAsync ···//其他注解 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } |
用SpringBoot框架来接收multipart/form-data文件
在本文中,您将学习如何使用 Spring Boot 实现 Web 服务中的文件上传和下载功能。首先会构建一个 REST APIs 实现上传及下载的功能,然后使用 Postman 工具来测试这些接口,最后创建一个 Web 界面使用 JavaScript 调用接口演示完整的功能。最终界面及功能如下:
macOS Catalina(10.15.4)/IntelliJ IDEA 2018.3/Tomcat 9.0.33/Maven项目调试报错"Caused by: java.util.zip.ZipException: zip file is empty"
macOS Catalina(10.15.4)/IntelliJ IDEA 2018.3/Tomcat 9.0.33/Maven项目调试时报错,这个项目以前是可以正常调试的,一段时间之后,就不能正常调试之下了。
struts2升级之后报错“java.lang.NoSuchMethodError: org.apache.commons.lang3.reflect.MethodUtils.getAnnotation”
struts2 从如下版本:
1 2 |
<struts2.version>2.5.10.1</struts2.version> <apache.commons.version>3.5</apache.commons.version> |
升级到
1 |
<struts2.version>2.5.20</struts2.version> |
之后报错如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
四月 13, 2019 10:07:59 上午 org.apache.catalina.core.StandardWrapperValve invoke 严重: Servlet.service() for servlet [default] in context with path [] threw exception [Filter execution threw an exception] with root cause java.lang.NoSuchMethodError: org.apache.commons.lang3.reflect.MethodUtils.getAnnotation(Ljava/lang/reflect/Method;Ljava/lang/Class;ZZ)Ljava/lang/annotation/Annotation; at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:44) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.doIntercept(ConversionErrorInterceptor.java:142) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:137) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:137) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:201) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:67) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.DateTextFieldInterceptor.intercept(DateTextFieldInterceptor.java:133) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:89) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:243) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:101) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:142) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:160) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:175) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:121) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:167) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:203) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:196) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249) at org.apache.struts2.factory.StrutsActionProxy.execute(StrutsActionProxy.java:48) at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:574) at org.apache.struts2.dispatcher.ExecuteOperations.executeAction(ExecuteOperations.java:79) at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:141) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:1025) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1137) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:317) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) |
解决方法为升级
1 2 3 4 5 |
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${apache.commons.version}</version> </dependency> |
到更高的版本,如下:
1 2 |
<struts2.version>2.5.20</struts2.version> <apache.commons.version>3.8.1</apache.commons.version> |
参考链接
java.lang.NoSuchMethodError: org.apache.commons.lang3.math.NumberUtils.isCreatable(Ljava/lang/String
升级Struts2之后报告HTTP Status 500 - java.lang.ClassNotFoundException: org.apache.jsp.index_jsp以及org.apache.jasper.JasperException: Unable to compile class for JSP
升级Struts2
从2.3.20.1
版本升级到2.5.5
版本后可能报告如下错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
HTTP Status 500 - Unable to compile class for JSP: type Exception report message Unable to compile class for JSP: description The server encountered an internal error that prevented it from fulfilling this request. exception org.apache.jasper.JasperException: Unable to compile class for JSP: An error occurred at line: [38] in the generated java file: [/var/lib/tomcat7/work/Catalina/localhost/Tools/org/apache/jsp/index_jsp.java] The method getJspApplicationContext(ServletContext) is undefined for the type JspFactory Stacktrace: org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:103) org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:366) org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:468) org.apache.jasper.compiler.Compiler.compile(Compiler.java:378) org.apache.jasper.compiler.Compiler.compile(Compiler.java:353) org.apache.jasper.compiler.Compiler.compile(Compiler.java:340) org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:657) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:357) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390) org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334) javax.servlet.http.HttpServlet.service(HttpServlet.java:727) org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:110) note The full stack trace of the root cause is available in the Apache Tomcat/7.0.52 (Ubuntu) logs. |
也有可能发生如下错误信息:
1 |
HTTP Status 500 - java.lang.ClassNotFoundException: org.apache.jsp.index_jsp |
具体信息如下图:
比较诡异的是,在Tomcat 8
的环境下,是可以正常运行的,但是在Tomcat 7
环境下却会报错。造成这个现象的原因就是在引入的Jar
包中包含了jsp-api.jar
这个Jar
包,只要在最后生成的war
包中排除这个文件即可。
Spring Junit 读取WEB-INF下的配置文件
假设spring
配置文件为applicationContext.xml
一.Spring
配置文件在类路径下面
在Spring
的Java
应用程序中,一般我们的Spring
的配置文件都是放在放在类路径下面(也即编译后会进入到classes
目录下)。
以下是我的项目,因为是用maven
管理的,所以配置文件都放在"src/main/resources
"目录下,这时候,在代码中可以通过
1 |
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); |
然后获取相应的bean
。
如果代码想用Junit
测试框架来测试,则Spring
提供了对Junit
支持,还可以使用注解的方式:
1 2 |
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:applicationContext.xml"}) |
只需要在相应的Test
类前面加上此两个注解(第二个注解用来指明Spring
的配置文件位置),就可以在Junit Test
类使用中Spring
提供的依赖注入功能。
二.Spring
配置文件在WEB-INF
下面
当然在做J2EE
开发时,有些人习惯把Spring
文件放在WEB-INF
目录(虽然更多人习惯放在类路径下面)下面,或者有些Spring
配置文件是放在类路径下面,而有些又放在WEB-INF
目录下面,如下图:
这时候,在代码中就不可以使用ClassPathXmlApplicationContext
来加载配置文件了,而应使用FileSystemXmlApplicationContext
。
1 |
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml"); |
然后获取相应的bean
。
如果代码想用Junit
测试框架来测试,则Spring
提供了对Junit
支持,还可以使用注解的方式:
1 2 |
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/applicationContext.xml"}) |
只需要在相应的Test
类前面加上此两个注解(第二个注解用来指明Spring
的配置文件位置),就可以在Junit Test
类使用中Spring
提供的依赖注入功能。
下面是我的一个Spring
管理下的Junit
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"file:src/main/webapp/WEB-INF/applicationContext.xml"}) public class SuFriendServiceImplOverRMITest { @Autowired private SuFriendService suFriendService; @Test public void getUserFollowerListTest(){ List list = suFriendService.getUserFollowerList("liug_talk@163.com"); System.out.println("------"+list); } } |