[Patrones de diseño en primavera]

Patrones de diseño en primavera.

1. Inversión de control (IoC) e inyección de dependencia (DI)

IoC (Inversión de control, Inversión de control) es un concepto muy, muy importante en Spring: no es una tecnología, sino una idea de diseño desacoplada. El objetivo principal de IoC es utilizar un "tercero" (contenedor de IoC en Spring) para lograr el desacoplamiento entre objetos con dependencias (objetos de administración de contenedores de IOC, simplemente puede usarlos), reduciendo así el acoplamiento entre códigos.

IoC es un principio, no un patrón, y los siguientes patrones (entre otros) implementan el principio de IoC.

imagen-20230922145057135

El contenedor Spring IoC es como una fábrica: cuando necesitamos crear un objeto, solo necesitamos configurar el archivo de configuración/las anotaciones sin pensar en cómo se crea el objeto. El contenedor IoC es responsable de crear objetos, conectarlos, configurarlos y manejar todo el ciclo de vida de esos objetos desde su creación hasta su destrucción completa.

En proyectos reales, si una clase de Servicio tiene cientos o incluso miles de clases como capa inferior, necesitamos crear una instancia de este Servicio. Es posible que tenga que descubrir los constructores de todas las clases inferiores de este Servicio cada vez, lo que puede causar que Drive gente loca. Si usa IOC, solo necesita configurarlo y hacer referencia a él cuando sea necesario, lo que aumenta en gran medida la mantenibilidad del proyecto y reduce la dificultad de desarrollo.

Con respecto a la comprensión de Spring IOC, se recomienda leer esta respuesta en Zhihu: https://www.zhihu.com/question/23277575/answer/169698662abrir en una nueva ventana , Es muy bueno.

¿Cómo entender la inversión de control? Por ejemplo: "El objeto a depende del objeto b. Cuando el objeto a necesita usar el objeto b, debe crearlo por sí mismo. Pero cuando el contenedor IOC se introduce en el sistema, hay una brecha entre objeto a y objeto b. La conexión directa se pierde. En este momento, cuando el objeto a necesita usar el objeto b, podemos especificar el contenedor IOC para crear un objeto by inyectarlo en el objeto a. El proceso por el cual el objeto a se vuelve dependiente del objeto b cambia de un comportamiento activo a un comportamiento pasivo y el control se invierte, de ahí el nombre de inversión de control.

DI (Dependency Inject, inyección de dependencia) es un patrón de diseño que implementa la inversión de control.La inyección de dependencia consiste en pasar variables de instancia a un objeto.

2. Patrón de diseño de fábrica

Spring usa el patrón de fábrica para crear objetos bean a través de BeanFactoryo .ApplicationContext

Compara los dos:

  • BeanFactory: Inyección retrasada (solo se inyectará cuando se use un determinado bean), en comparación con , ocupará ApplicationContextmenos memoria y el programa se iniciará más rápido.
  • ApplicationContext: Cuando se inicia el contenedor, independientemente de si lo usa o no, todos los beans se crean a la vez. BeanFactorySolo proporciona el soporte de inyección de dependencia más básico.Después ApplicationContextde la expansión BeanFactory, además de BeanFactoryalgunas funciones, hay funciones adicionales, por lo que la mayoría de los desarrolladores ApplicationContextlas usarán más.

ApplicationContextTres clases de implementación:

  1. ClassPathXmlApplication: Trate los archivos de contexto como recursos de classpath.
  2. FileSystemXmlApplication: carga información de definición de contexto desde un archivo XML en el sistema de archivos.
  3. XmlWebApplicationContext: carga información de definición de contexto desde el archivo XML en el sistema web.

Ejemplo:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class App {
    
    
	public static void main(String[] args) {
    
    
		ApplicationContext context = new FileSystemXmlApplicationContext(
				"C:/work/IOC Containers/springframework.applicationcontext/src/main/resources/bean-factory-config.xml");

		HelloApplicationContext obj = (HelloApplicationContext) context.getBean("helloApplicationContext");
		obj.getMsg();
	}
}

3. Patrón de diseño singleton

Método para realizar

1. Estilo chino hambriento

Código:

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Single single = Single.getSingle();
    }
}
class Single{
    
    
    private Single(){
    
    }
    private static Single s = new Single();
    public static Single getSingle(){
    
    
        return s;
    }
}

2. Estilo del hombre perezoso

La carga diferida solo inicia la creación de instancias cuando realmente se usa.

