Spring原理

Spring原理分为:

Bean的作用域和生命周期

了解SpringBoot自动配置流程

Bean的作用域

1.概念

Spring是通过DI依赖注入和AOP来管理对象,具体就是:

1)通过@Controller,@Service,@Repository,@Component,@Configuration,@Bean来声明对象。

2)通过ApplicationContext或者BeanFactory来获取对象。

3)通过@Autowired或者set方法来注入依赖对象。

下面是一个以“Dog”类为例子来说明Spring是如何管理对象的:

@Data
public class Dog {
    private String name;
}

通过@Bean来将bean实例存储到Spring容器中

@Component
public class DogBeanConfig {
    @Bean
    public Dog dog() {
        Dog dog = new Dog();
        dog.setName("大黄");
        return dog;
    }
}

从Spring容器中获取Bean对象

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        //获取Spring的上下文
        ApplicationContext context = SpringApplication.run(Application.class, args);
        //从上下文中获取对象
        Dog dog = context.getBean(Dog.class);
        System.out.println(dog);
    }

}

也可以通过在代码中直接注入ApplicationContext的方法来获取Spring容器

@SpringBootTest
class ApplicationTests {
    @Autowired
   private ApplicationContext context;

    @Test
    void contextLoads() {
        DogBeanConfig bean = context.getBean(DogBeanConfig.class);
        System.out.println(bean);

        DogBeanConfig bean2 = context.getBean(DogBeanConfig.class);
        System.out.println(bean2);
    }

}

而且运行后可以发现输出的Bean的对象地址是一样的:

这也就是单例模式的实际使用场景,

默认情况下,Spring容器的bean都是单例的,这种行为方式,我们就称之为Bean的作用域。

Bean的作用域是指Bean在Spring框架中的某种行为方式。

2.Bean的作用域种类

在Sping中支持6种作用域,后四种在SpringMVC环境中才生效

1.singleton:单例作用域。

2.prototype:原型作用域(多例作用域)

3.request:请求作用域。

4.session:会话作用域。

5.Applicantion:全局作用域。

6.websocket:HTTPWebSocket作用域。

作用域类型生效范围生命周期适用场景
singleton整个应用上下文在应用启动时创建,仅创建一个实例,所有请求共享该实例。无状态的服务、工具类等。
prototype每次获取 Bean 时每次请求都会创建一个新的实例,不同请求使用不同的实例。有状态的 Bean、需要保持独立状态的组件。
request单个 HTTP 请求每个 HTTP 请求创建一个实例,请求处理完成后销毁。保存单个请求上下文信息的组件,如请求参数处理器。
session单个用户会话每个用户会话创建一个实例,会话过期后销毁。保存用户会话状态的组件,如用户会话信息管理。
application整个 ServletContext(全局应用)与 ServletContext 生命周期绑定,应用启动时创建,应用停止时销毁。全局数据共享组件,如应用统计信息。
websocket单个 WebSocket 会话每个 WebSocket 连接创建一个实例,连接关闭后销毁。WebSocket 通信处理组件,如消息处理器。

Spring中默认是singleton,如果要换为其他的,使用@Scope注解,例如:

 @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Dog dog2() {
        Dog dog = new Dog();
        dog.setName("黄黄");
        return dog;
    }

也可以使用@RequestScope等注解,其中@RequesScope对应request,@SessionScope=session,@ApplicationScope对应于application

各个作用域获取Bean的情况:

代码以多例作用域为例,其他的类似:

 @Qualifier("prototypeDog")
    @Autowired
    private Dog prototypeDog;
    @Autowired
    ApplicationContext applicationContext;
     @RequestMapping("/protype")
    public String protype() {
         Dog contextDog = (Dog) applicationContext.getBean("prototypeDog");
         return "dog"+prototypeDog.toString()+"\n"+contextDog.toString();
     }

1.单例作用域:多次访问,获取的Bean都是同一个对象,即使刷新浏览器。

2.多例作用域:每次获取的对象都不一样,但是注入的对象由于在Spring启动时就已经注入了,所以多次请求也不会发生变化。

3.请求作用域:在一次请求中,@Autowired和getBean相同,但是每次请求都会重新创建对象。

4.会话作用域:在一个session中,多次请求获取的都是一个对象,换一个浏览器,发现会重新创建对象。

5.Application作用域:在一个应用中,多次访问获取的都是一个对象。

Bean的生命周期

生命周期指的是一个对象从诞生到销毁的整个生命周期。

Bean的生命周期分为以下5个部分:

1.实例化(为Bean分配内存空间)对应构造方法。

2.属性赋值(Bean注入和装配,比如@Autowired)

3.初始化

        a.执行各种通知,如BeanNameAware,BeanFactoryAware,ApplicationContextAware的接口方法。

        b.执行初始化方法

                xml定义init-method

                使用注解的方式@PostConstruct

                执行初始化后置方法BeanPostProcessor。

4.使用Bean

5.销毁Bean        

        销毁容器的各种方法,如@PreDestory,DispossableBean接口方法,destroy-method。

以下是执行流程:

代码演示:

@Component
public class BeanLifeComponent implements BeanNameAware {
    private UserComponent userComponent;
    
    public BeanLifeComponent() {
        System.out.println("执行构造函数");
    }
    @Autowired
    public void setUserComponent(UserComponent userComponent) {
        System.out.println("设置属性userComponent");
        this.userComponent = userComponent;
    }
    @Override
    public void setBeanName(String name) {
        System.out.println("执行setBeanName方法"+name);
    }
    //初始化
    @PostConstruct
    public void postConstruct() {
        System.out.println("执行postConstruct方法");
    }
    public void use() {
        System.out.println("执行力use方法");
    }
    //销毁前执行方法
    @PreDestroy
    public void preDestroy() {
        System.out.println("执行:preDestroy");
    }
}

执行结果:

SpringBoot自动配置

SpringBoot的自动配置就是当Spring容器启动后,bean等对象就自动存到了IoC容器中,不需要去手动配置。

SpringBoot自动配置就是指SpringBoot是如何将依赖jar包中的配置类以及Bean加载到SpingIoC容器中的。

1.Spring加载Bean

SpringBoot中比较常见的就是第三方依赖给我们提供一个注解,这个注解一般都是以@Enablexxxx开头的注解,注解中封装的就是@Import注解。

2.SpringBoot原理分析

源码阅读:

以@SpringBootApplication为例:

首先看源码上面的注解:

@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}
)}
)

@SpringBootApplication是一个组合注解,注解中包含了:

1.元注解

        @Target 描述注解的使用范围。

        @Retention 描述注解保留的时间范围。

        @Documented 描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。

        @Inherited 使被它修饰的注解具有继承性。

2.@SpringBootConfiguration

     里面就是@Configuration,标注当前类为注解类,其实只是做了一层封装改了个名字而已。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

3.@Indexed注解,一个用来加速应用启动的注解。

4.@EnableAutoConfiguration:开启自动配置,Spring自动配置的核心注解,下面会仔细说明。

5.@ComponentScan:包扫描。

可以通过basePackageClasses或basePackages来定义要扫描的特定包,如果没有定义特定的包,将从声明该注解的类的包开始扫描,这也就是为什么SpringBoot项目的声明的注解类必须要遭启动类的目录下。

 excludeFilters自定义过滤器,通常用于排除一些类,注解等。

3.@EnableAutoConfiguration详解

看下@EnableAutoConfiguration注解的实现:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {};
}

这个注解包含两部分:

1.@Import({AutoConfigurationImportSelector.class})

使用@Import注解,导入了实现ImportSelector接口的实现类

   源码中的一段代码:
 


    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

其中“AutoConfigurationEntryautoConfigurationEntry=this.getAutoConfigurationEntry(annotationMetadata);”

就是获取自动配置的配置类信息。

点进getAutoConfigurationEntry方法:

其中有一个“getCandidateConfigurations”方法:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, this.getBeanClassLoader());
        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;
    }

getCandidateConfigurations方法的作用:获取所以基于META——INF/Spring/org.Springframework.boot.autoconfigure.AutoConfiguration.imports文件,META-INF/Spring.factories文件中配置类的集合。

在引入的起步依赖中,通常都有包含以上两个文件。

这里面包含了很多第三方依赖的配置文件:

注意的是,在加载自动配置类的时候,并不是将所有的配置全部加载进来,而是通过@Conditional等注解的判断进行动态加载。

@Conditional是Spring底层注解,意思就是根据不同的条件,来进行自己不同的条件判断,如果满足指定的条件,那么配置类里面的配置才会生效。

4.@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”

查看Registrar的源码:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
        }
    }

Registrat实现了ImportBeanDefinitionRegistrar类,这时就可以被@Import导入到spring容器中。

(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])):启动类所在的包名。

所以:@AutoConfigurationPackage就是将启动类所在的包下面所有的组件都扫描注册到spring容器中。

SpringBoot配置的大致流程如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值