No importa si no comprende el código fuente de Spring.Hoy, comprendamos la lógica subyacente desde la perspectiva del diseño de la arquitectura.

prefacio

¿Por qué necesitas Spring?¿Qué es Spring?

Para tal problema, la mayoría de la gente está en un estado confuso, diciéndolo, pero no diciéndolo completamente.Hoy intentaremos desentrañar el misterio de la primavera desde la perspectiva del diseño de la arquitectura.

Este artículo se presenta de una manera simple a profunda. No tienes que entrar en pánico. Puedo garantizarte que mientras sepas programación, podrás entenderlo.

Este artículo está basado en Spring 5.2.8 y tarda unos 20 minutos en leerse

caso

Primero veamos un caso: hay un tipo que tiene un automóvil Geely y, por lo general, conduce un automóvil Geely al trabajo.

Código:

public class GeelyCar {

    public void run(){
        System.out.println("geely running");
    }
}
复制代码
public class Boy {
		// 依赖GeelyCar
    private final GeelyCar geelyCar = new GeelyCar();

    public void drive(){
        geelyCar.run();
    }
}
复制代码

Un día, el tipo ganó dinero, compró otro Hongqi y quería conducir un auto nuevo.

Simple, reemplaza la dependencia con HongQiCar

Código:

public class HongQiCar {

    public void run(){
        System.out.println("hongqi running");
    }
}
复制代码
public class Boy {
    // 修改依赖为HongQiCar
    private final HongQiCar hongQiCar = new HongQiCar();

    public void drive(){
        hongQiCar.run();
    }
}
复制代码

Estoy cansado de conducir un auto nuevo y quiero volver a cambiar al auto viejo. En este momento, habrá un problema: el código ha sido cambiado una y otra vez.

Claramente, este caso viola nuestro Principio de Inversión de Dependencia (DIP) : los programas no deberían depender de implementaciones, sino de abstracciones

optimizado

Ahora optimizamos el código de la siguiente manera:

Boy confía en la interfaz Car, mientras que GeelyCar y HongQiCar anteriores se implementan para la interfaz Car.

Código:

Definir la interfaz del coche

public interface Car {

    void run();
}
复制代码

Cambie el GeelyCar y HongQiCar anteriores a la clase de implementación de Car

public class GeelyCar implements Car {

    @Override
    public void run(){
        System.out.println("geely running");
    }
}
复制代码

Igual que HongQiCar

La persona confía en la interfaz del automóvil en este momento

public class Boy {
		// 依赖于接口
    private final Car car;
		
    public Person(Car car){
        this.car = car;
    }

    public void drive(){
        car.run();
    }
}
复制代码

En este momento, el tipo quiere cambiar el automóvil para conducir, simplemente ingrese cualquier parámetro y el código ya no cambiará.

limitación

El caso anterior no parece tener ningún problema después de la transformación, pero aún existen ciertas limitaciones.Si se agrega una nueva escena en este momento:

Un día, el tipo no podía conducir después de beber, por lo que necesitaba encontrar un chofer. Al chofer no le importa qué tipo maneja, ni qué tipo de automóvil conduce, y el tipo de repente se convierte en una abstracción. En este momento, el código debe cambiarse nuevamente. El código del chofer que depende del tipo puede parecer Me gusta esto:

private final Boy boy = new YoungBoy(new HongQiCar());
复制代码

A medida que aumenta la complejidad del sistema, tales problemas serán cada vez más difíciles de mantener, entonces, ¿cómo debemos resolver este problema?

pensar

En primer lugar, podemos estar seguros: no hay ningún problema en usar el Principio de Inversión de Dependencia , resuelve nuestro problema hasta cierto punto.

Sentimos que el problema está en el proceso de pasar parámetros: pasamos lo que sea que el programa necesite. Una vez que haya múltiples relaciones de clases dependientes en el sistema, los parámetros entrantes se volverán extremadamente complejos.

Tal vez podamos invertir la idea : ¡lo que tenemos, lo que usa el programa!

