스프링캠프 2016 발표 - deep dive into spring boot autoconfiguration

Post on 14-Jan-2017

2.439 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

KSUG�이수홍

Deep�dive�into�Spring�Boot��

Autoconfiguration

Why�Autoconfiguration?

스프링부트�이전의�설정

기존�MVC�설정

@Configuration @EnableWebMvc public class WebAppConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(“/resource/") .addResourceLocations("/resource/**"); } @Bean public ViewResolver internalResourceViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }…

DB�설정

@EnableTransactionManagement @Configuration public class DatabaseConfig { @Bean public DataSource dataSource( @Value("${db.driver}") Class<Driver> driverClass, @Value("${db.url}") String url, @Value("${db.user}") String user, @Value("${db.password}") String password) { return new SimpleDriverDataSource( BeanUtils.instantiateClass(driverClass), url, user, password); } }…

JPA�설정

@Configuration public class JpaConfig { @Autowired private DataSource dataSource; @Bean public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource); em.setPackagesToScan("com.springcamp.test"); em.setPersistenceProvider(new HibernatePersistenceProvider()); em.setMappingResources("META-INF/orm.xml"); em.setJpaProperties(hibernateProperties()); return em; } @Bean public Properties hibernateProperties() { Properties properties = new Properties(); properties.put(DIALECT, MySQLDialect.class.getName()); properties.put(HBM2DDL_AUTO, ddlAutoProp); return properties; } ….

새로운�프로젝트�마다�반복적인�설정

스프링부트에서�어떻게�설정하는가?

기존�MVC�설정

@Configuration @EnableWebMvc public class WebAppConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(“/resource/") .addResourceLocations("/resource/**"); } @Bean public ViewResolver internalResourceViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }}

기존�MVC�설정

@Configuration @EnableWebMvc public class WebAppConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(“/resource/") .addResourceLocations("/resource/**"); } @Bean public ViewResolver internalResourceViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }}

기존�MVC�설정

@Configuration @EnableWebMvc public class WebAppConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(“/resource/") .addResourceLocations("/resource/**"); } @Bean public ViewResolver internalResourceViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }}

“/resource/""/resource/**"

ResourceHandler

기존�MVC�설정

@Configuration @EnableWebMvc public class WebAppConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(“/resource/") .addResourceLocations("/resource/**"); } @Bean public ViewResolver internalResourceViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }}

“/resource/""/resource/**"

ResourceHandler

Prefix("/WEB-INF/views/")Suffix(".jsp")

스프링�부트�MVC�설정

1. 의존성�추가 spring-boot-starter-web�

2. 환경변수�추가� spring.mvc.view.prefix:�/WEB-INF/viewsspring.mvc.view.suffix:�.jsp

기존�DB�설정

@EnableTransactionManagement @Configuration public class DatabaseConfig { @Bean public DataSource dataSource( @Value("${db.driver}") Class<Driver> driverClass, @Value("${db.url}") String url, @Value("${db.user}") String user, @Value("${db.password}") String password) { return new SimpleDriverDataSource( BeanUtils.instantiateClass(driverClass), url, user, password); } }}

기존�DB�설정

@EnableTransactionManagement @Configuration public class DatabaseConfig { @Bean public DataSource dataSource( @Value("${db.driver}") Class<Driver> driverClass, @Value("${db.url}") String url, @Value("${db.user}") String user, @Value("${db.password}") String password) { return new SimpleDriverDataSource( BeanUtils.instantiateClass(driverClass), url, user, password); } }}

기존�DB�설정

@EnableTransactionManagement @Configuration public class DatabaseConfig { @Bean public DataSource dataSource( @Value("${db.driver}") Class<Driver> driverClass, @Value("${db.url}") String url, @Value("${db.user}") String user, @Value("${db.password}") String password) { return new SimpleDriverDataSource( BeanUtils.instantiateClass(driverClass), url, user, password); } }}

"${db.driver}" "${db.url}" "${db.user}" "${db.password}"

기존�DB�설정

@EnableTransactionManagement @Configuration public class DatabaseConfig { @Bean public DataSource dataSource( @Value("${db.driver}") Class<Driver> driverClass, @Value("${db.url}") String url, @Value("${db.user}") String user, @Value("${db.password}") String password) { return new SimpleDriverDataSource( BeanUtils.instantiateClass(driverClass), url, user, password); } }}

"${db.driver}" "${db.url}" "${db.user}" "${db.password}"SimpleDriverDataSource

스프링�부트�DB�설정

1. 의존성�추가 spring-boot-starter-jdbc�

2. 환경변수�추가spring.datasource.url:�접속�url��� spring.datasource.driverClassName:�드라이브spring.datasource.username:�saspring.datasource.password:

JPA�설정

@Configuration public class JpaConfig { @Autowired private DataSource dataSource; @Bean public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource); em.setPackagesToScan("com.springcamp.test"); em.setPersistenceProvider(new HibernatePersistenceProvider()); em.setMappingResources("META-INF/orm.xml"); em.setJpaProperties(hibernateProperties()); return em; } @Bean public Properties hibernateProperties() { Properties properties = new Properties(); properties.put(DIALECT, MySQLDialect.class.getName()); properties.put(HBM2DDL_AUTO, ddlAutoProp); return properties; } //..}

JPA�설정

@Configuration public class JpaConfig { @Autowired private DataSource dataSource; @Bean public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource); em.setPackagesToScan("com.springcamp.test"); em.setPersistenceProvider(new HibernatePersistenceProvider()); em.setMappingResources("META-INF/orm.xml"); em.setJpaProperties(hibernateProperties()); return em; } @Bean public Properties hibernateProperties() { Properties properties = new Properties(); properties.put(DIALECT, MySQLDialect.class.getName()); properties.put(HBM2DDL_AUTO, ddlAutoProp); return properties; } //..}

JPA�설정

@Configuration public class JpaConfig { @Autowired private DataSource dataSource; @Bean public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource); em.setPackagesToScan("com.springcamp.test"); em.setPersistenceProvider(new HibernatePersistenceProvider()); em.setMappingResources("META-INF/orm.xml"); em.setJpaProperties(hibernateProperties()); return em; } @Bean public Properties hibernateProperties() { Properties properties = new Properties(); properties.put(DIALECT, MySQLDialect.class.getName()); properties.put(HBM2DDL_AUTO, ddlAutoProp); return properties; } //..}

properties.put(DIALECT, MySQLDialect.class.getName()); properties.put(HBM2DDL_AUTO, ddlAutoProp);

스프링�부트�JPA�설정

1. 의존성�추가 spring-boot-starter-data-jpa�

2. 환경변수�추가spring.jpa.show-sql:�truespring.jpa.hibernate.ddl-auto:�create-drop

반복적인�설정�부분�제거��의존성과�환경변수�추가

그리고�소스

@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

설정�부분�해결?!

설정�클래스는�다�어디�갔을까?

그�내부를�들여다�보자

Into�the�스프링�부트

@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

Into�the�스프링�부트

@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

Into�the�스프링�부트

@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

스프링�컨테이너를�실행

Into�the�스프링�부트

@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

스프링�컨테이너를�실행

Into�the�스프링�부트

@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

스프링�컨테이너를�실행부트�단순함을�위한�어노테이션�

Into�the�@SpringBootApplication

@Configuration@EnableAutoConfiguration@ComponentScanpublic @interface SpringBootApplication { … }

Into�the�@SpringBootApplication

@Configuration@EnableAutoConfiguration@ComponentScanpublic @interface SpringBootApplication { … }

Into�the�@SpringBootApplication

@Configuration@EnableAutoConfiguration@ComponentScanpublic @interface SpringBootApplication { … } @EnableAutoConfiguration�

스프링부트�마법의�실마리

About�@EnableAutoConfiguration

•특정�기준의�설정클래스를�로드�

•@Enable*�모듈화된�설정의�일종

• JavaConfig에서�모듈화된�설정시�사용�(�스프링�3.1�부터�지원�)��

• 기존�XML�커스텀태그(<mvc:*/>,<context:*/>�…)와�대응�

