# 一.注解使用

# 1.value 注解

如果配置文件中没有 demo.num 配置属性,启动时就会报错,spring 加载不到此属性值。

@value("${demo.num}")
1

如果配置文件中没有 demo.num 配置属性,取默认值 100。

@value("${demo.num:100}")
1
//获取环境
@Value("${spring.profiles.active}")
private String active;
1
2
3

# 2.@JsonFormat 注解

在 Java 编程中,@jsonformat是 Jackson 库中的一个注解,用于指定在将 Java 对象序列化为 JSON 字符串时,如何格式化日期和时间字段。Jackson 是一个广泛使用的 Java 库,用于处理 JSON 数据的序列化和反序列化。

@JsonFormat注解可以应用在类的字段或方法上,以指定日期和时间的格式化方式。它可以接受不同的参数来定义日期和时间的显示样式。以下是一些常用的参数:

  • pattern: 用于指定日期时间的格式模式,类似于 SimpleDateFormat 中的模式。例如:"yyyy-MM-dd HH:mm:ss"
  • timezone: 指定时区,以便正确地将日期时间转换为相应的时区。
  • locale: 指定地区,用于格式化数字和日期时的本地化。
  • shape: 定义日期时间字段的序列化形状,如Shape.STRING表示序列化为字符串,Shape.NUMBER表示序列化为数字,等等。

示例用法:

@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date financialYearStart;
1
2

在上面的例子中,@JsonFormat注解应用在timestamp字段上,指定了日期时间的格式为"yyyy-MM-dd HH:mm:ss",并将时区设置为"UTC"

请注意,为了使用@JsonFormat注解,您需要在项目中引入 Jackson 库的相关依赖。这通常可以通过在项目的构建文件(如 Maven 的pom.xml或 Gradle 的build.gradle)中添加依赖项来完成。

# 3.@TableField 注解

如果您需要在 MyBatis-Plus 中标识某个字段在数据库表中不存在,您可能需要使用以下方法之一:

使用@TableField注解的 exist 属性: @TableField 注解是 MyBatis-Plus 中用于标识数据库字段与实体类属性之间映射关系的注解。它有一个 exist 属性,可以用来表示字段是否在数据库中存在。将 exist 属性设置为 false 可以表示该字段在数据库中不存在。

import com.baomidou.mybatisplus.annotation.TableField;
public class MyEntity {
    @TableField(exist = false)
    private String nonExistentColumn;
}
1
2
3
4
5

# 4.@TableLogic 注解

使用@TableLogic注解: 如果您想要标识一个逻辑删除的字段,在 MyBatis-Plus 中可以使用 @TableLogic 注解来表示。这是一个用于标识逻辑删除字段的注解。

import com.baomidou.mybatisplus.annotation.TableLogic;
public class MyEntity {
    @TableLogic
    private Integer deleted;
}
1
2
3
4
5

# 5.@Nullable

@Nullable 注解可以使用在方法、属性、参数上,分别表示方法返回可以为空、属性值可以为空、参数值可以为空。

@Nullable 是 Spring 框架中的一个注解,用于标识方法返回值、方法参数或属性可以为 null。它提供了一种在编码时明确表达某些元素可以为 null 的方式,从而帮助开发者更好地理解代码,并在静态分析工具或框架中使用这些信息来提供更准确的警告或建议。

在 Spring 框架中,@Nullable 是 Spring Framework 5.0 版本引入的,用于支持 Java 8 及以上版本的编译器。它位于 org.springframework.lang 包中。

使用 @Nullable 的主要目的是帮助开发者在代码中指示某个元素(如方法的返回值、参数或属性)可以接受 null 值。这对于方法的调用者来说是一个重要的信息,因为它可以帮助他们了解哪些地方可能会产生 null 值,从而编写更健壮的代码。

示例用法:

  1. 方法返回值上的 @Nullable 注解:
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Nullable
    public User findUserById(Long id) {
        return userRepository.findById(id);
    }
}
1
2
3
4
5
6
7
8
9
10
11

