Skip to content

Latest commit

ย 

History

History
356 lines (237 loc) ยท 11.1 KB

File metadata and controls

356 lines (237 loc) ยท 11.1 KB

Spring Design Pattern

  1. Singleton pattern
  2. Factory Method pattern
  3. Proxy pattern
  4. Template pattern

Singleton Pattern

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์˜ค์ง ํ•œ๊ฐœ์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์กด์žฌํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜

  • ๊ณต์œ ๋œ ์ž์›์ด๋‚˜ ๋กœ๊น…๊ฐ™์€ cross cutting ์„œ๋น„์Šค์— ์œ ์šฉํ•˜๋‹ค
  1. Singleton Bean

    • Spring์—์„œ ์‹ฑ๊ธ€ํ†ค์€ Application์ด ์•„๋‹Œ IoC ์ปจํ…Œ์ด๋„ˆ ๋‹น ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ ์ œํ•œ

  2. Autowired Singletons

    ํ•˜๋‚˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ์— ์กด์žฌํ•˜๋Š” 2๊ฐœ์ด์ƒ์˜ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๊ฐ™์€ ํƒ€์ž„์˜ ๋นˆ์„ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.

    @RestController
    public class LibraryController {
         
        @Autowired
        private BookRepository repository;
     
        @GetMapping("/count")
        public Long findCount() {
            System.out.println(repository);
            return repository.count();
        }
    }
    @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();
        }
    }
    • ์ด ๋•Œ repository ๊ฐ์ฒด์˜ ๊ฐ’์€ ๋™์ผํ•˜๋‹ค -> ๋™์ผํ•œ Bean์„ ์ฃผ์ž…ํ–ˆ์Œ

      com.baeldung.spring.patterns.singleton.BookRepository@3ea9524f
      com.baeldung.spring.patterns.singleton.BookRepository@3ea9524f
    • Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) ์„ ์‚ฌ์šฉํ•˜๋ฉด Bean์„ ํ”„๋กœํ† ํƒ€์ž…์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๊ฐœ๋ณ„ ์ธ์Šคํ„ด์Šค๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ

Factory Method Pattern

  1. Application Context

    • ์Šคํ”„๋ง์€ ์ด ๊ธฐ์ˆ ์€ DI ํ”„๋ ˆ์ž„์›Œํฌ์˜ root์—์„œ ์‚ฌ์šฉํ•จ
    • ์Šคํ”„๋ง์€ Bean์„ ์ƒ์‚ฐํ•˜๋Š” ๊ณต์žฅ์œผ๋กœ์จ Bean Container๋กœ ์ทจ๊ธ‰ํ•œ๋‹ค
    • ๊ฒฐ๋ก , BeanFactory interface๋ฅผ Bean Container์˜ ์ถ”์ƒํ™”๋กœ ์ •์˜ํ•จ
    public interface BeanFactory {
     
        getBean(Class<T> requiredType);
        getBean(Class<T> requiredType, Object... args);
        getBean(String name);
     
        // ...
    }
    • getBean ๋ฉ”์†Œ๋“œ๋Š” factory ๋ฉ”์„œ๋“œ์ž„ -> bean์˜ type๊ณผ ์ด๋ฆ„๊ฐ™์ด ๋ฉ”์†Œ๋“œ์— ์ œ๊ณต๋œ ๊ธฐ์ค€๊ณผ ์ผ์น˜ํ•˜๋Š” Bean์œผ๋กœ ๋ฆฌํ„ดํ•จ.

    • ๊ทธ ํ›„ ApplicationContext์ธํ„ฐํŽ˜์ด์Šค๋กœ BeanFactory๋ฅผ ํ™•์žฅ ํ•˜์—ฌ ์ถ”๊ฐ€์ ์ธ Application Configuration์„ ๋„์ž…ํ•œ๋‹ค. ์ด Configuartion์„ ์‚ฌ์šฉํ•˜์—ฌ XML์ด๋‚˜ JAVA ์–ด๋…ธํ…Œ์ด์…˜ ๊ฐ™์€ ์™ธ๋ถ€ configuration์„ ๊ธฐ๋ฐ˜์œผ๋กœ Bean ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹œ์ž‘ํ•œ๋‹ค.

    • ApplicationContext Using the ApplicationContext class implementations like AnnotationConfigApplicationContext, we can then create beans through the various factory methods inherited from the BeanFactory interface.

      • ๊ฐ„๋‹จํ•œ application configuration ์ƒ์„ฑ

        @Configuration
        @ComponentScan(basePackageClasses = ApplicationConfig.class)
        public class ApplicationConfig {
        }
      • Foo ๊ฐ์ฒด ์ƒ์„ฑ

        @Component
        public class Foo {
        }
      • Bar ๊ฐ์ฒด ์ƒ์„ฑ

        @Component
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        public class Bar {
          
            private String name;
              
            public Bar(String name) {
                this.name = name;
            }
              
            // Getter ...
        }
      • ๋งˆ์ง€๋ง‰์œผ๋กœ ApplicationContext์˜ AnnotationConfigApplicationContext ๊ตฌํ˜„์„ ํ†ตํ•ด ๋นˆ์„ ์ƒ์„ฑ

        @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));
        }

        getBean ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ์จ ๋‹จ์ง€ ํด๋ž˜์Šค ํƒ€์ž…๊ณผ ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ(Bar) ๋ฅผ ํ†ตํ•ด configured bean์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค

  2. External Configuration

    ์ด ํŒจํ„ด์€ ์™ธ๋ถ€ configuration ๊ธฐ๋ฐ˜์œผ๋กœ ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ–‰๋™์„ ์™„์ „ํžˆ ๋ณ€ํ™”์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ๋ณ€ํ•˜๊ธฐ ์‰ฝ๋‹ค

    ๋งŒ์•… ์šฐ๋ฆฌ๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— autowired๋œ ๊ฐ์ฒด์˜ ๊ตฌํ˜„์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ๋ฐ”๋ž€๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ApplicationContext ๊ตฌํ˜„์ฒด๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค

    ์˜ˆ๋ฅผ ๋“ค๋ฉด ์šฐ๋ฆฌ๋Š” AnnoationConfigApplicationContext๋ฅผ ClassPathXmlApplicationContext๊ฐ™์€ XML ๊ธฐ๋ฐ˜ configuration ํด๋ž˜์Šค๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

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

Proxy Pattern

the proxy pattern is a technique that allows one object โ€” the proxy โ€” to control access to another object โ€” the subject or service.

ํ”„๋ก์‹œ ํŒจํ„ด์€ ํ•œ ๊ฐœ์ฒด (ํ”„๋ก์‹œ)๊ฐ€ ๋‹ค๋ฅธ ๊ฐœ์ฒด (์ฃผ์ฒด ๋˜๋Š” ์„œ๋น„์Šค)์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ์ œ์–ด ํ•  ์ˆ˜์žˆ๊ฒŒํ•˜๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค.

  1. Transactions

    ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์ฃผ์ œ์™€ ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์ฃผ์ œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. Spring์—์„œ Bean์€ ๊ธฐ๋ณธ Bean์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ์ œ์–ดํ•˜๋„๋ก ํ”„๋ก์‹œ๋ฉ๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•  ๋•Œ ์ด ์ ‘๊ทผ ๋ฐฉ์‹์„ ๋ด…๋‹ˆ๋‹ค.

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

    @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•จ. ์ด ์–ด๋…ธํ…Œ์ด์…˜์€ ์Šคํ”„๋ง์—๊ฒŒ ์›์ž์„ฑ์žˆ๊ฒŒ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ํ•จ.

  2. CGLib Proxies

    Spring creates a proxy that wraps our BookRepository bean and instruments our bean to execute our create method atomically

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

    BookRepository object ID ์™€ EnhancerBySpringCGLIB object ID

    ์Šคํ”„๋ง์ด ์‚ฌ์šฉํ•˜๋Š” ๋‘๊ฐ€์ง€ ํ”„๋ก์‹œ

    • CGLib Proxies - Used when proxying classes
    • JDK Dynamic Proxies - Used when Proxying interfaces

