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

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를 확인하게 된다.

 

 

반응형
반응형

1. AOP란?

만약 다음과 같이 Bean으로 만들 클래스 내부에 로깅, 트랜잭션, 실제 코드인 비즈니스 로직이 함께 있다고 생각해보자. 로깅, 트랜잭션, 비즈니스 로직 순서대로 작동하겠지만 이는 좋은 코드라고 할 수 없다. 따라서 로깅, 트랜잭션과 비즈니스 로직을 분리하기 위해 AOP가 존재한다

Bank 클래스

AOP는 다음 그림처럼 선처리, 후처리(런타임시 짜집기함)를 가능하게 지원하기 때문에 메인 Bean을 만드는 클래스를 따로 두고 로깅, 트랜잭션을 선처리 후처리로 다음과 같은 구조를 만드는 것이다.

 

다음 그림처럼 a.f()가 실직적인 타겟이되는 joinpoint가 되고 그림 앞에 보라색, 주황색이 선처리 후처리를 담당하는 어드바이스가 된다. 어드바이스는 joinpoint는 Pointcut이 가리키게 된다

다음 그림을 보면 조금더 이해하기 쉽다

AOP 구조 설명


2. AOP실습

#1) spring-aspect를 이용한 실습

1. pom.xml dependency추가

다음과 같은 디펜던시를 추가하면, Maven Dependencies에 spring-aspects라이브러리가 추가된 것을 확인할 수 있다.

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>4.2.5.RELEASE</version>
		</dependency>

 

2. Logger 클래스 추가

다음과 같은 Logger클래스를 추가한다.

public class Logger{
	public void aboutToSound() {
		System.out.println("Before: about to sound");
	}
}

 

3. animal.xml Namespaces 수정 및 aop추가

다음과 같이 Namespaces에 aop를 추가한다

aop 추가

aop추가를 완료하였다면 다음과 같은 코드를 추가한다 <aop:aspect ref="Logger">를 통해 Logger클래스를 Aspect로 이용하겠다는 의미이며, <aop:pointcut expression="execution(void kr.ac.hansung.helloDI.*.sound())" id="selectSound" />를 통해 helloDI패키지의 sound() 함수를 joinpoint로 지정하겠다는 의미이다. 이때 *은 와일드카드로서 어떤 클래스든 상관없다는 의미를 가진다. <aop:before method="aboutToSound" pointcut-ref="selectSound" />는 before어드바이스를 추가하겠다는 의미로 aboutToSound라는 메서드를 이용하며 ref로는 위에 선언한 pointcut의 id를 가진다. 따라서 pointcut은 jointpoint를 가르키고 advice는 그 pointcut을 가리킨다

	<aop:config>
		<aop:aspect ref="Logger" id="myAspect">
			<aop:pointcut
				expression="execution(void kr.ac.hansung.helloDI.*.sound())"
				id="selectSound" />
			<aop:before method="aboutToSound"
				pointcut-ref="selectSound" />
		</aop:aspect>
	</aop:config>

 

4. 실행

메인 함수를 실행하면 다음과 같은 결과를 확인할 수 있다. 메인 함수에서는 Logger빈을 사용하지 않았지만 다음과 같이 before advice가 실행된 것을 확인할 수 있다.

실행

 

#2) AOP 에노테이션을 이용한 실습

1. animal.xml수정

다음과 같이 animal.xml을 수정한다 기존 코드를 주석 처리하고 <aop:aspectj-autoproxy>를 추가함으로 에노테이션을 사용하겠다고 명시한다.

<!-- 	<aop:config>
		<aop:aspect ref="Logger" id="myAspect">
			<aop:pointcut
				expression="execution(void kr.ac.hansung.helloDI.*.sound())"
				id="selectSound" />
			<aop:before method="aboutToSound"
				pointcut-ref="selectSound" />
		</aop:aspect>
	</aop:config> -->

	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 

2. Logger 클래스 수정

다음과 같이 Logger 클래스를 수정한다 @Aspect로 클래스가 Aspect라는 걸 명시하고 @Pointcut을 통해 sound() 메서드를 joinpoint로 지정하겠다는 것을 명시하고 @Before로 이전 어드바이스를 추가한다.

@Aspect
public class Logger{
	
	@Pointcut("execution(void kr.ac.hansung.helloDI.*.sound()")
	private void selectSound() {}
	
	@Before("selectSound()")
	public void aboutToSound() {
		System.out.println("Before: about to sound");
	}
}

 

3. 실행

