Dependencies and Configuration in Detail

2023. 12. 10. 20:37Spring Framework/Spring IoC

📌 Spring Bean의 의존성(Dependencies) 및 상세 설정(Configuration) 정리

🔹 1️⃣ 의존성(Dependency) 정의 방법

Spring에서는 Bean의 속성이나 생성자 아규먼트를 설정할 때 다양한 방법으로 값을 지정할 수 있습니다.
주요 방식은 다음과 같습니다.

방식 설명
직접 값 지정 (Primitive, String 등) 단순한 값(문자열, 숫자, boolean 등) 설정
idref 특정 Bean의 id를 안전하게 참조하는 방법
ref 다른 Bean을 참조하여 의존성 설정
내부 Bean (Inner Bean) Bean 내부에서 직접 새로운 Bean을 정의
컬렉션 (List, Set, Map, Properties) 컬렉션 타입 속성 설정
컬렉션 병합 (Merging) 부모 Bean의 컬렉션을 자식 Bean이 상속하여 확장 가능
null 값 지정 Bean 속성을 null로 설정
p-namespace XML에서 속성을 한 줄로 간결하게 표현
c-namespace XML에서 생성자 주입을 한 줄로 간결하게 표현
중첩 속성(Compound Property) 다단계 속성을 한 번에 설정 가능

🔹 2️⃣ 값(Primitive, String 등) 설정

📍 디폴트 <value> 사용

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="mypassword"/>
</bean>
package com.intheeast.ioc.dependenciesandconfigurationindetail.straightvalues.config;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

@Configuration
public class AppConfig {    

    @Bean
    public DataSource myDataSource() throws ClassNotFoundException {
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();

        dataSource.setDriverClass(com.mysql.cj.jdbc.Driver.class);
        dataSource.setUrl("jdbc:mysql://localhost:3306/sbdt_db");
        dataSource.setUsername("root");
        dataSource.setPassword("1234");
        return dataSource;
    }
}

➡️ 문자열, 숫자 등의 디폴트 값을 설정하는 방식

📍 p-namespace를 활용한 간결한 표현

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
    p:driverClassName="com.mysql.jdbc.Driver"
    p:url="jdbc:mysql://localhost:3306/mydb"
    p:username="root"
    p:password="mypassword"/>

➡️ 속성을 줄여 가독성을 높이는 방법, 다만 IDE에서 자동 완성 지원이 부족할 수 있음

🔹 3️⃣ idref를 활용한 안전한 Bean 참조

idref다른 Bean의 id를 문자열로 안전하게 참조하는 방법입니다.

<bean id="theTargetBean" class="com.example.MyBean"/>

<bean id="theClientBean" class="com.example.ClientBean">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>

➡️ idref는 잘못된 ID 입력을 방지하는 역할을 하며, 런타임 오류를 줄여줍니다.

잘못된 방법 (ref를 직접 문자열로 넣는 경우)

<property name="targetName" ref="theTargetBean"/>  <!-- 런타임 시 오류 발생 가능 -->

idref를 사용하면 Spring이 theTargetBean이 존재하는지 사전에 검증

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public TargetBean theTargetBean() {
        return new TargetBean();
    }

    @Bean
    public ClientBean theClientBean() {
        ClientBean client = new ClientBean();
        client.setTargetName("theTargetBean");  // 직접 ID를 설정
        return client;
    }
}

🔹 4️⃣ ref를 활용한 Bean 의존성 주입

ref다른 Bean 객체를 직접 참조하는 방법입니다.

<bean id="serviceA" class="com.example.ServiceA"/>

<bean id="serviceB" class="com.example.ServiceB">
    <property name="serviceA" ref="serviceA"/>
</bean>

➡️ serviceBserviceA를 의존하는 경우, ref="serviceA"를 통해 주입

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

	// serviceB가 serviceA를 의존하는 경우, ref="serviceA"를 통해 주입

    @Bean
    public ServiceA serviceA() {
        return new ServiceA();
    }

    @Bean
    public ServiceB serviceB() {
        ServiceB serviceB = new ServiceB();
        anotherBean.setDependency(serviceA()); // ref bean="serviceA"
        return serviceB;
    }
}

 

📍 부모 컨텍스트의 Bean 참조 (parent 속성 사용)

<!-- 부모 컨텍스트 -->
<bean id="accountService" class="com.example.AccountService"/>

<!-- 자식 컨텍스트 -->
<bean id="accountService" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/>
    </property>
</bean>

➡️ 부모 컨텍스트에 있는 Bean을 자식 컨텍스트에서 참조하는 경우 parent 속성을 사용

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApp {

    public static void main(String[] args) {
        ApplicationContext parentContext = new AnnotationConfigApplicationContext(ParentConfig.class);
        AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext(ChildConfig.class);
        childContext.setParent(parentContext);

        AccountService accountService = childContext.getBean("accountService", AccountService.class);
        // childContext에서 accountService 빈 사용
    }
}

@Configuration
public class ParentConfig {

