SpringBoot项目原理

SpringBoot项目原理,第1张

SpringBoot项目原理

你看这篇文章可以学习如下:

  • 首先这篇文章要有一定的基础才可以看,如果你是入门,我建议你还是跟着一个视频或者一本书先入门。

  • 第二你可以从这篇文章知道SpringBoot的配置原理

  • 另外后续会陆陆续续写完SpringBoot文章了

一、首先来看Spring程序的Bean配置

首先了解一下什么是bean配置

  • bean配置就是对我们自己编写代码入到Spring容器的一个过程

  • Spring有三种配置Bean的方式

    • 基于java的
    • 基于XMl的
    • 基于组件扫描的
  • 回顾一下:Spring控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI

    • 怎么说呢,我们以前写java代码是要new的,现在不用啦,然后(DI)注入就之后就可以用了
    • SpringBoot则是自动配置(自动扫描componentscan)和自动注入(Autowired),不理解这句话不要紧,后面会懂的。
1、基于java的配置
  • 使用@Configuration注解将类声明为一个配置类,
  • 再使用@Bean注解将方法返回的实例对象添加到容器的上下文中
  • @Bean注解的bean的ID默认是方法名,如果要指定名称,可以通过name属性来设置
@Configuration
public class WebConfig {

    @Bean
    public User user() {
        return new User("Hello world");
    }

}

2、基于Xml的配置
  • 不适用任何注解,自己配置



    
        
    

    



3、组件扫描的类型

从两个方面对其进行配置:

  • 组件扫描(使用Component注解将普通类配置成Bean)
  • 自动装配(使用@Autowired(自动注入)使Spring满足Bean的依赖)
  • 另外还要注意一些配置扫描的@ComponentScan(“路径”)

1、使用Component注解将普通类配置成Bean

@Component
public class Hello {
    public void HelloWorld(String name){
        System.out.println("Hello" + name);
    }
}

2、使用@Autowired(自动注入)使Spring满足Bean的依赖

@Component
public class User {

    @Autowired
    private Hello hello;

    public void hello() {
        hello.HelloWorld();
    }
}
二、来看看SpringBoot实现一个配置

下面这几句话要背下来:

  • SpringBoot本质还是一个Spring容器,只是它通过导入依赖实现了不同的功能
  • 我们编写的代码就称为组件:通常可以用如下的注解将它入到SpringBoot容器里面:通过注解将它们编写好
    • @Component:泛指各种组件
    • @Component。
    • @Controller:控制层
    • @Service:业务层
    • @Repository:数据访问层
    • 还有如下SpringBoot
    • @Autowired,先不解释
    • @SpringBootConfiguration先不解释
    • 还有等等---------
  • 然后通过@ComponentScan手动入容器或者@AutoConfigurationPackage自动的包扫描的时候它来配置
  • 然后我们就可以通过@Autowired来用,而Spring要通过一系列的xml哦或者其他的配置
1、先创建项目

随便你创建一个什么样子的项目,这里重点在于理解其中的原理,而不是重点在于项目。

  • 我用我之前创建的一个项目
2、分析项目 (1)点我们的父依赖(用Ctrl + 鼠标左键)

(2)同样的道理(点进去看)

  • 重点就是这个依赖了
 
    org.springframework.boot
    spring-boot-dependencies
    2.5.6
  

(3)小总结一下
- 这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
- 以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
3、启动器:spring-boot-starter

我们导入的依赖就是启动器

  • 比如点artifactId:这个就是jwt的启动器
		
            io.jsonwebtoken
            jjwt
            0.9.0
        

4、启动类分析



重点看这里,重点看这里,重点看这里:首先可能目路搞的有点乱,但是我建议还是好好的认认真真的看每一个字

@SpringBootApplication
@MapperScan("com.xxxx.server.mapper")
public class yebApplication {
    public static void main(String[] args) {
        SpringApplication.run(yebApplication.class,args);
    }
}

(1)点这个SpringBootApplication注解看

