Springboot

SpringBoot

2.3.x2.3.12.RELEASE

2.4及以上是不稳定版本,没有RELEASE后缀 2.3及以下是稳定版本,引入包需要加入 .RELEASE后缀

配置文件说明: https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.server

Starter说明: https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters

版本匹配说明: https://blog.csdn.net/2301_77025309/article/details/130912352

1、Demo

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.1</version>
        </dependency>
@SpringBootApplication
public class QuickStarter {
    public static void main(String[] args) {
        SpringApplication.run(QuickStarter.class, args);
    }
}
@RestController
public class MyController {
    @GetMapping("/say")
    public String say() {
        return "Hello SpringBoot Test !!!";
    }
}

1.1、配置文件读取说明

1、配置文件路径优先级

配置文件所在目录优先级
项目根目录/config/application.properties1
项目根目录/application.properties2
resources/config/application.properties3
resources/application.properties4

2、配置文件类型优先级(统计目录之间)

配置文件类型优先级
application.properties1
application.yml2
application.yaml3

3、多环境切换

方式1、启动类直接指定加载的文件

@SpringBootApplication
public class QuickStarter {

    public static void main(String[] args) {
//        SpringApplication.run(QuickStarter.class, args);
         new SpringApplicationBuilder(QuickStarter.class).properties("spring.config.location=classpath:/application-pro.yml").run(args);
    }
}

方式2、配置多个文件,yml指定

spring:
  profiles:
    active: dev

配置多个文件有两种方式

​ 1、yml里面用3个 ‘ - ’ 进行拆分不同的版本。例如

---
server:
  port: 8081
spring:
  config:
    activate:
      on-profile: dev
---
server:
  port: 8082
spring:
  config:
    activate:
      on-profile: test

2、配置不同的文件进行区分 application-{version}.yml

image-20230804215346527

注意,外配置文件优先级要高于yml里面配置的文件

方式3、IDEA启动参数配置。

​ 方式1、VM配置 -Dspring.profiles.active=test

​ 方式2、层序参数 --spring.profiles.active=pro

方式2的优先级高于方式1

image-20230804220746684

方式4、java命令指定

Java -jar xxx.jar --spring.profiles.active=dev 这个指定的版本优先级仅次于方式一指定版本

1.2、配置文件数据读取

想要读取配置文件,需要将Bean注解到Spring容器

方式1、@Value

​ @Value(“a”) 表示双引号内的值即为赋值

​ @Value(“${a}”) 表示配置文件中的a配置的值即为赋值

