ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 의존 자동 주입
    Spring_FrameWork 2019. 3. 15. 11:32




    01 준비

    전과 같이 폴더를 작성하고 pom.xml 파일작성


    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
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
            http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>sp4</groupId>
        <artifactId>sp4-chap04</artifactId>
        <version>0.0.1-SNAPSHOT</version>
     
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>4.1.0.RELEASE</version>
            </dependency>
        </dependencies>
     
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.7</source>
                        <target>1.7</target>
                        <encoding>utf-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
     
    </project>
    cs

    전에 사용했던 코드를 일부 재활용한다.

    02 @Autowired 애노테이션을 이용한 의존 자동 주입


    자동 주입 기능을 사용하면 스프링이 알아서 의존 객체를 찾아서 주입해준다.
    예를 들어 설정에 의존 객체를 명시하지 않아도 스프링이 필요한 의존 빈 객체를 찾아서 주입해준다.

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


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


    1. 자동 주입 대상에 @Autowired 애노테이션 사용

    2. XML 설정에 <context:anntation-config/> 설정 추가


    위 두가지를 하면 , @Autowired가 적용된 의존 주입 대상에 대해서는 <property>태그나 

    <contructor-arg> 태그를 사용하지 않아도 스프링에서 알맞게 의존 객체를 주입한다.

    실제로 예제코드에 @Autowired 애노태이선을 적용해 보자.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package spring;
     
    import java.util.Date;
     
    import org.springframework.beans.factory.annotation.Autowired;
     
    public class MemberRegisterService {
        private MemberDao memberDao;
        @Autowired
        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);
        }
    }
    cs


    @Autowired 애노테이션을 생성자에 적용했는다.

    @Autowired 애노테이션을(필드멤버) 필드와 설정 메서드에도 적용할 수있다(setter)

    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
    34
    package spring;
     
    import org.springframework.beans.factory.annotation.Autowired;
     
    public class MemberInfoPrinter {
        
        @Autowired
        private MemberDao memberDao;
        private MemberPrinter printer;
        
        public void setMemberDao(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
        
        @Autowired
        public void setPrinter(MemberPrinter printer) {
            this.printer = printer;
        }
        
        public void printMemberInfo(String email) {
            Member member = memberDao.selectByEmail(email);
            
            if(member==null) {
                System.out.println("데이터없음");
                return;
            }
            printer.print(member);
            System.out.println();
            
        }
        
        
    }
     
    cs


    @Autowired 애노테이션을 적용하면 @Autowired 애노테이션이 적용된 의존 대상에 한해 

     XML 설정에서 의존 주입관련 설정을 하지 않아도 된다. 

    즉 Autowired로 지정한 의존 대상에 대해서 <constructor-arg> <property> 태그를 사용하지 않아도 된다.

    이런식 

    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
    <?xml version="1.0" encoding="UTF-8"?>
     
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
     
        <context:annotation-config />
        
        <bean id="memberDao" class="spring.MemberDao">
        </bean>
     
        <bean id="memberRegSvc" class="spring.MemberRegisterService">
        </bean>
        
        <bean id="memberPrinter" class="spring.MemberPrinter">
        </bean>
        
        <bean id="infoPrinter" class="spring.MemberInfoPrinter">
        </bean>
     
     </beans>
    cs


    <context:annotation-config> 태그를 추가 했다. 

    @Autowired 나  @Resource가 적용된 필드나 메서드에 대해 자동주입을 처리할 수있다.

    @Autowired애노테이션을 사용하면 스프링은 타입을 이용해서 의존 대상 객체를 검색한다.

    MemberResgisterService의 생성자의 파라미터 타입과

    MemberInfoPrinter의 memDao 필드의 타입은 둘다 MemberDao이며, 따라서 이 두 의존 주입대상타입은

    MemberDao 인 "memeberDao" 라는 빈객체가 선택된다.

    비슷하게 MemberInfPrinter 클래스의 setPrinter() 메서드의 파라미터타입은 MemberPrinter 이므로

    이 타입에 할당할 수있는 MemberPrinter 타입의 memberPrinter 빈 객체를 주입 대상으로 선택한다.


    자동 주입에서의 타입 매칭 

    동일한 타입의 빈 객체를 탖아서 주입대상으로 선택하는 것으로 생각할 수있다.

    하지만 정확하게 말하면 해당 타입에 할당 할 수있는 빈객체를 찾아서 주입 대상으로 선택한다.


    예를 들어

    MemberDao를 상속받은 CachedMemberDao를 사용해서 <bean>을 설정하고, @Autowired애노테이선은

    MemberDao 타입의 필드에 설정했다고 생각하자.


    <bean id="CachedMemDao" class="spring.ChachedMemberDao"/>


    public class MemberPrinter(){

    @Autowired private MemberDao memDao;

    }


    따라서 @Autowired 적용된 MemberDao 타입의 필드이 memDao 에 ChecheMemberDao 타입의 빈객체를 할당할 수있으므로 

    checheMemDao 빈객체가 의존 주입 대상이 될 수 있다.


    동일한 타입을 가진 빈객체가 두개 있다면 @Autowired 애노테이션을 어떻게 처리할까?

    xml 설정 파일

    <bean id="p1" class="spring.Member.Print"/>

    <bean id="p2" class="spring.Member.Print"/>


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class MemberInfoPrinter {
        
        @Autowired
        private MemberDao memberDao;
        private MemberPrinter printer;
        
        public void setMemberDao(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
        
        @Autowired
        public void setPrinter(MemberPrinter printer) {
            this.printer = printer;
        }
        
    cs


    결과는


    자동주입할 객체를 선택할 수 없으므로 오류가 발생


    이런경우를 해결하기 위해 처리하는 것이

    @Qualifier 애노테이션


    <context:annotation-config> 태그의 처리


    <context:annotation-config> 태그를 사용하면 내부적으로 다음 클래스르 빈 객체로 등록한다.


    1. AutowiredAnnotationBeanPostProcessor: @Autowired에 대한 의존 주입을 처리한다.

    2. RequiredAnnotationBeanPostProcessor :@Required에대한 의존주입을 처리한다.

    3. ConfigurationClassPostProessor : @Configuration에 대한 설정 처리를 한다.
    4. CommonAnnotationBeanPostProessor : JSP-250(@PostContruct 등)에 대한 설정 처리를 한다.


    만약에 위 처리기중 @Autowired만 처리된 객체에 주입하고싶다면

    <bean clas="org.springframework.beans.factory.annotation.AutowiredAnnotaionBeanPostProcessor"/>

    만 빈으로 등록해도된다.


    물론 <context:annotation-config> 태그를 사용하는 것이 편하다.


    2.1 @Qualifier 애노테이션을 이용한 의존 객체 선택


    @Qualifier 를 설정하려면 두가지를 설정해주면 된다.

    1. 설정에서 빈의 한정자 값을 설정한다.
    2. @Autowired애노태이션이 적용된 주입 대상에 @Qualifier 애노태이션을 설정한다. 이때 @Qualifier값으로 한정자를 설정한다.
    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
    <?xml version="1.0" encoding="UTF-8"?>
     
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
     
        <context:annotation-config />
        
        
        
        <bean id="memberDao" class="spring.MemberDao">
        </bean>
     
        <bean id="memberRegSvc" class="spring.MemberRegisterService">
        </bean>
        
        <bean id="printer1" class="spring.MemberPrinter">
            <qualifier value="sysout"></qualifier>
        </bean>
        
        <bean id="printer2" class="spring.MemberPrinter">
        </bean>
        
        <bean id="infoPrinter" class="spring.MemberInfoPrinter">
        </bean>
     
     </beans>
    cs

    1. 빈태그에 <qualifier> 태그를 자식 태그로 추가해서 한정자의 값을 설정했다. 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package spring;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
     
    public class MemberInfoPrinter {
        
        @Autowired
        private MemberDao memberDao;
        private MemberPrinter printer;
        
        public void setMemberDao(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
        
        @Autowired
        @Qualifier("sysout")
        public void setPrinter(MemberPrinter printer) {
            this.printer = printer;
        }
        
    cs

    @Autowired한곳에 @Qualifier 지정


    지정할 객체가 printer1,printer2 가 존재하지만 퀄러파이어 값이 sysout을 가지는 printer1 객체를 사용한다.


    @Qualifier 객체 사용할때 주의 할점  @Qualifier에 지정한 한정자 값을을 갖는 <bean> 객체가 존재하지 않으면 주입할 객체를 찾지못해 익셉션이 발생한다는 점이다.

    정확히 일치하는 한정자를 찾지 못하면 

    스프링 컨테이너 생성에 실패하게 된다.


    2.2Autowired의 필수 여부 지정 

    @Autowired를 지정하면 반드시 주입할 의존 객체가 존재해야한다.


    1
    2
    3
    4
    5
    6
    public class MemberRegisterService {
        private MemberDao memberDao;
        @Autowired
        public MemberRegisterService(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
    cs


    설정파일에 MemberDao 타입의 빈객체가 존재안하면 어떻게 될까


    Exception encountered during context initialization - cancelling refresh attempt

    org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'memberRegSvc' defined in class path resource [appCtx.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [spring.MemberDao]: : No qualifying bean of type [spring.MemberDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [spring.MemberDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}


    오류남 


    하지만 @Autowired 애노테이션이 적용된 대상에 꼭 의존 객체를 주입하지 않아도 될때가 있다.

    requred 값을 false로 지정해서 필수가 아님을 설정 할 수 있다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class MemberInfoPrinter {
        
        @Autowired(required=false)
        private MemberDao memberDao;
        private MemberPrinter printer;
        
        public void setMemberDao(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
    cs



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class MemberRegisterService {
        private MemberDao memberDao;
        @Autowired(required=false)
        public MemberRegisterService(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
     
        
            
        public MemberRegisterService() {
        }
     
    cs

    이런식으로하면 정상적으로 실행은되지만 주입받아야하는 memberDao가 null값으로 남게된다.

    MemberRegisterService에 기본 생성자를 만든이유는

    파라미터 생성자만 있고 기본 생성자가 존재하지 않을 경우 

    required(true or false)에 상관없이 객체를 생성할 때  MemberDao를 생성해야하는데 사용할 MemberDao  객체를 찾을수 없으므로 익셉션이 발생됨


    생성자에 @Autowired에 required 값이 false인 경우에는 이런문제가 발생치 않도록 기본 생성자를 생성해놔야 한다.

    의존객체가 주입되지 않는것이아닌 주입한 의존객체가 null이여도 익셉션을 일으키지 않을 뿐이다.


    2.3 @Autowired 애노테이션과 이름 매칭

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class MemberInfoPrinter {
        
        @Autowired(required=false)
        private MemberDao memberDao;
        private MemberPrinter printer;
        
        @Autowired(required=false)
        public void setMemberDao(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
        
        @Autowired
        public void setPrinter(MemberPrinter printer) {
            this.printer = printer;
        }
    cs

    다음과 같은 코드와 

    1
    2
    3
    4
    5
    6
    7
    8
    9
        <bean id="printer" class="spring.MemberPrinter">
        </bean>
     
        <bean id="printer2" class="spring.MemberPrinter">
        </bean>
            
        <bean id="infoPrinter" class="spring.MemberInfoPrinter">
        </bean>
     
    cs

    다음과 같은 설정이 존재한다.


    동일한 객체가 2개가 있으므로 익셉션이 발생할것같다.

    그러나 발생하지 않는다

    동일한 객체가 2개이상 존재할경우 @Autowired가 적용된 필드나 설정 메서드의 프로퍼티의 이름과 같은 이름의 빈객체가 존재하는지 확인해서 존재하면 그 객체를 우선해서 주입한다.


    2.4 @Autowired 애노테이션의 적용순서

    실제 의존 객체를 찾는 순서를 정리하면 다음과 같다.

    1.타입이 같은 빈객체를 검색한다. 한개면 그 빈객체를 사용한다. @Qualifier가 명시되어 있을 경우 @Qualifier와 같은 값을 갖는 빈객체여야한다.

    2. 타입이 같은 빈객체가 두개이상 존재하면 @Qulifier로 지정한 빈객체를 찾는다 .존재하면 그 객체를 사용한다.

    3. 타입이 값은 빈객체가 두개이상 존재하고 @Qulifier가 없는 경우 , 이름이 같은 빈객체를 찾는다. 존재하면 그 객체를 사용한다.

    위경우가 아니면 익셉션 발생


    2.5@Autowired 애노테이션의과 파라미터 갯수

    @Autowired 애노테이션은 파라미터가 두개 이상이 메서드나 생성자에도 적용할 수있다.
    즉 다음과 가틍 코드를 이용하면 한 메서드를 이용해서 필요한 의존 객체를 자동으로 주입 받을 수있다.

    1
    2
    3
    4
    5
    @Autowired
        public void injectionDependency(MemberDao memberDao,MemberPrinter printer) {
            this.memberDao = memberDao;
            this.printer = printer;
        }
    cs

    @Qulifier 애노테이션을 적용할때 해당  파라미터를 지정해서 사용할수있다. 

    파라미터가 하나인 경우에도 가능하다.

    1
    2
    3
    4
    5
    6
    7
     
        @Autowired
        public void injectionDependency(MemberDao memberDao,
                @Qualifier("sysout")MemberPrinter printer) {
            this.memberDao = memberDao;
            this.printer = printer;
        }
    cs


    03 @Resource 애노테이션을 이용한 의존 자동 주입

    @Autowired는 애노테이션이 타입을 이용해서 검색한다면 , @Resource 애노테이션은 빈의 이름을 이용해서 주입할 객체를 검색한다.

    @Resource 애노테이션을 사용하려면 두가지만 추가하면 된다.

    1.자동 주입 대상에 @Resource 애노테이션 사용
    2. XML설정에 <context:annotaiton-config/> 설정 추가 


    @Resource 애노테이션은 빈의 이름을 사용해서 주입할 빈 객체를 찾기때문에 
    @Resource 애노테이션의 값으로 빈객체의 이름을 지정한다. 

    한번 지정해 보자 


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package spring;
     
    import java.util.Date;
     
    import javax.annotation.Resource;
     
     
    public class MemberRegisterService {
        @Resource(name="memberDao")
        private MemberDao memberDao;
        
        public MemberRegisterService(MemberDao memberDao) {
            this.memberDao = memberDao;
        }
     
        
            
        public MemberRegisterService() {
        }
     
     
    cs


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package spring;
     
    import javax.annotation.Resource;
     
     
    public class MemberInfoPrinter {
        
        @Resource(name="memberDao")
        private MemberDao memDao;
        private MemberPrinter printer;
        
        
        
        public void setMemberDao(MemberDao memberDao) {
            this.memDao = memberDao;
        }
     
     
        @Resource(name="memberPrinter")
        public void setPrinter(MemberPrinter printer) {
            this.printer = printer;
        }
     
     
    cs

    @Resource 애노테이션은 생성자에 적용할수없고 필드나 메서드에만 적용할 수있다.

    이런이유로 생성자에는 적용을 안했다.

    name 속성으로 주입할 bean 객체의 이름을 지정한다.  



    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
    <?xml version="1.0" encoding="UTF-8"?>
     
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
     
        <context:annotation-config />
     
     
        <bean id="memberDao" class="spring.MemberDao">
        </bean>
        
        <bean id="memberRegSvc" class="spring.MemberRegisterService">
        </bean>
     
     
        <bean id="memberPrinter" class="spring.MemberPrinter">
        </bean>
        
     
        <bean id="infoPrinter" class="spring.MemberInfoPrinter">
        </bean>
     
    </beans>
    cs

    @Resource에 name 속성을 이용하지 않으면 필드나 설정메서드의 타입을 입력한다. 


    3.1@Resource 애노테이션의 적용 순서 


    1,name 속성에 지정한 빈객체를 찾는다 존재하면 사용


    2. name 속성이 없을 경우 동일한 타입의 빈객체를 찾는다. 존재하면 해당 객체 주입


    3. name 속성이 없고 동일한 타입을 같는 빈객체가 두개 이상일 경우, 같은 이름을 가진 빈 객체를 찾는다.


    4. name 속성이 없고 동일한 타입일 갖는 빈객체가 두개이상이고 같은 이름을 가진 빈객체가 없는 경우

     @Qualifier를 이용해서 주입할 빈객체를 찾는다.


    04 자동 주입과 명시적 의존 주입 간의 관계

    자동 주입과 명시적인 의존주입설정 (<construct-arg>태그나 <property> 태그)를 함깨 사용할 수 있다. 

    이 경우 명시적인 의존주입 설정이 자동 주입에 우선한다. 

    예를 들어 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package spring;
     
     
    import org.springframework.beans.factory.annotation.Autowired;
     
     
    public class MemberInfoPrinter {
        
        @Autowired
        private MemberDao memDao;
        private MemberPrinter printer;
        
        
     
        @Autowired
        public void setPrinter(MemberPrinter printer) {
            this.printer = printer;
        }
     
     
    cs

    이런 코드가 있다.

    설정은 MemberInfoPrinterdml printer 프로퍼티에 "printer2" 빈을 주입하도록 설정하고 있다. 

    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
    <?xml version="1.0" encoding="UTF-8"?>
     
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
     
        <context:annotation-config />
     
     
        <bean id="memberDao" class="spring.MemberDao">
        </bean>
        
        <bean id="memberRegSvc" class="spring.MemberRegisterService">
        </bean>
     
     
        <bean id="printer" class="spring.MemberPrinter"/>
        
        <bean id="printer2" class="spring.MemberPrinter"/>
        
        <bean id="infoPrinter" class="spring.MemberInfoPrinter">
            <property name="printer" ref="printer2"/>
        </bean>
     
    </beans>
    cs

    ㅇ냍

    <property>를 이용한 의존설정이 자동주입보다 우선 하기 때문에 <property> 태그로 지정한 "printer2" 빈객체가 주입된다.

    'Spring_FrameWork' 카테고리의 다른 글

    빈 라이프사이클과 범위  (0) 2019.03.19
    DI정리  (0) 2019.03.18
    스프링이란?  (0) 2019.03.13
    SpringSecurityPractice_02  (0) 2019.01.10
    Spring Web Scurity Practice  (0) 2019.01.06
Designed by Tistory.