자라선

[Spring] Dependency Injection 본문

Develop/Spring framework

[Spring] Dependency Injection

자라선 2020. 7. 31. 11:13

1. DI, 의존성 주입

Spring의 대표적인 특징중 가장 기본이 된다는 의존성 주입은

객체에 의존성을 부여함으로써 결합도를 낮추고 응집도를 높이는 것을 말한다.

의존성, 즉 A가 B에게 의존하는 관계다 라고 할때

B가 변화하면 A에게 영향이 간다는 것을 말한다.

 

쉽게 말해 A가 B를 상속한다면, A가 B의 메소드를 사용할때 B의 메소드가 변경되면

A의 결과값도 변화하며 B의 변경에 따라 A또한 달라질수 있다는 것을 말한다.

하지만 반대로 A가 변화한다 하더라도 B는 아무런 영향에 끼치지 않는다. (JAVA extends와 비슷함)

 

2. DI 사용

public class UserDao {
	public void add(User user) throws ClassNotFoundException, SQLException{
		Connection c = getConnection();
		PreparedStatement ps = c.prepareStatement("insert into USERS(id, name, password) values(?,?,?)");
		...
	}
    ...

	public Connection getConnection() throws SQLException, ClassNotFoundException{
		Class.forName("com.mysql.jdbc.Driver");
		return DriverManager.getConnection("jdbc:mysql://localhost:13306/test","test","test");
	}
}

위와 같은 UserDao 에서 Connection을 가져오기 위해 메소드를 선언하였다.

위의 Dao에서야 getConnection() 메소드를 반환하여 객체 내에있는 메소드에 적용시키면 되지만

UserDao 이외에 다른 Dao에서도 사용할려면 또 getConnection() 메소드를 생성하여 똑같이 만들어주어야한다.

그럼 DB Connection 변경시 모든 Dao객체에 수정해주어야한다는 문제가 생긴다.

public interface ConnectionMaker {
	public Connection getConnection() throws SQLException, ClassNotFoundException;
}
public class ConnectionMakerImpl implements ConnectionMaker{
	@Override
	public Connection getConnection() throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
		return DriverManager.getConnection("jdbc:mysql://localhost:13306/test","test","test");
	}
}
public class UserDao {
	ConnectionMaker connectionMaker;
	public UserDao(ConnectionMaker connectionMaker) {
		this.connectionMaker = connectionMaker;
	}
....
}

하지만 위와 같이 별도의 객체로 분리하여 인터페이스로 상속받아 구현 후

UserDao의 생성자로 해당 인스턴스를 받게 된다면 ConnectionMakerImpl 하나만으로 모든

Dao에 영향을 줄수 가 있다.

여기서 생성자로 해당 인스턴스를 주는것이 주입하는 것이며, UserDao 뿐만이 아닌 모든 Dao는 ConnectionMakerImpl의 인스턴스가 필요로 하기 때문에 의존성이 부여됐다는 것을 알수가 있다.

 

3. DI 와 응집도

이건 내가 이번에 배운건데...

만약 2번의 코드 처럼 Connection 을 부여할때마다 카운터를 증가시킬려고 한다면 어떻게 할까?

나였다면 ConnectionMakerImpl 클래스 안에 전역 변수 선언 후 메소드 호출마다 Count +1 했을것 이다..

하지만 이것은 잘못되었다. 응집도를 높이기 위해서는 ConnectionMakerImpl 객체 맞지 않는 관심을 만들어주어선 안된다. 즉 ConnectionMakerImpl 객체는 DB Connection을 반환하기 위한 클래스이고 카운트를 증가 시킬 객체가 아니다.

그래서 2번 기준으로 본다면 UserDao 와 ConnectionMakerImpl 사이에 별도의 카운터를 증가시킬 목적의 객체가 필요하다는 것이다.

@Configuration
public class CountingDaoFactory {

	@Bean
	public UserDao userDao() {
		return new UserDao(connectionMaker());
	}
	
	@Bean
	public ConnectionMaker connectionMaker() {
		return new CountingConnectionMaker(realConnectionMaker());
	}
	
	@Bean
	public ConnectionMaker realConnectionMaker() {
		return new DConnectionMaker();
	}
	
}
public class CountingConnectionMaker implements ConnectionMaker{

	int counter = 0;

	private ConnectionMaker realConnectionMaker;
	
	public CountingConnectionMaker(ConnectionMaker realConnectionMaker) {
		this.realConnectionMaker = realConnectionMaker;
	}

	@Override
	public Connection getConnection() throws SQLException, ClassNotFoundException {
		this.counter ++;
		return realConnectionMaker.getConnection();
	}
	
	public int getCounter() {
		return counter;
	}
	
}
public class main {

	public static void main(String[] args) throws ClassNotFoundException, SQLException {
		//UserDao userdao = new DaoFactory().userDao();
		ApplicationContext context = new AnnotationConfigApplicationContext(CountingDaoFactory.class);
		UserDao userdao = context.getBean("userDao", UserDao.class);
		
		CountingConnectionMaker ccm = context.getBean("connectionMaker", CountingConnectionMaker.class);
		User user = new User();
		user.setId("" + new Random().nextInt(10000));
		user.setName("name");
		user.setPassword("123");
		
		userdao.add(user);
		System.out.println("결과1 : " + userdao.get(user.getId()));
		
		System.out.println("Dao 호출 횟수 : " + ccm.getCounter());
	}

}

위의 코드를 보면 CountingDaoFactory 을 빈 오브젝트로 올리고 main에서 userDao 빈을 반환받는다.

그러면 CountingDaoFactory 생성자에 DConnectionMaker객체로 만들어주면서

동시에 Override를 통해 getConnection() 메소드를 만들어준다

이때 우리는 Dao사용마다 라는 조건이 있기 때문에 카운트를 증가시키도록 하고

main에서 UserDao의 add 나 get를 호출할때마다 카운터가 증가되고,

카운터를 조회하기 위한 getCounter()를 생성된 bean을 통해 조회할수 있다.

위와 같이 관심을 분리하여 응집도를 높였는데

CountingDaoFactory 객체는 DB Connection 기능을

CountingConnectionMaker 객체는 카운트 증가 기능을 부여하였다.

두 객체는 서로 의존성 주입을 사용했다는것도 알수 있다.

'Develop > Spring framework' 카테고리의 다른 글

[Spring] DTD Schema  (0) 2020.07.31
[Spring] Bean xml setting  (0) 2020.07.31
[Spring] ApplicationContext Code  (0) 2020.07.31
[Spring] IoC Container  (0) 2020.07.31
[Spring] CGLIB is required to process  (0) 2020.07.31
Comments