Golpee directamente la pregunta real de la entrevista de Ali: ¿Qué patrones de diseño usa el marco Spring?

prefacio

Hace unos días, a un lector de cara Ali se le hizo una pregunta: ¿Qué patrones de diseño usa Spring Framework? , La respuesta no es muy buena, ¡así que planeo escribir un artículo sobre esto!

modo proxy

El llamado proxy significa que implementa la misma interfaz que el objeto proxy. El cliente debe usar el proxy para interactuar con la clase de destino del proxy, y el proxy generalmente realiza algún procesamiento específico durante el proceso de interacción (antes y después de la interacción). ., por ejemplo, realice el procesamiento previo antes de llamar a este método y realice el procesamiento posterior después de llamar a este método.

El proxy se divide en proxy estático y proxy dinámico. El AOP de Spring adopta el método de proxy dinámico.

Spring mejora el aspecto de nivel de método de la clase a través de proxy dinámico, genera dinámicamente la clase de proxy del objeto de destino y establece el interceptor en el método de la clase de proxy, y mejora la función del método de proxy ejecutando la lógica en el interceptor, realizando así AOP.

Sobre el proxy dinámico, puede leer mi artículo anterior, que es muy detallado : Resumen del proxy dinámico, todo lo que necesita saber está aquí, ¡sin tonterías!

modo de estrategia

Como mencionamos anteriormente, Spring AOP se implementa a través de proxies dinámicos.

Específico para la implementación del código, Spring admite dos implementaciones de proxy dinámico, una es la implementación de proxy dinámico proporcionada por JDK y la otra es la implementación de proxy dinámico proporcionada por Cglib.

Spring elegirá dinámicamente una implementación de proxy dinámico diferente en tiempo de ejecución. Este escenario de aplicación es en realidad un escenario de aplicación típico del patrón de estrategia.

Solo necesitamos definir una interfaz de estrategia y dejar que diferentes clases de estrategia implementen esta interfaz de estrategia. En correspondencia con el código fuente de Spring, AopProxy es una interfaz de estrategia, y JdkDynamicAopProxy y CglibAopProxy son dos clases de estrategia que implementan la interfaz AopProxy.

Entre ellos, la definición de la interfaz AopProxy es la siguiente:

En el patrón de estrategia, la creación de estrategias generalmente se logra a través del método de fábrica. En correspondencia con el código fuente de Spring, AopProxyFactory es una interfaz de clase de fábrica y DefaultAopProxyFactory es una clase de fábrica predeterminada que se usa para crear objetos AopProxy.

El código fuente es el siguiente:

El escenario de aplicación típico del patrón de estrategia es decidir dinámicamente qué estrategia usar a través de variables de entorno, valores de estado, resultados de cálculo, etc.

En correspondencia con el código fuente de Spring, podemos referirnos a la implementación del código de la función createAopProxy() en la clase DefaultAopProxyFactory recién dada.

Entre ellos, la décima línea de código es la condición de juicio para la cual se selecciona dinámicamente la estrategia.

patrón decorador

Sabemos que los cachés generalmente se usan junto con las bases de datos. Si la caché de escritura tiene éxito, pero la transacción de la base de datos se revierte, habrá datos sucios en la caché.

Para resolver este problema, necesitamos poner la operación de escritura en caché y la operación de escritura en la base de datos en la misma transacción, ambas tienen éxito o ambas fallan.

Para lograr tal función, Spring usa el patrón decorador.

TransactionAwareCacheDecorator agrega soporte para transacciones y procesa los datos de la caché por separado cuando las transacciones se confirman y revierten.

TransactionAwareCacheDecorator implementa la interfaz Cache y delega todas las operaciones a targetCache para su implementación, agregando funciones de transacción a las operaciones de escritura. Este es un escenario de aplicación típico y una implementación de código del patrón decorador.

patrón único

El patrón singleton significa que solo se permite generar una instancia de una clase durante toda la operación del sistema.

En Spring, los beans se pueden definir en dos modos: Prototipo (múltiples instancias) y Singleton (singletons) Los Spring Beans son de modo singleton por defecto.

Entonces, ¿cómo implementa Spring el patrón singleton?

La respuesta es a través del registro singleton, específicamente el uso de HashMap. El código simplificado es el siguiente:

public class DefaultSingletonBeanRegistry {
    
    //使用了线程安全容器ConcurrentHashMap,保存各种单实例对象
    private final Map singletonObjects = new ConcurrentHashMap;

