Spring原理

一.Bean的作用域

1.1 介绍

Bean的作用域是指Bean在Spring框架中的某种行为模式,比如单例作用域表示Bean在整个Spring中只有一份,全局共享,当其他人修改了Bean的值,那么另一个人就会读取到修改后的值,Bean的这种行为模式就称之为Bean的作用域

1.2 作用域分类

在Spring中支持六种作用域,后四种在Spring MVC环境中才生效

作用域说明
单例作用域 singletonBean的默认作用域,每个Spring IoC容器内同名称的bean只有一个实例(单例)
原型/多例作用域 prototype每次使用该bean时会创建新的实例(非单例)
请求作用域 request每个HTTP请求生命周期内,创建新的实例(web环境中)
会话作用域 session每个HTTP Session生命周期内,创建新的实例(web环境中)
全局作用域 application每个ServletContext生命周期内,创建新的实例(web环境中)
HTTP WebSocket作用域 websocket每个WebSocket生命周期内,创建新的实例(web环境中)
//创建不同作用域的Bean
import org.example.springprinciple.model.Dog;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.*;
import org.springframework.web.context.annotation.*;
@Configuration
public class DogConfig {
    @Bean
    @Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON)
    public Dog singletonDog(){
        return new Dog();
    }

    @Bean
    @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Dog prototypeDog(){
        return new Dog();
    }

    @Bean
    @RequestScope
    public Dog requestDog(){
        return new Dog();
    }

    @Bean
    @SessionScope
    public Dog sessionDog(){
        return new Dog();
    }

    @Bean
    @ApplicationScope
    public Dog applicationDog(){
        return new Dog();
    }
}

//DogController类(接口,提供web环境)
import org.example.springprinciple.model.Dog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/dog")
public class DogController {

    @Autowired
    private ApplicationContext context;

    @Autowired
    private Dog singletonDog;

    @Autowired
    private Dog prototypeDog;

    @Autowired
    private Dog requestDog;

    @Autowired
    private Dog sessionDog;

    @Autowired
    private Dog applicationDog;

    @RequestMapping("/singleton")
    public String singleton(){
        Dog contextDog = context.getBean("singletonDog",Dog.class);
        return "contextDog:" + contextDog + ",autowired注入:" + singletonDog;
    }

    @RequestMapping("/prototype")
    public String prototype(){
        Dog contextDog = context.getBean("prototypeDog",Dog.class);
        return "contextDog:" + contextDog + ",autowired注入:" + prototypeDog;
    }

    @RequestMapping("/request")
    public String request(){
        Dog contextDog = context.getBean("requestDog",Dog.class);
        return "contextDog:" + contextDog + ",autowired注入:" + requestDog;
    }

    @RequestMapping("/session")
    public String sessionDog(){
        Dog contextDog = context.getBean("sessionDog",Dog.class);
        return "contextDog:" + contextDog + ",autowired注入:" + sessionDog;
    }

    @RequestMapping("/application")
    public String applicationDog(){
        Dog contextDog = context.getBean("applicationDog",Dog.class);
        return "contextDog:" + contextDog + ",autowired注入:" + applicationDog;
    }
}

在这里插入图片描述

二.Bean的生命周期

生命周期指的是一个对象从诞生到销毁的整个生命过程,Bean的生命周期分为以下五个部分:

  1. 实例化:为Bean分配内存空间
  2. 属性赋值:Bean注入和装配,比如@Autowired
  3. 初始化:(1) 执行各种通知(如BeanNameAwareBeanFactoryAwareApplicationContextAware的接口方法)(2) 执行初始化方法(xml定义init-method,使用注解的方式@PostConstruct,执行初始化后置方法BeanPostProcessor
  4. 使用Bean
  5. 销毁Bean:使用销毁容器的各种方法,如@PreDestroyDisposableBean接口方法,destroy-method

三.SpringBoot自动配置

3.1 介绍

SpringBoot的自动配置就是当Spring容器启动后,一些配置类和Bean对象都能够就自动存入到IoC容器中,不需要我们手动去声明,从而简化了开发,省去繁琐的配置操作

3.2 Spring加载Bean

Spring通过五大注解和@Bean注解可以把Bean加载到Spring IoC容器中,但前提是这些注解类需要和SpringBoot启动类(@SpringBootApplication注释的类)在同一个目录下,才能够生效

package org.example.test;//启动类位于org.example.springprinciple,与该类不在同一目录
import org.springframework.stereotype.Component;
@Component
public class TestConfig {
    public void config(){
        System.out.println("config");
    }
}

//测试类
import org.example.test.TestConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
@SpringBootTest
class TestConfigTest {
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    void config() {
        TestConfig testConfig = applicationContext.getBean("testConfig",TestConfig.class);
        System.out.println("获取到的Bean:" + testConfig);

    }
}

在这里插入图片描述
可以看到当注解类和SpringBoot启动类不在同一目录下时,SpringBoot没有扫描到TestConfig这个类,所以不会把Bean加载到Spring IoC容器中,那么如何解决这些问题呢?同时,当我们引入第三方Jar包时,第三方Jar包中的代码目录肯定也不在启动器目录下,怎么让Spring帮我们管理这些Bean呢?带着这两个疑问,我们来看下面的解决方案

3.2.1 @ComponentScan

通过@ComponentScan注解,可以指定Spring的扫描路径,从而让Spring能够扫描到需要加载Bean的类。虽然能够解决注解类和SpringBoot启动类不在同一目录的问题,但是对于第三方Jar包,如果每个包都要用到@ComponentScan,就会变得很繁琐,因而Spring没有采取这种方式

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
//添加扫描路径
//可以添加多个路径,如@ComponentScan({包1,包2})
@ComponentScan("org.example")
public class SpringPrincipleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringPrincipleApplication.class, args);
    }
}