Problemas de seguridad de subprocesos:
verifique dos veces el compilador de optimización de bloqueo
(JIT), la CPU puede reordenar las instrucciones, lo que resulta en el uso de instancias no inicializadas, que se pueden modificar agregando la palabra clave volátil. Para los campos modificados por volátil, se puede evitar que las instrucciones se repitan .Fila.

Código:

class Single {
    
    
	// 禁止指令重排
    private volatile static Single s;
    private Single() {
    
    
    }

    public static Single getSingle() {
    
    
        if (s == null) {
    
    
            synchronized (Single.class) {
    
    
                if (s == null) {
    
    
                    s = new Single();
                    // 字节码层
                    // JIT , CPU 有可能对如下指令进行重排序
                    // 1 .分配空间
                    // 2 .初始化
                    // 3 .引用赋值
                    // 如重排序后的结果为如
                    // 1 .分配空间
                    // 3 .引用赋值 如果在当前指令执行完,有其他线程来获取实例,将拿到尚未初始化好的实例
                    // 2 .初始化
                }
            }
        }
        return s;
    }
}

2. Clase interna estática

Esencialmente, el mecanismo de carga de clases se utiliza para garantizar la seguridad de los subprocesos.
La inicialización de la clase solo se activará cuando se use realmente, por lo que también es una forma de carga diferida.

Código:

class Single {
    
    
    private static class InnerSingle {
    
    
        private static Single s = new Single();
    }
    private Single() {
    
    
        // 防止利用反射攻击
        if(InnerSingle.s == null){
    
    		
            throw new RuntimeException("单例不允许多个实例");
        }
    }
    public static Single getSingle() {
    
    
        return InnerSingle.s;
    }
}

4. Clase de enumeración

Naturalmente, no admite la reflexión para crear instancias correspondientes y tiene su propio mecanismo de deserialización. El
mecanismo de carga de clases se utiliza para garantizar la seguridad de los subprocesos.

Código:

public enum EnumSingleton {
    
    
    INSTANCE;
}

Patrón singleton en primavera

En nuestro sistema, hay algunos objetos que en realidad solo necesitamos uno, como: grupo de subprocesos, caché, cuadro de diálogo, registro, objeto de registro, objetos que actúan como controladores de dispositivos para impresoras, tarjetas gráficas, etc. De hecho, este tipo de objeto solo puede tener una instancia. La creación de varias instancias puede causar algunos problemas, como un comportamiento anormal del programa, uso excesivo de recursos o resultados inconsistentes.

Ventajas de utilizar el patrón singleton :

  • Para los objetos de uso frecuente, se puede omitir el tiempo dedicado a crear objetos, lo que supone una sobrecarga del sistema muy considerable para esos objetos pesados;
  • Dado que se reduce la cantidad de operaciones nuevas, también se reducirá la frecuencia de uso de la memoria del sistema, lo que reducirá la presión del GC y acortará el tiempo de pausa del GC.

El alcance predeterminado de los beans en Spring es singleton. Además del alcance singleton, los beans en Spring también tienen los siguientes alcances:

  • prototipo : se crea una nueva instancia de bean cada vez que se recupera. En otras palabras, getBean()dos veces consecutivas, obtienes diferentes instancias de Bean.
  • Solicitud (disponible solo para aplicaciones web): cada solicitud HTTP generará un nuevo bean (bean de solicitud), que solo es válido dentro de la solicitud HTTP actual.
  • sesión (disponible solo para aplicaciones web): cada solicitud HTTP de una nueva sesión generará un nuevo bean (bean de sesión), que solo es válido dentro de la sesión HTTP actual.
  • aplicación/sesión global (disponible solo para aplicaciones web): cada aplicación web crea un Bean (Bean de aplicación) cuando se inicia, y el bean solo es válido durante el tiempo de inicio actual de la aplicación.
  • websocket (disponible solo para aplicaciones web): cada sesión de WebSocket genera un nuevo bean.

Spring ConcurrentHashMapimplementa el patrón singleton mediante una forma especial de implementar un registro singleton.

El código central de la implementación singleton de Spring es el siguiente:

// 通过 ConcurrentHashMap(线程安全) 实现单例注册表
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    
    
        Assert.notNull(beanName, "'beanName' must not be null");
        synchronized (this.singletonObjects) {
    
    
            // 检查缓存中是否存在实例
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
    
    
                //...省略了很多代码
                try {
    
    
                    singletonObject = singletonFactory.getObject();
                }
                //...省略了很多代码
                // 如果实例对象在不存在,我们注册到单例注册表中。
                addSingleton(beanName, singletonObject);
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    }
    //将对象添加到单例注册表
    protected void addSingleton(String beanName, Object singletonObject) {
    
    
            synchronized (this.singletonObjects) {
    
    
                this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));

            }
        }
}