Cuando solo implementamos HongQiCar y YoungBoy, ¡el conductor usa YoungBoy conduciendo HongQiCar!

¡Cuando solo implementamos GeelyCar y OldBoy, el chofer cambiará naturalmente a OldBoy conduciendo GeelyCar!

Y cómo revertir es un gran problema que Spring resuelve.

Introducción a la primavera

Spring es un marco de desarrollo integral de peso ligero y peso pesado. Su propósito es resolver la complejidad del desarrollo de aplicaciones de nivel empresarial. Brinda soporte de infraestructura integral para desarrollar aplicaciones Java, de modo que los desarrolladores de Java ya no tengan que preocuparse por la clase y las dependencias entre clases, puede concentrarse en desarrollar aplicaciones (crud).

Spring proporciona una rica funcionalidad para el desarrollo de nivel empresarial, y la capa inferior de estas funciones se basa en sus dos características principales: Inyección de dependencia (DI) y Programación orientada a aspectos (AOP).

Los conceptos centrales de Spring

contenedor IoC

El nombre completo de IoC es Inversion of Control, que significa inversión de control. IoC también se conoce como Inyección de dependencia (DI), que es un proceso de inyección de objetos a través de dependencias: los objetos solo se pasan a través de constructores, métodos de fábrica o cuando el Se crea una instancia del objeto, se le asignan propiedades para definir sus dependencias (es decir, otros objetos con los que se combinan), y luego el contenedor inyecta estas dependencias necesarias cuando se crea el bean. Este proceso es fundamentalmente el inverso del propio bean que controla la creación de instancias o la ubicación de sus dependencias mediante el uso de una clase de construcción directa o un mecanismo como el patrón de ubicación del servicio (de ahí el nombre Inversión de Control).

El principio de inversión de dependencia es el principio de diseño de IoC, y la inyección de dependencia es la implementación de IoC.

envase

En Spring, podemos usar XML, anotaciones Java o código Java para escribir información de configuración y, a través de la información de configuración, obtener instrucciones sobre cómo crear instancias, configurar y ensamblar objetos, y crear instancias, configurar y ensamblar objetos de aplicación se denomina contenedor.

En general, solo necesitamos agregar algunas anotaciones, de modo que después de crear e inicializar el contenedor, podamos obtener un sistema o aplicación configurable y ejecutable.

Frijol

En Spring, los objetos que son instanciados por el contenedor Spring IOC -> gestión de ensamblaje -> constituyen el esqueleto del programa se denominan Beans. Los frijoles son uno de los muchos objetos en una aplicación.

Los tres puntos anteriores están vinculados entre sí: Spring es un contenedor IoC que coloca beans y maneja las dependencias entre beans por medio de la inyección de dependencia.

POA

La programación orientada a aspectos es un complemento funcional de la programación orientada a objetos (OOP).El objeto principal de OOP es la clase, mientras que AOP es el aspecto. Desempeña un papel muy importante en el procesamiento de registros, la gestión de seguridad y la gestión de transacciones. AOP es un componente importante del marco Spring.Aunque el contenedor IOC no depende de AOP, AOP proporciona funciones muy poderosas para complementar IOC.

AOP nos permite mejorar nuestras funciones comerciales sin modificar el código original: cortar una parte de la función en una posición que especificamos, como imprimir registros entre cadenas de llamadas de métodos.

Ventajas de la primavera

1. Spring simplifica el desarrollo de Java a nivel empresarial a través de DI y AOP

2. El diseño poco intrusivo de Spring hace que la contaminación del código sea extremadamente baja

3. El contenedor IoC de Spring reduce la complejidad entre los objetos comerciales y desacopla los componentes entre sí.

4. El soporte AOP de Spring permite el procesamiento centralizado de algunas tareas comunes, como seguridad, transacciones, registro, etc., lo que mejora la reutilización.

5. La alta apertura de Spring no obliga a la aplicación a depender completamente de Spring.Los desarrolladores pueden elegir libremente parte o todo el marco de Spring.

6. La alta extensibilidad de Spring permite a los desarrolladores integrar fácilmente sus propios marcos en Spring