​ @Value(“#{2*3}”) EL表达式赋值为表达式结果 可以使用已经注入容器的Bean,也可以使用系统静态函数

方式2、@ConfigurationProperties(prefix=“info.userinfo”)

​ 1、在启动类上加入@EnableConfigurationProperties(UserInfo.class) 指定配置类

​ 2、在启动类上加入@ConfigurationPropertiesScan(“com.lc.properties”)指定包下面的所有的类为配置类

必须要将配置类上加@ConfigurationProperties(prefix=“info.userinfo”)注解才行。

方式3、Environment

Environment env = run.getBean(Environment.class);
String property = env.getProperty("info.userinfo.username");

1.2、配置文件书写说明

info:
  userinfo:
    username: 张三
    age: ${random.int}
    average: 97.3
    list: a,b,c
    list2: [1,2,3]
    list3:
      - x
      - y
      - z
    map:
      a: 1
      b: 2
    map2: {a1: 10,b1: 20}
    pcInfos:
      - pcname: xx
      - pcname: yy
    text: |
      天下没有不散的宴席
      人生没有不尽的缘分
    word: >
      天生我材必有用
      千金散尽还复来
tdata: {aa: 10, bb: xw ,cc: 18.5}

1、:后面必须跟空格

2、可用$进行上述属性的引用或着系统函数的引用 ${random.int:11} 冒号后面表示取不到值时的默认值

3、List三种配置方式 Map类比对象都有两种配置方式

4、| 表示大文本 不去除换行符读取 > 表示大文本去除换行符合并

2、Starter配置

1、热部署

引入依赖:spring-boot-devtools

yam配置:

#热部署配置说明
spring:
  devtools:
    restart:
      enabled: true #是否开启热部署 false直接关闭 不启动热部署
      additional-paths: src/main #变更重启目录
      exclude: static/** #哪些目录变更不重启项目

方式1: 直接使用默认配置,每次修改完后重新构建代码image-20230811183818217 即可进行重新部署

方式2:勾选标记部分,每次代码修改后自动部署

image-20230811183929762 image-20230811184032975

方式3、使用开源插件JRebel

image-20230811184139334

2、log

引入依赖:spring-boot-starter-logging

配置方式:

1、yml配置

#日志配置
log-pattern-format: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%logger{50}] - %msg%n"  #日志格式
logging:
  file: #file下面配置以name为主,如果name不存在 则读取path 并新建一个spring.log文件,如果两个同时存在以name为主
    #path: logs
    name: logs/mylogInfo.log
  charset: #日志编码 console控制台 file为日志文件
    file: UTF-8
    console: UTF-8
  logback: #打包策略
    rollingpolicy:
      file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz #滚动日志文件名的模式
      max-history: 7 #归档日志文件的最大数量
      max-file-size: 10MB #最大日志文件大小
      clean-history-on-start: true #是否在启动时清理归档日志文件
      total-size-cap: 1GB #要保留的日志备份的总大小
  pattern:
    console: ${log-pattern-format} #Appender模式,输出到控制台。仅支持默认的Logback设置
    file: ${log-pattern-format} 
  level:
    root: info #跟目录日志级别
    com.lc.controller.MyController: trace #指定文件路径日志级别

2、配置文件配置

logback.xml

3、如果集成了lombok则 在Bean上直接使用注解@Slf4j 否则使用

Logger logger = LoggerFactory.getLogger(this.getClass());

3、test

引入依赖:spring-boot-starter-test

配置方式:

image-20230812192415407

弹出

image-20230812192447959

新建配置:

@SpringBootTest(classes={QuickMain.class}) //需要制定springboot启动类,引入配置
class MyControllerTest {
    @Autowired
    private MyController controller;

    @Test
    void hello() {
        controller.hello();
    }
}

4、web

引入依赖:spring-boot-starter-web

配置yml:

server:
  port: 80 #端口号
  servlet:
    context-path: / #请求路径前缀
  tomcat:
    max-connections: 8192 #最大连接数
    threads:
      max: 200 #工作线程的最大数量
      min-spare: 10 #工作线程的最小数量
spring:
  application:
    name: quick #项目名称

1、静态资源错误页面配置 必须是resources/public/error下面 500.html 和 404.html

2、thymeleaf界面编程

引入依赖:spring-boot-starter-thymeleaf

Resources/templates文件夹下新建html。注意html需要引入xmlns:th=“http://www.thymeleaf.org”

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p th:text="${welcome}" > 这里显示动态数据 </p>
<div th:text="${welcome}" > 这里显示动态数据 </div>
<span th:text="${welcome}" > 这里显示动态数据 </span>

<p th:text="#{login.error.a}"></p>

<p th:text="${message}">这里是国际化消息 </p>
</body>
</html>

Controller需要设定Model

@Controller
public class IndexController {

    @GetMapping("/index")
    public String index(Model model){
        System.out.println("进入 日志");
        String message = MessageUtils.getMessage("login.error.test");
        model.addAttribute("welcome", "welcome to  my  html ");
        model.addAttribute("message" , message);
        return "index";
    }
}

3、国际化编程

依赖为Web依赖 在resources/i18n 新建 image-20230811095732149

新建如下demo文件 login.properties、login_en_US.properties、login_zh_CN.properties

如果是thymeleaf 参考上图,国际化#{}获取即可。 Java代码返回,则需要根据地区进行配置。配置代码如下

@Configuration
public class LanguageLocaleConfig implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        Locale locale = Locale.CHINA;
        String language = request.getHeader("Accept-Language");
        if (StringUtils.isNotBlank(language)) {
            String[] splits = language.split("-");
            if (splits.length > 1) {
                locale = new Locale(splits[0], splits[1]);
            }
        }
        return locale;
    }
    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
    }
    @Bean
    public LocaleResolver localeResolver() {
        return new LanguageLocaleConfig();
    }
}

@Component
public class MessageUtils {

    private static MessageSource messageSource;

    public MessageUtils(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    public static String getMessage(String key) {
        return messageSource.getMessage(key, null, LocaleContextHolder.getLocale());
    }
}

4、过滤器Filter

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String filterName = filterConfig.getFilterName();
        log.info("filterConfig = {} , filterName = {}" ,filterConfig,filterName);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("filter----------doFilter");
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {
        log.info("---destroy-----");
    }
}

5、拦截器Interceptor

@Slf4j
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("Interceptor-----preHandle");
        return true; //True 才会继续往下执行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("Interceptor-----postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("Interceptor-----afterCompletion");
    }
}

注册Bean

@Configuration
public class MyWebMVCConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {//拦截器过滤
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/index");
    }
    @Bean
    public FilterRegistrationBean<MyFilter> myFilterRegistrationBean() {//过滤器注册
        FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new MyFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1);
        return registrationBean;
    }
}

5、actuator

引入依赖:spring-boot-starter-actuator

yaml配置:

#Actuator
management:
  endpoints:
    web:
      exposure:
        include: '*' #开放所有的端点
        #exclude: '*' #关闭所有端点
      base-path: /tab #actuator请求路径重写
  endpoint:
    shutdown:
      enabled: true #服务下线  POST请求
    health:
      show-details: always #展示详细健康信息
  server:
    port: 9090 #actuator请求端口
  info:
    env:
      enabled: true #启用自定义配置
info:
  app:
    name: zhangsan
    age: 10

访问: http://localhost:9090/tab/

6、admin

Server端:

​ 引入依赖:spring-boot-admin-starter-server 启动类注解 @EnableAdminServer 启动Server

client端:

​ 引入依赖:spring-boot-admin-starter-client

​ yml配置,注册服务

spring.boot.admin.client.instance:
 name: quick 
 url: http://localhost:9000/

如果启动服务监控,需要启动Actuator。

7、Data

yaml必须配置:

spring:
  datasource:
    druid:
      url: jdbc:mysql://127.0.0.1:3306/lc_01_mybatis?serverTimezone=UTC&userUnicode=true&charsetEncoding=utf-8
      username: mybatis
      password: mybatis
      driver-class-name: com.mysql.cj.jdbc.Driver

1、mybatis

引入依赖:mybatis-spring-boot-starter如果使用分页插件pagehelper-spring-boot-starter,则不需要引入mybatis-spring-boot-starter

配置:

1、启动类配置@MapperScan(basePackages = {“com.lc.dao”})

2、Dao接口配置:@Mapper

yaml配置:

mybatis:
  mapper-locations: classpath:mappers/*.xml
  type-aliases-package: com.lc.pojo
  configuration:
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
pagehelper:
  helper-dialect: mysql # 指定分页插件使用哪种语言
  params: count=countSql # 为了支持startPage(Object params)方法,增加该参数来配置参数映射,用于从对象中根据属性名取值,可以配置pageNum,pageSize,pageSizeZero, reasonable, 不配置映射是使用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
  reasonable: true # 分页合理化参数,默认为false,当该值为true,pageNum<=0默认查询第一页,pageNum>pages时会查询最后一页,false时直接根据参数进行查询
  support-methods-arguments: true  # 默认为false, 为true时允许在运行时根据多数据源自动识别对应的方言进行分页

mapper配置:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lc.dao.StudentDao">

    <resultMap id="baseMap" type="student">
        <id column="stu_id" property="stuId"/>
        <result column="stu_name" property="stuName"/>
        <result column="stu_age" property="stuAge"/>
        <result column="course_id" property="courseId"/>
    </resultMap>
    <insert id="insertStudent" parameterType="student">
       insert into student(stu_name,stu_age,course_id) values(#{stuName},#{stuAge},#{courseId})
    </insert>
    <select id="findAll" resultMap="baseMap">
        select stu_id, stu_name,stu_age,course_id from student
    </select>
</mapper>

pageHelper使用:

@Override
public PageInfo<Student> getAllStudent() {
    PageHelper.startPage(1, 2);
    List<Student> list = studentDao.findAll();
    return new PageInfo<Student>(list);
}

事务配置:

@Autowired
private PlatformTransactionManager transactionManager;

@Override
    @Transactional//声明式事务
    public void add() {
        Student stu1 = new Student("add1-张三", 10, 9);
        int a = studentDao.insertStudent(stu1);
//        int i = 1 / 0;
        Student stu2 = new Student("add1-李四", 11, 10);
        int b = studentDao.insertStudent(stu2);
    }

    @Override
    public void add2() {
        TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());//编程式事务
        try {
            Student stu1 = new Student("add2-张三", 10, 9);
            int a = studentDao.insertStudent(stu1);
//            int i = 1 / 0;
            Student stu2 = new Student("add2-李四", 11, 10);
            int b = studentDao.insertStudent(stu2);
            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
        }

    }

2、jdbcTemplate

引入依赖:spring-boot-starter-jdbc

@Test
void jdbcTemplate() {
    String sql = "select stu_id, stu_name,stu_age,course_id from student ";
    List<Student> maps = jdbcTemplate.query(sql, new RowMapper<Student>() {
        @Override
        public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
            Student student = new Student();
            student.setStuId(rs.getInt("stu_id"));
            student.setStuName(rs.getString("stu_name"));
            student.setStuAge(rs.getInt("stu_age"));
            student.setCourseId(rs.getInt("course_id"));
            return student;
        }
    });
    maps.stream().forEach(System.out::println);
}

3、jpa

引入依赖:spring-boot-starter-data-jpa

@Data
@Entity
@Table(name = "student")
public class JPAStudent {
    @Id
    @GeneratedValue
    private Integer stuId;
    private String stuName;
    private Integer stuAge;
    private Integer courseId;
}

@Repository
public interface MyJPA extends JpaRepository<JPAStudent, Long> {

}

@Test
void jpa(){
    List<JPAStudent> all = jpa.findAll();
    all.stream().forEach(System.out::println);
}

4、mybatisPlus

引入依赖:mybatis-plus-boot-starter

@Autowired
private MP mp;

@Mapper
public interface MP extends BaseMapper<Student> {
}

@Test
    void mp(){
        List<Student> students = mp.selectList(null);
        students.stream().forEach(System.out::println);
    }

8、定时任务

@SpringBootApplication
@EnableScheduling //启动类激活 定时任务
public class QuickMain {
    public static void main(String[] args) {
        SpringApplication.run(QuickMain.class, args);
    }
}

@Slf4j
@Component
public class MyTask {

    @Scheduled(cron="*/10 * * * * ?") //每个10s执行一次
    public void task1(){
        log.info("Task1 每10秒执行一次 当前时间:{}" , new Date() );
    }
    @Scheduled(fixedRate =20000) //下一次执行 间隔20秒
    public void task2(){
        log.info("Task2 间隔20s执行一次  当前时间:{}" , new Date() );
    }
    @Scheduled(cron="0 0 14 * * ?")//定点 每天14点执行
    public void task3(){
        log.info("Task3 14点执行 当前时间:{}" , new Date() );
    }

}

