1.maven基于java ,所以配置环境变量应先配置java再配置maven
2.关于压缩包后缀
tar.gz属于linux环境
zip属于windows环境
3.事务手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
配合 @Transactional 注解一起使用
之前我好奇我同事为什么要在拦截里手动回滚,不是已经有@Transactional 注解了么
后来才知道,有try catch的情况下,事务会失效
4.synchronized锁this和锁class的区别
synchronized(this) 只锁定当前new出来的对象 比方A a1 = new A(); A a2 = new A(); 他只锁住a1或者只锁住a2
synchronized(A.class) 锁全局 不管new出来的哪个对象都被锁住 下图是一个使用例子 注意final Class
5.private方法写了注解也无法增强
两种说法 1.springAOP不会去管理private方法 2.spring AOP无法实现或者继承private方法
反正结果是:private方法不会走AOP,不生效
6..druid和普通配置
在引入德鲁伊之后,我发现以下两种配置的效果是相同的
spring.datasource.druid.url=jdbc:h2:tcp://172.11.11.111:11111/openapi;AUTO_SERVER=TRUE;
spring.datasource.druid.driver-class-name=org.h2.Driver
spring.datasource.druid.username=openapi
spring.datasource.druid.password=openapi@123
spring.datasource.url=jdbc:h2:tcp://172.11.11.111:11111/openapi;AUTO_SERVER=TRUE;
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=openapi
spring.datasource.password=openapi@123
然后翻了翻源码,找到了名字带AutoConfig的配置文件,发现他这两个配置都读 那个@EnableConfigurationProperties
7.@DependsOn 设置bean的加载顺序
不是简单的order 而是设置条件 当一个bean加载之后,再加载当前bean
在加载dynamicDataSourceFactory 这个bean之后 再加载当前 DynamicRoutingDataSource 这个bean
踩坑记录:部署到线上之后,由于当前bean引用了另一个bean 一直报空指针,猜想可能与bean的加载顺序有关,所以使用@DependsOn,问题解决
补充:今天看到@AutoConfigureAfter这个注解也有相似的地方,先加载指定配置类再加载当前配置类,是需要有@Configuration注解的,网上说还需要spring.factories配置的
8.线上启动报错,找不到SubscriberDataObserver类
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'channelInfoService':
Invocation of init method failed;
nested exception is com.alipay.sofa.runtime.api.ServiceRuntimeException:
Unable to get implementation of reference component,
there's some error occurred when register this reference component.
注意caused by
Caused by: java.lang.ClassNotFoundException: com.alipay.sofa.registry.client.api.SubscriberDataObserver
解决办法: SubscriberDataObserver是registry-client-all包下的,pom中引入依赖
<!--SOFARegistry客户端-->
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>registry-client-all</artifactId>
<version>5.2.1</version>
</dependency>
9.启动报错日志中,看到依赖包后边有叹号
第一眼就觉得是版本冲突问题,确实,引入sofa-boot时没有指定版本号,就是用默认版本了
解决办法:指定版本号
<!--sofa boot rpc-->
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>rpc-sofa-boot-starter</artifactId>
<version>5.5.2</version>
</dependency>
10.当引入依赖不指定版本号
两种情况,一种会报错一种不会报错
当你的<parent>中已经引入了该依赖,不指定版本号时不会报错,会默认引入父依赖中指定的版本,
但是如果你指定了版本号,那就引入你指定的版本号,覆盖了父依赖的
比如我引入了sofaboot-dependencies,然后sofaboot-dependencies中本来就引入了rpc-sofa-boot-starter
<parent>
<groupId>com.alipay.sofa</groupId>
<artifactId>sofaboot-dependencies</artifactId>
<version>3.1.5</version>
</parent>
我再我得项目中又去引入rpc-sofa-boot-starter,不写版本号,就不会报错,但是会引入默认版本
sofaboot-dependencies是3.1.5时,rpc-sofa-boot-starter的默认版本是6.0.3
但我像上边的6一样指定了版本号,那么此时引入的rpc-sofa-boot-starter版本就是5.5.2
11.对象转json
起因:对象转json会走这个实体的一个get方法,但是这个get方法在这个实体中并没有对应的属性,比如getDoubleValue(),并没有doubleValue属性
看了转json的原理:原来只要有get方法就可以
https://blog.csdn.net/Topdandan/article/details/80369870
public String getIdName() {
return id+name;
}
String json=JSON.toJSONString(s);
System.out.println(json);
上面的输出结果为:
{"id":23,"idName":"23呵呵","name":"呵呵"}
可以看到,student对象中并无idName属性,但却有相应的键值对,所以只需要一个getXxx()方法!
可以看到,对象中并无doubleValue属性,但却有相应的键值对,所以只需要一个getDoubleValue()方法!
11.1.不想在转json时候走这个get方法怎么办
@JSONField(serialize = false)
本来用@ JsonIgnore,但是对于fastjson不生效
12.排除jar包
情景:某个项目A要脱离一个项目B运行,但是这个项目A中已经引入了一些B代码,需要把这部分代码复制出来,
并且A的pom中没有直接引入B,而是A间接引入了项目C,然后项目C中引入了项目B
解决办法,既然是C引入了B,就直接引入C,然后在C中排除B
排除之后,关于B的代码就会报错,可以直接摘出来
<dependency>
<groupId>com.wish.plat</groupId>
<artifactId>p-bc-commons</artifactId>
<version>0.1.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.wish.plat</groupId>
<artifactId>plat-parameter-api</artifactId>
</exclusion>
</exclusions>
</dependency>
13.连接池的作用
以前一直不知道,到底为什么要弄一个池子管理链接
重点是:实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,
1.为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection。
这样我们就不需要每次都创建连接、释放连接了,这些操作都交给了连接池。
2.用池来管理Connection,这样可以重复使用Connection。
有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。
当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。
池就可以再利用这个Connection对象了。
14.线程池的作用
线程池作用就是限制系统中执行线程的数量。
使用线程池有如下作用:
- 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
- 可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
15.@Qualifier注解的用处
当一个接口有多个实现的时候,为了指名具体调用哪个类的实现
16.不加访问修饰,默认friendly
补习以下基础
如果一个类的成员没有任何权限修饰,那么它门就是缺省包访问权限,用friendly来表示,注
意friendly不是Java中的关键字,这里是个人喜欢的方式用它表示而已。同一个包内其它类可以访问,但包外
就不可以
18.优雅的拼接字符串
可以用StringBuffer,StringBuilder。如果只拼接一个,也可以用String提供的函数
String由concat方法,不用加号+了
concat(String str)
说明:将指定字符串连接到此字符串的结尾。
返回:String
示例:
String str = "abc";
str = str.concat("123");
System.out.println(str);
输出结果:abc123
19.优雅的toString()
MoreObjects.toStringHelper
通过.add方法拼接节点
20.以小数点分割
在Java中小数点是一个特殊符号,是对象调用属性和方法的特殊符号,所以编译会出错的,用\\是把小数点转义成单纯的小数点
String[] split = attr.split("\\.");
关于java中转义字符的使用 https://www.cnblogs.com/cai170221/p/7098802.html
21.bigdecimal
21.1double转bigdecimal 先转string再转bigdecimal
先使用Double.toString(double)方法,然后使用BigDecimal(String)构造方法
BigDecimal bd1 = new BigDecimal(Double.toString(a));
21.2除法时抛出异常
BigDecimal rate = new BigDecimal(1).divide(new BigDecimal(3), 6, BigDecimal.ROUND_HALF_UP);
1除以3 保留六位小数 向上取整
21.3.double计算保留两位小数
运算后结果可以用.doubleValue再转成double
public static double divide(double a, double b, int scale){
BigDecimal bd1 = new BigDecimal(Double.toString(a));
BigDecimal bd2 = new BigDecimal(Double.toString(b));
//运算完再转成double
return bd1.divide(bd2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
21.4.bigdecimal的加减乘除
不是符号 有自己的方法 比如上边的除
22.@RestController与@Controller
@RestController相当于@ResponseBody+@Controller
比如 在类上边加@Controller 再在类中一个方法上加@ResponseBody 返回的就是json
没有@ResponseBody注解的方法 返回的就是一个页面
@Controller
public class HelloController {
@GetMapping(value="/hello")
@ResponseBody
public String say(){//返回json 数据
return "gril";
}
@GetMapping(value="/hello1")
public String say1(){//返回视图
return "sys/index1";
}
}
如果用@Controller返回页面 根据yml中配置的前缀后缀去找具体页面名称
24.异常转换之后找不到哪行代码报错
这个困扰我时间还挺长的,开始感觉无从下手
后来组长提示:把错误日志打出来就行了
但是,业务逻辑那么多,都打印出来能累死
解决办法:在拦截中可以e.printStackTrace
有两种解决办法
24.1.在网上找的
ByteArrayOutputStream exception= new ByteArrayOutputStream();
1.将异常栈信息先输出到ByteOutputStream
2.然后再将ByteOutputStream 转换为字符串
3.就获得了异常的完整输出
e.printStackTrace(new PrintStream(exception));
String excepString = exception.toString();
log.err(excepString )
24.2.apache提供的工具类
ExceptionUtils.getStackTrace(Throwable throwable)
org.apache.commons.lang3.exception下
因为Throwable是异常父类(老大),所以任何异常都可以用这个方法
public static String getStackTrace(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
两种方法的原理都是把e.printStackTrace输出到一个地方,然后放到log中
24.3.我的部分代码
ExceptionUtils.getStackTrace
} catch (RuntimeException runtimeException) {
String stackTrace = ExceptionUtils.logStackTrace(runtimeException);
log.error(stackTrace);
pf1013.setReturnMessage(runtimeException.getMessage());//返回信息
pf1013.setErrorInfo( "exception"+ExceptionUtils.getStackTrace(runtimeException).substring(0,7000));
throw new BizRuntimeException(ERR_CODE_BUSS_CHECK_PF_000075,runtimeException.getMessage());
} catch (FormatException formatException) {
String stackTrace = ExceptionUtils.getStackTrace(formatException);
log.error(stackTrace);
pf1013.setErrorInfo( "exception"+stackTrace.substring(0,7000));
throw new BizRuntimeException(ERR_CODE_BUSS_CHECK_PF_000075, formatException.getMessage());
} catch (Exception exception) {
String stackTrace = ExceptionUtils.getStackTrace(exception);
log.error(stackTrace);
pf1013.setErrorInfo("exception"+ExceptionUtils.getStackTrace(exception).substring(0,7000));
throw exception;
} catch (Throwable throwable) {
String stackTrace = ExceptionUtils.getStackTrace(throwable);
log.error(stackTrace);
pf1013.setErrorInfo("exception"+ExceptionUtils.getStackTrace(throwable).substring(0,7000));
throw throwable;
25.一个坑人的bug
同事找我帮他看他为什么强转失败,症结居然是:他引入的项目中有一个实体类,自己的项目居然新建了同一个名字的实体类,然后用两个不同的实体类强转!
28.创建对象的几种方式
1.new
2.反射
3.clone
4.反序列化
29.jdbc完整的访问数据库的过程
1.导包 java.sql
2.加载驱动 class.forname
3.连接数据库 用户名密码url
4.执行查询 statement
5.解析返回值 自己创建实体解析
6.手动关闭连接 .close
mybatis底层用的也是jdbc,如preparestatement
31.相互引用解决办法
遇到一个问题,两个项目都要引用对方的东西,但是循环相互引用肯定是不可能的了。
解决办法
把两个项目都想要用的东西写在被引用哪个项目里,比如下图 plat-approval-api是接口层,需要用到实体作为返回类型,但是之前实体我是写道plat-approval-engine里边的,但是plat-approval-engine是实现impl层,肯定要引用 plat-approval-api,但是不能循环引用,所以把实体写到plat-approval-api里边,这样plat-approval-api本身可以直接用实体,然后plat-approval-engine中pom依赖中还是有plat-approval-api,也可以引用。完美解决
32.sofarpc初次接触
这个新公司和阿里云有合作,所以比较喜欢用阿里的东西
sofarpc是通过调用类名和方法提供服务的,并不是调用接口的方式
33.项目之间的引用构建关系
p被ext引用,ext被m引用。那么,p重新构建之后,m想用p中新的代码,是否需要重新构建ext?
答:不用
34.接着上边,如果p和m用@ImportResource了同一个资源
@ImportResource注解用于导入Spring的配置文件,让配置文件里面的内容生效
重复加载,报错
35.明明有这个类,但是报找不到
原因:启动类的扫描路径中不包括这个
解决办法:在启动类的扫描路径中加上报错路径,如下
36.不在循环中查数据库的解决办法
在循环之前,把结果集查到,循环中,从这个结果集里边取
改之前的代码
改之后的代码
37.打包配置
1.默认打包为jar包 可以不写
2.名称
项目名+版本号.jar
artifactId-version.package 这几个标签拼接起来的