¿Existen problemas de seguridad de subprocesos con beans singleton?

La mayoría de las veces no utilizamos subprocesos múltiples en nuestros proyectos, por lo que pocas personas prestan atención a este problema. Los Singleton Beans tienen problemas de subprocesos, principalmente porque existe competencia por recursos cuando varios subprocesos operan el mismo objeto.

Hay dos soluciones comunes:

  1. Intente evitar definir variables miembro mutables en Beans.
  2. Defina una variable miembro en la clase ThreadLocaly guarde las variables miembro requeridas en ThreadLocal(una forma recomendada).

Sin embargo, la mayoría de los beans en realidad no tienen estado (sin variables de instancia) (como Dao, Servicio). En este caso, los beans son seguros para subprocesos.

4. Patrón de diseño de agencia

Aplicación del patrón proxy en AOP

AOP (Programación orientada a aspectos) puede encapsular lógica o responsabilidades (como procesamiento de transacciones, gestión de registros, control de permisos, etc.) que no tienen nada que ver con el negocio, pero que los módulos comerciales comúnmente invocan para reducir el código duplicado en el sistema. , reduce el acoplamiento entre módulos y facilita la escalabilidad y mantenibilidad futuras.

Spring AOP se basa en un proxy dinámico . Si el objeto a ser proxy implementa una determinada interfaz, entonces Spring AOP usará JDK Proxy para crear el objeto proxy. Para los objetos que no implementan la interfaz, JDK Proxy no se puede usar como proxy. Esta vez, Spring AOP usará Cglib para generar una subclase del objeto proxy como proxy, como se muestra en la siguiente figura:

imagen-20230922150551320

Por supuesto, también puede utilizar AspectJ. Spring AOP ha integrado AspectJ. AspectJ debe considerarse como el marco AOP más completo en el ecosistema Java.

Después de usar AOP, podemos abstraer algunas funciones comunes y usarlas directamente donde sean necesarias, lo que simplifica enormemente la cantidad de código. También es conveniente cuando necesitamos agregar nuevas funciones, lo que también mejora la escalabilidad del sistema. AOP se utiliza en funciones de registro, gestión de transacciones y otros escenarios.

¿Cuál es la diferencia entre Spring AOP y AspectJ AOP?

Spring AOP es una mejora en tiempo de ejecución, mientras que AspectJ es una mejora en tiempo de compilación. Spring AOP se basa en Proxying, mientras que AspectJ se basa en la manipulación de Bytecode.

Spring AOP ha integrado AspectJ, que debe considerarse como el marco AOP más completo del ecosistema Java. AspectJ es más poderoso que Spring AOP, pero Spring AOP es relativamente más simple.

Si tenemos menos aspectos no hay mucha diferencia de rendimiento entre ambos. Sin embargo, cuando hay demasiados aspectos, es mejor elegir AspectJ, que es mucho más rápido que Spring AOP.

5. Método de plantilla

El patrón Método de plantilla es un patrón de diseño de comportamiento que define el esqueleto de un algoritmo en funcionamiento y al mismo tiempo difiere algunos pasos a subclases. Los métodos de plantilla permiten a las subclases redefinir la implementación de ciertos pasos específicos de un algoritmo sin cambiar la estructura del algoritmo.

public abstract class Template {
    
    
    //这是我们的模板方法
    public final void TemplateMethod(){
    
    
        PrimitiveOperation1();
        PrimitiveOperation2();
        PrimitiveOperation3();
    }

    protected void  PrimitiveOperation1(){
    
    
        //当前类实现
    }

    //被子类实现的方法
    protected abstract void PrimitiveOperation2();
    protected abstract void PrimitiveOperation3();

}
public class TemplateImpl extends Template {
    
    

    @Override
    public void PrimitiveOperation2() {
    
    
        //当前类实现
    }

    @Override
    public void PrimitiveOperation3() {
    
    
        //当前类实现
    }
}

En Spring JdbcTemplate, HibernateTemplatelas clases que terminan en Plantilla y que operan en la base de datos utilizan el patrón de plantilla. En circunstancias normales, usamos la herencia para implementar el patrón de plantilla, pero Spring no usa este método, sino que usa el patrón de devolución de llamada para cooperar con el patrón del método de plantilla, lo que no solo logra el efecto de reutilización del código, sino que también aumenta la flexibilidad. . .

6. Patrón de observador

