1 网站跨域问题
网站跨域是指请求的目标网站与当前所在的发请求的网站,不在一个域名映射下面,这样的请求就会被浏览器拒绝,认为是跨域调用
解决办法
1 jsonp进行异步跨域请求
2 被调方设置请求头允许跨域
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
3 httpclient进行内部转发
4 通过nginx api网站进行反向代理转发
5 通过spring-cloude zuul网关转发
2 城市级联查询参数回显的问题
碰到前端树插件的时候,给它设置了一个值,发现并没有生效的时候,我们可能要调用一下trigger("change") 事件
var province=$("#province").val();
if(province!=0){
$("select[name='province']").val(province);
$("select[name='province']").trigger("change");
var city=$("#city").val();
$("select[name='city']").val(city);
}
3 导出文件查询参数传递的问题
在我们到处文件的时候可能是一个列表查询,这个时候我们要怎么传递参数呢,就可以向下面这个样子.
//导出数据
$("#export").bind("click",function(){
var param=$("#searchForm").serialize();
window.location.href="${ctx}/dealer/enroll/export.do?
dealerId=${dealerId}&"+param;
});
4 异步加载页面的问题
后台返回jsp页面,js异步加载回来一个页面 往指定区域丢就可以了使用html标签做全局替换
$.ajax({
type: "GET",
url: tml,
data:{
param:param,
pageNo:pageNo,
pageSize:pageSize
} ,
dataType: 'html',
success: function(res){
$("#wrapper").html(res);
},
errot:function(error){
poptys("网络异常,请稍后再试!");
}
});
5 百万数据导出poi报错问题
我们平时倒入的数据量都不大可能不会超过几十万,可是上百万以后你会发现经常出现问题,这个时候就要求我们用10版以后的doc,也就是 SXSSFWorkbook才能导出百万级的数据
(1)集成java包
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>${poi.version}</version>
</dependency>
(2)编写工具类
public class ExcelUtils {
/**
* 导出Excel
* @param sheetName sheet名称
* @param title 标题
* @param values 内容
* @param wb HSSFWorkbook对象
* @return
*/
public static SXSSFWorkbook getXSSFWorkbook(int total,String []title,String [][]values, XSSFWorkbook wbs){
// 第一步,创建一个HSSFWorkbook,对应一个Excel文件
if(wbs == null){
wbs = new XSSFWorkbook();
}
SXSSFWorkbook wb= new SXSSFWorkbook(wbs, 100);
double rel=Double.valueOf(total)/1000000;
int step=(int) Math.ceil(rel);
for(int st=0;st<step;st++) {
String sheetName = "线索信息表"+st;
// 第二步,在workbook中添加一个sheet,对应Excel文件中的sheet
Sheet sheet = wb.createSheet(sheetName);
// 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
Row row = sheet.createRow(0);
// 第四步,创建单元格,并设置值表头 设置表头居中
CellStyle style = wb.createCellStyle();
style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 创建一个居中格式
//声明列对象
Cell cell=null;
//创建标题
for(int i=0;i<title.length;i++){
cell= row.createCell(i);
cell.setCellValue(title[i]);
cell.setCellStyle(style);
}
//创建内容
for(int i=0;i<values.length;i++){
row = sheet.createRow(i + 1);
for(int j=0;j<values[i].length;j++){
//将内容按顺序赋给对应的列对象
CellUtil.createCell(row, j, String.valueOf(i));
row.createCell(j).setCellValue(values[i][j]);
}
}
}
return wb;
}
}
(3) 实现导出控制
@RequestMapping(value="/export.do")
public String export(HttpServletRequest request, HttpServletResponse response) throws Exception {
Env env=EnvUtils.getEnv();
Map<String, String> queryparam = new HashMap<String, String>();
paramQuery(env, queryparam);
long dealerId = env.paramLong("dealerId");
int total = dealerEnrollService.findTotalByDearId(dealerId, queryparam);
List<DealerEnroll> dealerEnrolls = dealerEnrollService.findByDearId(0,0,dealerId,queryparam);
SXSSFWorkbook wb=null;
OutputStream os=null;
long start = System.currentTimeMillis();
try {
String[] title = {"序号","投放序号","姓名","手机","城市","车系","报名时间","效果"};
String fileName = "线索信息表"+System.currentTimeMillis()+".xlsx";
String[][] content = new String[total][title.length];
for (int i = 0; i < total; i++) {
content[i][0] = dealerEnrolls.get(i).getId()+"";
content[i][1] = dealerEnrolls.get(i).getLuanchId()+"";
content[i][2] = dealerEnrolls.get(i).getName();
content[i][3] = dealerEnrolls.get(i).getPhone();
content[i][4] = dealerEnrolls.get(i).getCity();
content[i][5] = dealerEnrolls.get(i).getSerialName();
content[i][6] = T.format(dealerEnrolls.get(i).getCreateat(), "yyyy-MM-dd");
content[i][7] = dealerEnrolls.get(i).getExportDesc();
}
wb = ExcelUtils.getXSSFWorkbook(total,title, content, null);
response.setContentType("application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename="+java.net.URLEncoder.encode(fileName, "UTF-8"));
response.addHeader("Pargam", "no-cache");
response.addHeader("Cache-Control", "no-cache");
os= response.getOutputStream();
wb.write(os);
long end = System.currentTimeMillis();
logger.info("export data expend time:="+(end-start));
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage());
}finally{
try {
os.flush();
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e.getMessage());
}
}
return "dealer/admin/enroll/list";
}
6 导出文件中文名丢失问题
我们在到出文件的时候可能没有文件名,所以就要求我们在response返回的时候一定要加上附件的标识.
response.setHeader("Content-Disposition", "attachment;filename="+java.net.URLEncoder.encode(fileName, "UTF-8"));
7 关于浏览行请求兼容性问题
有的时候我们开发代码,放到ie浏览器上请求多次的效果发现跟请求一次的效果一样,这个时候其实是因为相同的请求被浏览器缓存了.那么我们要怎么做呢,就是在请求参数里面加一个可变的时间戳.这样浏览器每一次就会认为是一个新的请求.
8 接口联条乱码问题
这个问题是我在跟其他团队进行接口联调过程中碰到的,因为不知道到底是发送方问题导致,还是接收方问题导致的乱码
问题,以至于互相甩锅了很久。最后自己想到了一个办法,检查对方传过来的编码,然后自己再对应的转化一下
/**
* @Title: getCode
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param str
* @return
* @author xiaozhengwen
* JSONObject 返回类型
* @throws
*/
private String getCode(String str) {
try {
String encode = "GB2312";
if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是GB2312
String s = encode;
return s; //是的话,返回“GB2312“,以下代码同理
}
encode = "ISO-8859-1";
if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是ISO-8859-1
String s1 = encode;
return s1;
}
encode = "UTF-8";
if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是UTF-8
String s2 = encode;
return s2;
}
encode = "GBK";
if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是GBK
String s3 = encode;
return s3;
}
encode= "GB18030";
if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是GBK
String s4 = encode;
return s4;
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
@RequestMapping(value="/asyncContractInto.do")
@ResponseBody
public JSONObject asyncContractInto(HttpServletRequest request,HttpServletResponse response,@RequestParam String cdata) {
String realIpAdrress = IpUtils.getRealIpAdrress(request);
try {
request.setCharacterEncoding("utf-8");
String str=cdata;
String code = getCode(str);
logger.info("原始合同数据编码格式:"+code);
logger.info("remote ip "+realIpAdrress);
//if(IP.indexOf(realIpAdrress)==-1) return new ResultJsonObj().unOk("", "IP校验不过").translateToJSON();
if(T.isBlank(cdata)) return new ResultJsonObj().unOk("", "请求参数为空").translateToJSON();
String strx=new String(str.getBytes("ISO-8859-1"),"UTF-8");
logger.info("cdata u8 decode:= "+strx);
String decode = URLDecoder.decode(strx);
logger.info("cdata := "+decode);
dealerContractService.asynContract(decode);
return new ResultJsonObj().ok("更新成功", "ok").translateToJSON();
}catch (DateDuplictException e){
return new ResultJsonObj().code(e.getCode(),e.getMsg()).translateToJSON();
}catch (AsynDealerException e){
return new ResultJsonObj().code(e.getCode(),e.getMsg()).translateToJSON();
}catch (RefundException e){
return new ResultJsonObj().code(e.getCode(),e.getMsg()).translateToJSON();
}catch (NoneDealerException e) {
return new ResultJsonObj().code(e.getCode(),e.getMsg()).translateToJSON();
}catch(AsynAuthException e){
logger.info("car server exception :="+e.getMsg());
return new ResultJsonObj().code(e.getCode(),e.getMsg()).translateToJSON();
}catch(AddBrandNumException e) {
return new ResultJsonObj().code(e.getCode(),e.getMsg()).translateToJSON();
}catch(Exception e) {
e.printStackTrace();
return new ResultJsonObj().error("", "服务器异常!"+e.getMessage()).translateToJSON();
}
}
9 redis数据类型使用错误问题
如下所示,是本人在利用redis记录缓存的时候,写过的一段代码,下面是错误的写法。我把一个Long类型的值,放到了redis中进行缓存,因为平时公司也会用guaua做对象缓存。所以用成了习惯。导致我调试了半天都没发现异常的位置。
String text = commonConfigValue.getText();
Long cash = commonConfigValue.getCash();
String token = UUID.randomUUID().toString();
json.put("text",text);
json.put("cash",cash);
json.put("token",token);
String keyfix=CONFIG_KEY+token;
jedisWrapper.set(keyfix,cash);
jedisWrapper.expire(keyfix,TIME);
下面是正确的写法。不说正不正确实际上就是说,一定要保证数据是redis能接收的数据类型,如果不是能接收的数据类型,容易报错。
String text = commonConfigValue.getText();
Long cash = commonConfigValue.getCash();
String token = UUID.randomUUID().toString();
json.put("text",text);
json.put("cash",cash);
json.put("token",token);
String keyfix=CONFIG_KEY+token;
jedisWrapper.set(keyfix,cash+"");
jedisWrapper.expire(keyfix,TIME);
而且不容易定位到问题的原因所在。我的问题是放了一个Long类型的值进去,而且设置了值为一。所以造成了取出来是空。我发现这个问题是我调整了值为100,结果从redis中取出来的时候,是一个问号。这才让我想到了,可能是在设置值的时候类型没有放置正确。导致了系统异常。
10 日期时间比较进制问题
接下来我先贴两段几乎一摸一样的代码,然后可以看到是什么问题
public class FASDF {
public static void main(String[] args) {
try {
String pattern = "yy:MM:dd hh:mm:ss";
DateFormat df = new SimpleDateFormat(pattern);
Date bTime = df.parse("2016:12:12 11:49:45");
Date eTime = df.parse("2016:12:12 12:00:00");
if (bTime.after(eTime)) {
System.out.println(bTime);
}
System.out.println(eTime);
System.out.println(bTime.compareTo(eTime));
} catch (ParseException e) {
e.printStackTrace();
}
}
}
执行结果
下一段代码
public class FASDF {
public static void main(String[] args) {
try {
String pattern = "yy:MM:dd HH:mm:ss";
DateFormat df = new SimpleDateFormat(pattern);
Date bTime = df.parse("2016:12:12 11:49:45");
Date eTime = df.parse("2016:12:12 12:00:00");
if (bTime.after(eTime)) {
System.out.println(bTime);
}
System.out.println(eTime);
System.out.println(bTime.compareTo(eTime));
} catch (ParseException e) {
e.printStackTrace();
}
}
}
可以看到如上代码几乎一摸一样,唯一的差别就是时间格式,好了那么问题来了,下面这段代码的结果是-1为什么
如上面两段代码,代码基本完全一样,但是执行结果却让人匪夷所思,唯一的不同仅在与时间的匹配模式中一个用了HH另一个用了hh 那么是什么问题导致的呢,区别就在于时间匹配模式时HH是24小时制的当为12或24小时的时候不会被程序转化为零时,这样比较的时候就会原本12时大于11时结果却恰恰相反。
11 java List迭代删除问题
印象中循环删除list中的元素使用for循环的方式是有问题的,但是可以使用增强的for循环,然后今天在使用时发现报错了,然后去科普了一下,再然后发现这是一个误区。下面就来讲一讲。。伸手党可直接跳至文末。看总结。。
JAVA中循环遍历list有三种方式for循环、增强for循环(也就是常说的foreach循环)、iterator遍历。
1、for循环遍历list
for(int i=0;i<list.size();i++){
if(list.get(i).equals("del"))
list.remove(i);
}
这种方式的问题在于,删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。
2、增强for循环
for(String x:list){
if(x.equals("del"))
list.remove(x);
}
这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错。
3、iterator遍历
Iterator<String> it = list.iterator();
while(it.hasNext()){
String x = it.next();
if(x.equals("del")){
it.remove();
}
}
这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。
总结:
(1)循环删除list中特定一个元素的,可以使用三种方式中的任意一种,但在使用中要注意上面分析的各个问题。
(2)循环删除list中多个元素的,应该使用迭代器iterator方式。
12 java 跨服务文件传输不能以流文件传输问题
问题描述: 由于中转服务是PHP的老服务,开发人员离职导致变更困难,不知道怎么接收文件流转发给下游,所以让端上的同学以Base64编码字符串转发到java服务端,进行解码以后再转发.
String fileString = agentFleetAccountUploadReq.getFile();
String fileName = agentFleetAccountUploadReq.getFileName();
if(StringUtils.isBlank(fileString) || StringUtils.isBlank(fileName)){
throw new
AccessServiceException(BizErrorCode.AGENT_FLEET_CITY_ERROR);
}
String decode = URLDecoder.decode(fileString, "utf-8");
decode=decode.replaceAll(" ","+");
log.info("fileFinaceUpload decode={}",decode);
File file = MoneyUtils.base64ToFile(decode.trim(),fileName);
MultipartFile multipartFile = MoneyUtils.fileTOMultipart(file);
如上代码所示接收文件名和文件base64编码对+做处理.然后Base64解码成文件
public static File base64ToFile(String base64, String fileName) {
if (base64 == null || "".equals(base64)) {
return null;
}
if (base64.contains("data:")) {
int start = base64.indexOf(",");
base64 = base64.substring(start + 1);
}
base64 = base64.replaceAll(" +","+");
//传输过程中还把Base64的"+"替换成了空格,这里要还原回来
base64 = base64.replaceAll("\r", "");
base64 = base64.replaceAll("\n", "");
byte[] buff = Base64.getDecoder().decode(base64);
File file = null;
FileOutputStream out = null;
try {
String[] split = fileName.split("\\.");
file = File.createTempFile(split[0], "."+split[1]);
out = new FileOutputStream(file);
out.write(buff);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}
再把文件转成多文本
public static MultipartFile fileTOMultipart(File file) throws IOException {
FileInputStream fileInputStream = new FileInputStream(file);
MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(),
ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
return multipartFile;
}
如上代码所示就实现了跨服务的文件base64编码解码.
13 httpClient转发文件问题
public JSONObject fileFinaceUpload(MultipartFile multipartFile){
log.info("fileFinaceUpload file={}",multipartFile.getOriginalFilename());
try {
if (multipartFile.isEmpty()) {
return new JSONObject();
}
String url = financeUrl + "/finance-api/openapi/upload/file";
HttpHeaders headers = new HttpHeaders();
String accessToken = getAccessToken();
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
MediaType type = MediaType.parseMediaType("multipart/form-data");
headers.setContentType(type);
headers.add("Authorization", "Bearer " + accessToken);
List<Object> fileList = new ArrayList<>();
ByteArrayResource byteArrayResource = new ByteArrayResource(multipartFile.getBytes()) {
@Override
public String getFilename() throws IllegalStateException {
return multipartFile.getOriginalFilename();
}
};
fileList.add(byteArrayResource);
map.put("file", fileList);
map.add("data_source", 3);
HttpEntity<MultiValueMap<String, Object>> request =
new HttpEntity<>(map, headers);
return restTemplate.postForObject(url, request, JSONObject.class);
}catch (Exception e){
log.error("uploadAttach e={} e=",e.getMessage(),e);
return new JSONObject();
}
}
14 Maven多模块实现统一版本管理
父Pom 模块
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.xh</groupId>
<artifactId>personal-platform</artifactId>
<packaging>pom</packaging>
<version>${project.version}</version>
...
<properties>
<project.version>2.0.3</project.version>
</properties>
<modules>
<module>commons-center</module>
</modules>
</project>
子pom
<project>
<parent>
<artifactId>personal-platform</artifactId>
<groupId>com.anchnet</groupId>
<version>${project.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>commons-center</artifactId>
<packaging>pom</packaging>
</project>
多模块项目中子模块的版本应该使用父工程的版本,单独设置版本的话会导致版本混乱。
打包
package、install、deploy
如果使用以上设置来发布,必须使用 flatten-maven-plugin
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.3.0</version>
<configuration>
<!--true:更新pom文件,不然无法更新module里的pom版本号,此处还有更高级的用法,具体参靠官方文档-->
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>