7. La ecología de Spring es extremadamente completa e integra varios marcos excelentes, lo que permite a los desarrolladores usarlos fácilmente.

Podemos vivir sin Java, pero no podemos vivir sin Spring~

Adaptar la caja con Spring

Ahora sabemos qué es Spring, y ahora tratamos de usar Spring para transformar el caso.

La estructura original no ha cambiado, solo agregue la anotación @Component en GeelyCar o HongQiCar, y Boy agregue la anotación @Autowired cuando use

Estilo de código:

@Component
public class GeelyCar implements Car {

	@Override
	public void run() {
		System.out.println("geely car running");
	}
}
复制代码

Igual que HongQiCar

En Spring, cuando una clase está marcada con la anotación @Component, significa que este es un Bean que puede ser administrado por el contenedor IoC

@Component
public class Boy {
	
	// 使用Autowired注解表示car需要进行依赖注入
	@Autowired
	private Car car;

	public void driver(){
		car.run();
	}
}
复制代码

Lo que dijimos antes: lo que implementamos, lo que usa el programa, aquí es equivalente a qué clase marcamos la anotación de Componente, qué clase será un Bean, y Spring lo usará para inyectar en el atributo Car de Boy

Entonces, cuando anotamos GeelyCar con Component, el auto de Boy es GeelyCar, y cuando anotamos HongQiCar con Component, el auto de Boy es HongQiCar.

Por supuesto, no podemos marcar anotaciones de componentes en GeelyCar y HongQiCar al mismo tiempo, porque Spring no sabe qué automóvil usar para la inyección. Spring también tiene dificultades para elegir (¿o no puede un niño conducir dos automóviles? )

Usando Spring Bootstrap

// 告诉Spring从哪个包下扫描Bean,不写就是当前包路径@ComponentScan(basePackages = "com.my.spring.test.demo")public class Main {	public static void main(String[] args) {		// 将Main(配置信息)传入到ApplicationContext(IoC容器)中		ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);		// 从(IoC容器)中获取到我们的boy		Boy boy = (Boy) context.getBean("boy");		// 开车		boy.driver();	}}
复制代码

Aquí puede interpretar el conocimiento que acabamos de presentar sobre Spring.

Pasar la clase Main con la anotación ComponentScan (información de configuración) al AnnotationConfigApplicationContext (contenedor IoC) para la inicialización es equivalente a: el contenedor IoC instancia, administra y ensambla beans obteniendo la información de configuración.

La forma de realizar la inyección de dependencia se realiza dentro del contenedor IoC, que también es el tema central de este artículo.

pensar

Tenemos una comprensión completa de las funciones básicas de Spring a través de un caso de transformación y tenemos una experiencia concreta de los conceptos anteriores, pero aún no sabemos cómo se completa la acción interna de la inyección de dependencia de Spring. Más para saber por qué, combinado con nuestro conocimiento y comprensión existentes de Spring, hagamos conjeturas audaces (esta es una habilidad muy importante)

De hecho, adivinar significa: si permitimos que lo implementemos nosotros mismos, ¿cómo implementaríamos este proceso?

Primero, debemos tener claro lo que debemos hacer: escanear las clases en el paquete especificado, instanciarlas y combinarlas según las dependencias.

Desglose de pasos:

Escanee la clase bajo el paquete especificado -> si esta clase identifica la anotación del Componente (un Bean) -> guarde la información de esta clase

Instanciar -> recorrer la información de clase almacenada -> instanciar estas clases a través de la reflexión

De acuerdo con la combinación de dependencias -> analizar la información de la clase -> determinar si hay campos en la clase que deben inyectarse -> inyectar los campos

Realización del esquema

Ahora tenemos una solución que parece lo mismo, intentemos implementarla ahora

definir anotaciones

Primero necesitamos definir las anotaciones que necesitamos usar: ComponentScan, Component, Autowired

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {

    String basePackages() default "";
}
复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {

    String value() default "";
}
复制代码
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Autowired {}
复制代码

Escanee las clases bajo el paquete especificado

