ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring]스프링 프로젝트 생성 및 DI(의존 주입)
    프로그래밍 언어/Spring 2019. 7. 17. 20:08

    스프링 공부할 때마다 참고하려고 오늘부터 스프링을 정리해보려고 합니다.


    오늘은 스프링 정의 ~ 자동 주입까지 공부하고 정리합니다☺️


    스프링은 DI(의존주입), AOP, MVC 지원, JDBC DB연동의 주요 4가지 특징을 가지는 프레임워크 입니다. 


    1. 메이븐 프로젝트 생성 방법

    New -> Maven Project를 선택 하면 아래 화면이 나옵니다.


    Group ID, Artifact ID 까지 다 써준 후 프로젝트를 생성합니다.

    (Group ID는 프로젝트의 식별 문자명, Artifact ID는 프로젝트 명을 의미)


    프로젝트에 있는 pom.xml은 프로젝트의 설정 정보를 가지고 있는 파일입니다.

    메이븐 프로젝트에서 가장 핵심이 되는 파일입니다.(매우 중요!)

    (프로젝트를 하면서 필요한 설정들은 모두 pom.xml에서 설정해주면 됩니다.)



    기본 폴더 구조로는 

    src/main/java || src/main/resources 2개의 폴더가 있습니다.

    src/main/java 폴더는 자바 소스 코드를 저장하는 폴더이고,

    src/main/resources 폴더는 자원 파일을 저장하는 폴더입니다.(대표적으로 빈 객체를 생성하는 xml파일)



    2. 빈(Bean) 객체 생성 / 사용 방법

    스프링은 기본적으로 객체를 생성하고 초기화하는 기능을 제공합니다.

    스프링에서 생성하는 객체를 빈(Bean) 객체라고 합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?xml version="1.0" encoding="UTF-8"?>
     
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">
     
        <bean id="greeter" class="chap02.Greeter"
            <property name="format" value="%s, 스프링 어려워"/>
        </bean>   
        <bean id="greeter1" class="chap02.Greeter">
            <property name="format" value="스프링 진짜... %s"/>
        </bean>
    </beans>



    1.스프링 코드

    1
    2
    3
    <bean id="greeter" class="chap02.Greeter"> 
            <property name="format" value="%s, 스프링 어려워"/>
    </bean>  



    2.자바 코드
    1
    2
    3
    Greeter greeter = new Greeter();
     
    greeter.setFormat("%s, 스프링 어려워"); 



    1번 코드와 2번 코드는 형식은 다르지만 의미는 똑같은 코드입니다.

    스프링에서는 자바와 다르게 객체를 코드와 분리된 xml에 파일에서 생성하고 관리합니다!


    ApplicationContext를 사용하여 xml 파일에 있는 빈 설정 정보를 읽어와 객체를 생성하고 초기화 하고,

    getBean() 메서드를 사용하여 xml 파일에 있는 빈 객체를 가져와 사용할 있게 해줍니다!

    ApplicationContext는 빈 객체 생성, 초기화, 관리하기 때문에 스프링 컨테이너 라고 합니다.

    1
    2
    3
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            
    Greeter g = (Greeter)ctx.getBean("greeter");



    이 코드는 위의 코드와 똑같은 코드입니다. 두 개의 코드 중 편한 코드로 사용하시면 됩니다!😊

    1
    2
    3
    GenericXmlApplicationContext beans = new GenericXmlApplicationContext("classpath:applicationContext.xml"); 
            
    Greeter g = beans.getBean("greeter",Greeter.class);
    cs



    ※ 싱글톤 객체(단일 객체)

    이 코드의 결과 값은 True 입니다. 결과 값이 True인 이유는

    스프링은 한 개의 <bean> 태그에서 한개의 빈 객체를 생성하기 때문입니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Main2 {
        public static void main(String[] args) {
            ApplicationContext beans = new ClassPathXmlApplicationContext("applicationContext.xml");
            
            Greeter g1 = (Greeter)beans.getBean("greeter");
            Greeter g2 = beans.getBean("greeter",Greeter.class);
            
            System.out.println("(g1==g2)=" + (g1==g2));
            ((ConfigurableApplicationContext)beans).close(); //ApplicationContext를 닫는 방법!    
        }
    }



    같은 클래스를 사용해서 서로 다른 두 개의 빈 객체를 생성하고 싶다면

    id를 다르게 해서 빈 객체를 생성하고 가져오면 됩니다!

    1
    2
    3
    4
    5
    6
    7
    <bean id="greeter" class="chap02.Greeter"> 
         <property name="format" value="%s, 스프링 어려워"/>
    </bean>
        
    <bean id="greeter1" class="chap02.Greeter">
         <property name="format" value="스프링 진짜... %s"/>
    </bean>



    3. DI(의존 주입)

    의존이란 한 클래스가 다른 클래스의 메서드를 실행할 때를 의존한다고 합니다.

    DI(의존 주입)는 스프링의 핵심적인 특징 중 하나로 객체 간의 결합을 느슨하게 해주는 기술입니다.

    DI는 객체를 직접 생성하지 않고 생성자나 Setter를 통해 의존 객체를 전달 받습니다.


    1
    2
    3
    Member member = new Member();
    Member02 member02 = new Member02(member);
    Member03 member03 = new Member03(member);


    만약 이 코드에서 Member를 Member를 상속받는 Member04 클래스로 변경해야 된다고 한다면,

    실제 객체를 생성해주는 new member() new Member04()로 바꿔주기만 하면 되기 때문에,

    직접 객체를 생성하는 것보다 수정할 코드가 줄어듭니다!


    3.1 생성자를 이용한 DI 방식


    아래 코드는 생성자에 의존 주입을 하는 코드입니다.

    생성자가 MemberDao라는 파라미터를 필요로 하기 때문에 빈 객체에서 생성자를 통해 주입 해줘야 합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package spring;
     
    import java.util.Date;
     
    public class MemberRegisterService {
        private MemberDao memberDao;
     
        public MemberRegisterService(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
     
        public void regist(RegisterRequest req) {
            Member member = memberDao.selectByEmail(req.getEmail());
            if (member != null) {
                throw new AlreadyExistingMemberException("dup email " + req.getEmail());
            }
            Member newMember = new Member(
                    req.getEmail(), req.getPassword(), req.getName(),
                    new Date());
            memberDao.insert(newMember);
        }
    }r



    생성자는 <constructor-arg> 태그를 사용해서 DI를 해줍니다!

    ref는 다른 빈 객체를 참조하겠다는 의미라고 생각하시면 됩니다.

    1
    2
    3
    <bean id="memberRegSvc" class="spring.MemberRegisterService">
            <constructor-arg ref="memberDao" />
    </bean>



    위의 코드를 자바 코드로 표현하면 아래 코드와 같습니다.

    1
    MemberRegisterService memberRegSvc = new MemberRegisterService(MemberDao);



    만약 생성자가 가지고 있는 파라미터가 2개 이상이라면,

    아래 코드처럼 빈 객체를 생성해 주시면 됩니다.😊

    1
    2
    3
    4
    <bean id="memberRegSvc" class="spring.MemberRegisterService">
            <constructor-arg ref="memberDao1" />
            <constructor-arg ref="memberDao2" />
    </bean



    3.2프로퍼티를 이용한 DI 방식 (Setter)


    아래 코드처럼 set메서드에서는 <property> 태그를 사용하여 DI를 해줍니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package spring;
     
    public class MemberInfoPrinter {
     
        private MemberDao memDao;
        private MemberPrinter printer;
     
        public void setMemberDao(MemberDao memberDao) {
            this.memDao = memberDao;
        }
     
        public void setPrinter(MemberPrinter printer) {
            this.printer = printer;
        }
     
        public void printMemberInfo(String email) {
            Member member = memDao.selectByEmail(email);
            if (member == null) {
                System.out.println("데이터 없음\n");
                return;
            }
            printer.print(member);
            System.out.println();
        }
    }



    <property> 태그를 사용하여 빈 객체를 만들어 주면 됩니다.

    여기서 주의할 점은 name은 set메서드에서 set을 빼고 맨 앞 문자를 소문자로 바꿔주고 그대로 써주셔야 합니다.

    예를 들어 메서드 이름이 setMemberDaoname="memberDao"로 써주시면 됩니다.(잘못쓰면 에러나요ㅋㅋ)

    1
    2
    3
    4
    <bean id="infoPrinter" class="spring.MemberInfoPrinter">
            <property name="memberDao" ref="memberDao" />
            <property name="printer" ref="memberPrinter" />
    </bean>



    3.2기본 데이터 타입 값 설정 방법


    위의 코드는 전부 객체를 파라미터로 갖는 생성자와 메서드를 DI하는 방법이였습니다.

    아래 코드는 파라미터가 기본 타입일 때의 DI 방법입니다.

    int를 파라미터로 갖는 생성자 1개와 set메서드가 2개가 있습니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package spring;
     
    public class VersionPrinter {
     
        private int majorVersion;
        private int minorVersion;
     
        public VersionPrinter() {
        }
     
        public VersionPrinter(int majorVersion, int minorVersion) {
            this.majorVersion = majorVersion;
            this.minorVersion = minorVersion;
        }
     
        public void setMajorVersion(int majorVersion) {
            this.majorVersion = majorVersion;
        }
     
        public void setMinorVersion(int minorVersion) {
            this.minorVersion = minorVersion;
        }
     
        public void print() {
            System.out.printf("이 프로그램의 버전은 %d.%d입니다.\n\n",
                    majorVersion, minorVersion);
        }
     
    }
     



    기본 타입은 value를 통해 값을 지정해주면 끝입니다.(간단하죠?ㅋㅋ)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <bean id="versionPrinter" class="spring.VersionPrinter">
            <property name="majorVersion" value="4" />
            <property name="minorVersion">
                <value>1</value>
            </property>
    </bean>
     
    <bean id="versionPrinter" class="spring.VersionPrinter">
            <constructor-arg value="4" />
            <constructor-arg value="1" />
    </bean>




    두 개 이상의 설정 파일 사용하는 방법 / import 태그


    빈 객체를 좀 더 알아보기 쉽게 관리하기 위해 설정 파일을 두 개 이상으로 나누는 경우에는 배열에 설정 파일을 담아 전달해주면 됩니다.



    String 배열에 담아 사용하시면 됩니다(간단하죠?ㅋㅋ)

    1
    2
    String[] conf = { "classpath:conf1.xml""classpath:conf2.xml" };
    ApplicationContext ctx = new GenericXmlApplicationContext(conf);



    import 태그를 사용하는 방법은 한 개의 xml 파일에 참조하고 싶은 xml 파일을 import 해주시면 됩니다.(매우 간단!)

    1
    <import resource="conf2.xml"/>



    4. 어노테이션

    1) @Autowired

    자동 주입 기능은 스프링이 알아서 의존 객체를 주입해주는 기능입니다.


    어노테이션을 사용하면 이 코드가

    1
    2
    3
    <bean id="memberRegSvc" class="spring.MemberRegisterService">
            <constructor-arg ref="memberDao" />
    </bean>



    따로 태그를 사용하지 않아도 의존 객체가 주입됩니다

    1
    <bean id="memberRegSvc" class="spring.MemberRegisterService"/>



    자동 주입 기능을 사용하는 방법은

    1) 자동 주입할 대상에 @Autowired 사용

    2) xml 설정 파일에 <context:annotation-config/> 설정 추가


    @Autowired는 타입을 이용해서 빈 객체를 검색하는 어노테이션 입니다.


    아래 코드는 생성자에 @Autowired를 지정한 코드입니다.

    원래 같으면 빈 객체에서 <constructor-arg>를 이용해서 의존 객체를 주입해 줘야 하지만,

    어노테이션을 사용했기 때문에 주입하는 코드는 생략됩니다!!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package spring;
     
    import java.util.Date;
     
    import javax.annotation.Resource;
     
    import org.springframework.beans.factory.annotation.Autowired;
     
    public class MemberRegisterService {
        @Resource(name="memberDao")
        private MemberDao memberDao;
     
        @Autowired //의존객체 자동주입
        //@Resource는 생성자에 적용이 불가능함, 필드, 메서드에만 적용 가능
        public MemberRegisterService(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
        
        // 기본 생성자
        public MemberRegisterService() {}
     
        public void regist(RegisterRequest req) {
            Member member = memberDao.selectByEmail(req.getEmail());
            if (member != null) {
                throw new AlreadyExistingMemberException("dup email " + req.getEmail());
            }
            Member newMember = new Member(
                    req.getEmail(), req.getPassword(), req.getName(),
                    new Date());
            memberDao.insert(newMember);
        }
    }
     



    아래 코드는 어노테이션 사용했을 때와 사용하지 않았을 때의 빈 객체 생성 방법입니다.

    어노테이션을 사용하지 않았다면 생성자가 파라미터로 객체를 가지고 있기 때문에 생성자 코드를 사용해서 의존 객체를 주입해줘야 하지만,

    어노테이션을 사용하면 자동으로 의존 객체를 주입해주기 때문에 생성자 코드가 생략됩니다.

    1
    2
    3
    4
    5
    6
    7
    // 어노테이션 설정을 하지 않은 빈 객체 생성 코드
    <bean id="memberRegSvc" class="spring.MemberRegisterService">
            <constructor-arg ref="memberDao" />
    </bean>
     
    // 어노테이션 설정 한 빈 객체 생성 코드
    <bean id="memberRegSvc" class="spring.MemberRegisterService"/>




    만약 @Autowired를 사용했는데 xml에 파일에 해당 빈 객체가 없을 때는 (required=false)를 사용해주면 됩니다.

    1
    2
    @Autowired(required=false)
        private MemberDao memDao;




    2) @Qualifier

    동일한 타입을 가진 빈 객체가 2개가 있을 때 @Autowired 를 사용하면 주입할 객체를 선택할 수 없게 되서 에러가 발생합니다.

    그럴 때는 @Qualifier를 사용해서 값을 지정해주면 됩니다. 쉽게 말하면 이름을 지정해서 구분할 수 있게 해주는 거죠ㅎㅎ


    value를 이용해서 이름을 지정해 줍니다.

    1
    2
    3
    4
    5
    <bean id="printer1" class="spring.MemberPrinter">
            <qualifier value="sysout"/>
    </bean>
     
    <bean id="printer2" class="spring.MemberPrinter" />



    그리고 @Qualifier를 사용해서 구분해 줍니다!(엄청 간단합니다!)

    1
    2
    3
    4
    5
    @Autowired
    @Qualifier("sysout")
    public void setPrinter(MemberPrinter printer) {
         this.printer = printer; // 전역변수 = 지역변수 초기화 하는 코드
    }




    3) @Resource

    @Resource는 빈의 이름을 이용해서 주입할 객체를 검색하는 어노테이션 입니다.
    아래 코드처럼 빈 객체의 이름을 통해 의존 객체를 주입해주는 어노테이션 입니다.
    name 속성을 사용하지 않으면 해당 타입의 객체를 자동으로 주입합니다~(@Autowired)

    1
    2
    3
    4
    @Resource(name="memberPrinter")
    public void setPrinter(MemberPrinter printer) {
        this.printer = printer; // 전역변수 = 지역변수 초기화 하는 코드
    }



    오늘 공부한 내용을 올려봤는데 아직 이해가 되지 않는 부분도 있어서,

    내일 다시 한 번 봐야할 것 같습니다~ 다들 열공하세요☺️

    댓글 0

Designed by Tistory.