모두 완료하였다면 다음과 같은 실행화면을 볼 수 있다

실행화면

반응형
반응형

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 에노테이션을 사용할 수 있다.

반응형
반응형

1. MVC 아키텍처란?

  • Model(Java Beans) 객체로 생각, 데이터를 나타냄
  • View (JSP) 사용자가 상태를 볼 수 있는 View page를 의미
  • Controller (Servlet) 뷰와 모델 사이에서 실질적으로 request를 처리하는 부분

MVC패턴

그림과 같은 MVC패턴은 다음과 같은 5가지 단계로 진행된다

1) 브라우저의 request를 Servlet으로 전달함

2) 객체를 생성하여 서블릿의 결과를 저장함

3) view페이지를 선택함

4) JSP 페이지에 객체를 전달함

5) JSP 페이지를 브라우저에 Response 함


2. MVC 패턴 실습하기

MVC패턴을 실제 프로젝트로 실습해 보자.

 

#1 프로젝트 생성

https://diqmwl-programming.tistory.com/18?category=740999의 프로젝트 생성 부분을 참고하여 MVCexample이라는 프로젝트를 생성한다

 

#2 index.jsp생성

다음 그림과 같이 WebContent폴더에 index.jsp를 생성한다

index.jsp생성

 

생성한 index.jsp는 로그인하기, 도움말이라는 링크를 제공하는 페이지로 다음과 같은 코드를 작성한다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!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>MVC Example</title>
</head>
<body>
MVC 예제 페이지 입니다.
	<p><a href="/MVCexample/home?action=login"> 로그인하기 </a></p>
	<p><a href="/MVCexample/home?action=help"> 도움말 </a></p>

</body>
</html>

 

코드를 생성하고 프로젝트를 실행한다면 index.jsp는 다음과 같이 실행된다.

index.jsp 실행

 

#3 Home 서블릿 생성

파일 작성을 완료하였다면 로그인하기, 도움말의 링크를 처리하기 위한 Home이라는 Servlet을 생성하기 위해 MVCeample >> Java Resources >> src >> controller라는 패키지를 생성한다.

패키지 생성을 완료하였다면 아래 그림과 같이 Home이라는 Servlet을 생성한다

서블릿 생성 1

 

index.jsp의 링크가 home으로 되어있으므로 URL Mapping을 다음과 같이 /home로 수정한다

서블릿 생성 2
서블릿 생성 3

서블릿 생성이 완료되었다면 다음과 같이 코드를 작성한다.

코드를 살펴보면 action이라고 request 받은 데이터를 String action에 저장하고 이 값이 login일 경우 view폴더의 loginform.jsp를, help일 경우 help.jsp, 이외의 경우는 error.jsp를 경로로 저장하게 된다.

이후 RequestDispatcher을 이용하여 설정한 경로로 forward 시켜주는 방식이다

package controller;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class Home
 */
@WebServlet("/home")
public class Home extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public Home() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		String action = request.getParameter("action");
		String page = null;
		
		if(action.equals("login")) {
			page = "/view/loginform.jsp";
		}
		else if(action.equals("help")) {
			page = "/view/help.jsp";
		}
		else {
			page = "/view/error.jsp";
		}
		
		RequestDispatcher dispatcher = request.getRequestDispatcher(page);
		dispatcher.forward(request, response);
	}

}

 

#4 view폴더, loginform.jsp, help.jsp error.jsp 생성

코드를 작성하고 현재 view 폴더와 loginform.jsp, help.jsp, error.jsp를 만들지 않았으므로 생성해주도록 한다.

먼저 WebContent 아래에 view라는 폴더를 생성시킨다. 이후 아래 그림처럼 loginform.jsp, help.jsp, error.jsp를 생성하고 다음과 같은 코드를 작성한다

view 폴더 구성

loginform.jsp

//loginform.jsp
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%>
<!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>
This is Login Page.

<form action="/MVCexample/doLogin" method="get">
	customer ID(id1, id2, id3, id4, id5)<br/>
	<input type="text" name="customerId"/> <br/>
	
	<input type="submit" value="submit"/> <br/>
</form>
</body>
</html>

 

help.jsp

<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%>
<!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>
This is help Page.
</body>
</html>

 

error.jsp

<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%>
<!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>
This is error Page.
</body>
</html>

모두 작성을 완료하였다면 index.jsp에서 링크를 클릭 시 다음과 같이 forward 된 것을 확인할 수 있다.

loginform.jsp

 

#5 Model 작성