9、参数校验

SpringBoot 2.3.x 移除了validation依赖需要手动引入依赖。

引入依赖:spring-boot-starter-validation

Demo

@RestController
public class MyController {

    @PostMapping("/test")
    public String print(@Validated  UserInfo userInfo){
        System.out.println("user = "+ userInfo);
        return userInfo.toString();
    }
    @GetMapping("/test1")
    public String print2(@Validated @NotNull String id){
        System.out.println("user = "+ id);
        return id;
    }
}

@RestControllerAdvice
public class ExceptionConfig { 
    @ExceptionHandler(BindException.class)
    public String handleBindingResult(BindException bindException) {
        String defaultMessage = bindException.getAllErrors().get(0).getDefaultMessage();
        return defaultMessage;
    }
}
public class UserInfo { //实体类
    @NotNull(message="name 不能为空")
    @Size(min = 6, max = 11, message = "账号长度必须是6-11个字符")
    private String name;
}
验证注解验证的数据类型说明
@AssertFalseBoolean,boolean验证注解的元素值是false
@AssertTrueBoolean,boolean验证注解的元素值是true
@NotNull任意类型验证注解的元素值不是null
@Null任意类型验证注解的元素值是null
@Min(value=值)BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型验证注解的元素值大于等于@Min指定的value值
@Max(value=值)和@Min要求一样验证注解的元素值小于等于@Max指定的value值
@DecimalMin(value=值)和@Min要求一样验证注解的元素值大于等于@ DecimalMin指定的value值
@DecimalMax(value=值)和@Min要求一样验证注解的元素值小于等于@ DecimalMax指定的value值
@Digits(integer=整数位数, fraction=小数位数)和@Min要求一样验证注解的元素值的整数位数和小数位数上限
@Size(min=下限, max=上限)字符串、Collection、Map、数组等验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小
@Pastjava.util.Date,java.util.Calendar;Joda Time类库的日期类型验证注解的元素值(日期类型)比当前时间早
@Future与@Past要求一样验证注解的元素值(日期类型)比当前时间晚
@NotBlankCharSequence子类型验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格
@Length(min=下限, max=上限)CharSequence子类型验证注解的元素值长度在min和max区间内
@NotEmptyCharSequence子类型、Collection、Map、数组验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@Range(min=最小值, max=最大值)BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子类型和包装类型验证注解的元素值在最小值和最大值之间
@Email(regexp=正则表达式,flag=标志的模式)CharSequence子类型(如String)验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式
@Pattern(regexp=正则表达式,flag=标志的模式)String,任何CharSequence的子类型验证注解的元素值与指定的正则表达式匹配
@Valid任何非原子类型指定递归验证关联的对象如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证