• 쉽게�자신의�설정�모듈을�작성�가능 (�@Import�어노테이션�사용�)

@Enable*�어노테이션이란?

@EnableWebMvc @EnableAspectJAutoProxy … @Configuration public class ApplicationConfiguration { … }

Into�the�@EnableAutoConfiguration�

@AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { … }

Into�the�@EnableAutoConfiguration�

@AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { … }

Into�the�@EnableAutoConfiguration�

@AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { … }

스프링부트�자동�설정�기능담당

Into�the�EnableAutoConfigurationImportSelector�1

public class EnableAutoConfigurationImportSelector implements DeferredImportSelector, … { @Override public String[] selectImports(AnnotationMetadata metadata) { … }}

Into�the�EnableAutoConfigurationImportSelector�1

public class EnableAutoConfigurationImportSelector implements DeferredImportSelector, … { @Override public String[] selectImports(AnnotationMetadata metadata) { … }}

Into�the�EnableAutoConfigurationImportSelector�1

public class EnableAutoConfigurationImportSelector implements DeferredImportSelector, … { @Override public String[] selectImports(AnnotationMetadata metadata) { … }}

설정�클래스(@Configuration)�리스트�받아서�활성화

Into�the�EnableAutoConfigurationImportSelector�1

public class EnableAutoConfigurationImportSelector implements DeferredImportSelector, … { @Override public String[] selectImports(AnnotationMetadata metadata) { … }}