看见上面又有这几个注解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
	), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
	)}
	)
  • @ComponentScan
    • 这个注解在Spring中很重要 ,它对应XML配置中的元素。

    • 作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

  • @SpringBootConfiguration:点进去看
    • 作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;
    • 我们继续进去这个注解查看:
      • 这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
      • 里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!
(2)回到 SpringBootApplication 注解中继续看
  • @EnableAutoConfiguration注解:开启自动配置功能
    • 这里要记住一点:以后我们要开启什么功能就要EnableXXX(记住,不理解不要紧)
    • 再点EnableAutoConfiguration进去看,比较重要的有下面几个
      • 1)@AutoConfigurationPackage
        • (1)点@AutoConfigurationPackage进去看:暴露; 如下
          @import({Registrar.class})
          public @interface AutoConfigurationPackage {
          }
        • (2)@import :Spring底层注解@import , 给容器中导入一个组件:Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;
      • 2)再来看@import({AutoConfigurationimportSelector.class}):AutoConfigurationimportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:
        • (1)这个类中有一个这样的方法:getCandidateConfigurations
        • (2)这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法
        • (3)我们继续点击查看 loadSpringFactories 方法
        • (4)发现一个多次出现的文件:spring.factories,全局搜索它
protected List getCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) {
    //这里的getSpringFactoriesLoaderFactoryClass()方法
    //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in meta-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}
public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    //这里它又调用了 loadSpringFactories 方法
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    MultiValueMap result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            //去获取一个资源 "meta-INF/spring.factories"
            Enumeration urls = classLoader != null ? classLoader.getResources("meta-INF/spring.factories") : ClassLoader.getSystemResources("meta-INF/spring.factories");
            linkedMultiValueMap result = new linkedMultiValueMap();

            //将读取到的资源遍历,封装成为一个Properties
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry entry = (Entry)var6.next();
                    String factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [meta-INF/spring.factories]", var13);
        }
    }
}

spring.factories文件:在这个位置,我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!

总结一下:通过@EnableAutoConfiguration实现完成自动扫描,然后原理是里面的@import({AutoConfigurationimportSelector.class}):这个类在起作用,去找到了配置下的文件spring.factories进行了自动配置

(3)这里先对:WebMvcAutoConfiguration有个大致的了解
  • 如果你用的idea的话,可以用Ctrl + n实现全局搜索:WebMvcAutoConfiguration然后进行查看
    里面全是Bean的配置
  • 在这里面有很多配置:比如重定向呀,控制器等的配置,接下来对这个输入的讲解
(4)启动方法run的学习
  • 分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行

  • SpringApplication这个类主要做了以下四件事情:

    • 推断应用的类型是普通的项目还是Web项目

    • 查找并加载所有可用初始化器 , 设置到initializers属性中

    • 找出所有的应用程序监听器,设置到listeners属性中

    • 推断并设置main方法的定义类,找到运行的主类

  • 看一下方法

5、WebMvcAutoConfiguration

对我们前面的内容大致有了理解后,接下来要深入的了解了

下面以:HttpEncodingAutoConfiguration(Http编码自动配置)为例分析,以他标准方便分析(你可以以ThymeleafAutoConfiguration为例子呀)等

//表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
@Configuration 

//启动指定类的ConfigurationProperties功能;
  //进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
  //并把HttpProperties加入到ioc容器中
@EnableConfigurationProperties({HttpProperties.class}) 

//Spring底层@Conditional注解
  //根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
  //这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(
    type = Type.SERVLET
)

//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass({CharacterEncodingFilter.class})

//判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
  //如果不存在,判断也是成立的
  //即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)

public class HttpEncodingAutoConfiguration {
    //他已经和SpringBoot的配置文件映射了
    private final Encoding properties;
    //只有一个有参构造器的情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }
    
    //给容器中添加一个组件,这个组件的某些值需要从properties中获取
    @Bean
    @ConditionalOnMissingBean //判断容器没有这个组件?
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
        return filter;
    }
    //。。。。。。。
}

明天再来了有点累了

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4971060.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-13
下一篇 2022-11-13

发表评论

登录后才能评论

评论列表(0条)

保存