Spring: varios patrones de diseño clásicos utilizados en Spring

prefacio

En las entrevistas, a menudo se hace un resumen de los patrones de diseño involucrados en Spring y preguntas sobre los patrones de diseño en Spring. Este documento presenta brevemente varios modelos de diseño aplicados en Sping basados ​​en la estructura del método de implementación, la esencia y el principio de implementación. ¡El análisis detallado se publicará en los siguientes artículos!

1. Fábrica sencilla

Método para realizar:

fábrica de frijoles. BeanFactory en Spring es la encarnación del modo de fábrica simple. El objeto Bean se obtiene al pasar un identificador único, pero si se crea después de pasar los parámetros o antes de pasar los parámetros depende de la situación específica.

sustancia:

Una clase de fábrica decide dinámicamente qué clase de producto debe crearse de acuerdo con los parámetros entrantes.

Principio de implementación:

La fase de inicio del contenedor de frijoles:

  • Lea el archivo de configuración xml del bean y convierta el elemento bean en un objeto BeanDefinition respectivamente.

  • Luego, estos beans se registran en beanFactory a través de BeanDefinitionRegistry y se almacenan en uno de sus ConcurrentHashMaps.

  • Después de registrar BeanDefinition con beanFactory, Spring nos proporciona un recorte extendido que nos permite insertar el código que definimos aquí implementando la interfaz BeanFactoryPostProcessor.

    Un ejemplo típico es: PropertyPlaceholderConfigurer, en él se inyecta el valor del marcador de posición que generalmente usamos al configurar el dataSource de la base de datos.

La fase de instanciación del bean en el contenedor:

La fase de creación de instancias es principalmente para crear instancias de beans a través de la reflexión o CGLIB. En esta etapa, Spring nos expone muchos puntos de extensión:

  • Varias interfaces de Aware , como BeanFactoryAware, para beans que implementan estas interfaces de Aware, Spring nos ayudará a inyectar una instancia de BeanFactory correspondiente al instanciar el bean.

  • La interfaz BeanPostProcessor es un bean que implementa la interfaz BeanPostProcessor Spring nos ayudará a llamar a los métodos en la interfaz al instanciar el bean.

  • La interfaz InitializingBean es un bean que implementa la interfaz InitializingBean Spring nos ayudará a llamar a los métodos en la interfaz al instanciar el bean.

  • La interfaz de DisabledBean es un bean que implementa la interfaz BeanPostProcessor Cuando el bean muere, Spring nos ayudará a llamar a los métodos en la interfaz.

Significado del diseño:

Acoplamiento flexible: las dependencias codificadas originales se pueden inyectar en las dependencias a través de Spring beanFactory, lo que significa que solo existían la parte que confía y la parte dependiente. Ahora hemos introducido una tercera parte, Spring beanFactory, que resuelve el bean. El problema de dependencia entre ellos logra el efecto de acoplamiento débil.

Procesamiento adicional de beans: a través de la exposición de la interfaz Spring, podemos realizar algunos procesamientos adicionales en la etapa de creación de instancias del bean. Estos procesamientos adicionales solo necesitan hacer que el bean implemente la interfaz correspondiente, luego Spring lo llamará durante la vida del bean. ciclo Implementamos la interfaz para manejar ese bean.

2. Método de fábrica

Método para realizar:

Interfaz FactoryBean.

Principio de implementación:

Un bean que implementa la interfaz FactoryBean es una clase de beans llamada factory. Su característica es que cuando Spring usa la llamada getBean() para obtener el bean, llamará automáticamente al método getObject() del bean, por lo que el valor devuelto no es el bean de fábrica, sino el valor devuelto del bean.getOjbect( ) método.

ejemplo:

Un ejemplo típico es la combinación de primavera y mybatis.

Ejemplo de código:

imagen

ilustrar:

Observamos el bean anterior, porque implementa la interfaz FactoryBean, no devuelve la instancia de SqlSessionFactoryBean, sino el valor de retorno de su SqlSessionFactoryBean.getObject().

3. Modo único

Las instancias de Spring Dependency Injection Bean son singletons por defecto.

La inyección de dependencia de Spring (incluido el método Lazy-init) ocurre en getBean de AbstractBeanFactory.

El método doGetBean de getBean llama a getSingleton para crear un bean.

Analizar el método getSingleton()