설정�클래스(@Configuration)�리스트�받아서�활성화

Into�the�EnableAutoConfigurationImportSelector�1

public class EnableAutoConfigurationImportSelector implements DeferredImportSelector, … { @Override public String[] selectImports(AnnotationMetadata metadata) { … }}

설정�클래스(@Configuration)�리스트�받아서�활성화

설정�클래스�리스트�(패키지명�포함�클래스명)

Into�the�EnableAutoConfigurationImportSelector�2

…protected List<String> getCandidateConfigurations(…) {

return SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(),

getBeanClassLoader()); }

…}

Into�the�EnableAutoConfigurationImportSelector�2

…protected List<String> getCandidateConfigurations(…) {

return SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(),

getBeanClassLoader()); }

…}

Into�the�EnableAutoConfigurationImportSelector�2

…protected List<String> getCandidateConfigurations(…) {

return SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(),

getBeanClassLoader()); }

…}Classpath의�모든�라이브러리의�“META-INF/spring.factories“�위치의�파일에서�설정파일�리스트�읽어온다.

Into�the�EnableAutoConfigurationImportSelector�2

Into�the�META-INF/spring.factories�

•Key=Value�형태로�설정클래스�리스트�기록�

•스프링부트의�기본�설정�정보는�아래의�라이브러리�안에�포함o.s.boot:spring-boot-autoconfigure:x.x.x.jar

# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ … org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ … org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ …

중간�정리

• 스프링부트의�설정의�시작@EnableAutoConfiguration�

• 설정리스트의�보관소�“META-INF/spring.factories”�

• 모든�설정클래스를�로딩!?

스프링부트는�지정된�모든�설정클래스가��

스프링컨테이너에�올라가는가?

당연히�NO!�

@Conditional

@Conditional은��어노테이션통한�조건적으로�Bean을�등록

@Conditional�

•스프링�4에서�도입된�어노테이션�

•조건부로�Bean을�(스프링컨테이너)에�등록

@Conditional�

@Bean @Conditional(PropertiesCondition.class) public CommandLineRunner propertiesConditional() { return (args) -> { System.out.println("test.hasValue 환경변수가 있으면 보입니다"); }; }

@Conditional�

@Bean @Conditional(PropertiesCondition.class) public CommandLineRunner propertiesConditional() { return (args) -> { System.out.println("test.hasValue 환경변수가 있으면 보입니다"); }; }

@Conditional�

@Bean @Conditional(PropertiesCondition.class) public CommandLineRunner propertiesConditional() { return (args) -> { System.out.println("test.hasValue 환경변수가 있으면 보입니다"); }; }

