'Spring' 태그의 글 목록 :: 잡다한 프로그래밍
반응형

1. Transaction에서 rollback이 되지않는 경우는?

- checked exception은 트랜잭션에서 롤백되지않는다. (명시적인 예외처리가 필요한 것, try catch해야하는것 들)

- unchecked exception은 롤백대상 (예외처리를 하지 않아도 IDE에서 에러 뱉지 않음, Runtime Exception...)

 

2. checked exception 경우에도 rollback을 하고싶으면?

만약 B에서 에러가 발생했을때 A를 rollback시키고 싶다면? 이럴때 transaction을 이용한다

하지만 B의 exception이 checked exception이라면?

@Transactional
public void test() {
    A();
    B();
}

만약 아래처럼 try catch를 이용하면 에러를 catch에서 처리했기 때문에, rollback이 발생하지 않는다.

@Transactional
public void test() {
    A();
    try {
    	B(); // checked exception을 발생시키는 부분
    } catch {
    
    }
}

 

- @Transcational 어노테이션에 rollbackFor라는 옵션을 이용한다

해당 에러는 throw하고, rollbackFor옵션을 사용하면 checked exception도 확인할 수 있게 된다.

@Transactional(rollbackFor = {Exception.class})
public void test() {
    A();
    B(); // checked exception을 발생시키는 부분에서는 error throw
}

 

- checked 예외를 unchecked 예외로 변경하여 throw한다

public void B() {
    try {
    
    }catch(Exception e) {
    	throw new RuntimeException("예외");
    }
}


@Transactional
public void test() {
    A();
    B(); // unchecked exception으로 바꿈
}

 

3. 아래와 같은 경우에는 롤백이 일어날까 일어나지 않을까?

	// ATest 클래스
    @Transactional
    public void A() {
        try {
            test.B();
        } catch (RuntimeException e) {
            System.out.println("예외 처리");
        }
    }
    
    
    // BTest클래스
    @Transactional
    public void B() {
        //로직
        mapper.save();
        throw new RuntimeException("예외"); // unchecked exception
    }

 

해당 코드는 다음과 같이 동작한다.

1. A클래스의 트랜잭션이 실행된다.

2. B메소드가 실행되면서 1번의 트랜잭션에 참여한다 (기본 propagation 속성이 PROPAGATION_REQUIRED)

3. save를 실행하는 부분의 처리가 끝나고 트랜잭션의 완료처리 (completion)을 진행함

4. checked Exception이 일어나면서 트랜잭션이 완료처리 됨

5. checked Exception때문에 해당 트랜잭션을 롤백 규칙을 적용(기본 규칙적용), 해당 메소드에서 바로 롤백하지않고 rollback mark를 함 (해당 마크는 전역으로 관리함)

6. A의 로직 수행 > 1번에서 생성된 트랜잭션의 완료처리가 진행, 이때 rollback mark를 확인해서 값이 true라면 rollback진행

따라서 이경우에는 롤백되어버린다

 

해당 propagation 옵션을 변경할경우 해당 rollback mark 문제를 해결할 수 있다

 

PROPAGATION_REQUIRES_NEW 사용시 매번 새로운 트랜잭션 생성

- A와 B가 각각 트랜잭션을 생성하고, 매 번 commit하므로 서로의 rollback에 영향을 미치지 않음

하지만 오버헤드 발생할 수 있음 (매번 새로운 커넥션 생성)

반응형
반응형

1. 사전 준비

- pom.xml 수정

다음과 같은 내용을 pom.xml에 추가합니다 1. mybatis (버전은 스프링 버전에 맞게 선택 가능) 2. mybatis-spring

3.dbcp2, 4. mysql-connector 5. spring-jdbc

		<!-- MySQL -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.3</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>2.0.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
			<version>2.7.0</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.47</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

 

- jdbc.properties생성

WEB-INF에 properties폴더 생성 > 다음과 같은 jdbc.properties 파일 생성

jdbc.username = user_id
jdbc.password = password
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://URL/DB_NAME?serverTimezone=Asia/Seoul&useSSL=false

 

- mysql 연동

다음과 같이 root-context.xml에 추가한다 

	<!-- MySQL dataSource -->
	<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
    
	<!-- jdbc.properties를 찾게 해주는 placeholder -->    
 	<context:property-placeholder location="/WEB-INF/properties/jdbc.properties" />

2. mybatis 연동 및 사용

- TestModel 생성

DB로부터 받아온 데이터를 담을 Model 클래스를 생성한다

@Getter
@Setter
public class Test_Model {

	private String date;

	private int count;

}

 

- mybatis-config.xml 생성

main/resources에 다음과 같은 mybatis-config.xml을 생성한다. 등록된 모델을 test라는 별칭으로 사용할 수 있다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration 
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<typeAliases>
		<typeAlias type="test.main.model.Test_Model" alias="test" />
	</typeAliases>

</configuration>

 

- rmcMapper.xml 생성

main/resource에 mappers라는 패키지를 만들고 패키지 내에 다음과 같은 testMapper.xml을 생성한다.

resultType를 config.xml에 지정한 test타입으로 반환하고 사용할 쿼리의 id를 getData로 지정한다.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="test.main.mapper.testMapper">
	<select id="getData" resultType="test">
		select * FROM test_db
	</select>
</mapper>

 

- root-config.xml 수정