public Object getSingleton(String beanName){
    //参数true设置标识允许早期依赖
    return getSingleton(beanName,true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //检查缓存中是否存在实例
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        //如果为空,则锁定全局变量并进行处理。
        synchronized (this.singletonObjects) {
            //如果此bean正在加载,则不处理
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                //当某些方法需要提前初始化的时候则会调用addSingleFactory 方法将对应的ObjectFactory初始化策略存储在singletonFactories
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //调用预先设定的getObject方法
                    singletonObject = singletonFactory.getObject();
                    //记录在缓存中,earlysingletonObjects和singletonFactories互斥
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

diagrama de proceso getSingleton()

PD: cuando se utiliza la inyección de dependencia de resorte, se utiliza el modo singleton de doble juicio y bloqueo

imagen

 Resumir

Definición de patrón Singleton: garantiza que una clase tiene solo una instancia y proporciona un punto de acceso global a ella.

Implementación de singleton en Spring: el patrón singleton en Spring completa la segunda mitad de la oración, es decir, proporciona un punto de acceso global BeanFactory. Pero no hay un control de singleton desde el nivel del constructor, esto se debe a que Spring administra objetos Java arbitrarios.

4. Modo adaptador

Método para realizar:

Adaptador HandlerAdatper en SpringMVC.

Principio de implementación:

HandlerAdatper ejecuta diferentes controladores de acuerdo con las reglas de los controladores.

Proceso de implementación:

Según el controlador devuelto por HandlerMapping, DispatcherServlet inicia una solicitud a HandlerAdatper para procesar el controlador.

El HandlerAdapter encuentra el Handler correspondiente de acuerdo con las reglas y lo deja ejecutar.Después de la ejecución, el Handler devuelve un ModelAndView al HandlerAdapter, y finalmente el HandlerAdapter devuelve un ModelAndView al DispatchServelet.

Significado de implementación:

HandlerAdatper facilita la extensión de Handler, simplemente agregue un nuevo Handler y un HandlerAdapter correspondiente.

Por lo tanto, Spring define una interfaz de adaptación, de modo que cada controlador tenga una clase de implementación de adaptador correspondiente, lo que permite que el adaptador ejecute el método correspondiente en lugar del controlador. De esta manera, al extender el controlador, solo necesita agregar una clase de adaptador para completar la extensión SpringMVC.

5. Patrón decorador

Método para realizar:

El patrón de envoltura utilizado en Spring tiene dos manifestaciones en el nombre de la clase: una es que el nombre de la clase contiene Wrapper y la otra es que el nombre de la clase contiene Decorator.

sustancia:

Agregue dinámicamente algunas responsabilidades adicionales a un objeto.

En términos de agregar funcionalidad, el patrón Decorator es más flexible que la creación de subclases.

6. Modo proxy

Método para realizar:

La capa inferior de AOP es la implementación del modo proxy dinámico.

Proxy dinámico:

Memoria incorporada, sin necesidad de escribir manualmente clases de proxy

Proxy estático:

La clase de proxy debe escribirse manualmente y la clase de proxy se refiere al objeto proxy.

Principio de implementación:

Los aspectos se entretejen cuando se ejecuta la aplicación. Por lo general, al tejer un aspecto, el contenedor AOP crea dinámicamente un objeto proxy para el objeto de destino. Spring AOP se entreteje en aspectos de esta manera.

Tejido: el proceso de aplicar un aspecto a un objeto de destino y crear un nuevo objeto proxy.

7. Modo observador

Método para realizar:

El modelo basado en eventos de Spring usa el patrón de observador, y el uso más común del patrón de observador en Spring es la implementación del oyente.

Implementación:

La implementación del mecanismo de eventos requiere tres partes, el origen del evento, el evento y el detector de eventos.

Clase abstracta ApplicationEvent[事件]

Heredados de EventObject de jdk, todos los eventos deben heredar ApplicationEvent y obtener el origen del evento a través del origen del parámetro del constructor.

La clase de implementación ApplicationContextEvent de esta clase representa el evento contenedor de ApplicaitonContext.

Código:

public abstract class ApplicationEvent extends EventObject {
    private static final long serialVersionUID = 7099057708183571937L;
    private final long timestamp;
    public ApplicationEvent(Object source) {
    super(source);
    this.timestamp = System.currentTimeMillis();
    }
    public final long getTimestamp() {
        return this.timestamp;
    }
}

Interfaz de ApplicationListener[事件监听器]

Heredado de EventListener de jdk, todos los oyentes deben implementar esta interfaz.

Esta interfaz tiene solo un método onApplicationEvent(), que acepta un objeto ApplicationEvent o su subclase como parámetro.En el cuerpo del método, el procesamiento correspondiente puede ser realizado por diferentes juicios de la clase Evento.

Todos los oyentes recibirán un mensaje cuando se active el evento.

Código:

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
     void onApplicationEvent(E event);
}

Interfaz ApplicationContext[事件源]

ApplicationContext es el contenedor global en primavera, que se traduce como "contexto de aplicación".

Implementa la interfaz ApplicationEventPublisher.

Responsabilidades:

Es responsable de leer los documentos de configuración de los beans, gestionar la carga de beans y mantener las dependencias entre beans.Se puede decir que es responsable de todo el ciclo de vida de los beans.Más popularmente, es lo que solemos llamar el Contenedor COI.

Código:

public interface ApplicationEventPublisher {
    void publishEvent(ApplicationEvent event);
}

public void publishEvent(ApplicationEvent event) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
         logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }
    getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent != null) {
    this.parent.publishEvent(event);
    }
}

