MySQL中的坑
MySQL断开连接
默认8小时–>没请求,断开连接
修改配置,避免断开连接
sql方式
interactive_timeout wait_timeout
set global
配置文件方式
80小时没请求就断开
[mysqld]
interactive_timeout =288000
wait_timeout=288000
Mysql表字段设置为not null
如何解决网络瓶颈
- 扩容
- 分散
- 压缩
核心流程的性能查看
在代码里加入一个日志打印,悄悄的把每个步骤的耗时打印出来,自己看一看,然后看看核心流程的时间耗时多长,有没有优化的空间
Spring中的坑与使用
注意springboot的配置文件先后顺序
定时任务不进行
线程数默认为
1
java代码方式
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@Configuration
public class ScheduleConfig {
@Bean
public TaskScheduler getTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(5);
//taskScheduler.getActiveCount();
return taskScheduler;
}
}
lombok的不适用场景
- 单字母驼峰
- 有子类继承
- 尽量手动生成!!
Spring的Bean默认名称生成规则
Spring默认包扫描机制:SpringApplication—>所在包及其子包下的所有目录
@ComponentScan
@ComponentScan(value = {"com.example"})
默认:首字母
小写
,其他不变
若开头第一个字母大写,第二个字母也大写,则不变,即名称为原类名
new出来的对象不被Spring所管理
需要成为Spring容器中的实例,才可以@Autowired
SpringBean相关的注解
@Autowired
:默认按类型注入
@Resource
:默认byName
@Primary
:存在多个相同的Bean,则@Primary用于定义首选项
Spring出现循环依赖
- 单例模式下
- 构造器循环依赖
没办法解决
- 属性注入
@Autowired
private UserService userService;
- set 方式
- 原型模式下
无法解决
每次都是new出来的,无法缓存对象
Bean的生命周期使用
- 实现InitializingBean :
该Bean完成实例化后,对该Bean的操作
- 实现BeanPostProcessor :
对所有的Bean的
postProcessBeforeInitialization
、postProcessAfterInitialization
进行操作
- 实现BeanFactoryPostProcessor :
spring容器创建后,beanFactory代指spring容器
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public class TestBean implements InitializingBean {
@Override
// 当前Bean初始化后,还没有注入属性,只会调用一次 3
public void afterPropertiesSet() throws Exception {
System.out.println("TestBean------>afterPropertiesSet");
}
}
@Component
class PostProcessor implements BeanPostProcessor {
@Override
//在Bean初始化前 2
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof TestBean)) return bean;
System.out.println("TestBean------>postProcessBeforeInitialization");
return bean;
}
@Override
//完成Bean的初始化,并且已经注入属性 4
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof TestBean)) return bean;
System.out.println("TestBean------>postProcessAfterInitialization");
return bean;
}
}
@Component
class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
//在BeanPostProcessor之前执行 1
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
BeanDefinition testBean = beanFactory.getBeanDefinition("testBean");
// 设置怎样定义bean
testBean.setScope(BeanDefinition.SCOPE_SINGLETON);
System.out.println("TestBean------>TestBeanFactoryPostProcessor");
}
}
SpringBoot测试
pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
代码
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
class DemoApplicationTests {
@Test
public static void test(String name) {
System.out.println(name);
}
}
@Transactional的使用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.22</version>
</dependency>
import org.springframework.transaction.annotation.Transactional;
@Transactional
- @Transactional所在的方法必须为public
- 所在的类必须
被spring容器管理!!
@Component - 数据库
必须支持事务
,如InnoDB 支持事务,MyISAM不支持! - 默认情况下, @Transactional 注解
只对抛出的 RuntimeException 异常有效
- 指定异常时的事务回滚,使用如下配置
// Exception.class 是所有异常的父类
@Transactional(rollbackFor={RuntimeException.class, Exception.class})
- @Transactional 必须加在指定类或方法上,而不是接口上
- 没有事务的方法调用本类中另一个@Transactional的方法
此时事务
不生效
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class TestBean {
public void test() {
testTransactional();
}
@Transactional
public void testTransactional() {}
}
解决方法
启动类加@EnableAspectJAutoProxy
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@EnableAspectJAutoProxy //必须加注解
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
修改后代码
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class TestBean {
public void test() {
//事务想要生效,还得利用代理来生效!!!
//获取代理对象(proxy)
// 启动类加上 @EnableAspectJAutoProxy
TestBean proxy = (TestBean) AopContext.currentProxy();
proxy.testTransactional();
}
@Transactional
public void testTransactional() {
}
}
- 出现异常
手动回滚
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
@Component
public class TestBean {
@Transactional
public void testTransactional(User user) {
try {
save(user);
} catch (Exception ex) {
ex.printStackTrace();
// 手动标记回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
private void save(User user) {
}
}
集合、接口中的坑
对象一定要初始化!
User user=null;
避免空指针
- 方法的返回值为空
- 方法、属性的调用者为空
- 对象未初始化
- 数组、集合未初始化
- 自动拆箱导致的空指针
- 方法传入的参数为空要判断!!
public static void main(String[] args) {
List<User> userList = null;
testFor(userList);
}
public static void testFor(List<User> users) {
if (users == null) return;
for (User user : users) {
}
}
- 操作引用类型的属性时,一定要记得判空!
if(user==null) {
return xxx;
}
if(user!=null){
//业务代码
}
for循环
- 使用 原始 for循环
for (int i = 0; i < 100; i++) { }
- 使用增强for循环
//注意判空
for (User user : users) { }
要好好判等!
- 基本数据类型 直接用
==
比较 - 引用数据类型 用
equals判断时
,要重写equals方法
抽象类与接口
- 抽象类是模板,不能被实例化
- 抽象类中不能有static和final!
- 接口中的默认方法不需要实现,可以由实现类直接调用
- 接口中只能是常量,抽象类可以 有普通变量
- 类可以 实现多个接口,只能 继承一个抽象类
- 接口 还可以继承多个接口!
- 接口中可以有默认方法和静态方法
- 抽象类使用
extends
,接口用implements
BigDecimal等大数
浮点数的计算,使用字符串初始化!!
使用 BigDecimal 表示和计算浮点数,且务必使用字符串的构造方法来初始化 BigDecimal:
//加
System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")));
//减
System.out.println(new BigDecimal("1.0").subtract(new BigDecimal("0.8")));
//乘
System.out.println(new BigDecimal("4.015").multiply(new BigDecimal("100")));
//除
System.out.println(new BigDecimal("123.3").divide(new BigDecimal("100")));
//BigDecimal.valueOf() 初始化也可以!
System.out.println(BigDecimal.valueOf(1.0).subtract(BigDecimal.valueOf(0.8)));
//0.2
比较浮点数相等compareTo不用equals
//Returns:
//-1, 0, or 1 as this BigDecimal is numerically less than, equal to, or greater than val.
System.out.println(new BigDecimal("1.0").compareTo(new BigDecimal("1"))==0);
//true
System.out.println(new BigDecimal("1.0").equals(new BigDecimal("1")))
//结果:false
数值溢出问题
//long的最大值
System.out.println(Long.MAX_VALUE);
// +1 之后溢出,成为long的最小值-9223372036854775808
System.out.println(Long.MAX_VALUE+1==Long.MIN_VALUE);
//long的最大值+1, 使用 BigInteger 防止溢出!!
System.out.println(new BigInteger(String.valueOf(Long.MAX_VALUE)).add(BigInteger.valueOf(1)));
精度问题
// scale 需要与小数位匹配
BigDecimal bigDecimal = new BigDecimal("12.222");
// 若设置的精度 比 以前的高,会自动补0
// BigDecimal res = bigDecimal.setScale(12);
//ROUND_HALF_UP 为四舍五入
BigDecimal res = bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println(res);
除法除不尽抛异常
//除不尽 会抛异常
// System.out.println(new BigDecimal("2").divide(new BigDecimal("3")));
System.out.println(
new BigDecimal("2.0")
.divide(
new BigDecimal("3"), 2, BigDecimal.ROUND_HALF_UP
));
long类型数字加上L,float加上F
日期
日期计算LocalDateTime
代码
//时间格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//获取 当前时间
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
System.out.println(formatter.format(localDateTime));
//指定 时间
LocalDateTime _1970 = LocalDateTime.of(1970, 1, 1, 0, 0, 0);
System.out.println(_1970);
//时间格式化器LocalDateTime---->String,调用format方法
String _1970_str = formatter.format(_1970);
System.out.println(_1970_str);
// 获取该月的第几天
int dayOfMonth = localDateTime.getDayOfMonth();
System.out.println(dayOfMonth);
// 将字符串解析为 LocalDateTime对象
LocalDateTime _2010 = LocalDateTime.parse("2010-01-01 00:00:00", formatter);
System.out.println(_2010);
//1970年1月1日 +10日
LocalDateTime _1970_plus10days = _1970.plusDays(10);
System.out.println(_1970_plus10days);
//1970年1月1日 -10日
LocalDateTime _1970_minus10days = _1970.minusDays(10);
System.out.println(_1970_minus10days);
//修改1970年1月1日的DayOfMonth,即为1970年1月20日
LocalDateTime _1970_setDayofMonth_10 = _1970.withDayOfMonth(20);
System.out.println(_1970_setDayofMonth_10);
//LocalDateTime转为LocalDate--->只有年月日
// LocalTime--->只有时分秒
LocalDate localDate = localDateTime.toLocalDate();
System.out.println(localDate);
//1970-1-1 00:00:00
_1970 = LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0);
// 1970年到 现在的时间间隔
// 年,一共多少个月,与天数,天数 仅仅在31天
Period _1970_now_period = Period.between(_1970.toLocalDate(), localDateTime.toLocalDate());
System.out.println(_1970_now_period.toTotalMonths());
System.out.println(_1970_now_period.getDays()); //22-1
System.out.println(_1970_now_period.getYears());
//获取两个时间的间隔,总的天数,
Duration between = Duration.between(_1970, LocalDateTime.now());
System.out.println(between.toHours());
System.out.println(between.toDays());
System.out.println(between.toMillis() - 28800000L);
System.out.println(System.currentTimeMillis());
//isAfter(xx)在xx之后
//isBefore(xx)在xx之前
System.out.println(localDateTime.isAfter(_1970));
System.out.println(localDateTime.isBefore(_1970));
SimpleDateFormat的使用 以及线程不安全
- 变为局部变量
- ThreadLocal
//new 出来一个 格式化器
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
// 当前时间转为字母串
String now = format.format(new Date());
System.out.println(now);
// 将字符串转为时间
String date = "2022-12-30";
try {
Date parseDate = format.parse(date);
System.out.println(parseDate);
} catch (ParseException e) {
e.printStackTrace();
}
// 时间转为 时间戳
long time = format.parse(date).getTime();
System.out.println(time);
System.out.println(System.currentTimeMillis());
时间格式转换
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.7</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
DateConverter
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.text.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
@Component
public class DateConverter extends SimpleDateFormat implements Converter<String, Date> {
public static final List<String> FORMARTS = new ArrayList<>();
static {
FORMARTS.add("yyyy-MM");
FORMARTS.add("yyyy-MM-dd");
FORMARTS.add("yyyy-MM-dd HH:mm");
FORMARTS.add("yyyy-MM-dd HH:mm:ss");
}
@Override
public Date convert(String source) {
String value = source.trim();
if ("".equals(value)) {
return null;
}
if (source.matches("^\\d{4}-\\d{1,2}$")) {
return parseDate(source, FORMARTS.get(0));
} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
return parseDate(source, FORMARTS.get(1));
} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
return parseDate(source, FORMARTS.get(2));
} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
return parseDate(source, FORMARTS.get(3));
} else if (isTimestamp(source)) {//long 时间戳转换
return parseDate(source, FORMARTS.get(3));
} else {
throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
}
}
/**
* 格式化日期
*
* @param dateStr String 字符型日期
* @param format String 格式
* @return Date 日期
*/
public Date parseDate(String dateStr, String format) {
Date date = null;
//long 时间戳转换
if (isTimestamp(dateStr)) {
long time = Long.parseLong(dateStr);
date = new Date(time);
}
try {
DateFormat dateFormat = new SimpleDateFormat(format);
date = dateFormat.parse(dateStr);
} catch (Exception e) {
}
return date;
}
public static boolean isNumeric(String str) {
Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
return pattern.matcher(str).matches();
}
public static boolean isTimestamp(String str) {
return str != null && str.length() == 13 && isNumeric(str);
}
@Override
public Date parse(String source) throws ParseException {
Date convert = this.convert(source);
return convert;
}
//设置所有接口返回的时间为毫秒级的时间戳
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
StringBuffer stringBuffer = new StringBuffer("" + date.getTime());
return stringBuffer;
// return super.format(date, toAppendTo, pos);
}
}
DateConverterConfig
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class DateConverterConfig {
@Autowired
private DateConverter dateConverter;
@Bean
@Primary
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
objectMapper.setDateFormat(dateConverter);
return objectMapper;
}
}
支持这四种格式 + 13位时间戳
返回的时间为毫秒级的时间戳
反射、序列化
反射
- 方法是基本类型时,反射 获取 Method的参数类型也必须一致,不能是 其包装类!!
- 如果 调用的方法 属于当前对象的父类,那么
getDeclaredMethod
获取 不到Method
Serializable
- 类中 存在引用对象,引用对象也实现序列化,该类 才可实现序列化
- 子类实现Serializable接口,父类存在 无参构造方法,子类可以序列化
深拷贝
涉及到的 所有对象都
必须实现Serializable接口
@Override
protected User clone() throws CloneNotSupportedException {
User user = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//ObjectOutputStream是包装流
ObjectOutputStream oos = new ObjectOutputStream(baos);
//写入到 baos流中
oos.writeObject(this);
//将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
User o = (User) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return user;
}