다음과 같이 root-config.xml에 mybatis를 설정하기 위한 위한 코드를 추가한다. mybatis-config.xml과 Mapper.xml을 bean으로 등록하고 sqlsession을 bean으로 등록한다.

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:/mybatis-config.xml" />
		<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml"></property>
	</bean>
	
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
		<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
	</bean>
	

3. Controller, Service, DAO 만들기

- Controller 생성

다음과 같은 Controller를 작성한다. 간단하게 getData를 해오는 코드이다.

@Controller
@RequestMapping(value = "/brokenSVC")
public class testController {
	@Autowired
	private BrokenService testService;
	
	@GetMapping(value = "test")
	public String home(Model model) {
		System.out.println(testService.getdata().get(0).getDate());
		return "testpage";
	}
}

 

- Service 생성

다음과 같은 Service를 작성한다.

@Service
public class BrokenService {

	@Autowired
	private testDAO testdao;

	public List<Test_Model> getData() {
		return testdao.getData();
	}
}

 

- DAO 생성

다음과 같은 DAO를 작성한다.

@Repository
public class BrokenDAO {
	
	@Autowired private SqlSession sqlSession;
	private static final String Namespace = "test.main.mapper.rmcMapper"; //rmcMapper가 있는 위치
	
	public List<Test_Model> getData() {
		return sqlSession.selectList(Namespace+".getData");
	}
}

4. 결론

mybatis를 사용할 경우 기존 방법은 DAO에 쿼리문이 섞여있다는 단점이 있었는데 쿼리를 완벽하게 분리하여 사용할 수 있다는 장점이 있다.

반응형
반응형

1. HTTPS란

  • HTTP는 인터넷에서 웹 서버와 사용자 브라우저간 문서를 전송하기 위한 통신 규약이다. HTTP는 정보를 텍스트로 주고받기 때문에 누군가가 네트워크 이를 가로챈다면 정보를 확인할 수 있어서 보안에 취약하다 HTTPS는 이를 암호화하여 보안상의 문제를 해결해주는 프로토콜이다
  • HTTPS는 새로운 프로토콜이 아닌 HTTP와 통신을 하는 소켓 부분을 SSL이나 TSL로 대체하는 것 즉 사용자는 SSL과 통신하여 암호화된 정보를 주고받는다.
  • HTTPS는 공개키 암호화 방식을 사용한다. 공개키 암호화 방식에는 비밀키와 공개키가 존재하는데 비밀키는 서버 쪽에 가지고 있으며 공개되어서는 안 되고, 공개키는 누구에게나 공개되어도 괜찮은 키이다. 먼저 클라이언트는 공개키를 통해 텍스트를 암호화하고 이는 비밀키로만 복호화할 수 있다 따라서 다른 사람이 정보를 가로채더라도 비밀키만 안전하다면 괜찮은 방식이다.

2. SpringBoot에 HTTPS 적용하기

 

#1) 사전준비

  • 스프링 부트 2.2.5 RELEASE, Dependency로 spring Web사용
  • Maven 사용 (Gradle사용 가능)
  • JAVA 1.8, 내장 톰캣 9 사용

 

#2) JAVA를 이용한 인증서 만들기

Intellij 터미널에서 다음과 같이 keystore.p12라는 키를 만든다 입력해야 할 이름, 도시 등 정보는 임의로 입력한다. 인증받은 키가 아니고 로컬에서 확인하는 용도 이므로 자유롭게 입력한다. 만약 인증된 키를 사용할 경우 값을 지불하고 이용해야 한다.

keytool -genkey -alias spring -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 4000

 

#3) application.properties 추가

키가 만들어졌다면 다음과 같은 내용을 추가한다

server.ssl.key-store= keystore.p12
server.ssl.key-store-password= 본인이 입력한 패스워드
server.ssl.key-store-type= PKCS12
server.ssl.key-alias= spring

#4) 프로젝트 실행

http://localhost:8080가 아닌 https://localhost:8080으로 접속해야 작동하는 것을 확인할 수 있다.


3. HTTP2란?

  • HTTP는 그림과 같이 기본적으로 1번의 연결로 1개의 리소스를 요청할 수 있다. 따라서 동시 요청이 어렵고 느리다.
  • 만약 총 4개의 데이터 a, b, c, d가 있다고 할 때 a가 만약 요청 시간이 가장 길다면 b, c, d는 순차적으로 요청시간이 느려지게 될 것이다. 이를 HOL Blocking 이라 한다
  • 무거운 Header구조를 가지고 있어 속도가 느리다
  • HTTP2는 HTTP/1.1의 무거운 헤더 정보를 압축하는 HPACK방식을 사용하여 데이터의 크기를 줄였다
  • 하나의 연결에 한 번의 정보가 아닌 여러 개의 메시지를 전달하여 순서에 상관없이 Stream으로 받을 수 있게 개선되었다
  • HTTP/1.1보다 약 15~50% 향상된다고 한다 (웹 기준)

 

4. SpringBoot에 HTTP2 적용하기

#1) 사전 준비

  • 스프링 부트 2.2.5 RELEASE, Dependency로 spring Web사용
  • Maven 사용 (Gradle사용 가능)
  • JAVA 9, 내장 톰캣 9 버전 사용 (이 버전을 이용하지 않을 시 과정이 복잡하여 버전을 올리는 것을 추천)

 

