Spring을 사용하면서 DB연동 할 일은 매우 많다. 그리고 DB연동에는 여러가지 과정을 거쳐야 하는데 Connection을 얻고 쿼리문을 날리고 결과를 리턴해야한다. 이러한 과정은 매우 번거롭다. 그래서 Spring에서 제공하는 JdbcTemplate을 이용하면 매우 쉽게 DB연동 작업을 할 수 있다.

<context:property-placeholder location="classpath:config/database.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
	<property name="driverClassName" value="${jdbc.driver}"/>
	<property name="url" value="${jdbc.url}"/>
	<property name="username" value="${jdbc.username}"/>
	<property name="password" value="${jdbc.password}"/>
</bean>

이와같은 XML설정을 추가하면 된다. 이 설정 하나로 매번 Connection을 얻어올 필요가 없어졌다. JdbcTemplate은 Connection을 얻어올 때 위의 정보에 따라 얻어올 것이다.

 

그러면 입력과 출력은 어떻게 하는가?

입, 출력에 사용되는 대표적인 메서드가 몇개 있다.

  1. update : 대표적으로 사용된다.
  2. queryForInt : 출력이 int형으로 나올때(insert, delete, update) 사용하게 된다.
  3. queryForObject : 출력이 객체 타입으로 나올때(select) select 결과는 주로 여러개의 컬럼 값이 나오게 된다. 하지만 이는 단 한개의 즉, 단 한줄의 행만 가지는 결과값이어야한다. ex) select count(*) from world; 이런식으로 말이다. 그렇다면 여러개가 나오는 경우는 어떻게 할까
  4. query : 이렇게 사용하면 된다. ex)select * from world; 여러개의 컬럼이 여러개의 결과(행)을 가진 형태에 사용된다.

그렇다면 여러개의 객체를 리턴 했을 때 어떻게 받는것이 효과적인가?

바로 RowMapper를 사용하는것이 좋다. 이렇게 말이다.

package tommy.spring.web.board.impl;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import tommy.spring.web.board.BoardVO;

public class BoardRowMapper implements RowMapper<BoardVO> {

	@Override
	public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
		BoardVO board = new BoardVO();
		board.setSeq(rs.getInt("seq"));
		board.setTitle(rs.getString("title"));
		board.setWriter(rs.getString("writer"));
		board.setContent(rs.getString("content"));
		board.setRegDate(rs.getDate("regDate"));
		board.setCnt(rs.getInt("cnt"));
		return board;
	}

}

이런 클래스를 만들고 이는 리턴된 객체가 VO형태로 매핑되어 객체가 리턴되는 클래스이다.

public BoardVO getBoard(BoardVO vo) {
    System.out.println("Spring JDBC로 getBoard() 기능 처리");
    Object[] args = {vo.getSeq()};	
    return jdbcTemplate.queryForObject(BOARD_GET, args, new BoardRowMapper());
}

public List<BoardVO> getBoardList(BoardVO vo) {
    System.out.println("Spring JDBC로 getBoardList() 기능 처리");
    return jdbcTemplate.query(BOARD_LIST, new BoardRowMapper());
}

이렇게 해당 쿼리문으로 부터 나온 객체는 RowMapper를 통해 객체가 리턴된다.

'BACK > Spring' 카테고리의 다른 글

[Back] Spring AOP( 관점 지향 프로그램 )  (0) 2020.07.01
[Back] Spring Annotation ( 스프링 어노테이션 )  (0) 2020.06.26
[Back] Spring 기초  (0) 2020.06.25

Spring은 AOP이다. (java : OOP(객체지향))

AOP란 관점 지향 프로그램으로 가장 핵심으로 관심 분리가 있다.

관심 분리란? 핵심관심과 횡단관심을 분리하는 것이다. 이렇게 말로만 하면 모호 하므로 아래 그림을 참고하자

https://img1.daumcdn.net/thumb/R720x0.q80/?scode=mtistory2&fname=http%3A%2F%2Fcfile8.uf.tistory.com%2Fimage%2F99F4E5475C722F6C09722E

 

