스프링 애너테이션
스프링 3.0부터는 DI와 같은 자바 코드와 관련된 설정은 직접 코드에서 할 수 있도록 애너테이션 기능을 제공한다. 현재 스프링 기반 애플리케이션에서는 XML에서 설정하는 방법과 애너테이션 기능을 사용하는 방법 두 가지를 혼합해서 사용하고 있다.
<context:component-scan> 태그 기능
<context:component-scan> 태그를 사용해 패키지 이름을 지정하면 애플리케이션 실행 시 해당 패키지에서 애너테이션으로 지정된 클래스를 빈으로 만들어준다.
<context:component-scan base-package="패키지이름" />
@Controller | 스프링 컨테이너가 지정한 클래스를 컨트롤러 빈으로 자동 변환 |
@Service | 스프링 컨테이너가 지정한 클래스를 서비스 빈으로 자동 변환 |
@Respository | 스프링 컨테이너가 지정한 클래스를 DAO빈으로 자동 변환 |
@Component | 스프링 컨테이너가 지정한 클래스를 빈으로 자동 변환 |
servlet-action.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/test/" />
<property name="suffix" value=".jsp"/>
</bean>
<!-- 클래스 레벨에 @RequestMapping을 처리 -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<!-- 메서드 레벨에 @RequestMapping을 처리 -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<!-- com.spring 패키지에 존재하는 클래스에 애너테이션이 적용되도록 설정 -->
<context:component-scan base-package="com.spring"/>
</beans>
MainController
@Controller("mainController")//빈 id는 mainController
@RequestMapping("/test")//@RequestMapping를 이용해 첫 번째 단계의 요청이 /test이면 mainController빈을 요청
public class MainController {
@RequestMapping(value = "/main1.do", method = RequestMethod.GET)
public ModelAndView main1(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mav = new ModelAndView();
mav.addObject("msg", "main1");
mav.setViewName("main");
return mav;
}
@RequestMapping(value = "/main2.do", method = RequestMethod.GET)
public ModelAndView main2(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mav = new ModelAndView();
mav.addObject("msg", "main2");
mav.setViewName("main");
return mav;
}
}
main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
request.setCharacterEncoding("UTF-8");
%>
<html>
<head>
<meta charset=UTF-8">
<title>결과창</title>
</head>
<body>
<h1>안녕하세요!!</h1>
<h1>${msg} 페이지입니다!!</h1>
</body>
</html>
http://localhost:8090/pro26/test/main1.do
http://localhost:8090/pro26/test/main2.do
로 각각 요청하면 각각 ${msg} 부분에 main1 / main2가 출력된다(컨트롤러에서 설정했던)
스프링 애너테이션 이용해서 로그인 기능
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<!-- 한글 처리를 하는 필터 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
LoginController
@Controller("loginController")
public class LoginController {
@RequestMapping(value = {"/test/loginForm.do", "/test/loginForm2.do"}, method = {RequestMethod.GET})
public ModelAndView loginForm(HttpServletRequest request, HttpServletResponse response) throws Exception{
ModelAndView mav = new ModelAndView();
mav.setViewName("loginForm");
return mav;
}
@RequestMapping(value = "/test/login.do", method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView login(@RequestParam("userID") String userID, @RequestParam("userName") String userName,
//required 값을 설정 안하면 기본으로 true. false로 설정하면 값이 안넘어왔을 때 null로 처리
@RequestParam(value = "email", required = false) String email,
HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setCharacterEncoding("utf-8");
ModelAndView mav = new ModelAndView();
mav.setViewName("result");
//메서드에서 @RequestParam을 사용하면 getParameter()메서드로 일일이 가져올 필요 없음
// String userID = request.getParameter("userID");
// String userName = request.getParameter("userName");
System.out.println("userID = "+userID);
System.out.println("userName = "+userName);
System.out.println("email = "+email);
mav.addObject("userID", userID);
mav.addObject("userName", userName);
return mav;
}
//@RequestParam을 사용해서 Map에 매개변수 값 설정
//전송되는 매개변수의 수가 많을 경우 일일이 변수 지정하기 불편하기 때문에 Map에 저장
@RequestMapping(value = "/test/login2.do", method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView login2(@RequestParam Map<String, String> info, HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setCharacterEncoding("utf-8");
ModelAndView mav = new ModelAndView();
//@RequestParam에서 절성한 Map 이름으로 바인딩
mav.addObject("info", info);
mav.setViewName("result");
return mav;
}
//@ModelAttribute를 사용해서 VO에 매개변수 값 설정
@RequestMapping(value = "/test/login3.do", method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView login3(@ModelAttribute("info")LoginVO loginVO, HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setCharacterEncoding("utf-8");
ModelAndView mav = new ModelAndView();
mav.setViewName("result");
return mav;
}
//Model 클래스를 사용해서 값 전달
@RequestMapping(value = "/test/login4.do", method = {RequestMethod.GET, RequestMethod.POST})
public String login4(Model model, HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setCharacterEncoding("utf-8");
model.addAttribute("userID", "hong");
model.addAttribute("userName", "홍길동");
return "result";
}
}
LoginVO
public class LoginVO {
private String userID;
private String userName;
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
loginForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<%
request.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인창</title>
</head>
<body>
<form method="post" action="${contextPath}/test/login2.do">
<input type="hidden" name="email" value="hong@test.com" />
<table width="400">
<tr>
<td>아이디 <input type="text" name="userID" size="10"></td>
</tr>
<tr>
<td>이름 <input type="text" name="userName" size="10"></td>
</tr>
<tr>
<td>
<input type="submit" value="로그인">
<input type="reset" value="다시입력">
</td>
</tr>
</table>
</form>
</body>
</html>
result.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
request.setCharacterEncoding("UTF-8");
%>
<html>
<head>
<meta charset=UTF-8">
<title>결과창</title>
</head>
<body>
<h1>아이디 : ${userID }</h1>
<h1>이 름 : ${userName }</h1>
<h1>아이디 : ${info.userID }</h1>
<h1>이 름 : ${info.userName }</h1>
</body>
</html>
설정한 거에 따라서 위와 같이 출력
@Autowired 이용해서 빈 주입
위에서 처럼 XML 파일을 통해 빈을 주입시키면 불편하기 때문에 @Autowired를 사용해서 빈을 직접 자바 코드에서 생성해 사용한다.
- 기존 XML 파일에서 각각의 빈을 DI로 주입했던 기능을 코드에서 애너테이션으로 자동으로 수행
- @Autowired를 사용하면 별도의 setter나 생성자 없이 속성에 빈을 주입할 수 있음
빈 주입 경로
sqlSession => memberDAO => memberService => memberController
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<!-- ContextLoaderListener를 사용해서 애플리케이션이 실행될 때 action-mybatis.xml을 읽어들이도록 설정 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/config/action-mybatis.xml
</param-value>
</context-param>
<!-- 한글 처리를 하는 필터 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
action-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/member/" />
<property name="suffix" value=".jsp"/>
</bean>
<!-- 클래스 레벨에 @RequestMapping을 처리 -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<!-- 메서드 레벨에 @RequestMapping을 처리 -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<!-- com.spring 패키지에 존재하는 클래스에 애너테이션이 적용되도록 설정 -->
<context:component-scan base-package="com.spring"/>
</beans>
action-mybatis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans">
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>/WEB-INF/config/jdbc.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
<property name="driver" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation"
value="classpath:mybatis/model/modelConfig.xml" />
<property name="mapperLocations" value="classpath:mybatis/mappers/*.xml" />
</bean>
<bean id="sqlSession"
class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
</bean>
</beans>
modelConfig.xml
<?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="com.spring.member.vo.MemberVO" alias="memberVO" />
</typeAliases>
</configuration>
MemberController
@Controller("memberController")
public class MemberController {
@Autowired
private MemberService memberService;
@Autowired
private MemberVO memberVO;
@RequestMapping(value = "/member/listMembers.do", method = RequestMethod.GET)
public ModelAndView listMembers(HttpServletRequest request, HttpServletResponse response) throws Exception{
String viewName = getViewName(request);
List membersList = memberService.listMembers();
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("membersList", membersList);
return mav;
}
//회원 가입창에서 전송된 회원 정보를 바로 MemberVO 객체에 설정하고, 이를 SQL문으로 전달해 회원 등록
@RequestMapping(value = "/member/addMember.do", method = RequestMethod.POST)
public ModelAndView addMember(@ModelAttribute("member") MemberVO memberVO, HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setCharacterEncoding("utf-8");
memberService.addMember(memberVO);
ModelAndView mav = new ModelAndView("redirect:/member/listMembers.do");
return mav;
}
//전송된 id에 해당하는 회원 삭제
@RequestMapping(value = "/member/removeMember.do", method = RequestMethod.GET)
public ModelAndView removeMember(@RequestParam("id") String id, HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setCharacterEncoding("utf-8");
memberService.removeMember(id);
ModelAndView mav = new ModelAndView("redirect:/member/listMembers.do");
return mav;
}
//요청명이 Form.do로 끝나면 form() 메서드를 호출
@RequestMapping(value = "/member/*Form.do", method = RequestMethod.GET)
public ModelAndView form(HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setCharacterEncoding("utf-8");
String viewName = getViewName(request);
ModelAndView mav = new ModelAndView();
mav.setViewName(viewName);
return mav;
}
private String getViewName(HttpServletRequest request) throws Exception {
String contextPath = request.getContextPath();
String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
if (uri == null || uri.trim().equals("")) {
uri = request.getRequestURI();
}
int begin = 0;
if (!((contextPath == null) || ("".equals(contextPath)))) {
begin = contextPath.length();
}
int end;
if (uri.indexOf(";") != -1) {
end = uri.indexOf(";");
} else if (uri.indexOf("?") != -1) {
end = uri.indexOf("?");
} else {
end = uri.length();
}
String viewName = uri.substring(begin, end);
if (viewName.indexOf(".") != -1) {
viewName = viewName.substring(0, viewName.lastIndexOf("."));
}
if (viewName.lastIndexOf("/") != -1) {
viewName = viewName.substring(viewName.lastIndexOf("/"), viewName.length());
}
return viewName;
}
}
MemberServiceImpl
@Service("memberService")//id가 memberService인 밴 자동 생성
public class MemberServiceImpl implements MemberService{
@Autowired
private MemberDAO memberDAO;
@Override
public List listMembers() throws DataAccessException {
List<MemberVO> membersList = null;
membersList = memberDAO.selectAllMemberList();
return membersList;
}
@Override
public int addMember(MemberVO memberVO) throws DataAccessException {
return memberDAO.insertMember(memberVO);
}
@Override
public int removeMember(String id) throws DataAccessException {
return memberDAO.deleteMember(id);
}
}
MemberDAOImpl
@Repository("memberDAO")
public class MemberDAOImpl implements MemberDAO{
//XML 설정 파일에서 생성한 id가 sqlSession인 빈을 자동 주입
@Autowired
private SqlSession sqlSession;
@Override
public List selectAllMemberList() throws DataAccessException {
List<MemberVO> membersList = sqlSession.selectList("mapper.member.selectAllMemberList");
return membersList;
}
@Override
public int insertMember(MemberVO memberVO) throws DataAccessException {
int re = sqlSession.insert("mapper.member.insertMember", memberVO);
return re;
}
@Override
public int deleteMember(String id) throws DataAccessException {
int re = sqlSession.delete("mapper.member.deleteMember", id);
return re;
}
}
member.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.member">
<resultMap id="memResult" type="memberVO">
<result property="id" column="id" />
<result property="pwd" column="pwd" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="joinDate" column="joinDate" />
</resultMap>
<select id="selectAllMemberList" resultMap="memResult">
<![CDATA[
select * from t_member order by joinDate desc
]]>
</select>
<insert id="insertMember" parameterType="memberVO">
<![CDATA[
insert into t_member(id,pwd, name, email)
values(#{id}, #{pwd}, #{name}, #{email})
]]>
</insert>
<delete id="deleteMember" parameterType="String">
<![CDATA[
delete from t_member
where
id=#{id}
]]>
</delete>
</mapper>
MemberVO
@Component("memberVO")
public class MemberVO {
private String id;
private String pwd;
private String name;
private String email;
private Date joinDate;
public MemberVO() {
}
public MemberVO(String id, String pwd, String name, String email) {
this.id = id;
this.pwd = pwd;
this.name = name;
this.email = email;
}
...getter/setter
}