#2) 인텔리제이에서 JAVA 1.8로 사용하다가 9 버전으로 바꾸고 에러가 나는 경우

File > ProjectStructure > Project Settings > project에 Project SDK 버전을 JAVA 9 버전으로 변경

File > ProjectStructure > Project Settings > Modules > Dependencies에 ModuleSDK버전을 JAVA 9 버전으로 변경

 

#3) application.properties 추가

키가 만들어졌다면 다음과 같은 내용을 추가한다. HTTP2는 SSL이 반드시 적용되어야 사용할 수 있다. HTTPS 과정을 마치고 진행해야 한다.

server.ssl.key-store= keystore.p12
server.ssl.key-store-password= 본인이 입력한 패스워드
server.ssl.key-store-type= PKCS12
server.ssl.key-alias= spring
server.http2.enabled=true

#4) 프로젝트 실행

프로젝트 실행 후 다음과 같이 요청을 날려보면 HTTP/2로 통신하는 것을 확인할 수 있다.

curl -I -k --http2 https://localhost:8080

 

반응형
반응형

1. 스프링 시큐리티란?

  • authentication(인증) 과 authorization(권한 부여)를 제어하는 프레임워크
  • De-facto (정해진 표준은 아니지만 사실상 표준)
  • servlet filter를 기반으로함(리퀘스트를 가로채서 선처리하고 리스폰스를 가로채서 후처리함) ex)filter로 UTF-8 인코딩 가능
  • 필터로 등록된 DelegatingFilterProxy가 어떤 URL을 가로챌것인지 정한다


2. 스프링 시큐리티 실습하기 (커스텀 로그인)

 

1) pom.xml에 spring-security dependency 추가하기

<dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>5.2.1.RELEASE</version>
</dependency> 
<dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>5.2.1.RELEASE</version
</dependency> 
<dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>5.2.1.RELEASE</version>
</dependency>

 

2) web.xml에 filter추가

	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

 

3) security-context.xml 작성

/WEB-INF/spring/appServlet 경로에 security-context.xml을 작성한다. 먼저 name은 text, password는 test1234로 ROLE_USER를 만들고 /secured의 경로로 접근할경우 ROLE_USER인 사람만 접근할 수 있게 설정한다

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
            	<security:user name="test" authorities="ROLE_USER" password="test1234"/>
            </security:user-service>
        </security:authentication-provider>
	</security:authentication-manager>


	<security:http use-expressions="true" auto-config="true">
		<security:intercept-url pattern="/secured/**"
			access="hasRole('ROLE_USER')" />
            
		<security:form-login login-page="/login" />
		<security:logout logout-url="/logout" />
	</security:http>
</beans>

 

4) web.xml 수정

다음과 같이 context-param내부에 security-context.xml경로를 추가한다

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/spring/root-context.xml
			/WEB-INF/spring/appServlet/security-context.xml
		</param-value>
	</context-param>

5) LoginController 작성

@Controller
public class LoginController {

  @RequestMapping(value=“/login”, method = RequestMethod.GET))
  public String login() {

  	return "login";
}

 

5) login.jsp 작성

다음과 같은 login.jsp를 작성한다 csrf 토큰값을 함께 사용하여 csfr공격에 대비한다

<form action="<c:url value="/login" />" method='POST'>

  <table>
    <tr><td>User:</td><td><input type='text' name='username' value=''></td>   </tr>
    <tr><td>Password:</td><td><input type='password' name='password' /></td>  </tr>
    <tr><td colspan='2'><input name="submit" type="submit“ value="Login" /></td> </tr>

    <input type="hidden"  name="${_csrf.parameterName}"value="${_csrf.token}"/>
  </table>

</form>

 

6) 결과

secured로 요청을 보내면 custom login form으로 요청이 전송되고 이를 지정된 ROLE_USER가 맞는지 판단함

 

7) 추가정보

로그인 실패 : /login?error로 요청이 전달됨

로그아웃 성공 : /login?logout으로 요청이 전달됨

따라서 다음과 같이 처리가능

@RequestMapping(value="/login", method = RequestMethod.GET)
public String login(
    @RequestParam(value = "error", required = false) String error, 
    @RequestParam(value="logout", required=false) String logout, 
    Model model) {

    if (error != null) {
        model.addAttribute("errorMsg", "Invalid username and password");
    }

    if(logout != null) {
         model.addAttribute("logoutMsg", "You have been logged out successfully ");
     }

     return "login";

}

 

세션의 시간 설정 및 중복로그인 방지 방법 invalid-session-url = 세션 시간초과

max-sessions = 최대 로그인하는 수, expired-url 중복로그인시 보낼 url

	<security:session-management invalid-session-url="/login?timeout=1">
			<security:concurrency-control max-sessions="1" expired-url="/login" />
		</security:session-management>
반응형
반응형

1. MVC란?

  • Model : 애플리케이션의 데이터
  • View : 사용저의 요청을 HTML로 그려주는 역할
  • Controller : 사용자에 요청에 따라 모델을 만들거나, 뷰에게 넘겨주는 역할

다음과 같은 spring MVC 아키텍처에서 우리는 controller와 view부분만 만들면 되며 나머지 부분은 스프링에서 제공한다. 사용자의 request가 들어올 경우 dispatcher servlet가 요청을 가로채고 handler Mapping에 전달한 후 컨트롤러로 요청이 전달하고 우리가 만든 컨트롤러에 의해 view 네임을 선택하면 view Resolver에 의해 view네임을 가진 jsp를 선택하게 된다.