    @Bean
    public AccountService accountService() {
        return new SimpleAccountService();
    }
}

@Configuration
public class ChildConfig {

    @Bean
    public AccountService accountService() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(accountService()); // 부모 컨텍스트의 빈 참조
        return (AccountService) proxyFactoryBean.getObject();
    }
}

🔹 5️⃣ 내부 Bean (Inner Bean) 정의

✔ 특정 Bean에서만 사용할 익명 Bean을 생성하는 방법입니다.

<bean id="outer" class="com.example.OuterClass">
    <property name="target">
        <bean class="com.example.InnerClass">
            <property name="name" value="John Doe"/>
            <property name="age" value="30"/>
        </bean>
    </property>
</bean>

➡️ InnerClassOuterClass에서만 사용되며, 개별적으로 접근할 수 없음

 

🔹 6️⃣ 컬렉션(List, Set, Map, Properties) 설정

✔ Spring에서는 list, set, map, props를 통해 컬렉션 데이터를 설정할 수 있습니다.

<bean id="complexObject" class="com.example.ComplexObject">
    <property name="adminEmails">
        <props>
            <prop key="admin">admin@example.com</prop>
            <prop key="support">support@example.com</prop>
        </props>
    </property>

    <property name="someList">
        <list>
            <value>List Element 1</value>
            <ref bean="myDataSource"/>
        </list>
    </property>

    <property name="someMap">
        <map>
            <entry key="key1" value="value1"/>
            <entry key="key2" value-ref="myDataSource"/>
        </map>
    </property>

    <property name="someSet">
        <set>
            <value>Set Element 1</value>
            <ref bean="myDataSource"/>
        </set>
    </property>
</bean>

➡️ 컬렉션 데이터를 Spring 설정 파일에서 직접 지정 가능

🔹 7️⃣ 컬렉션 병합(Collection Merging)

✔ 부모 Bean에서 정의한 컬렉션을 자식 Bean에서 확장 가능

<bean id="parent" abstract="true" class="com.example.ComplexObject">
    <property name="adminEmails">
        <props>
            <prop key="admin">admin@example.com</prop>
            <prop key="support">support@example.com</prop>
        </props>
    </property>
</bean>

<bean id="child" parent="parent">
    <property name="adminEmails">
        <props merge="true">
            <prop key="sales">sales@example.com</prop>
            <prop key="support">support@otherdomain.com</prop>
        </props>
    </property>
</bean>

➡️ merge="true"를 사용하면 부모의 컬렉션 데이터를 유지하면서 추가/수정 가능

public class ComplexObject {
    private Properties adminEmails;
    private List<Object> someList;
    private Map<String, Object> someMap;
    private Set<Object> someSet;

    // Getters and Setters for each property
    public void setAdminEmails(Properties adminEmails) {
        this.adminEmails = adminEmails;
    }

    public void setSomeList(List<Object> someList) {
        this.someList = someList;
    }

    public void setSomeMap(Map<String, Object> someMap) {
        this.someMap = someMap;
    }

    public void setSomeSet(Set<Object> someSet) {
        this.someSet = someSet;
    }
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.*;

@Configuration
public class AppConfig {

    @Bean
    public ComplexObject moreComplexObject() {
        ComplexObject complexObject = new ComplexObject();

        Properties adminEmails = new Properties();
        adminEmails.put("administrator", "administrator@example.org");
        adminEmails.put("support", "support@example.org");
        adminEmails.put("development", "development@example.org");
        complexObject.setAdminEmails(adminEmails);

        List<Object> someList = new ArrayList<>();
        someList.add("a list element followed by a reference");
        someList.add(myDataSource());
        complexObject.setSomeList(someList);

        Map<String, Object> someMap = new HashMap<>();
        someMap.put("an entry", "just some string");
        someMap.put("a ref", myDataSource());
        complexObject.setSomeMap(someMap);

        Set<Object> someSet = new HashSet<>();
        someSet.add("just some string");
        someSet.add(myDataSource());
        complexObject.setSomeSet(someSet);

        return complexObject;
    }

    @Bean
    public MyDataSource myDataSource() {
        return new MyDataSource();
    }
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;

import java.util.Properties;

@Configuration
public class AppConfig {

    @Bean
    public ComplexObject parent() {
        ComplexObject parent = new ComplexObject();

        Properties adminEmails = new Properties();
        adminEmails.put("administrator", "administrator@example.com");
        adminEmails.put("support", "support@example.com");
        parent.setAdminEmails(adminEmails);

        return parent;
    }