3、原理

Demo说明:

package com.lc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
//1、注解 @SpringBootApplication
//2、启动方法 SpringApplication.run(DemoApplication.class, args); 

1、注解说明:@SpringBootApplication

@SpringBootConfiguration //定义为一个配置类 参考下面 part1
@EnableAutoConfiguration //开启自动扫描 参考下面 part2
@ComponentScan// 包扫描 排除已经扫描进来的配置类、和自动配置类
(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{
  
}
//part1 核心就是Configuration注解
@Configuration
public @interface SpringBootConfiguration {}
  
//part2
@AutoConfigurationPackage //扫描主程序包:加载自己的组件 参考下面part3
@Import(AutoConfigurationImportSelector.class) //加载所有自动配置类:加载starter导入的组件 参考下面part5
public @interface EnableAutoConfiguration {}

//part3
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}

//导入的核心配置类
//part4  加载主程序包
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
		@Override //将对应注解所在的包自动配置到容器  
  						//AnnotationMetadata metadata这个就是注解的类即核心启动类,
  						//当方法启动时只加载该类所在包的Bean
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}
		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImports(metadata));
		}
	}  
 
//part5  加载starter
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
      
  	@Override //加载starter
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
					() -> String.format("Only %s implementations are supported, got %s",
							AutoConfigurationImportSelector.class.getSimpleName(),
							deferredImportSelector.getClass().getName()));
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
				.getAutoConfigurationEntry(annotationMetadata);//核心步骤
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}
   