loginform에서 사용자가 입력하는 id의 실질적인 객체를 만들기 위해 Model을 생성한다 먼저 MVCexample >> Java Resources >> src >> model 패키지를 생성한다. 이후 아래 사진과 같이 Customer 클래스를 생성한다

 

model 생성

생성한 Class의 코드는 다음과 같다 id, name, email을 가지고 있으며 Customer이라는 생성자와, 각각의 게터, 세터 메소드가 정의되어있다.

package model;

public class Customer {

	private String id;
	private String name;
	private String email;
	
	public Customer(String id, String name, String email) {
		this.id = id;
		this.name = name;
		this.email = email;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}
	
}

 

#6 Service 생성

실제로 만든 모델에 데이터를 저장하거나, 데이터를 읽어오거나 하는 함수들이 정의된 서비스이다. 사용자의 요청이 들어왔을 때 서블릿에서 이를 처리하는 함수를 구현해도 상관없으나 Service로 분리하여 보다 보기 좋은 코드를 구현한다

model 생성과 같은 방법으로 MVCexample >> Java Resources >> src >> service 패키지를 생성한다. 이후 다음 그림처럼 CustomerService 클래스를 생성한다

service 생성

실제 코드는 다음과 같다. 먼저 CustomerService를 생성하면 customers라는 Map을 생성한다 이때 String은 id, 값으로는 Customer 모델을 받고 customers 객체가 생성될 때 id1~id5의 정보를 미리 저장해 놓는다. 또한 DoLogin 서블릿에서 사용할 findCustomer함수를 정의한다 이는 검색한 아이디가 있을 경우 id의 cusomer을 반환하고 없을 경우 null을 반환한다.

 

package service;

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

import model.Customer;

public class CustomerService {
	
	private Map<String, Customer> customers;

	public CustomerService() {
		customers = new HashMap<String, Customer>();
		
		addCustomer( new Customer("id1", "test1", "test1@naver.com"));
		addCustomer( new Customer("id2", "test2", "test2@naver.com"));
		addCustomer( new Customer("id3", "test3", "test3@naver.com"));
		addCustomer( new Customer("id4", "test4", "test4@naver.com"));
		addCustomer( new Customer("id5", "test5", "test5@naver.com"));
	}
	
	public void addCustomer(Customer customer) {
		customers.put(customer.getId(), customer);
	}
	
	public Customer findCustomer(String id) {
		if(id != null) {
			return (customers.get(id.toLowerCase()));
		}
		else{
			return null;
		}
	}
	
}

 

#6 DoLogin 서블릿 작성

loginform.jsp에서 사용자가 정해진 id를 입력하면 이를 실질적으로 처리할 서블릿인 DoLogin 서블릿을 생성한다.

다음 그림과 같이 URL Mapping을 /doLogin으로 생성한다.

DoLogin 서블릿 생성

DoLogin의 코드는 다음과 같다. loginform.jsp에서 사용자가 입력한 customerId의 값을 저장하고

CustomerService의 객체를 선언한다. 다음으로 findCustomer함수를 사용하여 사용자가 입력한 결과를 Customer라는 Model객체에 저장하여 이를 setAttribute라는 함수를 통해 customer라는 이름으로 저장하여 success.jsp로 forward 한다.

package controller;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import model.Customer;
import service.CustomerService;

/**
 * Servlet implementation class DoLogin
 */
@WebServlet("/doLogin")
public class DoLogin extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public DoLogin() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		
		String customerId = request.getParameter("customerId");
		
		CustomerService service = new CustomerService();
		Customer customer = service.findCustomer(customerId);
		request.setAttribute("customer", customer);
		
		String page;
		if(customer == null) {
			page = "/view/error.jsp";
		}
		else {
			page = "/view/success.jsp";
		}
		
		RequestDispatcher dispatcher = request.getRequestDispatcher(page);
		dispatcher.forward(request, response);
	
	}

}

 

#7 success.jsp생성

view폴더에 success.jsp를 생성하고 다음과 같은 코드를 작성한다. ${}는 JSP EL으로서 변수를 사용하는 방법이다.

넘겨받은 customer를 다음과 같이 화면에 출력하는 코드이다

<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%>
<!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>
This is Login Success page.

<ul>
<li> Name : ${customer.id }</li>
<li> Name : ${customer.name }</li>
<li> Name : ${customer.email }</li>
</ul>
</body>
</html>

모두 완료하였을 경우 다음과 같은 결과 화면을 확인할 수 있다

success.jsp

반응형

+ Recent posts