[Traducción] patrones de diseño en primavera

1. Introducción

Los patrones de diseño son una parte importante del desarrollo de software. Estas soluciones no solo resuelven problemas recurrentes, sino que también ayudan a los desarrolladores a comprender el diseño del marco identificando patrones comunes.

En este tutorial, estudiaremos los cuatro patrones de diseño más comunes utilizados en el marco de Spring:

  • Patrón Singleton
  • Patrón de método de fábrica
  • Modo proxy
  • Modo de plantilla

También estudiaremos cómo Spring usa estos patrones para reducir la carga sobre los desarrolladores y ayudar a los usuarios a realizar rápidamente tareas engorrosas.


2. Modo Singleton

El modo Singleton es un mecanismo para garantizar que solo haya una instancia de objeto por aplicación. Este modo es útil cuando se administran recursos compartidos o se brindan servicios entre dominios, como el registro.

2.1 Habas Singleton

Por lo general, los singletons son globalmente únicos para la aplicación, pero en Spring, esta restricción es más amplia. El singleton definido por Spring es el único en el contenedor Spring IOC. En la práctica, esto significa que Spring solo creará un bean para cada tipo de contexto de aplicación.

El enfoque de Spring es diferente de una definición singleton estricta, porque una aplicación puede tener múltiples contenedores Spring. Por lo tanto, si tenemos múltiples contenedores, pueden existir múltiples objetos de la misma clase en una sola aplicación.

Por defecto, Spring crea todos los beans como singletons.

2.2 Montaje automático de objetos singleton

Por ejemplo, podemos crear dos controladores en un contexto de aplicación e inyectar beans del mismo tipo en cada controlador.

Primero, creamos un BookRepository para administrar nuestros objetos de dominio de Book.

A continuación, creamos el LibraryController, que usa BookRepository para devolver el número de libros en la biblioteca:

@RestController
public class LibraryController {
     
    @Autowired
    private BookRepository repository;
 
    @GetMapping("/count")
    public Long findCount() {
        System.out.println(repository);
        return repository.count();
    }
}

Finalmente, creamos un BookController que se enfoca en operaciones específicas del libro, como encontrar un libro por su ID:

@RestController
public class BookController {
      
    @Autowired
    private BookRepository repository;
  
    @GetMapping("/book/{id}")
    public Book findById(@PathVariable long id) {
        System.out.println(repository);
        return repository.findById(id).get();
    }
}

Luego, iniciamos esta aplicación y realizamos GET en / count y / book / 1:

curl -X GET http://localhost:8080/count
curl -X GET http://localhost:8080/book/1

En el resultado de la aplicación, vemos que dos objetos BookRepository tienen el mismo ID de objeto:

com.baeldung.spring.patterns.singleton.BookRepository@3ea9524f
com.baeldung.spring.patterns.singleton.BookRepository@3ea9524f

Los ID de objeto de BookRepository en LibraryController y BookController son los mismos, lo que demuestra que Spring inyecta el mismo bean en ambos controladores.

Podemos usar la anotación @scope (ConfigurableBeanFactory.scope_prototype) para cambiar el alcance del bean de singleton a prototype para crear una instancia separada del bean BookRepository.

Hacer esto le indica a Spring que cree un objeto separado para cada Bean BookRepository que cree. Por lo tanto, si verificamos la ID de objeto de BookRepository en cada controlador nuevamente, encontraremos que ya no son los mismos.


3. Patrón de método de fábrica

El patrón de método de fábrica requiere que las clases de fábrica tengan métodos abstractos para crear los objetos requeridos. Por lo general, queremos crear diferentes objetos basados ​​en un contexto específico.

Por ejemplo, nuestra aplicación puede requerir objetos del vehículo. En el entorno náutico, queremos hacer barcos, pero en el entorno aeroespacial, queremos hacer aviones:

Para esto, podemos crear una implementación de fábrica para cada objeto requerido y devolver el objeto requerido del método de fábrica concreto.

3.1 Contexto de aplicación

Spring utiliza esta técnica sobre la base de su marco de inyección de dependencia (DI). Fundamentalmente, Spring trata un contenedor de frijoles como una fábrica para generar frijoles. Por lo tanto, Spring define la interfaz BeanFactory como una abstracción del contenedor Bean:

public interface BeanFactory {
 
    getBean(Class<T> requiredType);
    getBean(Class<T> requiredType, Object... args);
    getBean(String name);
 
    // ...
}