ApplicationEventMulticaster clase abstracta[事件源中publishEvent方法需要调用其方法getApplicationEventMulticaster]

Pertenece al emisor del evento, y su función es transmitir el Evento publicado por el contexto de la Aplicación a todos los oyentes.

Código:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
    implements ConfigurableApplicationContext, DisposableBean {
    private ApplicationEventMulticaster applicationEventMulticaster;
    protected void registerListeners() {
    // Register statically specified listeners first.
    for (ApplicationListener<?> listener : getApplicationListeners()) {
    getApplicationEventMulticaster().addApplicationListener(listener);
    }
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String lisName : listenerBeanNames) {
    getApplicationEventMulticaster().addApplicationListenerBean(lisName);
    }
  }
}

8. Modo de estrategia

Método para realizar:

La interfaz de recursos de acceso a recursos del marco Spring. Esta interfaz proporciona capacidades de acceso a recursos más sólidas. El propio marco Spring utiliza la interfaz de recursos ampliamente para acceder a los recursos subyacentes.

Introducción a la interfaz de recursos

La interfaz de origen es una abstracción de estrategias específicas de acceso a recursos y también es la interfaz implementada por todas las clases de acceso a recursos.

La interfaz de recursos proporciona principalmente los siguientes métodos:

  • getInputStream(): localiza y abre el recurso y devuelve el flujo de entrada correspondiente al recurso. Cada llamada devuelve un nuevo flujo de entrada. La persona que llama debe ser responsable de cerrar el flujo de entrada.

  • existe(): Devuelve si el recurso al que apunta Resource existe.

  • isOpen(): Devuelve si el archivo de recursos está abierto. Si el archivo de recursos no se puede leer varias veces, debe cerrarse explícitamente al final de cada lectura para evitar la fuga de recursos.

  • getDescription(): Devuelve la información de descripción del recurso, que generalmente se usa para generar la información cuando hay un error en el procesamiento del recurso, generalmente el nombre de archivo completo o la URL real.

  • getFile: Devuelve el objeto File correspondiente al recurso.

  • getURL: Devuelve el objeto URL correspondiente al recurso.

Los últimos dos métodos generalmente no se usan y Resource proporciona funciones de acceso a recursos tradicionales solo cuando no se puede lograr un acceso simple.

La interfaz de recursos en sí misma no proporciona la lógica de implementación para acceder a los recursos subyacentes. Para diferentes recursos subyacentes, Spring proporcionará diferentes clases de implementación de recursos, y las diferentes clases de implementación son responsables de las diferentes lógicas de acceso a los recursos.

Spring proporciona las siguientes clases de implementación para la interfaz de recursos:

  • UrlResource: la clase de implementación para acceder a los recursos de red.

  • ClassPathResource: la clase de implementación para acceder a los recursos en la ruta de carga de clases.

  • FileSystemResource: la clase de implementación para acceder a los recursos en el sistema de archivos.

  • ServletContextResource: la clase de implementación para acceder a los recursos relativos a la ruta de ServletContext.

  • InputStreamResource: la clase de implementación para acceder a los recursos de flujo de entrada.

  • ByteArrayResource: la clase de implementación para acceder a los recursos de la matriz de bytes.

Estas clases de implementación de recursos proporcionan la lógica de acceso a los recursos correspondiente para diferentes recursos subyacentes y proporcionan un paquete conveniente para facilitar el acceso a los recursos de los programas cliente.

9. Modo de método de plantilla