    @Bean
    public ComplexObject child() {
        GenericBeanDefinition childDefinition = new GenericBeanDefinition();
        childDefinition.setParentName("parent");
        childDefinition.setBeanClass(ComplexObject.class);

        ComplexObject child = new ComplexObject();

        Properties adminEmails = new Properties();
        adminEmails.put("sales", "sales@example.com");
        adminEmails.put("support", "support@example.co.uk"); // overriding
        child.setAdminEmails(adminEmails);

        return child;
    }
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class AppConfig {

    @Bean
    public SomeClass something() {
        SomeClass someClass = new SomeClass();

        Map<String, Float> accounts = new HashMap<>();
        accounts.put("one", 9.99f);
        accounts.put("two", 2.75f);
        accounts.put("six", 3.99f);

        someClass.setAccounts(accounts);

        return someClass;
    }
}

 

🔹 8️⃣ null 및 빈 문자열 설정

✔ 속성을 null로 설정하려면 <null/> 태그 사용

<bean class="com.example.MyBean">
    <property name="email">
        <null/>
    </property>
</bean>

➡️ exampleBean.setEmail(null); 과 동일한 효과

✔ 속성을 빈 문자열로 설정

<bean class="com.example.MyBean">
    <property name="email" value=""/>
</bean>

➡️ exampleBean.setEmail(""); 과 동일한 효과

🔹 9️⃣ p-namespace & c-namespace 단축 표현

p-namespace (Setter 주입 간결화)

<bean id="john" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/>

c-namespace (생성자 주입 간결화)

<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo" c:thingThree-ref="beanThree"/>

➡️ 코드를 간결하게 만들지만, IDE 자동 완성이 부족할 수 있음

🔹 🔟XML 기반 Properties 설정 방법

Spring에서는 java.util.Properties 타입을 설정할 때 XML의 <value> 요소를 활용할 수 있습니다.

<bean id="mappings"
    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">

    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>
Spring 컨테이너는 <value> 내부의 텍스트를 java.util.Properties 인스턴스로 변환

JavaBeans PropertyEditor 메커니즘을 활용하여 변환이 자동으로 수행됨

💡 Spring에서는 value 속성 스타일보다 value 요소를 중첩하는 방식을 선호
➡️ 가독성이 높고, 여러 개의 key-value를 쉽게 관리 가능

 

🔹 Java 기반 Properties 설정 방법

💡 Spring의 @Configuration을 사용하여 Properties를 설정할 수도 있습니다.
Java 코드로 설정하면 더욱 직관적이며, IDE 자동 완성을 활용할 수 있습니다.

📍 Java 기반 구성 (@Configuration 사용)

package com.intheeast.ioc.dependenciesandconfigurationindetail.straightvalues.config;

import java.util.Properties;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

@Configuration
public class AppConfig {

    @Bean
    public static PropertySourcesPlaceholderConfigurer properties() {
        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        Properties properties = new Properties();
        properties.setProperty("jdbc.driver.className", "com.mysql.cj.jdbc.Driver");
        properties.setProperty("jdbc.url", "jdbc:mysql://localhost:3306/sbdt_db");
        configurer.setProperties(properties);
        return configurer;
    }

    @Value("${jdbc.driver.className}")
    private String driverClassName;

    @Value("${jdbc.url}")
    private String url;

    @Bean
    public DataSource myDataSource() throws ClassNotFoundException {
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();

        dataSource.setDriverClass((Class<? extends java.sql.Driver>) Class.forName(driverClassName));
        dataSource.setUrl(url);
        dataSource.setUsername("root");
        dataSource.setPassword("1234");
        return dataSource;
    }
}

PropertySourcesPlaceholderConfigurer를 사용하여 Properties 객체를 생성
@Value 어노테이션을 활용하여 프로퍼티 값(jdbc.driver.className, jdbc.url)을 주입
Spring의 SimpleDriverDataSource를 사용하여 DataSource 설정
Class.forName(driverClassName)을 사용하여 동적으로 드라이버 클래스 로드 가능

 

💡 Java 기반 구성 방식의 장점
IDE 자동 완성 지원 (오타 방지 및 유지보수 용이)
Java 코드에서 직접 Properties를 다룰 수 있어 가독성 및 확장성 증가
테스트 시 Properties 값을 동적으로 변경 가능

 

🔹 Spring Bean 설정 정리 📝

✔ Bean 속성을 설정하는 다양한 방법 지원
ref, idref, inner bean, 컬렉션, null 값 등의 다양한 설정 방식 제공
p-namespace, c-namespace를 활용하여 XML을 간결하게 표현 가능
컬렉션 병합을 통해 부모 Bean 데이터를 재사용 가능

 

💡 Spring 설정을 활용하면 객체 간 결합도를 낮추고, 유지보수성을 높일 수 있습니다! 🚀

 

출처 : https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-properties-detailed.html

 

Dependencies and Configuration in Detail :: Spring Framework

The , , , and elements set the properties and arguments of the Java Collection types List, Set, Map, and Properties, respectively. The following example shows how to use them: [email protected] [email protected] [email protected] a list element followed

docs.spring.io

 

'Spring Framework > Spring IoC' 카테고리의 다른 글

Bean Overview  (0) 2024.06.11
Using @Autowired  (0) 2023.12.10
Fine-tuning Annotation-based Autowiring with @Primary or @Fallback  (0) 2023.07.08
Dependency Injection of Spring Framework  (0) 2023.05.01
Bean Definition Inheritance  (0) 2023.04.28