Spring原理

这次我们来研究Bean的相关知识和spring boot自动配置的相关流程

1.Bean的作用域

1概念

在SpringIoC&DI阶段,我们学习了Spring是如何帮助我们管理对象的.

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

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

  3. 通过 @Autowired , Setter ⽅法或者构造⽅法等来为应⽤程序注⼊所依赖的Bean对象

我们编写代码,从spring容器中获取bean

package com.example.springconfig;

import com.example.springconfig.demos.model.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class SpringConfigApplication {

    public static void main(String[] args) {

         ApplicationContext context=SpringApplication.run(SpringConfigApplication.class, args);
        User user1 = context.getBean(User.class);
        user1.setName("lisi");
        System.out.println( "当前对象地址:"+System.identityHashCode(user1));


        User user2 = context.getBean(User.class);
        System.out.println( "当前对象地址:"+System.identityHashCode(user2));
    }

}







package com.example.springconfig.demos.config;

import com.example.springconfig.demos.model.User;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {
    @Bean
    public User user1(){
        return  new User(1,"zhangsan");
    }
}




package com.example.springconfig.demos.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
    private Integer id;
    private  String name;

}

运行出结果

发现输出对象地址的值是一样的,说明从spring容器中取出的对象都是同一个

这也是"单例模式" 单例模式:确保⼀个类只有⼀个实例,多次创建也不会创建出多个实例

默认情况下,Spring容器中的bean都是单例的,这种⾏为模式,我们就称之为Bean的作⽤域.

Bean的作⽤域是指Bean在Spring框架中的某种⾏为模式.

⽐如单例作⽤域:表⽰Bean在整个Spring中只有⼀份,它是全局共享的.那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值、

2.Bean的作用域

在Spring中⽀持6种作⽤域,后4种在SpringMVC环境才⽣效

  1. singleton:单例作⽤域

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

  3. request:请求作⽤域

  4. session:会话作⽤域

  5. Application:全局作⽤域

  6. websocket:HTTPWebSocket作⽤域

接下来 我们来看看这几个bean的作用域

在这之前我们先来看一下一些注解的详细

堆和栈之间的关系

各个bean作用域中的代码

package com.example.springconfig.demos.controller;

import com.example.springconfig.demos.model.User;
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;

import javax.annotation.Resource;
import javax.jws.soap.SOAPBinding;

@RequestMapping("/scope")
@RestController
public class BeanScopeController {

    @Autowired
      private ApplicationContext context;
    @Resource(name = "singletonUser")
    private  User singletonUser;

    @Resource(name = "prototypeUser")
    private  User prototypeUser;
    @Resource(name = "requestUser")
    private  User requestUser;
    @Resource(name = "sessionUser")
    private  User sessionUser;
    @Resource(name = "applicationUser")
    private  User applicationUser;
    @RequestMapping("/single")
    public  String  single() {
        /**
         *1.从context中获取对象
         * 2.属性注入获取对象
         */
        User user = (User) context.getBean("singletonUser");
        return "context中获取的对象是:" + user + ",属性注入获取的对象:" + System.identityHashCode(singletonUser);
    }
    @RequestMapping("/prototype")
    public  String  prototype(){
        /**
         *1.从context中获取对象
         * 2.属性注入获取对象
         */
        User user= (User) context.getBean("prototypeUser");
        return "context中获取的对象是:"+user+",属性注入获取的对象:"+System.identityHashCode(prototypeUser);
    }
    @RequestMapping("/request")
    public  String  request(){
        /**
         *1.从context中获取对象
         * 2.属性注入获取对象
         */
        User user= (User) context.getBean("requestUser");
        return "context中获取的对象是:"+user+",属性注入获取的对象:"+System.identityHashCode(requestUser);
    }

    @RequestMapping("/session")
    public  String  session(){
        /**
         *1.从context中获取对象
         * 2.属性注入获取对象
         */
        User user= (User) context.getBean("sessionUser");
        return "context中获取的对象是:"+user+",属性注入获取的对象:"+System.identityHashCode(sessionUser);
    }
    @RequestMapping("/application")
    public  String  application(){
        /**
         *1.从context中获取对象
         * 2.属性注入获取对象
         */
        User user= (User) context.getBean("applicationUser");
        return "context中获取的对象是:"+user+",属性注入获取的对象:"+System.identityHashCode(applicationUser);
    }





}




package com.example.springconfig.demos.config;

import com.example.springconfig.demos.model.User;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.annotation.ApplicationScope;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.annotation.SessionScope;
import org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor;

@Configuration
public class BeanConfig {
    @Bean
    public User user1(){
        return  new User(1,"zhangsan");
    }
    @Bean
  @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
//    @Scope("singleton")
    public  User singletonUser(){
        return  new User();
    }
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public  User prototypeUser(){
        return new User();
    }

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.TARGET_CLASS)
    public  User requestUser(){
        return  new User();
    }
    @Bean
    @SessionScope
    public User sessionUser(){
    return  new User();
    }

    @Bean
    @ApplicationScope
    public  User applicationUser(){
        return  new User();
    }

}