위 그림을 보면 일반적으로 드는 예로 은행구조이다. 우린 은행에서 업무를 본다 주로 계좌이체나 대출 이런것들이 있을것이다. 이런 기능들은 프로그래밍 입장에서 핵심 기능들이다. 그리고 이러한 기능들을 하는데 무조건 실행하는 공통적인 기능들이 있다 바로 기록을 남기는 로깅이나 트랜젝션 등 이 있다 이런 것들을 횡단관심 (부가 기능)이라 한다. 이것을 왜 분리해야하는지 의문이들것이다.

객체 지향 관점에서 먼저 작성 해 보자. 코드 양이 좀 많다.

먼저 모든 메서드가 실행되기 전에 실행 될 코드를 작성해보자

package com.springbook.biz.common;

public class LogAdvice {
	public void printLog() {
		System.out.println("[공통 로그] 비즈니스 로직 수행 전 동작");
	}
}

해당 코드는 모든 메서드가 실행 되기 전에 실행 될 것이다. 그러려면 service에서 객체를 생성해서 매번 호출 해야한다.

package com.springbook.biz.board.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.LogAdvice;

@Service("boardService")
public class BoardServiceImpl implements BoardService {

	@Autowired
	private BoardDAO boardDAO;
	private LogAdvice log;
	
	public BoardServiceImpl() {
	}
	
	@Override
	public void insertBoard(BoardVO vo) {
		log.printLog();
		boardDAO.insertBoard(vo);
	}

	@Override
	public void updateBoard(BoardVO vo) {
		log.printLog();
		boardDAO.updateBoard(vo);
	}

	@Override
	public void deleteBoard(BoardVO vo) {
		log.printLog();
		boardDAO.deleteBoard(vo);
	}

	@Override
	public BoardVO getBoard(BoardVO vo) {
		log.printLog();
		return boardDAO.getBoard(vo);
	}

	@Override
	public List<BoardVO> getBoardList(BoardVO vo) {
		log.printLog();
		return boardDAO.getBoardList(vo);
	}

}

이렇게 말이다. 이건 문제가 크게 되지 않는다. 다만 호출해야 하는 메서드랑 클래스가 달라진다면?? 코드를 수정해야한다. 

package com.springbook.biz.common;

public class Log4jAdvice {
	public void printLogging() {
		System.out.println("[공통 로그-Log4j] 비즈니스 로직 수행 전 동작");
	}
}
package com.springbook.biz.board.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.Log4jAdvice;

@Service("boardService")
public class BoardServiceImpl implements BoardService {

	@Autowired
	private BoardDAO boardDAO;
	private Log4jAdvice log;
	
	public BoardServiceImpl() {
	}
	
	@Override
	public void insertBoard(BoardVO vo) {
		log.printLogging();
		boardDAO.insertBoard(vo);
	}

	@Override
	public void updateBoard(BoardVO vo) {
		log.printLogging();
		boardDAO.updateBoard(vo);
	}

	@Override
	public void deleteBoard(BoardVO vo) {
		log.printLogging();
		boardDAO.deleteBoard(vo);
	}

	@Override
	public BoardVO getBoard(BoardVO vo) {
		log.printLogging();
		return boardDAO.getBoard(vo);
	}

	@Override
	public List<BoardVO> getBoardList(BoardVO vo) {
		log.printLogging();
		return boardDAO.getBoardList(vo);
	}

}

이처럼 말이다.

이제 AOP기능을 추가해보자 먼저 boardServiceImpl 클래스를 원상태로 돌리고 applicationContext.xml에 가서 추가해준다.

<bean id="log" class="com.springbook.biz.common.LogAdvice" /> <!-- 먼저 횡단관심에 해당하는 객체 생성  id는 aop:aspect ref와 매칭된다.-->
	
	<aop:config> <!-- AOP 설정에 해당하는 요소 -->
		<aop:pointcut expression="execution(* com.springbook.biz..*Impl.*(..))" id="allPointcut"/> <!-- id는 pointcut-ref와 매칭된다. -->
		<aop:aspect ref="log">
			<aop:before method="printLog" pointcut-ref="allPointcut"/>
		</aop:aspect>
	</aop:config>

그리고 실행시켜주면 아래 결과처럼 모든 메서드 앞에 실행 된것을 볼 수 있다.

package com.springbook.biz.board;