//获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   // 获得@Congiguration标注的Configuration类即被审视introspectedClass的注解数据,
   // 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
   // 将会获取到exclude = FreeMarkerAutoConfiguration.class和excludeName=""的注解数据
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   //[1] 得到spring.factories文件配置的所有自动配置类 2.7版本前加载META-INF/spring.factories文件中的Bean
  //2.7 版本后同时加载META-INF/spring/%s.imports文件中的bean
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   // 利用LinkedHashSet移除重复的配置类
   configurations = removeDuplicates(configurations);
   // 得到要排除的自动配置类,比如注解属性exclude的配置类
   // 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
   // 将会获取到exclude = FreeMarkerAutoConfiguration.class的注解数据
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   // 检查要被排除的配置类,因为有些不是自动配置类,故要抛出异常
   checkExcludedClasses(configurations, exclusions);
   // [2] 将要排除的配置类移除
   configurations.removeAll(exclusions);
   // [3] 因为从spring.factories文件获取的自动配置类太多,如果有些不必要的自动配置类都加载
   //进内存,会造成内存浪费,因此这里需要进行过滤
    注意这里会调用AutoConfigurationImportFilter的match方法来判断是否符合
   //@ConditionalOnBean,@ConditionalOnClass或@ConditionalOnWebApplication,后面会重点分
   //析一下
   configurations = filter(configurations, autoConfigurationMetadata);
   // 【4】获取了符合条件的自动配置类后,此时触发AutoConfigurationImportEvent事件,
   // 目的是告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类
   // 该事件什么时候会被触发?--> 在刷新容器时调用invokeBeanFactoryPostProcessors后置处理器时触发
   fireAutoConfigurationImportEvents(configurations, exclusions);
   // 【5】将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回
   return new AutoConfigurationEntry(configurations, exclusions);
}   
}
  