@Conditional�

@Bean @Conditional(PropertiesCondition.class) public CommandLineRunner propertiesConditional() { return (args) -> { System.out.println("test.hasValue 환경변수가 있으면 보입니다"); }; }

class PropertiesCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getEnvironment() .containsProperty("test.hasValue"); } }

@Conditional�

@Bean @Conditional(PropertiesCondition.class) public CommandLineRunner propertiesConditional() { return (args) -> { System.out.println("test.hasValue 환경변수가 있으면 보입니다"); }; }

class PropertiesCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getEnvironment() .containsProperty("test.hasValue"); } }

@Conditional�

@Bean @Conditional(PropertiesCondition.class) public CommandLineRunner propertiesConditional() { return (args) -> { System.out.println("test.hasValue 환경변수가 있으면 보입니다"); }; }

class PropertiesCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getEnvironment() .containsProperty("test.hasValue"); } }

true이면�Bean이�등록

@Profile�

@Profile

@Profile("dev") @Configuration class HelloProfileDevConfig { @Bean public HelloWorld helloDevWorld() { return new HelloWorld("dev"); } } @Profile("test") @Configuration class HelloProfileTestConfig { @Bean public HelloWorld helloTestWorld() { return new HelloWorld("test"); } }

@Profile

@Profile("dev") @Configuration class HelloProfileDevConfig { @Bean public HelloWorld helloDevWorld() { return new HelloWorld("dev"); } } @Profile("test") @Configuration class HelloProfileTestConfig { @Bean public HelloWorld helloTestWorld() { return new HelloWorld("test"); } }

@Profile

@Profile("dev") @Configuration class HelloProfileDevConfig { @Bean public HelloWorld helloDevWorld() { return new HelloWorld("dev"); } } @Profile("test") @Configuration class HelloProfileTestConfig { @Bean public HelloWorld helloTestWorld() { return new HelloWorld("test"); } }

@Profile("dev")

@Profile

@Profile("dev") @Configuration class HelloProfileDevConfig { @Bean public HelloWorld helloDevWorld() { return new HelloWorld("dev"); } } @Profile("test") @Configuration class HelloProfileTestConfig { @Bean public HelloWorld helloTestWorld() { return new HelloWorld("test"); } }

@Profile("test")

@Profile("dev")

Into�@Profile

Into�@Profile

@Conditional(ProfileCondition.class) public @interface Profile { String[] value();

}

Into�@Profile

@Conditional(ProfileCondition.class) public @interface Profile { String[] value();

}

Into�@Profile

@Conditional(ProfileCondition.class) public @interface Profile { String[] value();

}@Conditional�를�통해�Profile이�구현

@Conditional�예제

@Conditional�확장�1

@ConditionalOnClass

@ConditionalOnProperties

@ConditionalOnBean @ConditionalOnMissingBean

@ConditionalOnMissingClass

@ConditionalOnResources

@Conditional�확장�1

@ConditionalOnClass

@ConditionalOnProperties

@ConditionalOnBean @ConditionalOnMissingBean

@ConditionalOnMissingClass

@ConditionalOnResources

스프링�컨테이너의�Bean�존재유무

@Conditional�확장�1

@ConditionalOnClass

@ConditionalOnProperties

@ConditionalOnBean @ConditionalOnMissingBean

@ConditionalOnMissingClass

@ConditionalOnResources

스프링�컨테이너의�Bean�존재유무

classpath에서�클래스의�존재유무

@Conditional�확장�1

@ConditionalOnClass

@ConditionalOnProperties

@ConditionalOnBean @ConditionalOnMissingBean

@ConditionalOnMissingClass

@ConditionalOnResources

스프링�컨테이너의�Bean�존재유무

classpath에서�클래스의�존재유무

환경�변수의�유무

@Conditional�확장�1

@ConditionalOnClass

@ConditionalOnProperties

@ConditionalOnBean @ConditionalOnMissingBean

@ConditionalOnMissingClass

@ConditionalOnResources

스프링�컨테이너의�Bean�존재유무

classpath에서�클래스의�존재유무