2. 스프링 MVC 실습하기

사전준비

다음과 같이 pom.xml에 필요한 라이브러리를 추가한다. 게터, 세터를 용이하게 만들어주는 lombok mysql과 연동하기 위한 dbcp, mysql-connector, spring-jdbc를 추가한다. 다음과 같은 deoendency를 추가한다

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.4</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>5.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.38</version>
		</dependency>

 

#1) 프로젝트 생성하기

다음과 같이 Spring Legacy Project의 Spring MVC Project를 클릭하고 helloSpringMVC라는 프로젝트를 생성한다

 

#2) home.jsp 수정

다음과 같이 기본으로 있는 home.jsp를 수정한다. 클릭할 시 createoffer로, offers로 요청을 보내는 a태그가 2개 있다. 이때 ${pageContext.request.contextPath}는 서버에서 설정된 contextPath를 의미한다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
</head>
<body>
<p> <a href="${pageContext.request.contextPath}/offers">show current offers</a>
<p> <a href="${pageContext.request.contextPath}/createoffer">Add a new offer</a>
</body>
</html>

 

#3) OffersController 생성하기

다음과 같은 OffersController를 생성한다.

package kr.ac.hansung.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import kr.ac.hansung.model.Offer;
import kr.ac.hansung.service.OffersService;

@Controller
public class OffersController {

	@RequestMapping("/offers")
	public String showOffers(Model model) {
		return "offers";
	}
	
	@RequestMapping("/createoffer")
	public String createOffer(Model model) {
		return "createoffer";
	}
}

 

#4) offers.jsp createoffers.jsp생성하기

jsp코드는 이후에 수정될 예정이니 controlle가 jsp로 제대로 요청을 전달하는지 확인만 할 목적으로 기본 jsp2개를 생성한다

 

#5) 실행하기

다음과 같이 제대로 매핑이 된 것을 확인할 수 있다.

 

#6) dao, model, service 생성하기

다음 그림과 같은 구조를 생성하기 위해 dao, model, service를 생성한다. 사용자의 요청이 들어오면 Dispatcher Servlet에서 이를 컨트롤러에 전달해주고 컨트롤러는 이에 맞는 서비스에게 전달하고 서비스는 DAO에게 DAO는 DB에 접근하고 모델을 통해 View로 사용자에게 response 하는 구조이다

 

OfferDAO

처음 프로젝트 생성할 때 패키 지명을 kr.ac.hansung으로 지정했기 때문에 DAO의 패키지명을 kr.ac.hansung.dao로 패키지를 만들고 내부에 다음과 같은 OfferDAO라는 클래스를 생성한다. @Repository 에노테이션을 통해 DAO클래스임을 명시하고 @Autowired를 통해 DB에 접근할 jdbctemplet을 의존성 주입시킨다.

package kr.ac.hansung.dao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import kr.ac.hansung.model.Offer;

@Repository
public class OfferDAO {
	private JdbcTemplate jdbcTemplateObject;

	@Autowired
	public void setDataSource(DataSource dataSource) { 
		this.jdbcTemplateObject = new JdbcTemplate(dataSource);
	}
	
	public int getRowCount() {
		String sqlStatement = "select count(*) from offers";
		return jdbcTemplateObject.queryForObject(sqlStatement, Integer.class);
	}
	
	public Offer getOffer(String name) {
		String sqlStatement = "select * from offers where name=?";
		
		return jdbcTemplateObject.queryForObject(sqlStatement, new Object[]{name}, new RowMapper<Offer>() {

			@Override
			public Offer mapRow(ResultSet rs, int rowNum) throws SQLException {
				Offer offer = new Offer();
				offer.setId(rs.getInt("id"));
				offer.setName(rs.getString("name"));
				offer.setEmail(rs.getString("email"));
				offer.setText(rs.getString("text"));
				
				return offer;
			}
		});
	}
	
	public List<Offer> getOffers() {
		String sqlStatement = "select * from offers";
		
		return jdbcTemplateObject.query(sqlStatement, new RowMapper<Offer>() {

			@Override
			public Offer mapRow(ResultSet rs, int rowNum) throws SQLException {
				Offer offer = new Offer();
				offer.setId(rs.getInt("id"));
				offer.setName(rs.getString("name"));
				offer.setEmail(rs.getString("email"));
				offer.setText(rs.getString("text"));
				
				return offer;
			}
		});
	}
	
	public boolean insert(Offer offer) {
		String sqlStatement = "insert into offers (name, email, text) values (?,?,?)";
		String name = offer.getName();
		String email = offer.getEmail();
		String text = offer.getText();
		
		return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text}) == 1);
	}
	
	public boolean update(Offer offer) {
		String sqlStatement = "update offers set name=?, email=?, text=? where id=?";
		String name = offer.getName();
		String email = offer.getEmail();
		String text = offer.getText();
		int id = offer.getId();
		
		return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text, id}) == 1);
	}
}

 

Offer

kr.ac.hansung.model 패키지를 생성하고 다음과 같은 Offer클래스를 생성한다. lombok의 게터 세터 에노테이션을 통하여 게터 세터를 만든다