1.singleton

每次获取的都是同一个对象

2.prototype

每次获取的对象是不同的

3.request

请求每次获取的对象都不一样

4.session

在⼀个session中,多次请求,获取到的对象都是同⼀个.

5.application

在⼀个应⽤中,多次访问都是同⼀个对象

Applicationscope就是对于整个web容器来说,bean的作⽤域是ServletContext级别的.这个和singleton有点类似区别在于:Applicationscope是ServletContext的单例,singleton是⼀个ApplicationContext的单例.在⼀个web容器中ApplicationContext可以有多个.(了解即可)

3.Bean的生命周期

⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期.Bean的⽣命周期分为以下5个部分:

  1. 实例化(为Bean分配内存空间)

  2. 属性赋值(Bean注⼊和装配,⽐如 @AutoWired )

  3. 初始化

a. 执⾏各种通知,如 BeanNameAware ,BeanFactoryAware ,ApplicationContextAware 的接⼝⽅法.

b. 执⾏初始化⽅法

▪ xml定义 init-method

▪ 使⽤注解的⽅式 @PostConstruct

▪ 执⾏初始化后置⽅法( BeanPostProcessor )

  1. 使⽤Bean

  2. 销毁Bean

a. 销毁容器的各种⽅法,如 @PreDestroy ,DisposableBean 接⼝⽅法, destroymethod.

演示代码代码如下

package com.example.springconfig.demos.component;

import com.example.springconfig.demos.model.User;
import org.apache.ibatis.annotations.Update;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class BeanLifeComponent {
    private User user;

    public BeanLifeComponent() {
        System.out.println("执行构造方法");
    }


    @Autowired
    @Qualifier("user1")
    public void setUser1(User user1) {
        this.user = user;
        System.out.println("属性注入");
    }
    @PostConstruct
    public  void  init(){
        System.out.println("执行初始化");
    }
    public  void use(){
        System.out.println("执行use");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("执行destroy方法");
    }
}

4.关于Bean相关的源码

我们举个例子来说一下,我们获取bean对象,并通过不同的方式拿取相关的值,

我们通过结果看到,源码里有类似代理类和目标类的东西,我们通过toString方法拿到的其实是spring代理的时候进行了重写,重写的逻辑是执行目标toString

代理类包含目标类

返回的user其实就是代理类 我们在执行程序的时候,代理类没有发生变化,目标类在发生变化

1.AbstractAutowireCapableBeanFactory

我们翻看AbstractAutowireCapableBeanFactory这个源码,在里面看到了

里面有实例化bean的源码

我们根据bean的周期来看

接下来就看属性注入

在属性注入里面有bean的初始化initializeBean 里面有bean初识化的各种代码

我们可以看到spring的源码是非常复杂的,我们现在也只是浅浅读了一下

5.spring自动配置

SpringBoot的⾃动配置就是当Spring容器启动后,⼀些配置类,bean对象等就⾃动存 ⼊到了IoC容器中,不需要我们⼿动去声明,从⽽简化了开发,省去了繁琐的配置操 作.

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

我们学习主要分以下两个⽅⾯:

  1. Spring是如何把对象加载到SpringIoC容器中的

  2. SpringBoot是如何实现的

我们先看看@SpringBootApplication这个注解 点进去

2.@SpringBootConfiguration

复制代码

3.@EnableAutoConfiguration

这里面的注解是实现自动配置的核心

我们再点击进入import中的AutoConfigurationImportSelector.class

这里面的这个方法是主要的方法,我们在点进getAutoConfigurationEntry

标注的这一行是拿到配置信息,我们继续点击查看怎样拿到配置信息

我们搜索这个路径

文件里面就是配置的文件

4.@AutoConfigurationPackage

我们接下来看这个注解

点进进入

我们点进Registrar

复制代码
里面就是获取包的名字之类的

总结

复制代码
复制代码
复制代码
复制代码
相关推荐
徐*红21 分钟前
java 线程池
java·开发语言
尚学教辅学习资料21 分钟前
基于SSM的养老院管理系统+LW示例参考
java·开发语言·java毕设·养老院
2401_8576363921 分钟前
计算机课程管理平台:Spring Boot与工程认证的结合
java·spring boot·后端
1 9 J23 分钟前
Java 上机实践4(类与对象)
java·开发语言·算法
Code apprenticeship24 分钟前
Java面试题(2)
java·开发语言
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
憨子周1 小时前
2M的带宽怎么怎么设置tcp滑动窗口以及连接池
java·网络·网络协议·tcp/ip
霖雨3 小时前
使用Visual Studio Code 快速新建Net项目
java·ide·windows·vscode·编辑器
SRY122404193 小时前
javaSE面试题
java·开发语言·面试
Fiercezm3 小时前
JUC学习
java