import java.util.List;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class BoardServiceClient {
	public static void main(String[] args) {
		AbstractApplicationContext container = new GenericXmlApplicationContext("applicationContext.xml");
		
		BoardService boardService = (BoardService)container.getBean("boardService");
		
		BoardVO vo = new BoardVO();
		vo.setTitle("임시제목");
		vo.setWriter("홍길동");
		vo.setContent("일빠...");
		
		boardService.insertBoard(vo);
		List<BoardVO> boardList = boardService.getBoardList(vo);
		for(BoardVO board : boardList) {
			System.out.println("---> "+board.toString());
		}
		
		container.close();
	}
}

위 xml 설정에서 알 수 없는 단어들이 많았을 것이다. AOP를 하면서 반드시 알아햐 하는 단어들이다.

  1. joinPoint : 조인 포인트는 직역하면 만나는 지점이다. 따라서 횡단 관심에 해당하는 메서드가 핵심기능을 만나는 메서드를 만나는 지점을 의미한다. 여기선 serviceImpl의 모든 메서드가 해당이 된다.
  2. pointCut : 포인트 컷은 직역하면 포인트를 자르다 라고 해석할 수 있는데이러한 조인포인트들 중에서 횡단관심이 실행 될 메서드 만을 잘라서 실행하는것을 의미한다.
  3. Advice : 횡단 관심으로 여기선 LogAdvice와 Log4jAdvice가 해당이 된다.
  4. Aspect : Aspect는 pointcut과 Advice의 결합으로 어떤 포인트 컷에 어떤 어드바이스가 결합하는지를 설정하는 것이다. 
  5. Weaving : 위빙은 핵심관심이 호출될 때 횡단관심이 삽입되는것을 의미하며 Aspect가 설정한 곳으로 위빙된다.

종합하면 위 설정중 aop:aspect라는 요소를 봤을 것이다. 

com.springbook.biz..*Impl.*(..) -> 모든 return형을 가진 com.springbook.biz 패키지에 끝자리가 Impl을 가진 모든 클래스의 모든 메서드의 0개 이상의 모든 파라미터를 가진 joinpoint 중 log라는 아이디를 가진 클래스의 printLog메서드 Advice 가 allPointcut이라는 아이디를 가진 포인트 컷에 위빙된다고 할 수 있다.

이렇게 설정한것이라고 할 수 있다. 이렇게 AOP를 이용하면 유지보수도 훨씬 쉬워질 것이다.

 

이렇게 xml 설정 뿐만 아니라 어노테이션으로도 할 수 있다.

xml에 방금 aop설정을 주석처리하고 아래 문장을 추가한다.

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.springbook.biz.common;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Service;

@Service
@Aspect
public class LogAdvice {
	
	@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
	public void allPointcut() {}
	
	@Before("allPointcut()")
	public void printLog() {
		System.out.println("[공통 로그] 비즈니스 로직 수행 전 동작");
	}
}

 이후 LogAdvice를 이렇게 수정하는데 여기서 어노테이션이 좀 많이 보일 것이다.

1. @Pointcut : 포인트 컷은 횡단 관심이 실행될 메서드의 범위를 괄호 안에 지정하고 메서드로 만들었다. 이때 메서드 명은 xml에서 id가 된다.

2. @Before : 횡단 관심이 위빙 될 위치를 지정하는 어노테이션으로 괄호 안에 Pointcut의 메서드를 지정하여 메서드의 Pointcut 어노테이션의 포인트컷이 된 메서드가 실행 될때 실행된다. (말이 좀 어렵다.....)

* 이것말고 @After(무조건 실행), @AfterTrowing(예외 발생시 실행), @AfterReturning(결과가 리턴 될 경우 실행), @Around(전후로 실행) 이 있다. xml 설정처럼 사용하면 된다.

3. @Aspect : Advice와 Pointcut의 합친 용어로 해당 클래스에 Pointcut과 Advice가 있다는 것을 알려주는 어노테이션

4. @Service : 해당 메서드가 실행되려면 클래스의 객체가 있어야 하므로 사용된다.

 

'BACK > Spring' 카테고리의 다른 글

[Back] Spring JdbcTemplate  (0) 2020.07.09
[Back] Spring Annotation ( 스프링 어노테이션 )  (0) 2020.06.26
[Back] Spring 기초  (0) 2020.06.25

Spring을 사용하는데 XML파일은 매우 중요하다. 따라서 XML관리가 매우 중요한데 XML에 모든 설정을 다 적으면 Xml 지옥이 펼쳐진다. 따라서 적당히 어노테이션을 이용하는것이 매우 중요하다.