package kr.ac.hansung.model;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Offer {
	private int id;
	private String name;
	private String email;
	private String text;
	
	public Offer() {
		
	}

	public Offer(String name, String email, String text) {
		this.name = name;
		this.email = email;
		this.text = text;
	}
	
	public Offer(int id, String name, String email, String text) {
		this.id = id;
		this.name = name;
		this.email = email;
		this.text = text;
	}

	@Override
	public String toString() {
		return "Offer [id=" + id + ", name=" + name + ", email=" + email + ", text=" + text + "]";
	}
}

 

OfferService

kr.ac.hansung.service 패키지를 생성하고 다음과 같은 OfferService 클래스를 생성한다. Service에노테이션을통해 이클래스가 서비스임을 명시하고 Autowired를 통해 DAO객체를 의존성 주입시킨다.

package kr.ac.hansung.service;

import java.util.List;

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

import kr.ac.hansung.dao.OfferDAO;
import kr.ac.hansung.model.Offer;

@Service
public class OffersService {

	private OfferDAO offersDao;


	@Autowired
	public void setOffersDao(OfferDAO offersDao) {
		this.offersDao = offersDao;
	}


	public List<Offer> getCurrent() {
		// TODO Auto-generated method stub
		return offersDao.getOffers();
	}
	
	
	
}

 

#7) service-context.xml dao-context.xml 생성하기

기존의 HomeController가 작동할 수 있었던 이유는 web.xml을 보면 알 수 있다. 먼저 에노테이션이 활성화되어있고 ViewResolver를 사용하여 jsp의 이름을 받을 경우 name.jsp로 변경하여 연결해주는 것을 확인할 수 있다. 또한 base-package가 정해져 있어 패키지의 에노테이션을 확인하는것을 알 수 있다. 이처럼 service와 dao의 bean을 생성하고 autowired해주기위해 service-context.xml 와 dao-context.xml을 생성한다. 

 

먼저 src/main/webapp/WEB-INF/spring/appServlet에 다음과 같은 service-context.xml을 생성한다. kr.ac.hansung.service패키지를 스캔하도록 되어있으며 에노테이션을 사용한다고 적혀있다.

 

service-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.1.xsd">


	<context:annotation-config></context:annotation-config>
	<context:component-scan base-package="kr.ac.hansung.service"></context:component-scan>
</beans>

 

다음으로는 service-context.xml과 같은 위치인 src/main/webapp/WEB-INF/spring/appServlet에 다음과 같은 dao-context.xml을 생성한다. dao-context에는 dbcp를 활용하여 DB에 접근하게 할 dataSource Bean이 정의되어있다. 또한 placeholder로 dbcp에서 참고할 properties파일의 위치가 정의되어있다.

 

dao-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.1.xsd">


	<context:annotation-config></context:annotation-config>
	<context:component-scan base-package="kr.ac.hansung.dao"></context:component-scan>

	<bean id="dataSource"
		class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName"
			value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>


	<context:property-placeholder
		location="/WEB-INF/props/jdbc.properties" />
		
</beans>

 

#8) jdbc.properties 만들기

src/main/webapp/WEB-INF에 props라는 폴더를 생성하고 다음과 같은 jdbc.properties 파일을 생성한다. 이는 dao-context.xml에서 dbcp를 이용하여 db에 접근할 때 사용하는 파일으로서 다음과 같다.

jdbc.username = 아이디를 입력
jdbc.password = 비밀번호를 입력
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/csemall

 

#9) web.xml 수정하기

이렇게 dao와 service를 생성하였어도 실행할 경우 에러가 발생한다. 이는 스프링에서 dao-context.xml와 service-context.xml을 바라보고 있지 않기 때문이다. 따라서 다음과 같이 web.xml을 수정한다. 기존에는 root-context.xml만 바라보고 있었지만 생성한 service-context.xml과 dao-context.xml을 추가해줌으로써 스프링이 xml파일을 바라보게 한다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets 
		and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
		/WEB-INF/spring/root-context.xml
		/WEB-INF/spring/appServlet/service-context.xml
		/WEB-INF/spring/appServlet/dao-context.xml
		</param-value>
	</context-param>

	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

 

#10) offers.jsp수정

DB에 존재하는 정보를 받아오는 jsp를 구성하기 위해 다음과 같이 offers.jsp를 수정한다.

<%@ page language="java" contentType="text/html; charset=US-ASCII"
	pageEncoding="US-ASCII"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Insert title here</title>
</head>
<body>

	<c:forEach var="offer" items="${offers}">
		<p>
			<c:out value="${offer}">
			</c:out>
		</p>
	</c:forEach>

</body>
</html>

 

#11) OffersController 수정

다음과 같이 OffersController을 수정한다.

package kr.ac.hansung.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import kr.ac.hansung.model.Offer;
import kr.ac.hansung.service.OffersService;

@Controller
public class OffersController {
	
	private OffersService offersService;
	
	@Autowired
	public void setOffersService(OffersService offersService) {
		this.offersService = offersService;
	}

	@RequestMapping("/offers")
	public String showOffers(Model model) {
		
		List<Offer> offers = offersService.getCurrent();
		
		model.addAttribute("offers", offers);
		return "offers";
	}
	
	@RequestMapping("/createoffer")
	public String createOffer(Model model) {
		
		return "createoffer";
	}
}

 

#12) 프로젝트 실행

프로젝트를 실행할 경우 다음과 같은 실행화면을 확인할 수 있다.

 

#13) MVC 프로젝트 정리