/*@Import使用说明
1、直接引入一个类 自动装载成一个Bean
2、引入一个ImportBeanDefinitionRegistrar通过registerBeanDefinitions方法加载一系列Bean
3、引入一个ImportSelector通过selectImports加载一个包下面的所有Bean*/

2、启动说明:

SpringApplication.run(DemoApplication.class, args);做了两件事情

(1、创建SpringApplication对象;在对象初始化时保存事件监听器,容器初始化类以及判断是否为web应用,保存包含main方法的主配置类

2、调用run方法;准备spring的上下文,完成容器的初始化,创建,加载等。会在不同的时机触发监听器的不同事件)

1、创建SpringApplication对象

public static ConfigurableApplicationContext run(Object source, String... args) {
		return run(new Object[] { source }, args);
	}
 
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
		return new SpringApplication(sources).run(args);
	}
 
public SpringApplication(Object... sources) {
		initialize(sources);
	}
private void initialize(Object[] sources) {
    // 在sources不为空时,保存配置类
    if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
    }
    // 判断是否为web应用
    this.webEnvironment = deduceWebEnvironment();
    // 获取并保存容器初始化类,通常在web应用容器初始化使用
    // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    // 获取并保存监听器
    // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 从堆栈信息获取包含main方法的主配置类
    this.mainApplicationClass = deduceMainApplicationClass();
}


