Spring에서의 복잡하고 방대산 설정에 대해 부담을 느껴 Spring Boot가 만들어졌다.
SpringBoot는 경쟁관계에 있는 다른 프레임워크처럼 커맨드 도구를 제공하고, tomcat이나 jetty 같은 내장 서버를 통해 복잡한 설정과 실행을 간소화했다.
==> Spring을 처음 사용하는 개발자도 애플리케이션 관련 설정을 쉽게 처리하고 관리함으로써 개발에 더 집중할 수 있게 되었다.
- 라이브러리 관리 자동화
- Maven기능에 더해
스타터(Starter)
라는 것을 이용하여 특정 기능에 필요한 라이브러리 의존성을 더욱 간단히 처리할 수 있다.
또한 라이브러리 버전도 자동으로 관리된다.
- 설정의 자동화
- 프로젝트에 추가된 라이브러리를 기반으로, 실행에 필요한 환경을 자동으로 설정해준다.
- 테스트 환경과 내장 tomcat
- Junit을 비롯한 테스트 관련 라이브러리들이 기본적으로 내장되어 있으며, tomcat서버를 내장하고 있어서 단지 main메서드를 실행하는 방식으로 서버를 구동하기 때문에 빠르게 실행 결과를 확인할 수 있다.
- 독립적으로 실행 가능한 JAR.
- 애플리케이션을 개발하고 테스트까지 마쳤으면
실제 운영 서버에 배포하기 위해서는packaing
을 해야한다.
SpringBoot에서는 독립적으로 실행 가능한 애플리케이션을 빠르게 개발하는 것을 목표로 하기 때문에 웹 애플리케이션도 WAR가 아닌 JAR파일로 패키징하여 사용할 수 있다.
- 정리
-- 단독 실행이 가능한 스프링 어플리케이션 생성
-- 내장형 Tomcat, Jetty 또는 Undertow를 지원(war 배포시 불필요)
-- 기본으로 설정되어 있는starter
컴포넌트들을 이용하여 쉽게 환경설정(dependency, build 등) 가능
-- Library 자동 인식을 통한 환경 구성 지원
-- 상용화 수준의 통계(metrics), 상태 점검(health check)및 외부 설정 제공
-- 설정을 위한XML
코드 대신에properties
및yaml
파일 사용
Spring과의 차이점:
1. pom.xml의 dependency 관리 추가가 더 편하다.
2. application.properties, yml이용 프로젝트 구동환경 설정
jar: jar파일로 묶어 tomcat없이 웹 서비스가 가능하다.
war: jar파일을 war파일로 묶어 tomcat을 사용해야 한다.
Spring Boot Project를 생성할 때 Version 설정에서
ver.2.xx
는 jdk 1.8 지원.
ver.3.xx
은 jdk 17지원.
com.example의 하위폴더에 파일을 만들어야 @ComponentScan
이 인식한다.
@configuration
: bean생성하는 xml을 읽어올 수 있는 기능을 한다.
src/main/resources에 커스텀할 파일을 넣어준다.
applcation.properties
설정
spring.banner.location=banner.txt
spring.banner.charset=utf-8
#spring.banner.image.location=banner.png
같은 서브 패키지인 경우 자동 scan한다. 특별한 설정은 필요 없다.
(SpringBootApplication
에서 Component-scan
을 하지 않아도 된다는 의미)
❗다만 Service 클래스에 Component생성은 해줘야한다.
(빈을 해당 클래스에서 생성해야 사용할 수 있기 때문이다.)
다른 패키지인 경우 명시적 등록한다.
@SpringBootApplication
의 빈에서 @Configuration
빈을 @Import
로 등록
com.example.person.JavaConfig.java
package com.example.person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 빈생성 .xml 역할을 한다.
public class JavaConfig {
@Bean("xxx")
public Person getPerson() {
return new Person("홍길동", 20);
}
//<bean id="getPerson" class="com.example.person.Person">
// <constructargs name="name" value="홍길동"/>
// <constructargs name="age" value="20"/>
//</bean> 과 동일하다.
}
com.example.SpringBootApplication.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.example.person.Person;
@SpringBootApplication
public class Boot02logback3LogApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Boot02logback3LogApplication.class, args);
Person p = ctx.getBean("xxx", Person.class);
System.out.println(p);
}
}
다른 패키지인 경우 명시적 등록한다.
@SpringBootApplication
의 빈에서 @Configuration
빈을 @Import
로 등록
com.example.SpringBootApplication.java
@SpringBootApplication
@Import(value=JavaConfig.class)
public class Boot04Bean01OtherPackageExplicitApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Boot04Bean01OtherPackageExplicitApplication.class, args);
DBService service = ctx.getBean("myService", DBService.class);
System.out.println(service.getList());
}
}
com.config.JavaConfig.java
@Configuration
public class JavaConfig {
@Bean("myService")
public DBService service() {
return new DBService();
}
}
com.service.DBService
public class DBService {
public List<String> getList(){
return Arrays.asList("홍길동", "이순신");
}
}
다른 패키지인 경우 scan한다.
@SpringBootApplication
의 빈에서
scanBasePackages = "com.*"
속성 이용 or@ComponentScan("com.*")
이용.
SpringBootApplication.java
//@SpringBootApplication(scanBasePackages = "com.*")
@SpringBootApplication
@ComponentScan("com.*")
public class Boot04Bean01OtherPackage2ExplicitApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Boot04Bean01OtherPackage2ExplicitApplication.class, args);
DBService service = ctx.getBean("myService",DBService.class);
System.out.println(service.getList());
}
}
Spring framework가 지원하는 핵심 기능 중 하나이다.
객체 사이의 의존 관계가 객체 자신이 아닌 외부에 의해서 결정된다는 개념이다.
IoC컨테이너는 어떤 객체(A)가 필요로 하는 의존관계에 있는 다른 객체(B)를 직접 생성하여 어떤 객체(A)로 주입(설정)해주는 역할을 담당한다.
이때 주입해주는 것을 의존성 주입(Dependency Injection:DI) 이라고 부른다.
Component-scan을 사용하지 않고 JavaConfig파일에서 명시적 등록하기.
DAO.java
public class DAO {
public List<String> list(){
return Arrays.asList("홍길동","이순신");
}
}
DBService.java
public class DBService {
private DAO dao;
public DBService(DAO dao) {
super();
this.dao = dao;
}
public List<String> list() {
return dao.list();
}
}
JavaConfiguration.java
@Configuration
public class configuration {
@Bean
public DAO dao() {
return new DAO();
}
@Bean
public DBService service() {
return new DBService(dao());
}
}
SpringBootApplication.java
@SpringBootApplication
public class Boot05DiConstructor2MethodParameterApplication {
public static void main(String[] args) {
ApplicationContext ctx =
SpringApplication.run(Boot05DiConstructor2MethodParameterApplication.class, args);
DBService service = ctx.getBean("service",DBService.class);
//Bean alias를 등록하지 않으면 자동으로 함수 이름이 alias
System.out.println(service.list());
}
}
DBService.java
public class DBService {
private DAO dao;
public DBService() {
super();
}
public DAO getDao() {
return dao;
}
public void setDao(DAO dao) {
this.dao = dao;
}
public List<String> list() {
return dao.list();
}
}
configuration.java
@Configuration
public class configuration {
@Bean
public DAO dao() {
return new DAO();
}
@Bean
public DBService service() {
DBService service = new DBService();
service.setDao(dao()); //set으로 dao생성자 주입
return service;
}
}
SpringBootApplication.java와 DAO.java는 위와 동일하다.
@Configuration
public class configuration {
@Bean("service")
public DBService service(OracleDAO dao) { //바로 받을 수 있다.
DBService service = new DBService();
service.setDao(dao);
return service;
}
}
기본적으로 컨테이너 한 개의 빈 객체를 생성하여 재사용된다.(per Container)
Bean의 Scope를 설정할 수 있는 방법: <bean>
태그의 scope
속성을 사용한다.
- singleton: 컨테이너 한 개의 빈 객체만 생성한다. (기본값)
- prototype: 빈을 요청할 때 마다 빈 객체를 생성한다.
JavaConfig.java
@Configuration
public class configuration {
@Bean("dao")
@Scope("prototype") //스코프가 붙으면 싱글톤 주소로 생성.
public OracleDAO dao() {
return new OracleDAO();
}
@Bean("service1")
public DBService service1(OracleDAO dao) {
DBService service = new DBService(dao());
return service;
}
@Bean("service2")
public DBService service2(OracleDAO dao) {
DBService service = new DBService(dao());
return service;
}
}
SpringBootApplication.java
@SpringBootApplication
public class Boot05Di2Setter2MethodParameterApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Boot05Di2Setter2MethodParameterApplication.class, args);
DBService service = ctx.getBean("service1",DBService.class);
DBService service2 = ctx.getBean("service2",DBService.class);
System.out.println(service.getDao().hashCode());
System.out.println(service2.getDao().hashCode());
System.out.println(service.getDao()==service2.getDao());
//dao가 singletone으로 생성되었기 때문에 서로 다른 주소이다.
}
}
결과
<list>
: java.util.List or 배열<set>
: java.util.set<map>
: java.util.Map<props>
: java.util.Properties
MysqlDao.java
와 OracleDao.java
를 interface인 DBDao.java
에 오버라이딩.
DBMySQLDAO.java
public class DBMySQLDAO implements DBDAO{
@Override
public List<String> list(){
return Arrays.asList("MySQL 홍길동","MySQL 이순신");
}
}
DBOracleDAO.java
public class DBOracleDAO implements DBDAO{
@Override
public List<String> list(){
return Arrays.asList("Oracle 홍길동","Oracle 이순신");
}
}
DBDAO.java
public interface DBDAO {
public abstract List<String> list();
}
DBService.java
List<DBDAO>
를 멤버변수로 받아 사용한다.
public class DBService {
private List<DBDAO> list;
public DBService(List<DBDAO> list) {
super();
this.list = list;
}
public void setList(List<DBDAO> list) {
this.list = list;
}
public List<DBDAO> getList(){
return list;
}
}
configuration.java
@Configuration
public class configuration {
@Bean
public DBOracleDAO dao() {
return new DBOracleDAO();
}
@Bean
public DBMySQLDAO dao2() {
return new DBMySQLDAO();
}
@Bean("myService")
public DBService service(List<DBDAO> list) {
return new DBService(list);
}
}
SpringBootApplication.java
@SpringBootApplication
public class Boot05Di2Setter2MethodParameterApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Boot05Di2Setter2MethodParameterApplication.class, args);
DBService service = ctx.getBean("myService",DBService.class);
System.out.println(service.getList().get(0).list());
System.out.println(service.getList().get(1).list());
}
}
List<DBDAO>
가 아닌 그냥 DBDAO라면?configuration.java
@Configuration
public class configuration {
// @Bean
// public OracleDAO dao() {
// return new OracleDAO();
// }
@Bean
public DBMySQLDAO dao2() {
return new DBMySQLDAO();
}
@Bean("myService")
public DBService service(DBDAO dao) {
return new DBService(dao);
}
}
@Bean
을 생성할 때 DBDAO는 OracleDAO나 MySQlDAO 중 하나밖에 받지 못하기 때문에 하나만 생성할 수 있다.
configuration에서 Bytype 또는 생성자 활용이 아닌 Autowired로 생성하기.
configuration.java
@Configuration
public class configuration {
@Bean
public OracleDAO dao() {
return new OracleDAO();
}
@Bean("myService")
public DBService service() {
return new DBService();
}
}
DBService
public class DBService {
@Autowired
private DBDAO DBDAO;
public DBService() {
super();
// TODO Auto-generated constructor stub
}
public DBService(com.example.dao.DBDAO dBDAO) {
super();
DBDAO = dBDAO;
}
public List<String> getList() {
return DBDAO.list();
}
}
@Autowired와 같이 사용한다.
같은 타입의 빈이 여러 개 있는 경우에 예외가 발생하는데, 이떄 @Qualifier를 통해 특정 빈을 사용하도록 명시적으로 설정 가능하게 한다.
configuration.java
@Configuration
public class configuration {
@Bean("xxx")
public DBOracleDAO dao() {
return new DBOracleDAO();
}
@Bean("yyy")
public DBMySQLDAO dao2() {
return new DBMySQLDAO();
}
}
DBService.java
//멤버변수 @Qualifier 설정
@Autowired
@Qualifier("yyy")
private DBDAO DBDAO;
OracleDAO.java 또는 MySQLDAO.java 중 사용할 DAO에 @Component(== @Repository)
설정.
DBDAO.java
public interface DBDAO {
public abstract List<String> list();
}
DBOracleDAO.java
@Repository
public class DBOracleDAO implements DBDAO{
@Override
public List<String> list(){
return Arrays.asList("Oracle 홍길동","Oracle 이순신");
}
}
DBService.java
//alise 설정
@Service("myService")
public class DBService {
@Autowired
private DBDAO DBDAO;
public DBService() {
super();
}
public DBService(com.example.dao.DBDAO dBDAO) {
super();
DBDAO = dBDAO;
}
public DBDAO getDBDAO() {
return DBDAO;
}
public void setDBDAO(DBDAO dBDAO) {
DBDAO = dBDAO;
}
public List<String> getList() {
return DBDAO.list();
}
}
@Autowired
는 결국 명칭 그대로 자동주입이다.
Configration을 없애고, Component-scan과 @Autowired를 사용한다.
DBDao.java
public interface DBDao {
public abstract List<String> list();
}
DBOracleDAO.java
@Repository
public class DBOracleDAO implements DBDao{
@Override
public List<String> list() {
return Arrays.asList("oracle 홍길동","oracle 이순신");
}
}
DBService.java
public interface DBSerivce {
public List<String> list();
}
DBOracleService
@Service("myService")
public class DBOracleService implements DBSerivce{
//@Autowired
private DBDao dao;
public DBOracleService(DBDao dao) {
super();
this.dao = dao;
}
@Override
public List<String> list() {
return dao.list();
}
}
SpringBootApplication.java
@SpringBootApplication
//@Component-scan 생략가능
public class Boot06RecommendPakageStructureApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Boot06RecommendPakageStructureApplication.class, args);
DBSerivce service = ctx.getBean("myService",DBSerivce.class);
System.out.println(service.list());
}
}
DBService.java
public class DBSerivce{
public void xxx() {
System.out.println("initMethod.xxx");
}
public void yyy() {
System.out.println("destoryMethod.xxx");
}
public List<String> list(){
return Arrays.asList("홍길동", "이순신");
}
}
JavaConfig.java
@Configuration
public class JavaConfig {
@Bean(value="myService", initMethod = "xxx", destroyMethod = "yyy")
public DBSerivce service() {
return new DBSerivce();
}
}
DBService.java
public class DBSerivce implements InitializingBean, DisposableBean{
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean.destroy=================");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean.afterPropertiesSet==============");
}
public List<String> list(){
return Arrays.asList("홍길동", "이순신");
}
}
JavaConfig.java
@Configuration
public class JavaConfig {
@Bean(value="myService")
public DBSerivce service() {
return new DBSerivce();
}
}
DBService.java
public class DBSerivce{
@PostConstruct
public void init() {
System.out.println("@PostConstruct.init==========");
}
@PreDestroy
public void dest() {
System.out.println("@PreDestroy.dest==========");
}
public List<String> list(){
return Arrays.asList("홍길동", "이순신");
}
}
JavaConfig.java
@Configuration
public class JavaConfig {
@Bean(value="myService")
public DBSerivce service() {
return new DBSerivce();
}
}
@Value는 특정 값을 주입하기 위한 용도.
주로 외부의 리소스나 환경정보 설정값을 사용하는 경우이다.
이를 .properties에서 값을 가져와 사용할 것이다.
application.properties
user.username=홍길동
user.age=20
User.java
public class User {
@Value("${user.username}")
String username;
@Value("${user.age}")
int age; //username과 age에 값 주입
@Override
public String toString() {
return "User [username="+username+"] [age="+age+"]";
}
}
JavaConfig.java
bean 생성
@Configuration
public class JavaConfig {
@Bean
public User user() {
return new User();
}
}
SpringBootApplication.java
@SpringBootApplication
public class Boot08externalizeConfig1ValueApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Boot08externalizeConfig1ValueApplication.class, args);
User user = ctx.getBean("user",User.class);
System.out.println(user.toString());
}
}
User.java
public class User {
String username;
int age;
@Override
public String toString() {
return "User [username="+username+"] [age="+age+"]";
}
public User(String username, int age) {
super();
this.username = username;
this.age = age;
}
}
JavaConfig.java
@Configuration
public class JavaConfig {
@Autowired
Environment env;
@Bean
public User user() {
return new User(env.getProperty("user.username"), Integer.parseInt(env.getProperty("user.age")));
}
}
User.java는 위의 Environment와 동일.
MyConfigurationProperties.java
@ConfigurationProperties(prefix = "user")
public class MyConfigurationProperties {
private String username;
private int age;
public MyConfigurationProperties() {
super();
// TODO Auto-generated constructor stub
}
public MyConfigurationProperties(String username, int age) {
super();
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "MyConfigurationProperties [username=" + username + ", age=" + age + "]";
}
}
JavaConfig.java
@Configuration
public class JavaConfig {
@Autowired
MyConfigurationProperties p;
@Bean
public User user() {
return new User(p.getUsername(), p.getAge());
}
}
SpringBootAppliaction.java
메인에 @EnableConfigurationProperties(MyConfigurationPropertis.class)를 넣어줘야 제대로 동작한다.
@SpringBootApplication
@EnableConfigurationProperties(MyConfigurationProperties.class)
public class Boot08externalizeConfig1ValueApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Boot08externalizeConfig1ValueApplication.class, args);
User user = ctx.getBean("myUser",User.class);
System.out.println(user.toString());
}
}
application.properties에 작성한 텍스트를 jdbc.properties
파일을 만들어서 옮겨준다.
user.username=\uD64D\uAE38\uB3D9
user.age=20
JavaConfig.java
@Property는 Spring의 placeholder와 같다.
@Configuration
@PropertySource(value = "classpath:jdbc.properties")
public class JavaConfig {
@Bean
public User user() {
return new User();
}
}
User.java
public class User {
@Value("${user.username}")
String username;
@Value("${user.age}")
int age;
@Override
public String toString() {
return "User [username="+username+"] [age="+age+"]";
}
}
JavaConfig.java
@Configuration
@PropertySource(value = "classpath:jdbc.properties")
public class JavaConfig {
@Autowired
Environment env;
@Bean
public User user() {
return new User(env.getProperty("user.username"),Integer.parseInt(env.getProperty("user.age")));
}
}
User.java
public class User {
String username;
int age;
public User(String username, int age) {
super();
this.username = username;
this.age = age;
}
@Override
public String toString() {
return "User [username="+username+"] [age="+age+"]";
}
}
JavaConfig.java
@Configuration
@Profile("prod")
public class JavaConfig {
@Bean
public DBDao MysqlDao() {
return new DBMySQLDAO();
}
}
JavaConfig2.java
@Configuration
@Profile("dev")
public class JavaConfig2 {
@Bean
public DBDao OracleDao() {
return new DBOracleDAO();
}
}
application.properties
원하는 config파일을 지정한 alias로 SpringBootapplication에서 동작하게 만들 수 있다.
spring.profiles.active=dev
#spring.profiles.active=prod
@Configuration
public class JavaConfig {
@Bean("xxx")
@Profile("prod") //application.properties에 설정
public DBDao MysqlDao() {
return new DBMySQLDAO();
}
@Bean("xxx")
@Profile("dev") //application.properties에 설정
public DBDao OracleDao() {
return new DBOracleDAO();
}
}
DBOracleDAO.java
@Repository
@Profile("prod")
public class DBOracleDAO implements DBDao{
@Override
public List<String> list() {
return Arrays.asList("oracle 홍길동", "oracle 이순신");
}
}
DBMySQLDAO.java
@Repository
@Profile("dev")
public class DBMySQLDAO implements DBDao{
@Override
public List<String> list() {
return Arrays.asList("mysql 홍길동", "mysql 이순신");
}
}
이렇게 유용한 정보를 공유해주셔서 감사합니다.