    protected Object getSingleton(String beanName) {
    //先到HashMap中拿Object
    Object singletonObject = singletonObjects.get(beanName);
    
    //如果没拿到通过反射创建一个对象实例,并添加到HashMap中
    if (singletonObject == null) {
      singletonObjects.put(beanName,
                           Class.forName(beanName).newInstance());
   }
   
   //返回对象实例
   return singletonObjects.get(beanName);
  }
}
复制代码

La lógica del código anterior es relativamente clara. Primero, vaya a HashMap para obtener un objeto de instancia única y, si no lo obtiene, cree uno y agréguelo a HashMap.

Patrón de fábrica simple

Hay tal escena:

Cuando el objeto A necesita llamar al método del objeto B, necesitamos crear una nueva instancia de B en A. Su desventaja es que una vez que cambian los requisitos, por ejemplo, cuando se necesita usar la clase C en lugar de B, el método de la clase A debe ser reescrita.

Si hay 100 clases en la aplicación que acoplan B de alguna manera, será difícil cambiar.

Use un patrón de fábrica simple:

El patrón de fábrica simple también se llama método de fábrica estático. Su esencia es que una clase de fábrica decide dinámicamente qué clase de producto debe crearse de acuerdo con los parámetros entrantes.

BeanFactory en Spring es la encarnación del patrón de fábrica simple, y BeanFactory es una interfaz central en el contenedor IOC de Spring. Su definición es la siguiente:

Podemos obtener el bean a través de su clase de implementación concreta (como ClassPathXmlApplicationContext):

BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml");
FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean");
复制代码

Como se puede ver en el código anterior, los usuarios no necesitan crear nuevos objetos por sí mismos, pero obtienen instancias de objetos a través del método getBean de la clase de fábrica.Este es un patrón de fábrica simple típico, pero Spring usa el mecanismo de reflexión para crear beans.

Patrón de método de fábrica

En una fábrica simple, todos los juicios lógicos y la creación de instancias los realiza la clase de fábrica; si no desea hacer juicios en la fábrica, puede proporcionar diferentes fábricas para diferentes productos. Diferentes fábricas producen diferentes productos, y cada fábrica corresponde a solo uno El objeto correspondiente, este es el patrón del método de fábrica.

FactoryBean en Spring es la encarnación de esta idea. FactoryBean puede entenderse como un bean de fábrica. Echemos un vistazo a su definición primero:

Definimos una clase FlyFishFactoryBean para implementar la interfaz FactoryBean, principalmente para crear un nuevo objeto FlyFish en el método getObject. De esta forma, lo que obtenemos a través de getBean(id) es la instancia de FlyFish generada por la fábrica, no la instancia de FlyFishFactoryBean en sí, de la siguiente manera:

BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml");
FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean");
复制代码

Patrón de observador

El patrón de observador implementado en Spring consta de tres partes: Evento evento (equivalente a mensaje), Oyente oyente (equivalente a observador), Publicador remitente (equivalente a persona observada)

Tomemos un ejemplo para ver cómo se usa el patrón de observador provisto por Spring

// Event事件
public class DemoEvent extends ApplicationEvent {
  private String message;

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

  public String getMessage() {
    return this.message;
  }
}

// Listener监听者
@Component
public class DemoListener implements ApplicationListener {
  @Override
  public void onApplicationEvent(DemoEvent demoEvent) {
    String message = demoEvent.getMessage();
    System.out.println(message);
  }
}

// Publisher发送者
@Component
public class DemoPublisher {
  @Autowired
  private ApplicationContext applicationContext;

  public void publishEvent(DemoEvent demoEvent) {
    this.applicationContext.publishEvent(demoEvent);
  }
}
复制代码

Del código, podemos ver que incluye principalmente tres partes:

  • Defina un evento (DemoEvent) que herede ApplicationEvent;
  • Defina un oyente (DemoListener) que implemente ApplicationListener;
  • Defina un remitente (DemoPublisher), el remitente llama a ApplicationContext para enviar mensajes de eventos.

En la implementación de Spring, ¿dónde está registrado el observador? ¿Cómo se registra?

Spring registra el observador en el objeto ApplicationContext.

De hecho, en lo que respecta al código fuente, ApplicationContext es solo una interfaz, y la implementación del código específico está contenida en su clase de implementación AbstractApplicationContext. Pongo el código relacionado con el patrón del observador de la siguiente manera. Solo necesita prestar atención a cómo envía eventos y registra oyentes.

Del código anterior, encontramos que el envío real del mensaje se realiza a través de la clase ApplicationEventMulticaster.

Solo extraje la parte más crítica del código fuente de la siguiente clase, es decir, la función de envío de mensajes de multicastEvent(), que admite dos tipos de modos de observador: sin bloqueo asíncrono y bloqueo síncrono a través del grupo de subprocesos.