환경�변수의�유무 Resources�존재�

@Conditional�확장�2

@ConditionalOnJava

@OnJndiCondition

@ConditionalOnWebApplication

@ConditionalOnExpression

@ConditionalOnNotWebApplication

@Conditional�확장�2

@ConditionalOnJava

@OnJndiCondition

@ConditionalOnWebApplication

@ConditionalOnExpression

현재�프로젝트가�웹애플리케이션인지�여부

@ConditionalOnNotWebApplication

@Conditional�확장�2

@ConditionalOnJava

@OnJndiCondition

@ConditionalOnWebApplication

@ConditionalOnExpression

현재�프로젝트가�웹애플리케이션인지�여부

JAVA�버전�종류�선택

@ConditionalOnNotWebApplication

@Conditional�확장�2

@ConditionalOnJava

@OnJndiCondition

@ConditionalOnWebApplication

@ConditionalOnExpression

현재�프로젝트가�웹애플리케이션인지�여부

JAVA�버전�종류�선택

@ConditionalOnNotWebApplication

SPEL의�true�false�여부

@Conditional�확장�2

@ConditionalOnJava

@OnJndiCondition

@ConditionalOnWebApplication

@ConditionalOnExpression

현재�프로젝트가�웹애플리케이션인지�여부

JAVA�버전�종류�선택

JNDI�lookup�가능�여부

@ConditionalOnNotWebApplication

SPEL의�true�false�여부

스프링부트는��@Conditional*을�통한�Autoconfiguration�

구현

스프링부트�설정

MVC�설정

Security�설정

JDBC�설정

JPA�설정

AOP�설정

스프링부트�설정

MVC�설정

Security�설정

JDBC�설정

JPA�설정

AOP�설정

서블릿�인터페이스�있는가?�이미�MVC�설정하는게�있는가?

스프링부트�설정

MVC�설정

Security�설정

JDBC�설정

JPA�설정

AOP�설정

서블릿�인터페이스�있는가?�이미�MVC�설정하는게�있는가?

스프링�시큐리티�라이브러리가�있는가?�등등

스프링부트�설정

MVC�설정

Security�설정

JDBC�설정

JPA�설정

AOP�설정

서블릿�인터페이스�있는가?�이미�MVC�설정하는게�있는가?

스프링�시큐리티�라이브러리가�있는가?�등등

DataSource�객체가�스프링�컨테이너에�존재하는가?�등

스프링부트�설정

MVC�설정

Security�설정

JDBC�설정

JPA�설정

AOP�설정

서블릿�인터페이스�있는가?�이미�MVC�설정하는게�있는가?

스프링�시큐리티�라이브러리가�있는가?�등등

DataSource�객체가�스프링�컨테이너에�존재하는가?�등

JPA,�하이버네이트�라이브리가�있는가?�DataSource�설정이�이미되었

는가?

스프링부트�설정

MVC�설정

Security�설정

JDBC�설정

JPA�설정

AOP�설정

서블릿�인터페이스�있는가?�이미�MVC�설정하는게�있는가?

스프링�시큐리티�라이브러리가�있는가?�등등

DataSource�객체가�스프링�컨테이너에�존재하는가?�등

JPA,�하이버네이트�라이브리가�있는가?�DataSource�설정이�이미되었

는가?

스프링�AOP,�AspectJ�라이브러리가�존재하는가?�

즉�spring.factories�의��설정�클래스는��

조건(@Conditional)에�따라�컨테이너에�등록

Autoconfiguration�지원�기술

• CoreSpring�(Security,�AOP,�Session,�Cache,�DevTools�…)�Atomikos,�…�

• WebSpring�(MVC,�Websocket,�Rest�Docs),�WS,�Jersey,�…�

• Template�Engines Freemarker,�Velocity,�Thymeleaf,�Mustache,�…�

• SQL�JPA,�JOOQ,�JDBC,�H2,�HSQLDB,�Derby,�MySQL,�PostreSQL�

• NoSQL,�Cloud,�Social,�I/O,�Ops,�…

커스텀�모듈�만들기�(소스)

감사합니다!

top related