Cada método getBean se considera un método de fábrica, que devolverá un bean que coincida con las condiciones proporcionadas al método, como el tipo y el nombre del bean.

Luego, Spring extendió BeanFactory con la interfaz ApplicationContext, que introdujo otras configuraciones de aplicación. Spring utiliza esta configuración para iniciar el contenedor Bean en función de alguna configuración externa (como archivos XML o anotaciones de Java).

Luego, usando la clase ApplicationContext, como AnnotationConfigApplicationContext, podemos crear beans a través de varios métodos de fábrica heredados de la interfaz BeanFactory.

Primero, creamos una configuración de aplicación simple:

@Configuration
@ComponentScan(basePackageClasses = ApplicationConfig.class)
public class ApplicationConfig {
}

A continuación, creamos una clase simple Foo que no acepta parámetros de constructor:

@Component
public class Foo {
}

Luego cree otra barra de clase que acepte un único parámetro de constructor:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Bar {
  
    private String name;
      
    public Bar(String name) {
        this.name = name;
    }
      
    // Getter ...
}

Finalmente, creamos nuestro bean a través de AnnotationConfigApplicationContext de ApplicationContext:

@Test
public void whenGetSimpleBean_thenReturnConstructedBean() {
     
    ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
     
    Foo foo = context.getBean(Foo.class);
     
    assertNotNull(foo);
}
 
@Test
public void whenGetPrototypeBean_thenReturnConstructedBean() {
     
    String expectedName = "Some name";
    ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
     
    Bar bar = context.getBean(Bar.class, expectedName);
     
    assertNotNull(bar);
    assertThat(bar.getName(), is(expectedName));
}

Usando el método de fábrica getBean, podemos usar solo el tipo de clase y los parámetros del constructor (para Bar) para crear un bean configurado.

3.2 Colocación externa

Este modo es universal porque podemos cambiar completamente el comportamiento de la aplicación en función de la configuración externa.

Si queremos cambiar la implementación de los objetos de cableado automático en la aplicación, podemos ajustar la implementación de ApplicationContext que utilizamos.

Por ejemplo, podemos cambiar AnnotationConfigApplicationContext a una clase de configuración basada en XML, como ClassPathXmlApplicationContext:

@Test
public void givenXmlConfiguration_whenGetPrototypeBean_thenReturnConstructedBean() { 
 
    String expectedName = "Some name";
    ApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
  
    // Same test as before ...
}

4. Modo proxy

Los agentes son una herramienta conveniente en nuestro mundo digital, y a menudo los usamos fuera del software (como los agentes de red). En código, el patrón proxy es una tecnología que permite que un objeto (proxy) controle el acceso a otro objeto (tema o servicio).

4.1 Asuntos

Para crear un proxy, creamos un objeto que implementa la misma interfaz que el sujeto y contiene una referencia al sujeto.

Entonces, podemos usar agentes en lugar de agentes.

En Spring, los beans proxy se usan para controlar el acceso a los beans básicos. Veremos este método cuando usemos transacciones:

@Service
public class BookManager {
     
    @Autowired
    private BookRepository repository;
 
    @Transactional
    public Book create(String author) {
        System.out.println(repository.getClass().getName());
        return repository.create(author);
    }
}

En nuestra clase BookManager, anotamos el método de creación con la anotación @Transactional. Este comentario le indica a Spring que ejecute automáticamente nuestro método de creación. Sin un proxy, Spring no podrá controlar el acceso a nuestro bean BookRepository y garantizar su coherencia transaccional.

4.2 agente CGLib

En cambio, Spring creó un proxy que envolvió nuestro bean BookRepository y detectó nuestro bean para ejecutar automáticamente nuestro método de creación.

Cuando llamamos al método de creación BookManager #, podemos ver el resultado:

com.baeldung.patterns.proxy.BookRepository$$EnhancerBySpringCGLIB$$3dc2b55c

Por lo general, queremos ver un ID de objeto BookRepository estándar. En cambio, vimos el ID de objeto EnhancerBySpringCGLIB.

En segundo plano, Spring envuelve nuestro objeto BookRepository como un objeto EnhancerBySpringCGLIB. Spring, por lo tanto, controla el acceso al objeto BookRepository (asegurando la consistencia transaccional).

En general, Spring usa dos tipos de proxies:

CGLib Proxies – Used when proxying classes
JDK Dynamic Proxies – Used when proxying interfaces

Cuando usamos transacciones para exponer el proxy subyacente, Spring usará el proxy en cualquier situación en la que se deba controlar el acceso al bean.