따라서 이번엔 Spring Annotation의 종류를 알아보자. 천천히 공부하면서 보이는 어노테이션 마다 추가하면서 정리 할 예정이다.

앞으로 할 어노테이션은 객체 생성과 연관이 있으므로

<context:component-scan base-package=”polymorphism”/>

를 applicationContext.xml에 추가한다.

 

1.@Component : 해당 클래스의 객체를 생성한다. @Component(“id”) id값값을 작성하여 해당 객체를 요청할 수 있게 한다. 작성하지 않는다면 해당 클래스 명의 맨 앞자리가 소문자로 변환되어 아이디가 자동으로 지정된다.

package polymorphism;

import org.springframework.stereotype.Component;

@Component("tv") // 이 tv아이디는
public class LgTV implements TV {

	@Override
	public void powerOn() {
		System.out.println("LgTV 전원 켜짐");
	}

	@Override
	public void powerOff() {
		System.out.println("LgTV 전원 꺼짐");
	}

	@Override
	public void volumeUp() {
		System.out.println("LgTV 소리 커짐");
	}

	@Override
	public void volumeDown() {
		System.out.println("LgTV 소리 작아짐");
	}
}
package polymorphism;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class TVUser {
	public static void main(String[] args) {
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		TV tv = (TV)factory.getBean("tv"); // @Component 어노테이션의 id와 getBean()의 아규먼트가 일치한다.
		tv.powerOn();
		tv.volumeUp();
		tv.volumeDown();
		tv.powerOff();
	}
}

 

2. @Autowired : Autowired 어노테이션은 변수나 메서드 위에 작성되고 Autowired가 붙은 변수는 자료형을 확인하고 현재 메모리에 있는 (생성된 객체) 자료형과 일치하는 객체에 주입한다. 반드시 객체가 먼저 생성되어있어야 한다. 또한 여러개의 자료형이 동시에 존재할 수 있는데 그 문제는 아래 어노테이션을 사용해서 해결할 수 있다.

package polymorphism;

public class SamsungTV implements TV {
	@Autowired
	private Speaker speaker;
	private int price;
	
	public SamsungTV(Speaker speaker, int price) {
		System.out.println("==> SamsungTV 다중 생성자 인젝션");
		this.speaker = speaker;
		this.price = price;
	}

	public Speaker getSpeaker() {
		return speaker;
	}

	public void setSpeaker(Speaker speaker) {
		System.out.println("setSpeaker()호출");
		this.speaker = speaker;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		System.out.println("setPrice()호출");
		this.price = price;
	}

	public SamsungTV() {
		System.out.println("SamsungTV 객체 생성");
	}

	public SamsungTV(Speaker speaker) {
		System.out.println("==> SamsungTV 생성자 인젝션");
		this.speaker = speaker;
	}
	
	@Override
	public void powerOn() {
		System.out.println("SamsungTV 전원 켜짐"+", 가격 : "+price);
	}

	@Override
	public void powerOff() {
		System.out.println("SamsungTV 전원 꺼짐");
	}

	@Override
	public void volumeUp() {
		speaker.volumeUp();
	}

	@Override
	public void volumeDown() {
		speaker.volumeDown();
	}

}
package polymorphism;

import org.springframework.stereotype.Component;

@Component("sony")
public class SonySpeaker implements Speaker{
	public SonySpeaker() {
		System.out.println("SonySpeaker 객체 생성");
	}
	public void volumeUp() {
		System.out.println("SonySpeaker 소리 올린다.");
	}
	public void volumeDown() {
		System.out.println("SonySpeaker 소리 내린다.");
	}
}

우리는 분명 저 스피커가 sony스피커라고 한 적이 없다 심지어 speaker는 인터페이스다. 하지만 자동으로 Autowired 어노테이션으로 자동으로 변수에 객체가 주입된것을 볼 수 있다.

 

3. @Qualifier : @Autowired 어노테이션으로 객체를 주입하면 해당자료형을 가지는 객체는 유일해야한다. 하지만 그럴일이 없다. 따라서 우리가 정확히 객체를 지정해야하는데 이때 사용하는것이 Qualifier어노테이션이다. 우리는 Component어노테이션으로 객체 생성시에 id값을 지정했다. 이것을 여기에 사용하게 된다.