在上面的示例中,findUserById 方法的返回值被标注为 @Nullable,表示该方法可能返回 null 值。

  1. 方法参数上的 @Nullable 注解:
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void updateUser(@Nullable User user) {
        if (user != null) {
            // 更新用户信息
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

在这个示例中,updateUser 方法的参数 user 被标注为 @Nullable,表示该参数可以为 null 值。

请注意,@Nullable 注解本身不会改变代码的运行时行为,它主要用于编译时的静态分析、IDE 的代码提示以及代码审查工具中。它可以帮助开发者更清晰地表达代码的意图,从而提高代码的可读性和可维护性。

值得注意的是,除了 @Nullable,Spring 还提供了 @NonNull 注解,用于表示方法返回值、方法参数或属性不应该为 null 值。这些注解在代码中的使用可以帮助开发者更好地处理 null 值相关的问题。

# 二.日志打印

# 1.日志顺序

日志级别顺序: DEBUG < INFO < WARN < ERROR < FATAL

SpringBoot 中使用 info 日志级别打印 mybatis SQL 语句,在 Spring Cloud 项目中,生产环境需要打印 mybatis 的 SQL 语句日志,但是 mybatis 打印的 SQL 的默认日志级别是 [debug],如果生产环境想看到 SQL 语句,就必须开启[debug] 级别的日志打印,这样做 debug 日志量过大,显然不可行。

**解决思路:**Spring Boot 中通过 logback 打印 mybatis 的 SQL 语句日志,并自定义日志输出实现将 SQL 语句 [debug] 日志级别上升到 [info] 日志级别。

# 2.日志原理

常用的 mybatis 日志输出是由 org.apache.ibatis.logging.stdout.StdOutImpl 控制的。 根据 StdOutImpl.java 可看出日志都是 System.out.println(s); 的控制台输出,配置及源码如下

#application.xml
mybatis:
  type-aliases-package: com.jiafupeng.mapper
  mapper-locations: classpath:mapper*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台输出日志
1
2
3
4
5
6
public class StdOutImpl implements Log {

  public StdOutImpl(String clazz) {

  }

  @Override
  public boolean isDebugEnabled() {
    return true;
  }

  @Override
  public boolean isTraceEnabled() {
    return true;
  }

  @Override
  public void error(String s, Throwable e) {
    System.err.println(s);
    e.printStackTrace(System.err);
  }

  @Override
  public void error(String s) {
    System.err.println(s);
  }

  @Override
  public void debug(String s) {
    System.out.println(s);
  }

  @Override
  public void trace(String s) {
    System.out.println(s);
  }

  @Override
  public void warn(String s) {
    System.out.println(s);
  }
}
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

# 3.自定义 log

要想改变 mybatis SQL 语句输出内容级别,则只需自定义 Log 实现类,重写 mybatis sql 打印方式及级别。代码如下

#application.xml
mybatis:
  type-aliases-package: com.jiafupeng.mapper
  mapper-locations: classpath:mapper*.xml
  configuration:
 log-impl: com.jiafupeng.util.MySlf4jImpl # mybatis自定义日志输出实现类 并将[debug]日志输出成[info]日志
1
2
3
4
5
6
@Slf4j
public class MySlf4jImpl implements Log {
    public MySlf4jImpl(String clazz) {
    }

    @Override
    public boolean isDebugEnabled() {
        return log.isInfoEnabled();
    }

    @Override
    public void debug(String s) {
        log.info(s);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 4.配置 log 输出

指定 logback 的日志级别为 info,也可在 [info] 级别日志中查看 mybatis 的 sql 语句。

#logback.xml
<!-- 日志输出级别 -->
<root level="info">
 <appender-ref ref="FILE-INFO"/>
</root>

<!-- 如果想将mybatis-sql[info]日志单独输出到一个文件中,就加上如下配置 -->
<!-- <logger name="com.jiafupeng.util.MySlf4jImpl" level="info" additivity="false">-->
<!-- <appender-ref ref="FILE-SQL"/>-->
<!-- </logger>-->
1
2
3
4
5
6
7
8
9
10

# 5.设置不打印

生产环境如果不想打印 mybatis sql 则注释掉打印实现类即可,或者使用 NoLoggingImpl.java 作为实现类,具体看源码。

#日志配置
mybatis:
  type-aliases-package: com.jiafupeng.mapper
  mapper-locations: classpath:mapper*.xml
#或
mybatis:
  type-aliases-package: com.jiafupeng.mapper
  mapper-locations: classpath:mapper*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl # 无日志(默认有debug日志)
1
2
3
4
5
6
7
8
9
10

# 6.配置文件

#logback日志配置
logging:
  config: classpath:logback-spring.xml
  level:
    root: info
    springfox: error #spring的日志检查级别
1
2
3
4
5
6

# 7.日志配置

logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="log.charset" value="utf-8"/>
    <property name="console.log.pattern"
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %blue(%msg%n)"/>
    <property name="file.log.pattern"
              value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${console.log.pattern}</pattern>
            <charset>${log.charset}</charset>
        </encoder>
    </appender>

    <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/xxx-dsc-xxx-insight-ai-replenish.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>
                logs/xxx-dsc-xx-xxx-ai-replenish-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <maxFileSize>128MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>${file.log.pattern}</pattern>
            <charset>${log.charset}</charset>
        </encoder>
    </appender>

    <logger name="com.xxx.dsc.xxx.xx.mapper" level="info" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>

    <root level="info">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="ROLLING"/>
    </root>
</configuration>
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

# 三.xml 配置

# 1.管理类

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <!--注入user类-->
    <bean id="user" class="com.kwan.spring5.User"></bean>
    <!--spring方式: set方法注入属性-->
    <bean id="book" class="com.kwan.spring5.Book">
        <!--使用property完成属性注入
            name:类里面属性名称
            value:向属性注入的值
        -->
        <property name="bname" value="Hello"></property>
        <property name="bauthor" value="World"></property>
    </bean>
    <!--(2)spring方式:有参数构造注入属性-->
    <bean id="orders" class="com.kwan.spring5.Orders">
        <constructor-arg name="oname" value="Hello"></constructor-arg>
        <constructor-arg name="address" value="China!"></constructor-arg>
    </bean>
</beans>
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
public class Spring_01_BookTest {
  @Test
  public void test1() {
    ApplicationContext ctx =
      new ClassPathXmlApplicationContext("spring1.xml");
    Book book = (Book) ctx.getBean("book");
    System.out.println(book);
  }
}
1
2
3
4
5
6
7
8
9

# 2.ApplicationContext

ClassPathXmlApplicationContext ctx =
    new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) ctx.getBean("user");
1
2
3

# 3.BeanFactory

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
User user1 = (User) bf.getBean("user");
1
2

# 4.项目根路径

FileSystemXmlApplicationContext fct =
    new FileSystemXmlApplicationContext("/src/main/resources/applicationContext.xml");
User user2 = (User) fct.getBean("user");
1
2
3

# 5.系统根路径

String rootPath = System.getProperty("user.dir");
BeanFactory bf1 = new XmlBeanFactory(
    new FileSystemResource(rootPath+"/src/main/resources/applicationContext.xml"));
User user3 = (User) bf1.getBean("user");
1
2
3
4

# 6.监听器

<!-- web启动读取applicationContext.xml -->
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:applicationContext.xml</param-value>
</context-param>
1
2
3
4
5
6
7
8

# 四.常见问题

# 1.事务问题

# 1.插入数据成功

事务注解失效的原因可能是因为在同一个类内部的方法调用(insert() 方法调用了 save() 方法)导致了事务失效。在 Spring 中,事务注解默认是通过代理机制来实现的,对于同一个类内部的方法调用,并不会触发代理的增强,从而导致事务注解不起作用。

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TestDao dao;

    @Override
    public void insert() {
        this.save();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save() {
        dao.insert("test");
        dao.update();
        throw new RuntimeException("aaa");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 2.未插入数据

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TestDao dao;

    @Override
    @Transactional
    public void insert() {
        this.save();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save() {
        dao.insert("test");
        dao.update();
        throw new RuntimeException("aaa");
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 3.未插入数据

save 方法的事务被 insert 方法的事务一起接管了

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TestDao dao;

    @Override
    @Transactional
    public void insert() {
        this.save();
        throw new RuntimeException();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save() {
        dao.insert("test");
        dao.update();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 4.修改方案

要解决这个问题,您可以将 save() 方法移动到另一个类中,然后通过依赖注入的方式调用它,从而确保事务注解生效。例如:

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TestDao dao;

    @Autowired
    private SaveService saveService;

    @Override
    public void insert() {
        saveService.save();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class SaveServiceImpl implements SaveService {

    @Autowired
    private TestDao dao;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save() {
        dao.insert("test");
        dao.update();
        throw new RuntimeException("aaa");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这样修改后,save() 方法被移动到了 SaveService 类中,而 TestServiceImpl 类中的 insert() 方法通过依赖注入的方式调用 save() 方法,这样事务注解应该能够正常生效。同时,注意确保依赖注入的配置正确。

# 2.Alias 标签

在 bean 标签里边有一个 alias 属性和 name 属性,可以指定 bean 的别名,但是有的场景下,在定义 bean 的时候就把他的别名都指定好是不适用的. 比如这个 Bean 在组件 A 中,想把他叫做 componentA,但是在组件 B 中又想把他叫做 componetB,所以还有一个单独的标签:< alias>专门解决上述场景的.

<bean id="myBean" class="com.itpluto.MyBean"></bean>
<alias name="myBean" alias="componetA">
<alias name="myBean" alias="componetB">
1
2
3

# 3.Import 标签

import 的使用场景主要是因为项目比较大,配置文件非常多的时候,会进行分模块,这个时候就可以用到 import 这个标签了。

  • 获取 resource 属性的值

    解析路径中的系统属性,比如:${user.dir}

  • 根据 resource 的值来判断是绝对路径还是相对路径

    • 如果是绝对路径直接加载解析文件即可
    • 如果是相对路径要先计算出绝对路径
  • 通知监听器,解析完成.

<import resource="customerContext.xml" />
<import resource="systemContext.xml" />
1
2

# 4.环绕通知

@Around(value = "myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
{
  Object[] args = proceedingJoinPoint.getArgs();
  Object result=null;
  try {
    //前置通知@Before
    System.out.println("环绕前置通知");
    //目标方法执行
    result = proceedingJoinPoint.proceed(args);
    //环绕返回通知@AfterReturning
    System.out.println("环绕返回通知");
  } catch (Throwable throwable) {
    //环绕异常通知@AfterThrowing
    System.out.println("环绕异常通知");
    throw new RuntimeException(throwable);
  } finally {
    //最终通知@After
    System.out.println("环绕最终通知");
  }
  return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 5.分层领域模型规约

  • DO ( Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象.
  • DTO ( Data Transfer Object) : 数据传输对象,Service 或 Manager 向外传输的对象
  • BO ( Business Object ) : 业务对象,可以由 Service 层输出的封装业务逻辑的对象。
  • Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
  • VO (View Object ) : 显示层对象,通常是 Web 向模板渲染引擎层传输的对象。

# 6.获取时间奇偶

// 获取当前时间
LocalDateTime currentTime = LocalDateTime.now();
// 获取当前小时
int currentHour = currentTime.getHour();
// 判断当前小时是奇数还是偶数,并返回相应的值
if (currentHour % 2 == 0) {
    return 2;
} else {
    return 3;
}
1
2
3
4
5
6
7
8
9
10

# 五.特殊工具

# 1.发邮件工具类

package com.deepexi.bfp.financial.service.domain.bill.service.impl;

import com.deepexi.bfp.financial.service.domain.bill.config.MailPropertiesConfig;
import com.deepexi.bfp.financial.service.domain.bill.service.IMailService;
import com.deepexi.bfp.financial.service.domain.bill.utils.SuperCsvUtil;
import com.deepexi.util.exception.ApplicationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.*;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Properties;

@Slf4j
@Service
public class MailServiceImpl implements IMailService {

    @Autowired
    private MailPropertiesConfig mailConfig;

    @Override
    public void send(String email, File file) {
        try {
            Properties props = new Properties();
            props.put("mail.smtp.host ", mailConfig.getHost());
            props.put("mail.smtp.auth", mailConfig.getAuth());
            Session session = Session.getInstance(props);
            Message message = new MimeMessage(session);
            InternetAddress from = new InternetAddress(mailConfig.getAddress());
            message.setFrom(from);
            InternetAddress to = new InternetAddress(email);
            message.setRecipient(Message.RecipientType.TO, to);
            message.setSubject(MimeUtility.encodeText("交易对帐平台"));
            message.setSentDate(new Date());
            MimeMultipart msgMultipart = new MimeMultipart("mixed");// 指定为混合关系
            message.setContent(msgMultipart);
            // 邮件内容
            MimeBodyPart htmlPart = new MimeBodyPart();
            htmlPart.setContent(
                    "<body>"
                            + "<div>"
                            + "交易对帐平台,对账差异信息,请查看附件" + "</div></body>",
                    "text/html;charset=UTF-8");
            //组装的顺序非常重要,一定要先组装文本域,再组装文件
            msgMultipart.addBodyPart(htmlPart);
            // 组装附件
            MimeBodyPart mimeBodyPart = new MimeBodyPart();
            FileDataSource file_datasource = new FileDataSource(file.getPath());
            DataHandler dh = new DataHandler(file_datasource);
            mimeBodyPart.setDataHandler(dh);
            // 附件区别内嵌内容的一个特点是有文件名,为防止中文乱码要编码
            mimeBodyPart.setFileName(MimeUtility.encodeText(dh.getName()));
            msgMultipart.addBodyPart(mimeBodyPart);
            message.saveChanges();
            Transport transport = session.getTransport("smtp");
            transport.connect(mailConfig.getHost(), mailConfig.getPort(), mailConfig.getUsername(), mailConfig.getPassword());
            transport.sendMessage(message, message.getAllRecipients());
            transport.close();
        } catch (MessagingException | UnsupportedEncodingException e) {
            throw new ApplicationException("发送邮件失败");
        }
        log.info("工单邮件发送完毕");
        SuperCsvUtil.deleteFile(file.getPath());
    }
}
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

# 2.异步请求

线程池:

public class ThreadPoolUntil {
    private static final int THREAD_POOL_SIZE = 10;
    private static ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

    public static void executeTask(Runnable task) {
        executorService.submit(task);
    }

    public static void shutdown() {
        executorService.shutdown();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

controller:

@ApiOperation(value = "首页-合计列表", nickname = "首页-合计列表")
@PostMapping("/totalList")
public DeferredResult<Payload<List<TotalListDayDTO>>> totalList(@RequestBody TotalListQuery totalListQuery
        , @RequestHeader(value = "brandDetailNo") String brandDetailNo) {
      totalListQuery.setBrandDetailNo(brandDetailNo);
      DeferredResult<Payload<List<TotalListDayDTO>>> deferredResult = new DeferredResult<>(10000L);
      // 设置超时处理
      deferredResult.onTimeout(() -> deferredResult.setErrorResult(new Payload("504", "请求超时")));
      // 设置错误处理
      deferredResult.onError((Throwable t) -> deferredResult.setErrorResult(new Payload("500", "系统错误")));
      // 创建任务
      Runnable task = () -> deferredResult.setResult(new Payload(skuDataBusinessService.totalList(totalListQuery)));
      ThreadPoolUntil.executeTask(task);
      return deferredResult;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 3.非spring获取bean

 TaskRecordService taskRecordService = SpringUtils.getBean(TaskRecordService.class);
1
/**
 * spring工具类 方便在非spring管理环境中获取bean
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor {
    /**
     * Spring应用上下文环境
     */
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringUtils.beanFactory = beanFactory;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws org.springframework.beans.BeansException
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     *
     * @param clz
     * @return
     * @throws org.springframework.beans.BeansException
     */
    public static <T> T getBean(Class<T> clz) throws BeansException {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name) {
        return beanFactory.containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.getAliases(name);
    }

    /**
     * 获取aop代理对象
     *
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker) {
        return (T) AopContext.currentProxy();
    }
}
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

# 4.日志配置

同一个文件输出:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="log.charset" value="utf-8"/>
    <property name="console.log.pattern"
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %green(%msg%n)"/>
    <property name="file.log.pattern"
              value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
    <property name="log.dir" value="./logs/"/>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${console.log.pattern}</pattern>
            <charset>${log.charset}</charset>
        </encoder>
    </appender>

    <appender name="SYSTEM_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.dir}system/%d.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>${file.log.pattern}</pattern>
            <charset>${log.charset}</charset>
        </encoder>
        <append>true</append>
    </appender>

    <springProfile name="dev,test">
        <logger name="com.chat.mapper" level="DEBUG" additivity="false">
            <appender-ref ref="STDOUT"/>
            <appender-ref ref="SYSTEM_LOG"/>
        </logger>
        <root level="INFO">
            <appender-ref ref="STDOUT"/>
            <appender-ref ref="SYSTEM_LOG"/>
        </root>
    </springProfile>
</configuration>
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

在这个配置中,<logger><root> 都引用了 STDOUTSYSTEM_LOG,这意味着所有的日志都会同时输出到控制台和 SYSTEM_LOG 文件中。这样,你就可以在一个地方查看所有的日志,而不是分散在不同的文件中。

不同文件输出:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="log.charset" value="utf-8"/>
    <property name="console.log.pattern"
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %green(%msg%n)"/>
    <property name="file.log.pattern"
              value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${console.log.pattern}</pattern>
            <charset>${log.charset}</charset>
        </encoder>
    </appender>

    <springProfile name="dev,test">
        <property name="log.dir" value="./logs/"/>
        <property name="file.log.pattern"
                  value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5level [%thread] %logger{36} - %msg%n"/>

        <appender name="SYSTEM_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">            
                <fileNamePattern>${log.dir}system/%d.log</fileNamePattern>  
            </rollingPolicy>
            <encoder>
                <pattern>${file.log.pattern}</pattern>
                <charset>${log.charset}</charset>
            </encoder>
            <append>true</append>
        </appender>

        <appender name="SQL_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">            
                <fileNamePattern>${log.dir}/sql/%d.log</fileNamePattern>  
            </rollingPolicy>
            <encoder>
                <pattern>${file.log.pattern}</pattern>
                <charset>${log.charset}</charset>
            </encoder>
            <append>true</append>
        </appender>
    </springProfile>

    <!--    如果是本地环境,则设置控制台打印-->
    <springProfile name="dev,test">
        <logger name="com.chat.mapper" level="DEBUG" additivity="false">
            <appender-ref ref="STDOUT"/>
        </logger>
        <root level="INFO">
            <appender-ref ref="STDOUT"/>
        </root>
    </springProfile>

    <!--    如果是dev和test环境,则不设置控制台打印,将日志保存到不同的文件-->
    <springProfile name="dev,test">
        <logger name="com.chat.mapper" level="DEBUG" additivity="false">
            <appender-ref ref="SQL_LOG"/>
        </logger>
        <root level="INFO">
            <appender-ref ref="SYSTEM_LOG"/>
        </root>
    </springProfile>
</configuration>
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
上次更新: 11/11/2024, 10:36:10 PM