3.2.2 @Import 导入类

通过@Import注解,可以导入具体的类,让Spring去加载这些类的Bean。这种方法同样能解决不在同一目录下的问题,但对于第三方Jar包也是过于繁琐,Spring没有采取这种方式

import org.example.test.TestConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
//可以导入多个类,如@Import({类1,类2})
@Import(TestConfig.class)
@SpringBootApplication
public class SpringPrincipleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringPrincipleApplication.class, args);
    }
}

3.2.3 @Import 导入ImportSelector接口实现类

同样使用了@Import注解,先实现了ImportSelect接口,再使用@Import导入到自定义注解中进行封装,添加到启动类上,这种方式有效地减少了导入的繁琐程度,Spring采取了与之类似的思想

//MyImportSelector类实现ImportSelector接口
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    	//String数组中添加要导入的类名
        return new String[]{"org.example.test.TestConfig"};
    }
}

//自定义注解导入MyImportSelector,进行封装
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportSelector.class)
public @interface EnableMyConfig {
}

//启动类添加自定义注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableMyConfig
@SpringBootApplication
public class SpringPrincipleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringPrincipleApplication.class, args);
    }
}

3.3 @SpringBootApplication 源码简单分析

3.3.1 整体分析

SpringBoot到底是怎么管理第三方Jar包的Bean,怎么自动配置,一切的起源都来自于SpringBoot启动类中的@SpringBootApplication注解。@SpringBootApplication是一个组合注解,注解中包含了:

  1. 元注解:@Target描述注解的范围,@Retention描述注解保留的时间范围,@Documented描述在使用javadoc工具为类生成帮助文档时是否要保留其注解信息,@Inherited使被修饰的注解具有继承性(如果一个类使用了被@Inherited修饰的注解,那么其子类自动具有该注解)
  2. @SpringBootConfiguration:标注当前类为配置类
  3. @EnableAutoConfiguration:开启自动配置,下面会详细讲
  4. @ComponentScan:默认扫描启动类的目录路径
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
	//源码方法体省略
}

3.3.2 @EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
	//源码方法体省略
}

@EnableAutoConfiguration主要包含两个部分:

  1. @Import({AutoConfigurationImportSelector.class}),使用@Import注解,导入了实现ImportSelector接口的实现类,获取自动配置的配置类信息
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	//源码其他方法省略
	//ImportSelector接口实现类
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
        	//获取自动配置的配置类信息
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

	//获取自动配置的配置类信息getAutoConfigurationEntry方法
   protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //获取配置文件中配置的所有自动配置类的集合
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            //对集合进行筛选(去除无需导入的类)
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }

	//获取配置文件中配置的所有自动配置类的集合getCandidateConfigurations方法
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, this.getBeanClassLoader());
        //获取所有基于META-INF/spring文件中配置类的集合
        List<String> configurations = importCandidates.getCandidates();
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/" + this.autoConfigurationAnnotation.getName() + ".imports. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
}
  1. @AutoConfigurationPackage:将启动类所在包下的所有组件扫描加载到Spring容器中
//@AutoConfigurationPackage注解源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

//AutoConfigurationPackages.Registrar.class
public abstract class AutoConfigurationPackages {
		//源码其他方法省略
	    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        	//(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])
        	//表示当前启动类所在包名
            AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值