Spring、MySQL、日期、BigDecimal、集合、反射、序列化中的坑与使用指南

MySQL中的坑

MySQL断开连接

默认8小时–>没请求,断开连接

修改配置,避免断开连接
sql方式

interactive_timeout  wait_timeout
set global 

配置文件方式
80小时没请求就断开

[mysqld]
interactive_timeout =288000
wait_timeout=288000

Mysql表字段设置为not null

如何解决网络瓶颈

  1. 扩容
  2. 分散
  3. 压缩

核心流程的性能查看

在代码里加入一个日志打印,悄悄的把每个步骤的耗时打印出来,自己看一看,然后看看核心流程的时间耗时多长,有没有优化的空间

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的不适用场景

  1. 单字母驼峰
  2. 有子类继承
  3. 尽量手动生成!!

Spring的Bean默认名称生成规则

Spring默认包扫描机制:SpringApplication—>所在包及其子包下的所有目录
@ComponentScan

@ComponentScan(value = {"com.example"})

默认:首字母小写,其他不变
开头第一个字母大写,第二个字母也大写,则不变,即名称为原类名

new出来的对象不被Spring所管理

需要成为Spring容器中的实例,才可以@Autowired

SpringBean相关的注解

@Autowired:默认按类型注入
@Resource:默认byName
@Primary:存在多个相同的Bean,则@Primary用于定义首选项

Spring出现循环依赖

  • 单例模式下
  1. 构造器循环依赖没办法解决
  2. 属性注入
@Autowired
private UserService userService;
  1. set 方式
  • 原型模式下无法解决
    每次都是new出来的,无法缓存对象

Bean的生命周期使用

  1. 实现InitializingBean

该Bean完成实例化后,对该Bean的操作

  1. 实现BeanPostProcessor

对所有的Bean的postProcessBeforeInitializationpostProcessAfterInitialization进行操作

  1. 实现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
  1. @Transactional所在的方法必须为public
  2. 所在的类必须被spring容器管理!!@Component
  3. 数据库必须支持事务,如InnoDB 支持事务,MyISAM不支持!
  4. 默认情况下, @Transactional 注解 只对抛出的 RuntimeException 异常有效
  5. 指定异常时的事务回滚,使用如下配置
// Exception.class 是所有异常的父类
@Transactional(rollbackFor={RuntimeException.class, Exception.class})
  1. @Transactional 必须加在指定类或方法上,而不是接口上
  2. 没有事务的方法调用本类中另一个@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() {

    }


}

  1. 出现异常手动回滚
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;

避免空指针

  1. 方法的返回值为空
  2. 方法、属性的调用者为空
  3. 对象未初始化
  4. 数组、集合未初始化
  5. 自动拆箱导致的空指针
  6. 方法传入的参数为空要判断!!
  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) {

        }
    }
  1. 操作引用类型的属性时,一定要记得判空!
if(user==null) {
	return xxx;
}
if(user!=null){
	//业务代码
}

for循环

  1. 使用 原始 for循环
for (int i = 0; i < 100; i++) {  }
  1. 使用增强for循环
//注意判空
for (User user : users) { }

要好好判等!

  1. 基本数据类型 直接用==比较
  2. 引用数据类型 用equals判断时,要重写equals方法

抽象类与接口

  1. 抽象类是模板不能被实例化
  2. 抽象类中不能有static和final!
  3. 接口中的默认方法不需要实现,可以由实现类直接调用
  4. 接口中只能是常量,抽象类可以 有普通变量
  5. 类可以 实现多个接口,只能 继承一个抽象类
  6. 接口 还可以继承多个接口!
  7. 接口中可以有默认方法和静态方法
  8. 抽象类使用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的使用 以及线程不安全

  1. 变为局部变量
  2. 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位时间戳

在这里插入图片描述
返回的时间为毫秒级的时间戳

反射、序列化

反射

  1. 方法是基本类型时,反射 获取 Method的参数类型也必须一致,不能是 其包装类!!
  2. 如果 调用的方法 属于当前对象的父类,那么getDeclaredMethod获取 不到Method

Serializable

  1. 类中 存在引用对象,引用对象也实现序列化,该类 才可实现序列化
  2. 子类实现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;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值