- Singleton pattern
- Factory Method pattern
- Proxy pattern
- Template pattern
์ ํ๋ฆฌ์ผ์ด์ ์ ์ค์ง ํ๊ฐ์ ์ธ์คํด์ค๊ฐ ์กด์ฌํ๋ ๋ฉ์ปค๋์ฆ
- ๊ณต์ ๋ ์์์ด๋ ๋ก๊น ๊ฐ์ cross cutting ์๋น์ค์ ์ ์ฉํ๋ค
-
Singleton Bean
- Spring์์ ์ฑ๊ธํค์ Application์ด ์๋ IoC ์ปจํ ์ด๋ ๋น ํ๋์ ๊ฐ์ฒด๋ก ์ ํ
-
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์ ํ๋กํ ํ์ ์ผ๋ก ๋ณ๊ฒฝํ์ฌ ๊ฐ๋ณ ์ธ์คํด์ค๋ก ์์ฑํ ์ ์๊ฒ ํจ
-
-
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์ ์์ฑํ ์ ์๋ค
-
-
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 ... }
the proxy pattern is a technique that allows one object โ the proxy โ to control access to another object โ the subject or service.
ํ๋ก์ ํจํด์ ํ ๊ฐ์ฒด (ํ๋ก์)๊ฐ ๋ค๋ฅธ ๊ฐ์ฒด (์ฃผ์ฒด ๋๋ ์๋น์ค)์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ์ด ํ ์์๊ฒํ๋ ๊ธฐ์ ์ ๋๋ค.
-
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์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํจ. ์ด ์ด๋ ธํ ์ด์ ์ ์คํ๋ง์๊ฒ ์์์ฑ์๊ฒ ๋ฉ์๋๋ฅผ ์คํํ๋๋ก ํจ. -
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$$3dc2b55cBookRepository object ID ์ EnhancerBySpringCGLIB object ID
์คํ๋ง์ด ์ฌ์ฉํ๋ ๋๊ฐ์ง ํ๋ก์
- CGLib Proxies - Used when proxying classes
- JDK Dynamic Proxies - Used when Proxying interfaces
Template Method Pattern : https://www.baeldung.com/java-template-method-pattern
DB์ query๋ฅผ ์คํ ํ ๋ ์ฐ๊ฒฐ -> query -> ์คํ ๋ง๋ฌด๋ฆฌ -> ์ฐ๊ฒฐ ๋๊ธฐ ์์ผ๋ก ์คํํ๋ค. ์ด๋ฌํ ์๋๋ฆฌ์ค๊ฐ Template Method Pattern์ ์ ํฉํ๋ค
-
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... } }
-
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());