Con la ayuda del código esqueleto del modo observador provisto por Spring, si queremos realizar el envío y monitoreo de un evento bajo Spring, solo necesitamos trabajar un poco, definir el evento, definir el oyente y enviar el evento al ApplicationContext.El resto del trabajo lo realiza Spring framework.

De hecho, esto también refleja la extensibilidad del framework Spring, es decir, extender nuevos eventos y oyentes sin modificar ningún código.

Modo de plantilla

Una pregunta que a menudo nos hacen en las entrevistas es:

Dígame cuáles son los pasos principales en el proceso de creación de Spring Bean.

Esto implica el patrón de plantilla. También refleja la extensibilidad de Spring. Usando el patrón de plantilla, Spring permite a los usuarios personalizar el proceso de creación de beans.

El siguiente es el ciclo de vida completo de Spring Bean, una imagen, clara:

Para más detalles, puede consultar el artículo anterior: Aplicación de los Puntos de Expansión de Spring

Si observa el código fuente detenidamente, encontrará que, de hecho, la implementación del patrón de plantilla aquí no es la implementación de la clase abstracta estándar, sino que es algo similar a la implementación de la devolución de llamada Callback, es decir, el La función que se va a ejecutar se encapsula en un objeto (por ejemplo, el método de inicialización se encapsula en un objeto InitializingBean) y se pasa a la plantilla (BeanFactory) para su ejecución.

Modo de observador y modo de plantilla, estos dos modos pueden ayudarnos a crear puntos de extensión, lo que permite a los usuarios del marco personalizar las funciones del marco en función de los puntos de extensión sin modificar el código fuente.

modo adaptador

En Spring MVC, la forma más común de definir un controlador es marcar una clase como clase de controlador a través de la anotación @Controller y marcar la URL correspondiente a la función a través de la anotación @RequesMapping

Sin embargo, también podemos definir un controlador haciendo que la clase implemente la interfaz del controlador o la interfaz del servlet.

Para estas tres definiciones, escribí tres piezas de código de muestra, de la siguiente manera:

// 方法一:通过@Controller、@RequestMapping来定义
@Controller
public class DemoController {
    @RequestMapping("/FlyFish")
    public ModelAndView getEmployeeName() {
        ModelAndView model = new ModelAndView("FlyFish");        
        model.addObject("message", "FlyFish");       
        return model; 
    }  
}

// 方法二:实现Controller接口 + xml配置文件:配置DemoController与URL的对应关系
public class DemoController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        ModelAndView model = new ModelAndView("FlyFish");
        model.addObject("message", "FlyFish");
        return model;
    }
}

// 方法三:实现Servlet接口 + xml配置文件:配置DemoController类与URL的对应关系
public class DemoServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req, resp);
  }
  
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("Hello World.");
  }
}
复制代码

Cuando se inicia la aplicación, el contenedor Spring carga estas clases de controlador, analiza la función de controlador correspondiente a la URL, la encapsula en un objeto de controlador y la almacena en un objeto HandlerMapping. Cuando llega una solicitud, DispatcherServlet busca el controlador correspondiente a la URL de solicitud de HanderMapping, luego llama y ejecuta el código de función correspondiente al controlador y finalmente devuelve el resultado de la ejecución al cliente.

Sin embargo, para los controladores definidos de diferentes maneras, las definiciones de sus funciones (nombres de función, parámetros de entrada, valores de retorno, etc.) no son uniformes.

DispatcherServlet llama al método service() DispatcherServlet necesita llamar a diferentes funciones según los diferentes tipos de controladores.

Spring usa el modo adaptador, y adaptamos las funciones en la clase Controlador definidas de diferentes maneras en una definición de función unificada.

Echemos un vistazo más de cerca a la implementación del código de Spring.

Spring define una interfaz unificada HandlerAdapter y define una clase de adaptador correspondiente para cada controlador.

Estas clases de adaptadores incluyen: AnnotationMethodHandlerAdapter, SimpleControllerHandlerAdapter, SimpleServletHandlerAdapter, etc.

En la clase DispatcherServlet, no necesitamos tratar diferentes objetos Controller de manera diferente, simplemente llame a la función handle() de HandlerAdapter de manera uniforme.


Autor: Programador Duan Fei
Enlace: https://juejin.cn/post/7066266639355871268
Fuente: Rare Earth Nuggets
Los derechos de autor pertenecen al autor. Para reimpresiones comerciales, comuníquese con el autor para obtener autorización, y para reimpresiones no comerciales, indique la fuente.

Supongo que te gusta

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