package polymorphism;

import org.springframework.stereotype.Component;

@Component("apple")
public class AppleSpeaker implements Speaker {

	public AppleSpeaker() {
		System.out.println("AppleSpeaker 객체 생성");
	}

	@Override
	public void volumeUp() {
		System.out.println("===> AppleSpeaker 소리 올린다.");
	}

	@Override
	public void volumeDown() {
		System.out.println("===> AppleSpeaker 소리 내린다.");
	}

}

위와 같이 클래스를 추가해보자. 그러면 컴파일과 동시에 Speaker의 자료형을 가진 객체가 두개가 메모리에 존재한다. 따라서 오류가 난다 이럴 때 Qulifier 어노테이션으로 아래와 같이 추가해 보자

@Autowired
@Qualifier("apple")
private Speaker speaker;

그렇다면 두 어노테이션을 해석해보자면 Autowired로 Speaker객체 가져와서 Qualifier로 Component id가 “apple”인 객체를 주입해 라고 해석할 수 있다.

 

4. @Primary : 위에서 처럼 Qualifier 어노테이션으로 객체를 직접 지정할 수 있지만 Primary어노테이션을 통해서 객체를 주입할 수 있다. 이 어노테이션의 의미는 같은 자료형을 가진 객체가 많이 만들어졌는데 객체에 주입하려고하면 해당 객체를 우선적으로 주입해라 라는 의미를 가지고 있다.

@Autowired
//@Qualifier("apple")
private Speaker speaker;
@Primary
@Component("apple")
public class AppleSpeaker implements Speaker {

Qualifier 어노테이션에 주석처리를 하고 AppleSpeaker에 @Primary 어노테이션을 붙여보자 결과가 잘 나오는것을 볼 수 있을 것이다.

5. @Resource : @Qualifier + @Autowired 이 두 가지의 어노테이션을 섞은 형태로 @Resource(name=”@Component id”) 형태이다.

위 예제에서는 @Resource(name=”apple”) 이런식으로 작성할 수 있을것이다.

6. @Service, @Controller, @Repository : MVC 모델에서 사용하는 어노테이션으로 모두 Component 어노테이션의 상속을 받아 객체 생성의 목적을 두고 사용하는 어노테이션으로 각자 클래스가 어떤 역할을 하는지 명시해주는 의미도 가지고 있다.

@Service ===> ServiceImpl : 비즈니스 로직을 처리하는 Service 클래스

@Repository ===> DAO : 데이터베이스 연동을 처리하는 DAO 클래스

@Controller ===> Controller : 사용자 요청을 제어하는 Controller 클래스

'BACK > Spring' 카테고리의 다른 글

[Back] Spring JdbcTemplate  (0) 2020.07.09
[Back] Spring AOP( 관점 지향 프로그램 )  (0) 2020.07.01
[Back] Spring 기초  (0) 2020.06.25
Spring

jar 파일로 이루어진 java framework의 하나이다.

Spring의 장점
  1. 쉬운 유지 보수
  2. 가볍다 (EJB에 비해서)
  3. 개발자의 역량 획일화
Spring의 특징
  1. 관점지향(AOP)프로그램 : 공통코드의 분리
  2. IoC(Inversion of Control) : 제어의 역행으로 객체의 관리를 코드가 아닌 framework이 해준다.
  3. Container : 객체를 생성, 관리를 담당한다.

*라이브러리와 프레임워크의 차이점

  • 라이브러리는 내가 짠 틀에 맞춰서 사용하는것이고 제어권이 없다.
  • 프레임워크는 프레임워크가 짠 틀에 맞춰서 사용하는 것이고 제어권이 있다.
Spring의 동작과정
  1. 스프링 설정 파일( applicationContext.xml )을 로딩하여 구동하고 객체를 생성한다. (이 과정에서 container가 객체를 생성하고)
  2. getBean(id) 메서드로 요청된 객체 요청 (id에 해당하는 객체를 요청하는구조이다.)
  3. 매핑된 해당 객체 반환 ( 따라서 객체는 기본적으로 한개이다.=singleton)

*스프링 설정 파일은 applicationContext.xml에 등록하는 것이고 ApplicationContext 그 자체는 spring or IoC Container를 뜻하는 말로 어플리케이션에서 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당한다

+ Recent posts