1. 사용자는 /으로 접속하게 되면 HomeController에 의해 home.jsp로 접속하게 된다.

2. 사용자가 offers로 요청을 보내게 되면 디스패쳐 서블릿에 의해 OffersController로 요청을 전달하고, /offers로 매핑된 showOffers함수를 호출한다. showOffers는 autowired 된 offerService의 getCurrent() 함수를 호출한다.

3. offerService의 getCurrent함수는 autowried 된 offerDAO의 getOffers를 호출한다

4. offerDAO의 getOffers는 DB에 저장된 객체를 리스트로 저장하여 서비스로 반환하고 서비스는 다시 컨트롤러로 반환한다.

5. 컨트롤러에서는 모델을 이용하여 view에 있는 jsp로 결과값과 함께 반환하고, 사용자는 마지막으로 반환된 offers.jsp를 확인하게 된다.

 

 

반응형
반응형

지난 #8 강의에 이어 MYSQL과 연동하여 Update, delete, insert 하는 방법을 설명하도록 한다.

 

1. INSERT

먼저 지난번 query와 달리 update메소드를 사용하며 다음과 같이 사용한다 update메서드 내부에는 insert sql문, new Object로 name, email, text를? 에 넣어주는 방식으로 사용하며 이때 반환하는 값이 1이면 true를 리턴한다.

	public boolean insert(Offer offer) {
		String sqlStatement = "insert into offers (name, email, text) values (?,?,?)";
		String name = offer.getName();
		String email = offer.getEmail();
		String text = offer.getText();
		
		return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text}) == 1);
	}

2. UPDATE

insert와 같이 update메소드를 사용하며 사용방법은 다음과 같다.

	public boolean update(Offer offer) {
		String sqlStatement = "update offers set name=?, email=?, text=? where id=?";
		String name = offer.getName();
		String email = offer.getEmail();
		String text = offer.getText();
		int id = offer.getId();
		
		return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text, id}) == 1);
	}

3. DELETE

마찬가지로 update 메소드를 사용하며 사용방법은 다음과 같다.

	public boolean delete(int id) {
		String sqlStatement = "delete from offers where id = ?";
		
		return (jdbcTemplateObject.update(sqlStatement, new Object[]{id}) == 1);
	}
반응형
반응형

1. Data Access Layer

DB에 접근하기 위해 다음 그림과 같이 DAO는 JDBC Template에 이는 JDBC Driver를 통해 Database에 접근하고 DataSource를 이용하여 DB에 실질적으로 커넥션 한다.


2. Data Source

데이터 베이스에 접근하기 위해 DataSource를 사용한다 DataSource의 종류는 다양하지만 DBCP라이브러리의 BasicDataSource를 통상 많이 사용하므로 실습에서도 이를 사용할 예정이다.


3. DAO란?

Data Access Object의 약자로서 서비스는 DB에 대해 아무것도 모르고, 실질적으로 DB에 접근하는 오브젝트를 의미한다. 쉽게 생각하여 DB의 CRUD를 실질적으로 수행하는 코드가 담긴 부분이다.


4. JDBC란?

JDBC란 DB와의 커넥션, SQL 사용, Close 커넥션 등 잡다한 일들을 해주는 프레임 워크이다. 기본 JAVA JDBC는 커넥션 등 try catch의 반복으로 반복적인 코드를 짜야하지만 Spring JDBC를 사용하면 SQL만 코드를 작성하고 나머지는 Spring 프레임워크에서 담당한다.


5. 실습하기

#1) 사전 준비

  • Mysql 5.7.28 설치

Mysql내부에는 csemall라는 DB, offers라는 테이블을 만들고 다음과 같은 구조로 이루어져 있다.

id(INT) name email text
1 Alice Alice@google.com alice
2 Bob Bob@google.com bob
3 Jun Jun@google.com jun

 

#2) Simple Spring Maven 프로젝트 실행

다음과 같은 helloDB프로젝트를 생성한다.

 

#3) pom.xml 수정

다음과 같은 dependency를 추가하고 저장을 누르면 maven에 라이브러리가 추가된 것을 확인할 수 있다.

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.38</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.1.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
			<version>2.1.1</version>
		</dependency>

 

#4) jdbc.properties 생성

props라는 패키지를 생성하고 그 밑에 jdbc.properties라는 파일을 생성한다. 파일 내부는 다음과 같다 이는 DB의 정보를 담고 있으며, 생성할 bean에 하드 코딩하는 것보다 좋은 방법이다.

jdbc.username = root
jdbc.password = 사용자 비밀번호를 입력
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/csemall

 

#5) beans.xml 생성

다음과 같이 beans.xml을 생성한다 context:annotation은 DAO클래스에서 사용할 @Autowired를 활성화한다는 의미이며 context:component는 @Component 에노 테이션이 있는 어떤 패키지를 scan 할 건지 명시하는 부분이다. bean으로 생성한 dataSource는 #4에서 생성한 jdbc.properties를 사용하여 실제 DB와 커넥션을 하는 부분이다 dbcp2의 BasicDataSource클래스를 사용하고 있고 이 클래스는 DataSource인터페이스를 따르는 클래스이다. 마지막으로 context:property-placeholder는 앞서 사용할 jdbc.properties가 어떤 위치에 있는지 명시하는 부분이다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
	
	<context:annotation-config></context:annotation-config>
	<context:component-scan base-package="csemall"></context:component-scan>

	<bean id="dataSource"
		class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
		<property name="driverClassName"
			value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>


	<context:property-placeholder
		location="kr/ac/hansung/spring/props/jdbc.properties" />