El patrón de observador es un patrón de comportamiento de un objeto. Representa una relación de dependencia entre objetos. Cuando un objeto cambia, todos los objetos que dependen de este objeto también reaccionarán. El modelo basado en eventos de Spring es una aplicación clásica del patrón de observador. El modelo basado en eventos de Spring es muy útil y puede desacoplar nuestro código en muchos escenarios. Por ejemplo, cada vez que agregamos un producto, necesitamos actualizar el índice del producto, en este momento podemos usar el patrón de observador para resolver este problema.

Tres roles en el modelo impulsado por eventos de Spring

rol del evento

ApplicationEvent( org.springframework.contextDebajo del paquete) Actúa como un evento. Esta es una clase abstracta que hereda java.util.EventObjecte implementa java.io.Serializablela interfaz.

Los siguientes eventos existen de forma predeterminada en Spring y todos son ApplicationContextEventimplementaciones de (heredados de ApplicationContextEvent):

  • ContextStartedEvent: ApplicationContextEvento desencadenado después del inicio;
  • ContextStoppedEvent: ApplicationContextEvento desencadenado después de detenerse;
  • ContextRefreshedEvent: ApplicationContextEvento desencadenado después de que se completa la inicialización o actualización;
  • ContextClosedEvent: ApplicationContextEvento desencadenado después del cierre.

imagen-20230922150831082

rol de oyente de eventos

ApplicationListenerActuando como un detector de eventos, es una interfaz con un solo método definido onApplicationEvent()para el procesamiento ApplicationEvent. ApplicationListenerEl código fuente de la clase de interfaz es el siguiente: puede ver la definición de la interfaz y los eventos en la interfaz solo necesitan implementarse ApplicationEvent. Por lo tanto, en Spring solo necesitamos implementar el método ApplicationListenerde la interfaz onApplicationEvent()para completar el evento de escucha.

package org.springframework.context;
import java.util.EventListener;
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
    
    void onApplicationEvent(E var1);
}

rol de editor de eventos

ApplicationEventPublisherAl actuar como editor de eventos, también es una interfaz.

@FunctionalInterface
public interface ApplicationEventPublisher {
    
    
    default void publishEvent(ApplicationEvent event) {
    
    
        this.publishEvent((Object)event);
    }

    void publishEvent(Object var1);
}

ApplicationEventPublisherpublishEvent()Este método de interfaz AbstractApplicationContextse implementa en la clase. Al leer la implementación de este método, encontrará que el evento realmente se ApplicationEventMulticastertransmite.

Resumen del proceso del evento de primavera.

  1. Definir un evento: implementar un evento heredado ApplicationEventy escribir el constructor correspondiente;
  2. Defina un detector de eventos: implemente ApplicationListenerla interfaz y anule onApplicationEvent()el método;
  3. Publicar mensajes utilizando publicadores de eventos: puede publicar mensajes mediante el método ApplicationEventPublisherde publishEvent().

Ejemplo:

// 定义一个事件,继承自ApplicationEvent并且写相应的构造函数
public class DemoEvent extends ApplicationEvent{
    
    
    private static final long serialVersionUID = 1L;

    private String message;

    public DemoEvent(Object source,String message){
    
    
        super(source);
        this.message = message;
    }

    public String getMessage() {
    
    
         return message;
          }


// 定义一个事件监听者,实现ApplicationListener接口,重写 onApplicationEvent() 方法;
@Component
public class DemoListener implements ApplicationListener<DemoEvent>{
    
    

    //使用onApplicationEvent接收消息
    @Override
    public void onApplicationEvent(DemoEvent event) {
    
    
        String msg = event.getMessage();
        System.out.println("接收到的信息是:"+msg);
    }

}
// 发布事件,可以通过ApplicationEventPublisher  的 publishEvent() 方法发布消息。
@Component
public class DemoPublisher {
    
    

    @Autowired
    ApplicationContext applicationContext;