Escanear todas las clases bajo un paquete específico suena como un momento de confusión, pero en realidad es equivalente a otro problema: ¿cómo atravesar el directorio de archivos?

Entonces, ¿qué se debe usar para almacenar información de clase? Echemos un vistazo al método getBean en el ejemplo anterior ¿Es como obtener el valor por clave en Map? Hay muchas implementaciones en Map, pero solo hay una segura para subprocesos, que es ConcurrentHashMap (no me digas HashTable)

Definir un mapa para almacenar información de clase

private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>(16);
复制代码

Para el proceso específico, la implementación del código también se adjunta a continuación:

Implementación de código, puede verlo junto con el diagrama de flujo:

Escanea la información de la clase

private void scan(Class<?> configClass) {
  // 解析配置类,获取到扫描包路径
  String basePackages = this.getBasePackages(configClass);
  // 使用扫描包路径进行文件遍历操作
  this.doScan(basePackages);
}
复制代码
private String getBasePackages(Class<?> configClass) {
  // 从ComponentScan注解中获取扫描包路径
  ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
  return componentScan.basePackages();
}
复制代码
private void doScan(String basePackages) {
  // 获取资源信息
  URI resource = this.getResource(basePackages);

  File dir = new File(resource.getPath());
  for (File file : dir.listFiles()) {
    if (file.isDirectory()) {
      // 递归扫描
      doScan(basePackages + "." + file.getName());
    }
    else {
      // com.my.spring.example + . + Boy.class -> com.my.spring.example.Boy
      String className = basePackages + "." + file.getName().replace(".class", "");
      // 将class存放到classMap中
      this.registerClass(className);
    }
  }
}
复制代码
private void registerClass(String className){
  try {
    // 加载类信息
    Class<?> clazz = classLoader.loadClass(className);
    // 判断是否标识Component注解
    if(clazz.isAnnotationPresent(Component.class)){
      // 生成beanName com.my.spring.example.Boy -> boy
      String beanName = this.generateBeanName(clazz);
      // car: com.my.spring.example.Car
      classMap.put(beanName, clazz);
    }
  } catch (ClassNotFoundException ignore) {}
}
复制代码

instanciar

Ahora que se han analizado todas las clases adecuadas, el siguiente paso es el proceso de creación de instancias.

Definir el Mapa que almacena el Bean

private final Map<String, Object> beanMap = new ConcurrentHashMap<>(16);
复制代码

El proceso específico, la implementación del código también se proporciona a continuación:

Implementación de código, puede verlo junto con el diagrama de flujo:

Atraviesa classMap para crear instancias de beans