5. Modo de plantilla

En muchos marcos, la mayor parte del código es código repetitivo.

Por ejemplo, al ejecutar una consulta en una base de datos, se debe completar la misma serie de pasos:

  1. Establecer una conexión
  2. Ejecutar consulta
  3. Realizar la limpieza
  4. Conexión cercana

Estos pasos son escenarios ideales para el patrón de método de plantilla.

5.1 Plantillas y devoluciones de llamada

El patrón de método de plantilla es una técnica que define los pasos necesarios para ciertas operaciones, implementa pasos de muestra y retiene pasos personalizables como abstracciones. Las subclases pueden implementar esta clase abstracta y proporcionar implementaciones concretas para los pasos que faltan.

Para consultas de bases de datos, podemos crear una plantilla:

public abstract DatabaseQuery {
 
    public void execute() {
        Connection connection = createConnection();
        executeQuery(connection);
        closeConnection(connection);
    } 
 
    protected Connection createConnection() {
        // Connect to database...
    }
 
    protected void closeConnection(Connection connection) {
        // Close connection...
    }
 
    protected abstract void executeQuery(Connection connection);
}

Además, podemos proporcionar pasos faltantes al proporcionar métodos de devolución de llamada.

El método de devolución de llamada es un método que permite al sujeto indicar al cliente que se han completado ciertas operaciones requeridas.

En algunos casos, el sujeto puede usar esta devolución de llamada para realizar operaciones, como resultados de mapeo.

Por ejemplo, en lugar de utilizar el método executeQuery, podemos proporcionar una cadena de consulta y un método de devolución de llamada para que el método execute procese los resultados.

Primero, creamos un método de devolución de llamada que acepta un objeto Resultados y lo asigna a un objeto de tipo T:

public interface ResultsMapper<T> {
    public T map(Results results);
}

Luego cambiamos nuestra clase DatabaseQuery para aprovechar esta devolución de llamada:

public abstract DatabaseQuery {
 
    public <T> T execute(String query, ResultsMapper<T> mapper) {
        Connection connection = createConnection();
        Results results = executeQuery(connection, query);
        closeConnection(connection);
        return mapper.map(results);
    }
 
    protected Results executeQuery(Connection connection, String query) {
        // Perform query...
    }
}

Este mecanismo de devolución de llamada es exactamente lo que Spring usa con la clase JdbcTemplate.

5.2 JdbcTemplate

La clase JdbcTemplate proporciona un método de consulta que acepta objetos String y ResultSetExtractor de consulta:

public class JdbcTemplate {
 
    public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
        // Execute query...
    }
 
    // Other methods...
}

ResultSetExtractor convierte el objeto ResultSet que representa el resultado de la consulta en un objeto de dominio de tipo T:

@FunctionalInterface
public interface ResultSetExtractor<T> {
    T extractData(ResultSet rs) throws SQLException, DataAccessException;
}

Al crear interfaces de devolución de llamada más específicas, Spring reduce aún más el código repetitivo.

Por ejemplo, la interfaz RowMapper se utiliza para convertir una sola fila de datos SQL en un objeto de dominio de tipo T.

public class JdbcTemplate {
 
    public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
        return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));
    }
 
    // Other methods...
}

Además de proporcionar lógica para convertir todo el objeto ResultSet (incluida la iteración sobre filas), podemos proporcionar lógica sobre cómo convertir una sola fila:

public class BookRowMapper implements RowMapper<Book> {
 
    @Override
    public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
 
        Book book = new Book();
         
        book.setId(rs.getLong("id"));
        book.setTitle(rs.getString("title"));
        book.setAuthor(rs.getString("author"));
         
        return book;
    }
}

Con este convertidor, podemos usar JdbcTemplate para consultar la base de datos y asignar cada fila de resultados:

JdbcTemplate template = // create template...
template.query("SELECT * FROM books", new BookRowMapper());

Además de la administración de la base de datos JDBC, Spring también usa las siguientes plantillas:


6. Resumen

En este tutorial, estudiamos los cuatro patrones de diseño más comunes aplicados en el marco Spring.

También exploramos cómo Spring puede usar estos patrones para proporcionar una funcionalidad rica mientras reduce la carga para los desarrolladores.


Publicado 26 artículos originales · ganado elogios 1 · vistas 9780

Supongo que te gusta

Origin blog.csdn.net/qq_31884013/article/details/105504037
Recomendado
Clasificación