2、利用创建好的SpringApplication对象调用run方法

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    // 配置属性
    configureHeadlessProperty();
    // 获取监听器
    // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的SpringApplicationRunListener
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 启动监听
    // 调用每个SpringApplicationRunListener的starting方法
    listeners.starting();
    try {
        // 将参数封装到ApplicationArguments对象中
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
        // 准备环境
        // 触发监听事件——调用每个SpringApplicationRunListener的environmentPrepared方法
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
        // 从环境中取出Banner并打印
        Banner printedBanner = printBanner(environment);
        // 依据是否为web环境创建web容器或者普通的IOC容器
        context = createApplicationContext();
        analyzers = new FailureAnalyzers(context);
        // 准备上下文
        // 1.将environment保存到容器中
        // 2.触发监听事件——调用每个SpringApplicationRunListeners的contextPrepared方法
        // 3.调用ConfigurableListableBeanFactory的registerSingleton方法向容器中注入applicationArguments与printedBanner
        // 4.触发监听事件——调用每个SpringApplicationRunListeners的contextLoaded方法
        prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
        // 刷新容器,完成组件的扫描,创建,加载等
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        // 触发监听事件——调用每个SpringApplicationRunListener的finished方法
        listeners.finished(context, null);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        // 返回容器
        return context;
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, analyzers, ex);
        throw new IllegalStateException(ex);
    }
}
image-20230823235242823
  1. 导入starter

  2. 依赖导入autoconfigure

  3. 寻找类路径下 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件

  4. 启动,加载所有 自动配置类 xxxAutoConfiguration

    1. 给容器中配置功能组件
    2. 组件参数绑定到 属性类中。xxxProperties
    3. 属性类配置文件前缀项绑定
    4. @Contional派生的条件注解进行判断是否组件生效
  5. 效果:

    1. 修改配置文件,修改底层参数
    2. 所有场景自动配置好直接使用
    3. 可以注入SpringBoot配置好的组件随时使用
image-20230823235449137

4、手写一个starter

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId> //Bean 配置基础包
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>2.7.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId> // 配置文件提醒jar
    <artifactId>spring-boot-configuration-processor</artifactId>
    <version>2.7.0</version>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.28</version>
</dependency>

编写配置文件

@Data
@ConfigurationProperties(prefix = "org.date", ignoreUnknownFields = true)
public class DateProperties { //properties配置类
    private Integer year;
    private Integer month;
    private Integer day;
}

@Data
public class DateTemplate {//基础Bean 注解直接使用
    private Integer year;
    private Integer month;
    private Integer day;
    public Date getDate(){
        System.out.println(year+"------"+month+"------"+day);
        Calendar cal =  Calendar.getInstance();
        cal.set(Calendar.YEAR,year);
        cal.set(Calendar.MONTH,month);
        cal.set(Calendar.DAY_OF_MONTH,day);
        Date time = cal.getTime();
        return time;
    }
}

@Configuration
@ConditionalOnClass(value = DateTemplate.class)
@EnableConfigurationProperties(value=DateProperties.class)
public class DateConfiguration { //核心加载类启动的时候加载
    @Autowired
    private DateProperties dateProperties;
    @Bean
    @ConditionalOnMissingBean()
    public DateTemplate dateTemplate() {
        DateTemplate dateTemplate = new DateTemplate();
        dateTemplate.setDay(dateProperties.getDay());
        dateTemplate.setMonth(dateProperties.getMonth());
        dateTemplate.setYear(dateProperties.getYear());
        return dateTemplate;
    }
}

2.6版本之前:META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=date.DateConfiguration

2.7版本之后:可以如下配置 且兼容上述配置

文件:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

内容:date.DateConfiguration

使用就是简单的starter引入然后注入DateTemplate使用方法

Condition注解说明

注解描述
@ConditionalOnBean:当容器中有指定Bean的条件下进行实例化。
@ConditionalOnMissingBean:当容器里没有指定Bean的条件下进行实例化。
@ConditionalOnClass:当classpath类路径下有指定类的条件下进行实例化。
@ConditionalOnMissingClass:当类路径下没有指定类的条件下进行实例化。
@ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
@ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
@ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
@ConditionalOnExpression:基于SpEL表达式的条件判断。
@ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
@ConditionalOnResource:当类路径下有指定的资源时触发实例化。
@ConditionalOnJndi:在JNDI存在的条件下触发实例化。
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值