Template Method Pattern

Template Method Pattern : https://www.baeldung.com/java-template-method-pattern

DB์— query๋ฅผ ์‹คํ–‰ ํ•  ๋•Œ ์—ฐ๊ฒฐ -> query -> ์‹คํ–‰ ๋งˆ๋ฌด๋ฆฌ -> ์—ฐ๊ฒฐ ๋Š๊ธฐ ์ˆœ์œผ๋กœ ์‹คํ–‰ํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ Template Method Pattern์— ์ ํ•ฉํ•˜๋‹ค

  1. Templates & Callbacks

    The template method pattern is a technique that defines the steps required for some action, implementing the boilerplate steps, and leaving the customizable steps as abstract

    ํ…œํ”Œ๋ฆฟ ๋ฐฉ๋ฒ• ํŒจํ„ด์€ ์ผ๋ถ€ ์ž‘์—…์— ํ•„์š”ํ•œ ๋‹จ๊ณ„๋ฅผ ์ •์˜ํ•˜๊ณ , ์ƒ์šฉ๊ตฌ ๋‹จ๊ณ„๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , ์‚ฌ์šฉ์ž ์ •์˜ ๊ฐ€๋Šฅํ•œ ๋‹จ๊ณ„๋ฅผ ์ถ”์ƒ์œผ๋กœ ๋‚จ๊ฒจ ๋‘๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ์„œ๋ธŒ ํด๋ž˜์Šค๋Š”์ด ์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ๋ˆ„๋ฝ ๋œ ๋‹จ๊ณ„์— ๋Œ€ํ•œ ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„์„ ์ œ๊ณต ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    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);
    }

    callbakc์œผ๋กœ ํ†ตํ•ด ๋†“์นœ ๋ถ€๋ถ„์„ ์ฑ„์šธ ์ˆ˜ ์žˆ๋‹ค. ์ฝœ๋ฐฑ ๋ฉ”์†Œ๋“œ๋Š” ๋Œ€์ƒ์ด ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์›ํ•˜๋Š” ์กฐ์น˜๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์Œ์„ ์•Œ๋ฆฌ๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.

    executeQuery ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  execute ๋ฉ”์†Œ๋“œ์— ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด ๋ฐ ์ฝœ๋ฐฑ ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    public interface ResultsMapper<T> {
        public T map(Results results);
    }
    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...
        }
    }
  2. JdbcTemplate

    JdbcTemplate ํด๋ž˜์Šค๋Š” query ์™€ ResultSetExtractor ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง

    public class JdbcTemplate {
     
        public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
            // Execute query...
        }
     
        // Other methods...
    }
    @FunctionalInterface
    public interface ResultSetExtractor<T> {
        T extractData(ResultSet rs) throws SQLException, DataAccessException;
    }

    Spring์€ ๋ณด๋‹ค ๊ตฌ์ฒด์ ์ธ ์ฝœ๋ฐฑ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ boilerplate ์ฝ”๋“œ๋ฅผ ๋”์šฑ ์ค„์ž…๋‹ˆ๋‹ค.

    @FunctionalInterface
    public interface RowMapper<T> {
        T mapRow(ResultSet rs, int rowNum) throws SQLException;
    }

    ์˜ˆ์ƒ๋˜๋Š” ResultSetExtractor์— RowMapper ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด Spring์€ RowMapperResultSetExtractor ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

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

    Instead of providing logic for converting an entire ResultSet object, including iteration over the rows, we can provide logic for how to convert a single row

    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;
        }
    }
    JdbcTemplate template = // create template...
    template.query("SELECT * FROM books", new BookRowMapper());