Definición del método de plantilla clásico:

La clase principal define el esqueleto (qué métodos se llaman y en qué orden), y la clase secundaria implementa algunos métodos específicos.

El mayor beneficio: reutilización de código, reduciendo la duplicación de código. Excepto por los métodos específicos que implementará la subclase, otros métodos y el orden de invocación de métodos están escritos previamente en la clase principal.

Entonces, hay dos tipos de métodos en el método de plantilla de clase principal:

Método común: código usado por todas las subclases

Diferentes métodos: Los métodos a cubrir por las subclases se dividen en dos tipos:

  • Método abstracto: el método abstracto en la clase principal debe ser anulado por la clase secundaria

  • Método Hook: es un método vacío en la clase padre, y la subclase lo hereda y también está vacío por defecto

Nota: ¿Por qué se llama enlace? Las subclases pueden controlar la clase principal a través de este enlace (método), ¡porque este enlace es en realidad un método (método vacío) de la clase principal!

La esencia del patrón del método de plantilla Spring:

Es una combinación del modo de método de plantilla y el modo de devolución de llamada, y es otro método de implementación del método de plantilla que no requiere herencia. Casi todas las extensiones complementarias de Spring usan este patrón.

Implementación:

La abstracción de JDBC y la integración de Hibernate adoptan un concepto o método de procesamiento, es decir, la combinación del patrón de método de plantilla y la interfaz Callback correspondiente.

El patrón de método de plantilla se usa para manejar la adquisición y liberación de recursos de una manera unificada y centralizada.Tome JdbcTempalte como ejemplo:

public abstract class JdbcTemplate {
     public final Object execute(String sql){
        Connection con=null;
        Statement stmt=null;
        try{
            con=getConnection();
            stmt=con.createStatement();
            Object retValue=executeWithStatement(stmt,sql);
            return retValue;
        }catch(SQLException e){
             ...
        }finally{
            closeStatement(stmt);
            releaseConnection(con);
        }
    }
    protected abstract Object executeWithStatement(Statement stmt, String sql);
}

Razón para introducir la devolución de llamada:

JdbcTemplate es una clase abstracta y no se puede usar de forma independiente. Debemos dar una implementación de subclase correspondiente cada vez que accedemos a los datos, lo que definitivamente es un inconveniente, por lo que se introducen devoluciones de llamada.

código de devolución de llamada

public interface StatementCallback{
    Object doWithStatement(Statement stmt);
}

Anular el método JdbcTemplate con el método de devolución de llamada

public class JdbcTemplate {
    public final Object execute(StatementCallback callback){
        Connection con=null;
        Statement stmt=null;
        try{
            con=getConnection();
            stmt=con.createStatement();
            Object retValue=callback.doWithStatement(stmt);
            return retValue;
        }catch(SQLException e){
            ...
        }finally{
            closeStatement(stmt);
            releaseConnection(con);
        }
    }

    ...//其它方法定义
}

Jdbc se utiliza de la siguiente manera:

JdbcTemplate jdbcTemplate=...;
    final String sql=...;
    StatementCallback callback=new StatementCallback(){
    public Object=doWithStatement(Statement stmt){
        return ...;
    }
}
jdbcTemplate.execute(callback);

¿Por qué JdbcTemplate no usa la herencia?

Debido a que hay demasiados métodos en esta clase, pero aún queremos usar la conexión de base de datos pública y estable existente de JdbcTemplate, ¿qué debemos hacer?

Podemos extraer las cosas modificadas como un parámetro y pasarlo al método de JdbcTemplate. Pero lo que cambió es un fragmento de código, y este código usará las variables en JdbcTemplate. ¿Cómo hacer?

Entonces usemos el objeto de devolución de llamada. En este objeto de devolución de llamada, defina un método para manipular variables en JdbcTemplate.Implementamos este método y concentramos los cambios aquí. Luego pasamos este objeto de devolución de llamada a JdbcTemplate para completar la llamada.

Referirse a

https://www.cnblogs.com/digdeep/p/4518571.html
https://www.cnblogs.com/tongkey/p/7919401.html
https://www.cnblogs.com/fingerboy/p/6393644. html
https://blog.csdn.net/ovoo_8/article/details/51189401
https://blog.csdn.net/z69183787/article/details/65628166
"Análisis en profundidad del código fuente de Spring"

Supongo que te gusta

Origin blog.csdn.net/qq_34272760/article/details/121122750
Recomendado
Clasificación