</beans>

 

#6) OfferDAO 클래스 생성

src/main/java아래의 csemall이라는 패키지를 만들고 다음과 같은 Class를 생성한다. @Component 에노테이션은 이 클래스는 id가 offerDAO라는 bean을 하나 생성하겠다는 의미이다. 또한 JdbcTemplate를 사용하여 bean에 생성한 dataSource를 주입(@Autowired)시켜 DB와 실질적인 커넥션을 해주는 부분이 담겨있다. getRowCount() 부분은 다음과 같은 sql을 query 하고 결과는 Integer로 받는다는 의미이다.

package csemall;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component("offerDAO")
public class OfferDAO {
	private JdbcTemplate jdbcTemplateObject; //얘는 new를 사용해됨 이후 DataSource는 DI를 통해 주입 시킬예정	

	@Autowired
	public void setDataSource(DataSource dataSource) { //DataSource는 인터페이스인데 우리가 앞서 bean에 선언한 datasource가 이 인터페이스를 따르고 DI한다
		this.jdbcTemplateObject = new JdbcTemplate(dataSource);
	}
	
	public int getRowCount() {
		String sqlStatement = "select count(*) from offers";
		return jdbcTemplateObject.queryForObject(sqlStatement, Integer.class);
	}
}

 

#6) Offer.java 클래스 생성

Lombok을 이용해 @Getter, @Setter를 생성하였고 DB에 저장된 id, name, email, text를 받는다. 이는 추후에 사용될 클래스이다.

package csemall;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Offer {
	private int id;
	private String name;
	private String email;
	private String text;
	
	public Offer() {
		
	}

	public Offer(String name, String email, String text) {
		this.name = name;
		this.email = email;
		this.text = text;
	}
	
	public Offer(int id, String name, String email, String text) {
		this.id = id;
		this.name = name;
		this.email = email;
		this.text = text;
	}
}

 

#7) MainApp.java 클래스 생성

다음과 같이 코드를 작성한다 ClassPathXmlApplicationContext를 이용하여 beans.xml을 읽어오고 읽어온 context의 offerDAO라는 빈을 이용하여 getRowCount() 함수를 출력한다

package csemall;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext context =
				new ClassPathXmlApplicationContext("kr/ac/hansung/spring/csemall/beans/beans.xml");
		
		OfferDAO offerDAO = (OfferDAO) context.getBean("offerDAO");
		System.out.println(offerDAO.getRowCount());
		
		context.close();
	}

}

 

#8) 실행화면

MainApp.java를 실행하면 다음과 같은 결과를 확인 할 수 있다.

반응형
반응형

1. Annotation이란?

앞 강의에서 DI란 무엇인지와 생성자를 이용하여 의존성 주입을 하는 방법을 실습해 보았다면 이번 강의에서는 Annotation을 이용하여 의존성 주입을 해볼 예정이다.

먼저 에노테이션이란 기존 XML을 이용하여 의존성 주입을 하는 것이 아닌 @Required 같은 태그를 이용하여 의존성 주입을 하는 것을 의미한다. 의존성 주입 에노테이션으로는 다음과 같은 3가지가 있다

 

#1 @Required

XML의 Bean의 property에 해당하는 부분 중 필수적인 부분을 명시할 때 다음과 같이 사용한다. 쉽게 말해 XML에 bean중 property가 꼭 있어야 하니 없으면 에러가 발생한다고 생각하면 좋다 즉 @required를 사용할때 xml에 property에 name에 해당하는 부분이 반드시 있어야 에러가 발생하지 않는다.

public class Boy{
private String name;

@required
public void setName(String name){
  this.name = name;
  }
  
}

 

#2 @Autowired

이전 강의에서 의존성 주입을 할 때 bean에 property를 사용하여 세터를 통한 주입을 했었다. 하지만 Class의 기존 세터 부분을 지우고 @Autowired 에노테이션을 추가하면 자동으로 의존성 주입이 이루어진다. 이는 다음 코드처럼 Boy클래스는 property를 사용하여 세터를 통해 bean을 생성했고 college라는 bean은 propery를 통해 boy라는 bean을 의존성 주입 했지만 College클래스에 @Autowired 에노테이션을 추가하고 세터부분을 지우고, 마찬가지로 bean의 property부분을 지우면 자동으로 의존성 주입이 이루어지는것을 확인할 수 있다.

public class Boy {
 private String name;
 private int age;
   
 //   getters and setters ...
}

public class College {
 
 @Autowired
 private Boy student; 
 
 //이부분부터
 public void setStudent(Boy aboy){
  this.stuendt = aboy;
 }
 //이부분까지 삭제
 
}

<bean id=“boy” class=“Boy”>
 <property name=“name” value=“Rony”/>
 <property name=“age” value=“10”/>
</bean>

<bean id=“college” class=“College”>
  <property name=“student” ref=“boy”/> //이부분 삭제
</bean>

 

#3 @Qualifier

만약 Autowired를 할때 다음과 같이 Boy 클래스의 bean이 두 개 있으면 어떤 걸 자동으로 Autowired를 할지 몰라 오류가 생긴다 따라서 이럴 때 @Qualifier를 사용한다. 다음과 같이 bean에는 qualifer의 value를 지정하고 Class에는 @Qualifier의 에노테이션과 함께 value를 설정하면 어떤 bean을 주입할지 정하게 된다