    public void publish(String message){
    
    
        //发布事件
        applicationContext.publishEvent(new DemoEvent(this, message));
    }
}

Cuando se llama al método de, por ejemplo DemoPublisher, la consola imprimirá: .publish()demoPublisher.publish("你好")接收到的信息是:你好

7.Modo adaptador

El patrón de adaptador convierte una interfaz en otra interfaz que el cliente desea y permite que clases con interfaces incompatibles trabajen juntas.

Patrón de adaptador en Spring AOP

Sabemos que la implementación de Spring AOP se basa en el modo proxy, pero la mejora o el consejo de Spring AOP utiliza el modo adaptador y la interfaz relacionada es AdvisorAdapter.

Los tipos de asesoramiento comúnmente utilizados son: BeforeAdvice(antes de llamar al método de destino, aviso previo), AfterAdvice(después de llamar al método de destino, aviso posterior), AfterReturningAdvice(después de ejecutar el método de destino, antes del retorno), etc. Cada tipo de Consejo tiene un interceptor correspondiente: MethodBeforeAdviceInterceptor, AfterReturningAdviceInterceptor, ThrowsAdviceInterceptoretc.

Las notificaciones predefinidas de Spring deben adaptarse a MethodInterceptorobjetos de tipo interfaz (interceptor de métodos) a través del adaptador correspondiente (por ejemplo: MethodBeforeAdviceAdapterllamando getInterceptoral método, MethodBeforeAdviceadaptarse a MethodBeforeAdviceInterceptor).

Patrón de adaptador en Spring MVC

En Spring MVC, DispatcherServletse llama según la información de la solicitud HandlerMappingy analiza la solicitud correspondiente Handler. Después de analizar el correspondiente Handler(que es lo que normalmente llamamos Controllercontrolador), HandlerAdapterel adaptador comienza a procesarlo. HandlerAdapterComo interfaz deseada, se utiliza la clase de implementación del adaptador específico para adaptar la clase de destino Controllercomo la clase que necesita adaptarse.

¿Por qué utilizar el patrón adaptador en Spring MVC?

Hay muchos tipos en Spring MVC Controllery diferentes tipos Controllermanejan las solicitudes de diferentes maneras. Si no utiliza el modo adaptador, DispatcherServletpuede obtener el tipo correspondiente directamente Controllery juzgar lo que necesita usted mismo, como el siguiente código:

if(mappedHandler.getHandler() instanceof MultiActionController){
    
    
   ((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
    
    
    ...
}else if(...){
    
    
   ...
}

Si agregamos otro Controllertipo, debemos agregar otra línea de declaración de juicio al código anterior. Esta forma dificulta el mantenimiento del programa y viola el principio de apertura y cierre en el patrón de diseño: abierto para expansión y cerrado para modificación.

patrón decorador

El patrón decorador puede agregar dinámicamente algunas propiedades o comportamientos adicionales a un objeto. En comparación con el uso de la herencia, el patrón decorador es más flexible. En pocas palabras, cuando necesitamos modificar la función original, pero no queremos modificar el código original directamente, diseñamos un Decorador para que quepa fuera del código original. De hecho, el patrón decorador se usa en muchos lugares del JDK, como en InputStreamlas familias, InputStreamhay subclases debajo de la clase FileInputStream(leer archivos), BufferedInputStream(aumentar el caché, mejorar en gran medida la velocidad de lectura de archivos), etc., que se pueden InputStreamampliar . sin modificar el código su función.

imagen-20230922151137216

Al configurar DataSource en Spring, DataSource puede ser diferentes bases de datos y fuentes de datos. ¿Podemos cambiar dinámicamente diferentes fuentes de datos según las necesidades del cliente con menos modificación del código de clase original? En este momento, se utilizará el patrón decorador (todavía no entiendo el principio específico de esto). El patrón contenedor utilizado en Spring contiene Wrappero en el nombre de la clase Decorator. Básicamente, estas clases agregan dinámicamente algunas responsabilidades adicionales a un objeto.

Resumir

¿Qué patrones de diseño se utilizan en el marco Spring?

  • Patrón de diseño de fábrica : Spring usa el patrón de fábrica para BeanFactorycrear ApplicationContextobjetos Bean.
  • Patrón de diseño de proxy : implementación de la funcionalidad Spring AOP.
  • Patrón de diseño singleton : los frijoles en primavera son singleton de forma predeterminada.
  • Patrón de método de plantilla : en Spring jdbcTemplate, hibernateTemplatelas clases que terminan en Plantilla y que operan en la base de datos utilizan el patrón de plantilla.
  • Patrón de diseño de contenedor : nuestro proyecto necesita conectarse a múltiples bases de datos, y diferentes clientes accederán a diferentes bases de datos según sus necesidades durante cada visita. Este modelo nos permite cambiar dinámicamente entre diferentes fuentes de datos según las necesidades del cliente.
  • Patrón de observador: el modelo basado en eventos de Spring es una aplicación clásica del patrón de observador.
  • Patrón de adaptador : la mejora o el consejo de Spring AOP utiliza el patrón de adaptador, y Spring MVC también utiliza la adaptación del patrón de adaptador Controller.

Fuente: Explicación detallada de los patrones de diseño en Spring | JavaGuide (Entrevista Java + Guía de estudio)

Supongo que te gusta

Origin blog.csdn.net/weixin_45483322/article/details/133184407
Recomendado
Clasificación