public void instantiateBean() {
  for (String beanName : classMap.keySet()) {
    getBean(beanName);
  }
}
复制代码
public Object getBean(String beanName){
  // 先从缓存中获取
  Object bean = beanMap.get(beanName);
  if(bean != null){
    return bean;
  }
  return this.createBean(beanName);
}
复制代码
private Object createBean(String beanName){
  Class<?> clazz = classMap.get(beanName);
  try {
    // 创建bean
    Object bean = this.doCreateBean(clazz);
    // 将bean存到容器中
    beanMap.put(beanName, bean);
    return bean;
  } catch (IllegalAccessException e) {
    throw new RuntimeException(e);
  }
}
复制代码
private Object doCreateBean(Class<?> clazz) throws IllegalAccessException {
  // 实例化bean
  Object bean = this.newInstance(clazz);
  // 填充字段,将字段设值
  this.populateBean(bean, clazz);
  return bean;
}
复制代码
private Object newInstance(Class<?> clazz){  try {    // 这里只支持默认构造器    return clazz.getDeclaredConstructor().newInstance();  } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {    throw new RuntimeException(e);  }}
复制代码
private void populateBean(Object bean, Class<?> clazz) throws IllegalAccessException {  // 解析class信息,判断类中是否有需要进行依赖注入的字段  final Field[] fields = clazz.getDeclaredFields();  for (Field field : fields) {    Autowired autowired = field.getAnnotation(Autowired.class);    if(autowired != null){      // 获取bean      Object value = this.resolveBean(field.getType());      field.setAccessible(true);      field.set(bean, value);    }  }}
复制代码
private Object resolveBean(Class<?> clazz){
  // 先判断clazz是否为一个接口,是则判断classMap中是否存在子类
  if(clazz.isInterface()){
    // 暂时只支持classMap只有一个子类的情况
    for (Map.Entry<String, Class<?>> entry : classMap.entrySet()) {
      if (clazz.isAssignableFrom(entry.getValue())) {
        return getBean(entry.getValue());
      }
    }
    throw new RuntimeException("找不到可以进行依赖注入的bean");
  }else {
    return getBean(clazz);
  }
}
复制代码
public Object getBean(Class<?> clazz){
  // 生成bean的名称
  String beanName = this.generateBeanName(clazz);
  // 此处对应最开始的getBean方法
  return this.getBean(beanName);
}
复制代码

combinación

Los dos métodos principales han sido escritos. A continuación, combínelos. Los implemento en una clase ApplicationContext personalizada. El método de construcción es el siguiente:

public ApplicationContext(Class<?> configClass) {
  // 1.扫描配置信息中指定包下的类
  this.scan(configClass);
  // 2.实例化扫描到的类
  this.instantiateBean();
}
复制代码

Diagrama de clases UML:

prueba

La estructura del código es la misma que en el caso, aquí mostramos si nuestro propio Spring puede ejecutarse normalmente

Funciona bien, los chinos no mienten a los chinos.

El código fuente se dará al final del artículo.

revisión

Ahora, hemos implementado de acuerdo con el esquema previsto, y la operación ha logrado el efecto esperado. Pero si lo estudias detenidamente, combinado con nuestro uso habitual de Spring, encontrarás muchos problemas con este código:

1. No se admite la inyección de constructores y, por supuesto, tampoco se admite la inyección de métodos, lo cual es una falta funcional.

2. El problema de cargar información de la clase, usamos el método de classLoader.loadClass al cargar la clase, aunque esto evita la inicialización de la clase (no use el método de Class.forName), es inevitable usar la clase metainformación Cargada en el metaespacio, que desperdicia nuestra memoria cuando escaneamos paquetes en busca de clases no deseadas.

3. La dependencia circular entre beans no se puede resolver. Por ejemplo, un objeto A depende del objeto B y el objeto B depende del objeto A. En este momento, si miramos la lógica del código nuevamente, encontraremos que caerá en un bucle infinito.

4. La escalabilidad es muy pobre. Escribimos todas las funciones en una clase. Cuando queremos mejorar las funciones (como los 3 problemas anteriores), necesitamos modificar esta clase con frecuencia, y esta clase se volverá cada vez más inflada. ., sin mencionar la iteración de nuevas funciones, el mantenimiento será un dolor de cabeza.

Mejoramiento

Los primeros tres problemas son similares a los problemas funcionales y las funciones se pueden cambiar.

En lo que debemos centrarnos es en la cuarta pregunta. Si un marco quiere volverse excelente, su capacidad iterativa debe ser buena, para que las funciones puedan enriquecerse. Hay muchos factores que afectan la capacidad iterativa, uno de los cuales es su extensibilidad. .

Entonces, ¿cómo debemos mejorar la escalabilidad de nuestra solución? Los seis principios de diseño nos brindan una buena guía.

En el esquema, ApplicationContext hace muchas cosas, que se pueden dividir principalmente en dos partes

1. Escanea las clases bajo el paquete especificado

2. Instanciar Bean

Con la idea del principio de responsabilidad única: una clase hace solo una cosa, un método solo hace una cosa.

Usamos un procesador para procesar el escaneo de las clases bajo el paquete especificado , porque la configuración de escaneo proviene de la clase de configuración, entonces lo llamamos procesador de clase de configuración: ConfigurationCalssProcessor

Lo mismo es cierto para instanciar beans La instanciación de beans se divide en dos cosas: instanciación e inyección de dependencia.

Crear una instancia de un frijol es equivalente a un proceso de producción de un frijol. Usamos una clase de fábrica para manejar este asunto, que se llama: BeanFactory. Dado que está produciendo un frijol, necesita materias primas (Clase), así que ponemos classMap y beanMap se define aquí

El proceso de inyección de dependencia en realidad está procesando anotaciones Autowired, por lo que se llama: AutowiredAnnotationBeanProcessor

Todavía sabemos que en Spring, no solo existe esta forma de uso, sino también xml, mvc, SpringBoot, por lo que abstraemos el ApplicationContext y solo implementamos el proceso principal. El método de anotación original se implementa mediante AnnotationApplicationContext.

Con la ayuda del Principio de Inversión de Dependencia: Los programas deben depender de abstracciones

En el futuro, la información de clase no solo puede provenir de la información de clase, sino también de los archivos de configuración, por lo que abstraemos ConfigurationCalssProcessor

La forma de inyección de dependencia no tiene que estar necesariamente marcada con anotaciones Autowired, sino que también se puede marcar con otras anotaciones, como Resource, por lo que abstraemos AutowiredAnnotationBeanProcessor

También puede haber muchos tipos de beans, que pueden ser singleton, multiinstancia o un bean de fábrica, por lo que abstraemos BeanFactory

Ahora, hemos optimizado nuestra solución con la ayuda de dos principios de diseño, que pueden describirse como "renacer" en comparación con antes.

diseño de primavera

En el paso anterior, implementamos nuestra propia solución y optimizamos la escalabilidad en función de algunas suposiciones. Ahora, echemos un vistazo al diseño real de Spring.

Entonces, ¿cuáles son los "roles" en Spring?

1. Bean: Spring como contenedor IoC, lo más importante es, por supuesto, Bean.

2. BeanFactory: Una fábrica que produce y maneja frijoles

3. BeanDefinition: Spring encapsula la definición de Bean, que es la clase en nuestro esquema.

4. BeanDefinitionRegistry: similar a la relación entre Bean y BeanFactory, BeanDefinitionRegistry se usa para administrar BeanDefinition

5. BeanDefinitionRegistryPostProcessor: un procesador para analizar clases de configuración, similar al ClassProcessor en nuestro esquema

6. BeanFactoryPostProcessor: clase principal BeanDefinitionRegistryPostProcessor, para que podamos realizar un procesamiento posterior después de analizar la clase de configuración

7. BeanPostProcessor: el posprocesador de Bean, que se utiliza para realizar algún procesamiento en el proceso de producción de beans, como la inyección de dependencia, similar a nuestro AutowiredAnnotationBeanProcessor

8. ApplicationContext: si los roles anteriores son todos los trabajadores que producen frijoles en la fábrica, entonces ApplicationContext es la fachada de nuestro Spring.ApplicationContext y BeanFactory están en una relación combinada, por lo que extiende completamente la función de BeanFactory y se basa en su base. Se agregaron más características específicas de la empresa, como el conocido ApplicationListener (escucha de eventos)

Las similitudes mencionadas anteriormente en realidad ponen el carro delante del caballo, porque de hecho, la implementación en nuestra solución debería ser similar a la implementación en Spring.Esto es solo para que todos lo entiendan mejor.

Después de haber experimentado el diseño y la optimización de nuestras propias soluciones, estos roles son realmente muy fáciles de entender.

A continuación, echemos un vistazo detallado a uno por uno.

BeanFactory

BeanFactory es una interfaz de nivel superior en Spring, que define la forma de obtener beans. Hay otra interfaz en Spring llamada SingletonBeanRegistry, que define la forma de operar los beans singleton. Aquí presentaré estos dos juntos. Porque son más o menos lo mismo , la anotación de SingletonBeanRegistry también escribe que se puede implementar junto con la interfaz BeanFactory para facilitar la gestión unificada.

BeanFactory

1. ListableBeanFactory: Interfaz, que define métodos relacionados con la obtención de la lista Bean/BeanDefinition, como getBeansOfType(Class type)

2. AutowireCapableBeanFactory: Interfaz, que define métodos relacionados con el ciclo de vida de Bean, como creación de beans, inyección de dependencia, inicialización

3. AbstractBeanFactory: clase abstracta, que básicamente implementa todos los métodos relacionados con las operaciones de Bean y define métodos abstractos relacionados con el ciclo de vida de Bean

4. AbstractAutowireCapableBeanFactory: Clase abstracta, que hereda AbstractBeanFactory e implementa el contenido relacionado con el ciclo de vida del Bean, aunque es una clase abstracta, no tiene métodos abstractos.

5. DefaultListableBeanFactory: hereda e implementa todas las clases e interfaces anteriores. Es la BeanFactory inferior en Spring e implementa la propia interfaz ListableBeanFactory.

6, ApplicationContext: también es una interfaz, tendremos una introducción especial a continuación

SingletonBeanRegistro

1. DefaultSingletonBeanRegistry: define el grupo de caché de beans, similar a nuestro BeanMap, e implementa operaciones relacionadas con singletons, como getSingleton (el caché de tres niveles que se pregunta con frecuencia en las entrevistas está aquí)

2. FactoryBeanRegistrySupport: brinda soporte para FactoryBean, como obtener beans de FactoryBean

BeanDefinición

BeanDefinition es en realidad una interfaz (inesperadamente), que define muchos métodos de operación relacionados con la información de la clase, que es conveniente usar directamente cuando se producen beans, como getBeanClassName

Su estructura aproximada es la siguiente (aquí hay un ejemplo de una subclase RootBeanDefinition):

Los diversos atributos que contiene deben ser familiares para todos.

Asimismo, tiene muchas clases de implementación:

1. AnnotatedGenericBeanDefinition: al analizar la clase de configuración y analizar la clase traída por la anotación de importación, se usará para la encapsulación

2. ScannedGenericBeanDefinition: encapsula la información de clase obtenida al escanear el paquete a través de @ComponentScan

3. ConfigurationClassBeanDefinition: encapsula la información de clase obtenida a través de la anotación @Bean

4. RootBeanDefinition: la clase principal de ConfigurationClassBeanDefinition, que generalmente se usa dentro de Spring para convertir otras BeanDefitions en esta clase

BeanDefinitionRegistro

Define operaciones relacionadas con BeanDefinition, como registerBeanDefinition, getBeanDefinition, en BeanFactory, la clase de implementación es DefaultListableBeanFactory

BeanDefinitionRegistryPostProcessor

Interjección: Hablando de esto, ¿ha encontrado que el nombre de Spring está extremadamente estandarizado? El equipo de Spring dijo una vez que los nombres de las clases en Spring se confirman después de repetidas deliberaciones, lo cual es realmente digno de ese nombre, por lo que es realmente una muy cómoda Para ver el código fuente de Spring, puede adivinar lo que hacen mirando el nombre de la clase y el nombre del método.

Esta interfaz solo define una función: procesar BeanDefintonRegistry, es decir, analizar Import, Component, ComponentScan y otras anotaciones en la clase de configuración para el procesamiento correspondiente, y registrar estas clases como BeanDefinition correspondiente después del procesamiento.

Dentro de Spring, solo hay una implementación: ConfigurationClassPostProcessor

BeanFactoryPostProcesador

El llamado posprocesador de BeanFactory, que define la lógica de procesamiento que se puede llamar después de analizar la clase de configuración, es similar a una ranura. Si queremos hacer algo después de analizar la clase de configuración, podemos implementar esta interfaz.

Dentro de Spring, solo ConfigurationClassPostProcessor lo implementa: se usa para manejar específicamente clases anotadas con Configuración

Hay un pequeño problema en el campo aquí, si conoce el siguiente código:

@Configuraiton
public class MyConfiguration{
  @Bean
  public Car car(){
      return new Car(wheel());
  }
  @Bean
  public Wheel wheel(){
      return new Wheel();
  }
}
复制代码

P: ¿Cuántas veces es nuevo el objeto Wheel cuando se inicia Spring? ¿Por qué?

BeanPostProcesador

Traducción de Jianghu: posprocesador de Bean

El posprocesador se ejecuta a través de todo el ciclo de vida del bean. Durante la creación del bean, se llama un total de 9 veces. En cuanto a las 9 veces, lo exploraremos la próxima vez. A continuación se describe su clase de implementación y función.

1. AutowiredAnnotationBeanPostProcessor: se utiliza para inferir el constructor para la creación de instancias y para procesar anotaciones Autowired y Value

2. CommonAnnotationBeanPostProcessor: procesar anotaciones en la especificación Java, como Resource, PostConstruct

3. ApplicationListenerDetector: se usa después de la inicialización del bean, agregando el bean que implementa la interfaz ApplicationListener a la lista de detectores de eventos

4. ApplicationContextAwareProcessor: se usa para devolver la llamada Beans que implementan la interfaz Aware

5. ImportAwareBeanPostProcessor: se utiliza para devolver la llamada al Bean que implementa la interfaz ImportAware

ApplicationContext

Como núcleo de Spring, ApplicationContext aísla BeanFactory en el modo de fachada, define el esqueleto del proceso de inicio de Spring en el modo de método de plantilla e invoca varios procesadores en el modo de estrategia... ¡Es realmente complejo y exquisito!

Su clase de implementación es la siguiente:

1. ConfigurableApplicationContext: interfaz, que define la configuración y las operaciones relacionadas con el ciclo de vida, como la actualización

2. AbstractApplicationContext: una clase abstracta que implementa el método de actualización. El método de actualización es el núcleo del núcleo de Spring. Se puede decir que todo Spring está en la actualización. Todas las subclases se inician a través del método de actualización. Después de llamar a este método , todo soltero

3. AnnotationConfigApplicationContext: use los lectores y escáneres de anotaciones relevantes al inicio, registre los procesadores necesarios en el contenedor Spring y luego llame al método de actualización mediante el proceso principal.

4. AnnotationConfigWebApplicationContext: implemente el método loadBeanDefinitions para que se le llame en el proceso de actualización para cargar BeanDefintion

5 、 ClassPathXmlApplicationContext: 同上

Se puede ver en la situación de las subclases que la diferencia entre las subclases es cómo cargar BeanDefinitons. AnnotationConfigApplicationContext se carga a través del procesador de clases de configuración (ConfigurationClassPostProcessor), mientras que AnnotationConfigWebApplicationContext y ClassPathXmlApplicationContext implementan el método loadBeanDefinitions por sí mismos, y los otros procesos son exactamente los mismos. mismo

proceso de primavera

Arriba, tenemos claros los roles y roles principales en Spring, ahora tratamos de combinarlos para construir un proceso de inicio de Spring.

También tome nuestro AnnotationConfigApplicationContext de uso común como ejemplo

La figura solo dibuja una parte del proceso general en Spring, y ampliaremos los detalles en los siguientes capítulos.

resumen

El llamado comienzo de todo es difícil. La intención original de este artículo es permitir que todos entiendan Spring de lo más superficial a lo más profundo, establecer inicialmente el sistema cognitivo de Spring, comprender la estructura interna de Spring y comprender el conocimiento de Spring ya no superficialmente. .

Ahora que se ha abierto la cabeza, creo que el aprendizaje de los siguientes contenidos vendrá de forma natural.

Este artículo no solo trata sobre el diseño arquitectónico de Spring, sino que también espera convertirse en un manual para que revisemos el contenido general de Spring en el futuro.

Finalmente, después de leer el artículo, creo que es fácil responder las siguientes preguntas frecuentes de la entrevista.

1. ¿Qué es BeanDefinition?

2. ¿Cuál es la relación entre BeanFactory y ApplicationContext?

3. ¿Clasificación y función de los postprocesadores?

4. ¿Cuál es el proceso principal de la primavera?

Si cree que no puede responder bien, lea el artículo nuevamente o deje sus propias opiniones en el área de comentarios.


Autor: Ao Bing
Enlace: https://juejin.cn/post/7052116392979464205
 

Supongo que te gusta

Origin blog.csdn.net/wdjnb/article/details/124477569
Recomendado
Clasificación