<bean id=“boy1” class=“Boy”>
 <qualifier value=“rony”/>
 <property name=“name” value=“Rony”/>
 <property name=“age” value=“10”/>
</bean>

<bean id=“boy2” class=“Boy”>
 <qualifier value=“tony”/>
 <property name=“name” value=“Tony”/>
 <property name=“age” value=“8”/>
</bean>

<bean id=“college” class=“College”>
</bean>


public class College {
 
@Autowired
@Qualifier(value=“tony”)
 private Boy student; 
 
  //   getters ...
}

 

#4 @Resource

@Qualifer가 value를 지정해서 의존성 주입을 시켜줬다면 Resource는 bean의 id로 의존성 주입을 해준다. 다음과 같이 사용할 수 있다.

public class College {
  
  @Resource(name=“boy1”)
  private Boy student; 
 
  //   getters and setters ...
}

 

#5 @Component

 

@Component는 Bean.xml에 빈을 사용한다 명시 하지 않아도 자동으로 클래스를 Bean으로 만들어주는 에노테이션이다

@Component
public class College {
  
  private Boy student; 
 
  //   getters and setters ...
}

2. Annotation 실습

실습할 예제는 이전 DI강의에서 사용했던 프로젝트로 실습하겠습니다.

#1) animal.xml수정

먼저 animal.xml을 다음과 같이 수정합니다. 생성자로 주입했던 constructor-arg 부분을 지워 주고 애노테이션을 사용한다는 코드를 추가합니다

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	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-4.3.xsd">
	<context:annotation-config></context:annotation-config>
	
	<bean id="dog" class="kr.ac.hansung.helloDI.Dog">
		<property name="myName" value="poodle"></property>
	</bean>

	<bean id="cat" class="kr.ac.hansung.helloDI.Cat">
		<property name="myName" value="bella"></property>
	</bean>

	<bean id="petOwner" class="kr.ac.hansung.helloDI.PetOwner">
		<constructor-arg ref="dog"></constructor-arg>
	</bean>
</beans>

 

#2)  @Autowired추가

PetOwner.java클래스의 생성자 부분을 지우고 다음과 같이 @Autowired에노테이션을 추가합니다

package kr.ac.hansung.helloDI;

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

public class PetOwner {
	
		@Autowired
		private AnimalType animal;

		public void play() {
			animal.sound();
		}
}

다음과 같이 코드를 수정하고 Run 하게 되면 다음과 같은 에러가 발생하는 것을 확인할 수 있습니다. 앞서 공부한 것처럼 2개의 bean 중 어떤 bean을 주입할지 모르기 때문에 다음과 같은 error가 발생하는 것입니다. 따라서 2가지 방법을 통해 해결해보도록 하겠습니다.

error

#3) @Qualifier추가 및 animal.xml수정

다음과 같이 Qualifier에노테이션을 사용하여 다음과 같이 어떤 빈을 주입할지 선택합니다.

 

PetOwner.java

package kr.ac.hansung.helloDI;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class PetOwner {
	
		@Autowired
		@Qualifier(value="cat")
		private AnimalType animal;

		public void play() {
			
			animal.sound();
		
		}
}

 

animal.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	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-4.3.xsd">
	<context:annotation-config></context:annotation-config>
	
	<bean id="dog" class="kr.ac.hansung.helloDI.Dog">
		<property name="myName" value="poodle"></property>
		<qualifier value="dog"></qualifier>
	</bean>

	<bean id="cat" class="kr.ac.hansung.helloDI.Cat">
		<property name="myName" value="bella"></property>
		<qualifier value="cat"></qualifier>
	</bean>

	<bean id="petOwner" class="kr.ac.hansung.helloDI.PetOwner">
	</bean>
</beans>

 

#4) 실행

수정을 완료하였다면 다음과 같은 실행 결과를 확인할 수 있다.

qualierfier 실행 성공

 

#5) @Resource 추가 및 animal.xml수정

@Qualifier대신 Resource에노테이션을 사용해보자 다음과 같이 animal.xml을 수정한다

 

PetOwner.java

package kr.ac.hansung.helloDI;
import javax.annotation.Resource;

public class PetOwner {
		@Resource(name="dog")
		private AnimalType animal;

		public void play() {
			
			animal.sound();
		
		}
}

 

animal.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	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-4.3.xsd">
	<context:annotation-config></context:annotation-config>
	
	<bean id="dog" class="kr.ac.hansung.helloDI.Dog">
		<property name="myName" value="poodle"></property>
	</bean>

	<bean id="cat" class="kr.ac.hansung.helloDI.Cat">
		<property name="myName" value="bella"></property>
	</bean>

	<bean id="petOwner" class="kr.ac.hansung.helloDI.PetOwner">
	</bean>
</beans>

 

#6) 실행

수정을 완료하고 실행하면 다음과 같은 결과를 확인할 수 있다.

Resource 성공


3. 정리

@Autowired를 사용하여 bean의 의존성 주입을 하며, 여러 빈이 있을 경우 Qualifier에노테이션을 사용하여 빈을 선택하거나, bean의 id를 통해 의존성 주입을 하는 @Resource 에노테이션을 사용할 수 있다.

반응형

+ Recent posts