'FRAMEWORK'에 해당되는 글 126건

  1. 2014.11.19 Spring MVC with Excel View Example (Apache POI and JExcelApi)
  2. 2014.11.19 Spring MVC – View (JSON, XML, PDF or Excel)
  3. 2014.11.11 <util:properties/> 와 Spring EL 로 값 가져오기
  4. 2014.11.10 Spring annotation
  5. 2014.11.05 RequestDispatcher forward method
  6. 2014.11.05 RequestDispatcher include method
  7. 2014.11.05 서블릿이란
  8. 2014.11.05 jsp 와 servlet 의 차이
  9. 2014.11.05 서블릿
  10. 2014.11.05 뷰에 모델(Model) 전달

Spring MVC with Excel View Example (Apache POI and JExcelApi)

FRAMEWORK/SPRING 2014. 11. 19. 17:29


SpringMvcExcelViewDemo.zip


SpringMvcExcelViewDemo2.zip




About Apache POI

Apache POI is a set of pure Java libraries for reading and writing Microsoft Office documents such as Word, Excel, Powerpoint, Outlook, etc. Click the following link to download its latest distribution (which is Apache POI 3.9, as of this writing):

Apache POI Download

The distribution comes with several jar files, but the only the poi-VERSION.jar file is required for typical usage of generating Excel documents (if you want to generate Excel XML format such as *.xlsx files, use the poi-ooxml-VERSION.jar file).

To generate an Excel document using Apache POI within Spring, we need to create a view class that extends from theAbstractExcelView class and override its method buildExcelDocument(). Then using Apache POI’s Excel API to generate the excel document.

About JExcelApi

JExcelApi is a Java library that is dedicated for reading, writing and modifying Excel spreadsheets. It supports Excel 2003 file format and older versions. You can download JExcelApi from the following link:

                JExcelApi Download

To work with JExcelApi, you need to add its only jar file: jxl.jar - to your project’s classpath. And Spring provides an abstract class calledAbstractJExcelView which should be extended to generate an Excel document using JExcelApi, similarly to the case of Apache POI.

This tutorial will use Apache POI for the sample application. However, you can also download a JExcelApi version of the project in the Attachments section.

In Eclipse IDE, create a Dynamic Web Project calledSpringMvcExcelViewDemo. We will end up with the following project structure:



The jar files used are:

      • spring-beans-3.2.3.RELEASE.jar
      • spring-context-3.2.3.RELEASE.jar
      • spring-context-support-3.2.3.RELEASE.jar
      • spring-core-3.2.3.RELEASE.jar
      • spring-expression-3.2.3.RELEASE.jar
      • spring-web-3.2.3.RELEASE.jar
      • spring-webmvc-3.2.3.RELEASE.jar
      • commons-logging-1.1.1.jar
    • Apache POI:
      • poi-3.9-20121203.jar

 

Recommended Book: Getting started with Spring Framework

 

1. Creating Model Class

We will generate an Excel document that contains a list of Java books, so create the following model class (Book.java):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package net.codejava.spring;
 
public class Book {
    private String title;
    private String author;
    private String isbn;
    private String publishedDate;
    private float price;
 
    public Book(String title, String author, String isbn, String publishedDate,
            float price) {
        this.title = title;
        this.author = author;
        this.isbn = isbn;
        this.publishedDate = publishedDate;
        this.price = price;
    }
 
    // getters and setters
 
}


2. Coding Entry JSP Page

We need to create a JSP page that displays a hyperlink on which the users will click to download the Excel file. Create a folder called jsp inside WEB-INF directory and create a JSP file called home.jsp under WEB-INF\jsp with the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ 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=UTF-8">
<title>Spring MVC Excel View Demo (Apache POI)</title>
</head>
<body>
    <div align="center">
        <h1>Spring MVC Excel View Demo (Apache POI)</h1>
        <h3><a href="/downloadExcel">Download Excel Document</a></h3>
    </div>
</body>
</html>

The hyperlink Download Excel Document points to a relative URL downloadExcel which will be handled by a Spring controller class as described below.

 


3. Coding Spring Controller

Create a Spring controller class called MainController with the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package net.codejava.spring;
 
import java.util.ArrayList;
import java.util.List;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
 
/**
 * A Spring controller that allows the users to download an Excel document
 * generated by the Apache POI library.
 *
 * @author www.codejava.net
 *
 */
@Controller
public class MainController {
 
    /**
     * Handle request to the default page
     */
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String viewHome() {
        return "home";
    }
 
    /**
     * Handle request to download an Excel document
     */
    @RequestMapping(value = "/downloadExcel", method = RequestMethod.GET)
    public ModelAndView downloadExcel() {
        // create some sample data
        List<Book> listBooks = new ArrayList<Book>();
        listBooks.add(new Book("Effective Java""Joshua Bloch""0321356683",
                "May 28, 2008"38.11F));
        listBooks.add(new Book("Head First Java""Kathy Sierra & Bert Bates",
                "0596009208""February 9, 2005"30.80F));
        listBooks.add(new Book("Java Generics and Collections",
                "Philip Wadler""0596527756""Oct 24, 2006"29.52F));
        listBooks.add(new Book("Thinking in Java""Bruce Eckel""0596527756",
                "February 20, 2006"43.97F));
        listBooks.add(new Book("Spring in Action""Craig Walls""1935182358",
                "June 29, 2011"31.98F));
 
        // return a view which will be resolved by an excel view resolver
        return new ModelAndView("excelView""listBooks", listBooks);
    }
}

As we can see, this controller class implements two request handling methods:

    • viewHome(): this method simply returns a logical view name “home” which will be resolved to the home.jsp page (We will configure view resolver for JSP later).
    • downloadExcel(): this method creates some dummy data, e.g. creating some books and add them to a list. Finally this method returns a logical view name “excelView” and passes the list of books as the name “listBooks” to the model. We will configure an Excel view class for this view later.

       

Recommended Book: Spring in Action


4. Coding Excel View Class

To generate an Excel document from the model data passed by the controller, create a subclass of theAbstractExcelView class as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package net.codejava.spring;
 
import java.util.List;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.springframework.web.servlet.view.document.AbstractExcelView;
 
/**
 * This class builds an Excel spreadsheet document using Apache POI library.
 * @author www.codejava.net
 *
 */
public class ExcelBuilder extends AbstractExcelView {
 
    @Override
    protected void buildExcelDocument(Map<String, Object> model,
            HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // get data model which is passed by the Spring container
        List<Book> listBooks = (List<Book>) model.get("listBooks");
         
        // create a new Excel sheet
        HSSFSheet sheet = workbook.createSheet("Java Books");
        sheet.setDefaultColumnWidth(30);
         
        // create style for header cells
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setFontName("Arial");
        style.setFillForegroundColor(HSSFColor.BLUE.index);
        style.setFillPattern(CellStyle.SOLID_FOREGROUND);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        font.setColor(HSSFColor.WHITE.index);
        style.setFont(font);
         
        // create header row
        HSSFRow header = sheet.createRow(0);
         
        header.createCell(0).setCellValue("Book Title");
        header.getCell(0).setCellStyle(style);
         
        header.createCell(1).setCellValue("Author");
        header.getCell(1).setCellStyle(style);
         
        header.createCell(2).setCellValue("ISBN");
        header.getCell(2).setCellStyle(style);
         
        header.createCell(3).setCellValue("Published Date");
        header.getCell(3).setCellStyle(style);
         
        header.createCell(4).setCellValue("Price");
        header.getCell(4).setCellStyle(style);
         
        // create data rows
        int rowCount = 1;
         
        for (Book aBook : listBooks) {
            HSSFRow aRow = sheet.createRow(rowCount++);
            aRow.createCell(0).setCellValue(aBook.getTitle());
            aRow.createCell(1).setCellValue(aBook.getAuthor());
            aRow.createCell(2).setCellValue(aBook.getIsbn());
            aRow.createCell(3).setCellValue(aBook.getPublishedDate());
            aRow.createCell(4).setCellValue(aBook.getPrice());
        }
    }
 
}

 

For working with JExcelApi, make the class extends the AbstractJExcelView class like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package net.codejava.spring;
 
import java.util.List;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import jxl.write.Label;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
 
import org.springframework.web.servlet.view.document.AbstractJExcelView;
 
/**
 * This class builds an Excel spreadsheet document using JExcelApi library.
 * @author www.codejava.net
 *
 */
public class ExcelBuilder extends AbstractJExcelView {
 
    @Override
    protected void buildExcelDocument(Map<String, Object> model,
            WritableWorkbook workbook, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
         
        // get data model which is passed by the Spring container
        List<Book> listBooks = (List<Book>) model.get("listBooks");
         
        // create a new Excel sheet
        WritableSheet sheet = workbook.createSheet("Java Books"0);
         
        // create header row
        sheet.addCell(new Label(00"Book Title"));
        sheet.addCell(new Label(10"Author"));
        sheet.addCell(new Label(20"ISBN"));
        sheet.addCell(new Label(30"Published Date"));
        sheet.addCell(new Label(40"Price"));
         
        // create data rows
        int rowCount = 1;
         
        for (Book aBook : listBooks) {
            sheet.addCell(new Label(0, rowCount, aBook.getTitle()));
            sheet.addCell(new Label(1, rowCount, aBook.getAuthor()));
            sheet.addCell(new Label(2, rowCount, aBook.getIsbn()));
            sheet.addCell(new Label(3, rowCount, aBook.getPublishedDate()));
            sheet.addCell(new jxl.write.Number(4, rowCount, aBook.getPrice()));
             
            rowCount++;
        }
    }
}

The above code is self-explanatory. As you can see, there are some differences between the Apache POI API and the JExcelApi.

 

Recommended Book: Spring Integration in Action


5. Configuring Excel View Class

Next, we need to tell Spring to use the above ExcelBuilder class as view class for the view name “excelView” returned from the controller’s downloadExcel() method. There are two ways to do this by creating either a .properties file or an XML file.

Using views.properties file:

Create a .properties file called views.properties under the project’s classpath (which is under src directory in the Eclipse project), with the following line:

1
excelView.(class)=net.codejava.spring.ExcelBuilder

That tells the Spring’s view resolver to use the net.codejava.spring.ExcelBuilder class to process output for the view name “excelView”.

 

Using views.xml file:

An alternative to the views.properties file is to use XML version. Create views.xml file under WEB-INF directory with the following content:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    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">
     
    <bean id="excelView" class="net.codejava.spring.ExcelBuilder" />
     
</beans>

Note that the bean’s ID attribute must correspond to the view name “excelView”.

 


6. Writing Spring Configuration File

Create a Spring configuration file named spring-mvc.xml under WEB-INF directory. In case you are usingviews.properties file, put the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
    <context:component-scan base-package="net.codejava.spring" />
 
   <bean id="viewResolver1" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        <property name="order" value="1"/>
        <property name="basename" value="views"/>
    </bean>
     
    <bean id="viewResolver2"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="order" value="2"/>
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
     
</beans>

As seen in the above configuration, there are two view resolvers used here:

    • ResourceBundleViewResolver: to resolve view names specified in the views.properties file.
    • InternalResourceViewResolver: to resolve view names to JSP pages.

The order property does matter here, in which the first resolver has higher priority than the second one.

 

In case the views.xml is used, configure Spring as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
    <context:component-scan base-package="net.codejava.spring" />
 
   <bean id="viewResolver1" class="org.springframework.web.servlet.view.XmlViewResolver">
        <property name="order" value="1"/>
        <property name="location" value="/WEB-INF/views.xml"/>
    </bean>
     
    <bean id="viewResolver2"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="order" value="2"/>
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
 
     
</beans>

 

Recommended Book: Spring Batch in Action


7. Configuring Spring MVC in web.xml

The final step is to configure Spring MVC in the web deployment descriptor file as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    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">
     
    <display-name>SpringMvcExcelViewDemo</display-name>
     
    <servlet>
        <servlet-name>SpringController</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>SpringController</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping
</web-app>


8. Testing the application

Type the following URL into browser to access the application we’ve built:

http://localhost:8080/SpringMvcExcelViewDemo/


 

Download Eclipse project for this application in the Attachments section below. 

Recommended Book: Pro Spring 3

Related Course: The Java Spring Tutorial


출처 - http://www.codejava.net/frameworks/spring/spring-mvc-with-excel-view-example-apache-poi-and-jexcelapi


'FRAMEWORK > SPRING' 카테고리의 다른 글

Spring MVC download files(02)  (0) 2014.12.02
Spring MVC download files(01)  (0) 2014.12.02
Spring MVC – View (JSON, XML, PDF or Excel)  (0) 2014.11.19
<util:properties/> 와 Spring EL 로 값 가져오기  (0) 2014.11.11
Spring annotation  (0) 2014.11.10
:

Spring MVC – View (JSON, XML, PDF or Excel)

FRAMEWORK/SPRING 2014. 11. 19. 17:22

Spring MVC – View (JSON, XML, PDF or Excel)

Content negotiation in Spring MVC allows you to view data in different format based on the request media type. Here we will see how to view the same data in different media types “json, xml, pdf & excel”.

Objectives:

  • How to configure spring to respond to different media type request?
  • How to return data in json, xml, pdf or excel format?
  • How to write PDF view using iText & Excel view using Apache POI for Excel 2007+?

Environment & Tools:

  • Eclipse
  • Maven
  • Jetty (or any other server)

Libraries:

About This Sample:

  • Client will send GET request to view data “list of articles” in one of the available formats “mime type” (json, xml, pdf or excel).
  • The request is mapped to Spring MVC controller which will respond to the client with the data in requested format.
  • Spring is configured to detect the requested content type and prepare data in required format accordingly.
  • Formatting data in JSON and XML is straight forward no coding is needed if the default output is meeting our requirement.
  • However formatting the data in PDF or Excel needs some coding to prepare the layout of these documents. For PDF we will be using iText and for Excel we will be using Apache POI
  • The data source in this example “list of articles” is an XML file “articles.xml” which will be converted into Java object “LinkedList<Article>” using Castor XML mapping before responding to the client. The data source can be DB, text file, or just hard coded data.

( 1 ) Project Structure



( 2 ) Data Model & Source

  • src/main/java/com/hmkcode/vo/Article.java

This is a class is data model that will hold our data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.hmkcode.vo;
 
import java.util.LinkedList;
import java.util.List;
 
public class Article {
 
    private String title;
    private String url;
    private List<String> categories;
    private List<String> tags;
 
    //getters & setters...
 
}
  • articles.xml

This XML file is just the source of data to be sent to the client. The data source can be also database, text file or hard-coded data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<linked-list>
    <article xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:type="java:com.hmkcode.vo.Article">
        <categories xsi:type="java:java.lang.String">Spring</categories>
        <tags xsi:type="java:java.lang.String">Spring</tags>
        <tags xsi:type="java:java.lang.String">JSON</tags>
        <tags xsi:type="java:java.lang.String">XML</tags>
        <tags xsi:type="java:java.lang.String">iText</tags>
        <tags xsi:type="java:java.lang.String">Apache POI</tags>
        <title> Spring MVC View (JSON, XML, PDF or Excel) </title>
    </article>
.....
.....
</linked-list>

( 3 ) Web & Spring Configuration

  • Controller.java
  • rest-servlet.xml
  • web.xml
  • index.html
  • /src/main/java/com/hmkcode/controllers/Controller.java

This class is the controller that will receive client request. Client can call this controller by send GET request to /rest/controller/get URL.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.hmkcode.controllers;
 
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.LinkedList;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
import com.hmkcode.vo.Article;
 
@Controller
@RequestMapping("/controller")
public class MyController {
 
    public MyController(){
        System.out.println("Init MyController");
    }
 
    @RequestMapping(value = "/get", method = RequestMethod.GET)
     public  LinkedList<Article>  get(Model model) {                
 
        FileReader reader;
 
        LinkedList<Article> articles = null;
 
        try {
 
            reader = new FileReader("articles.xml");
            //convert "unmarshal" data from XML "articles.xml" to Java object LinkedList<Article>
            articles = (LinkedList) Unmarshaller.unmarshal(LinkedList.class, reader);
            model.addAttribute("articles",articles);
 
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (MarshalException e) {
            e.printStackTrace();
        } catch (ValidationException e) {
            e.printStackTrace();
        }
 
        return articles;
 
    }
 
}
  • /src/main/webapp/WEB-INF/rest-servlet.xml

In this file we configure spring to handle the request of different media type by setting upContentNegotiatingViewResolver bean. Two properties of ContentNegotiatingViewResolver need to be set  “mediaTypes” & “defaultViews“. The first property “mediaTypes” will contain a map of accepted types, the key of this map is the extension that will be passed with request e.g. /rest/controller/get.xlsx while the value is the media type standard name e.g. application/json. The second “defaultViews” list the beans that will generate the data in the requested format.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    xsi:schemaLocation="
 
 
 
 
 
 
 
 
 
 
 
 
    <context:component-scan base-package="com.hmkcode.controllers" />
    <mvc:annotation-driven />
 
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">     
        <property name="order" value="1" />     
        <property name="mediaTypes">       
            <map>          
              <entry key="json" value="application/json" />          
              <entry key="xml" value="application/xml" />          
              <entry key="pdf" value="application/pdf" />
              <entry key="xlsx" value="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />       
            </map>     
        </property>      
 
        <property name="defaultViews">       
            <list>         
                <!-- JSON View -->         
                <bean            class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">         
                </bean>         
 
                <!--  XML view -->           
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">           
                <constructor-arg>               
                    <bean class="org.springframework.oxm.castor.CastorMarshaller">                             
                    </bean>           
                </constructor-arg>         
                </bean>         
 
                <!--  PDF view -->         
                <bean class="com.hmkcode.view.PDFView">            
                </bean>
 
                <!--  XLSX "Excel" view -->         
                <bean class="com.hmkcode.view.ExcelView">            
                </bean>
            </list>     
        </property>     
 
        <property name="ignoreAcceptHeader" value="true" />    
    </bean>
</beans>
  • /src/main/webapp/WEB-INF/web.xml

This is the standard web.xml to define our servlets. Here we define the URL pattern of the requests to be passed to Spring DispatcherServlet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
  <display-name>SpringMVC</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>rest</servlet-name>             
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>rest</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>
  • /src/main/webapp/index.html

Just a simple file to display the first page of our web app. Twitter bootstrap is used just for good looking design!.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!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=ISO-8859-1">
<title>Spring MVC - View (JSON, XML, PDF, Excel)</title>
    <link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
</head>
 
<body>
 
<h1>Spring MVC - View (JSON, XML, PDF, Excel)</h1>
<div style="margin:10px;width:700px;text-align:center">   
    <a href="/spring-mvc-json-pdf-xls/rest/controller/get.json" class="label label-info">JSON</a>
    <a href="/spring-mvc-json-pdf-xls/rest/controller/get.xml" class="label label-info">XML</a>
    <a href="/spring-mvc-json-pdf-xls/rest/controller/get.pdf" class="label label-info">PDF</a>
    <a href="/spring-mvc-json-pdf-xls/rest/controller/get.xlsx" class="label label-info">Excel</a>
</div>
 
</body>
</html>

( 4 ) Views PDF & Excel

Spring provides an abstract classorg.springframework.web.servlet.view.document.AbstractPdfView that we can extends to write our PDF view implementation. This class supports only old version of iText “Bruno Lowagie’siText“. If we would like to use the newer version of iText then we need to rewriteAbstractPdfView to be compatible with iText (5.x).

Spring provides abstract classorg.springframework.web.servlet.view.document.AbstractExcelView that we can extends to write our Excel view implementation. This class support only Excel 97 – 2007 or “.xls” file. If we want to create Excel 2007+  or “.xlsx” document we need to rewrite AbstractExcelView to be compatible with POI-XSSF.

  • /src/main/java/com/hmkcode/view/PDFView.java

This is class create our PDF document using iText (5.4.2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.hmkcode.view;
 
import java.util.LinkedList;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.hmkcode.view.abstractview.AbstractPdfView;
import com.hmkcode.vo.Article;
import com.itextpdf.text.Anchor;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.Font.FontFamily;
import com.itextpdf.text.pdf.PdfWriter;
 
public class PDFView extends AbstractPdfView {
 
    protected void buildPdfDocument(       
            Map<String, Object> model,       
            Document document,       
            PdfWriter writer,       
            HttpServletRequest req,       
            HttpServletResponse resp)       
                    throws Exception {
 
        // Get data "articles" from model
        @SuppressWarnings("unchecked")
        LinkedList<Article> articles = (LinkedList<Article>) model.get("articles");
 
        // Fonts
        Font fontTitle = new Font(FontFamily.TIMES_ROMAN, 14, Font.BOLD, BaseColor.BLACK);
        Font fontTag = new Font(FontFamily.HELVETICA, 10, Font.BOLD, BaseColor.WHITE);
 
        for(Article article:articles){
 
            // 1.Title
            document.add(new Chunk("Title: "));
            Chunk title = new Chunk(article.getTitle(), fontTitle);
            document.add(title);
            document.add(new Chunk(" "));
 
            // -- newline
            document.add(Chunk.NEWLINE);
 
            // 2.URL
            document.add(new Chunk("URL: "));
            Anchor url  = new Anchor(article.getUrl());
            url.setName(article.getUrl());
            url.setReference(article.getUrl());
            document.add(url);
 
            // -- newline
            document.add(Chunk.NEWLINE);
 
            // 3.Categories
            Chunk cat = null;
            document.add(new Chunk("Category: "));
            for(String category:article.getCategories()){
                cat =  new Chunk(category, fontTag);
                cat.setBackground(new BaseColor(72, 121, 145), 1f, 0.5f, 1f, 1.5f);
                document.add(cat);
                document.add(new Chunk(" "));
 
            }
 
            // -- newline
            document.add(Chunk.NEWLINE);
 
            // 4.Tags
            Chunk tg = null;
            document.add(new Chunk("Tags: "));
            for(String tag:article.getTags()){
                tg =  new Chunk(tag, fontTag);
                tg.setBackground(new BaseColor(97, 97, 97), 1f, 0.5f, 1f, 1.5f);
                document.add(tg);
                document.add(new Chunk(" "));
 
            }
 
            // -- newline
            document.add(Chunk.NEWLINE);
            document.add(Chunk.NEWLINE);
 
        }
 
    }
 
}
  • /src/main/java/com/hmkcode/view/ExcelView.java

This class create Excel document using Apache POI-XSSF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.hmkcode.view;
 
import java.util.LinkedList;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.poi.ss.formula.functions.Hyperlink;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
 
import com.hmkcode.view.abstractview.AbstractExcelView;
import com.hmkcode.vo.Article;
 
public class ExcelView extends AbstractExcelView {
 
    @Override
    protected void buildExcelDocument(Map<String, Object> model,
            Workbook workbook, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
 
        Sheet sheet = workbook.createSheet("sheet 1");
 
        @SuppressWarnings("unchecked")
        LinkedList<Article> articles = (LinkedList<Article>) model.get("articles");
 
        Row row = null;
        Cell cell = null;
        int r = 0;
        int c = 0;
 
        //Style for header cell
        CellStyle style = workbook.createCellStyle();
        style.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.index);
        style.setFillPattern(CellStyle.SOLID_FOREGROUND);
        style.setAlignment(CellStyle.ALIGN_CENTER);
 
        //Create header cells
        row = sheet.createRow(r++);
 
        cell = row.createCell(c++);
        cell.setCellStyle(style);
        cell.setCellValue("Title");
 
        cell = row.createCell(c++);
        cell.setCellStyle(style);
        cell.setCellValue("URL");
 
        cell = row.createCell(c++);
        cell.setCellStyle(style);
        cell.setCellValue("Categories");
 
        cell = row.createCell(c++);
        cell.setCellStyle(style);
        cell.setCellValue("Tags");
 
        //Create data cell
        for(Article article:articles){
            row = sheet.createRow(r++);
            c = 0;
            row.createCell(c++).setCellValue(article.getTitle());
            row.createCell(c++).setCellValue(article.getUrl());
            row.createCell(c++).setCellValue(article.getCategories().toString());
            row.createCell(c++).setCellValue(article.getTags().toString());
 
        }
        for(int i = 0 ; i < 4; i++)
            sheet.autoSizeColumn(i, true);
 
    }
 
}

출처 - http://hmkcode.com/spring-mvc-view-json-xml-pdf-or-excel/


:

<util:properties/> 와 Spring EL 로 값 가져오기

FRAMEWORK/SPRING 2014. 11. 11. 14:38


SpringProperty.zip



XML 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version=" 1.0"="" encoding="UTF-8" ?=""><beans xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="
 
    ....
 
    <util:properties id="prop" location="classpath:config/properties/sample.properties"/>
 
    ....
 
</beans>





sample.properties

1
2
3
4
sample.prop1 = test
 
# 우쭈쭈~
sample.prop2 = \uc6b0\ucb48\ucb48~






Spring EL 로 값 가져오기(SampleBean.java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.tistory.stove99;
 
import java.util.Properties;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
@Component
public class SampleBean {
    // Spring EL 로 값 가져오기
    // Spring EL 이기 때문에 자유롭게 메소드 호출도 가능함. String 의 concat 메소드 호출
    @Value("#{prop['sample.prop1'].concat(' abc')}") private String value1;
    @Value("#{prop['sample.prop2']}") private String value2;
     
     
     
    // util:properties 로 생성된 빈은 java.util.Properties 의 인스턴스이기 때문에
    // 요렇게 Autowired 해서 쓸 수 있다.
    @Autowired Properties prop;
     
     
     
     
    public String val(String key){
        return prop.getProperty(key);
    }
     
    public String toString(){
        return String.format("value1 : %s, value2 : %s", value1, value2);
    }
}

Spring EL에 대해서 더 알아보고 싶으면 요 사이트를 참고하면 친절하게 알려줌.

http://static.springsource.org/spring/docs/3.0.x/reference/expressions.html






Test(SampleTest.java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import com.tistory.stove99.SampleBean;
 
 
public class SampleTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config/spring/*-context.xml");
         
        SampleBean sample = context.getBean(SampleBean.class);
         
        // test
        System.out.println(sample.val("sample.prop1"));
         
        // value1 : test abc, value2 : 우쭈쭈~
        System.out.println(sample);
    }
}



출처 - http://stove99.tistory.com/150

:

Spring annotation

FRAMEWORK/SPRING 2014. 11. 10. 17:59

목차

@Component
@Required
@Autowired
@Resource
@Scope
@PostConstruct
@PreDestroy
@Inject
@Service
@Repository
@Controller
@RequestMapping
@RequestParam
@SessionAttributes
@InitBinder

@ModelAttribute

@RequestBody
@ResponseBody

@PathVariable






@Component

패키지 : org.springframework.stereotype

버젼 : spring 2.5

개요 : <context:component-scan> 태그를 설정파일에 추가하면 해당 어노테이션이 적용된 클래스를 빈으로 등록하게 된다. 범위는 디폴트로 singleton이며 @Scope를 사용하여 지정할 수 있다.


설정위치: 클래스 선언부 위 

추가설정 : XML 설정파일에 <context:component-scan>을 정의하고 적용할 기본  패키지를 base-package 속성으로 등록한다.


context:annotation-config 태그는 어노테이션과 관련해서 다음의 BeanPostProcessor를 함께 등록 한다.

           - @Required(RequiedAnnotationBeanPostProcessor)

           - @Autowired(AutowiredAnnotationBeanPostProcessor)

           - @Resource, @PostConstruct, @PreDestory(CommonAnnotationBeanPostProcessor)

           - @Configuration(ConfigurationClassPostProcessor)


* 그 외 Repository, Service, Controller 포함


예를 들어 <context:component-scan base-package="xxx" />에서 xxx패키지 하위에서 


@Component로 선언된 클래스를 bean으로 자동 등록한다.

bean의 이름은 해당클래스명(첫글자는 소문자)이 사용된다.       


<context:component-scan /> 요소에는 scoped-proxy 속성이 존재 한다. 

scoped-proxy 속성은 <aop:scoped-poxy/> 요소처럼 WebApplicationContext 에서만 유효하며 

"session", "globalSession", "request" 이외의 scope는 무시 되며 아래의 3가지 값을 설정 할 수 있다.

        no : proxy를 생성하지 않는다.(기본값)

        interfaces : JDK Dynamic Proxy를 이용한 Proxy 생성

        targetClass : 클래스에 대해 프록시를 생성(CGLIB를 이용한 Proxy 생성)


사용 예 :

1
2
3
4
5
@Component
@Scope("prototype")   // 생략하면 싱글톤
public class Test {
       .....
}


   - CGLIB

   기존의 자바 클래스파일로부터 자바의 소스코드를 동적으로 생성하는 라이브러리(자바 소스 변경)

    http://sourceforge.net/projects/cglib/


     - 스캔 대상 클래스 범위 지정하기

       <context:include-filter> 태그와 <context:exclude-filter> 태그를 사용하면 자동 스캔 대상에 포함시킬 클래스와 포함시키지 않을 클래스를 구체적으로 명시할 수 있다.

      <context:component-scan base-package="spring.demo" scoped-proxy="no">

           <context:include-filter type="regex" expression="*HibernateRepository"/>

           <context:exclude-filter type="aspectj" expression="..*IBatisRepository"/>

      </context:component-scan>


      위와 같이 <context:include-filter> 태그와 <context:exclude-filter> 태그는 각각 type 속성과 expresseion 속성을 갖는데, type 속성에 따라 expression 속성에 올 수 있는 값이 달라지는데 type 속성에 입력 가능한 값을 다음과 같다


     * Type 속성에 올 수 있는 값

        annotation : 클랙스에 지정한 어노테이션이 적용됐는지의 여부

            expression 속성에서는 "org.example.SomeAnnotation"와 같은 어노테이션 이름을 입력한다.

        assignable : 클래스가 지정한 타입으로 할당 가능한지의 여부. 

            expression 속성에는 "org.exampleSomeClass" 와 같은 타입 이름을 입력한다.

       regex : 클래스 이름이 정규 표현식에 매칭되는 지의 여부.

           expression 속성에는 "org\.example\.Default.*" 와 같이 정규표현식을 입력한다.

       aspectj : 클래스 이름이 AspectJ 의 표현식에 매칭되는 지의 여부.

          expression 속성에는 "org.example..*Service+" 와 같이 AspectJ 의 표현식을 입력한다.



@Required

패키지 : org.springframework.beans.factory.annotation

버젼 : spring 2.0

Required 어노테이션은 필수 프로퍼티임을 명시하는 것으로 필수 프로퍼티를 설정하지 않을 경우 

빈 생성시 예외를 발생시킨다.


설정위치 : setter 메소드

추가설정 : 

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" /> 클래스를 빈으로 등록시켜줘야 한다. 

해당 설정 대신에<context:annotation-config> 태그를 사용해도 된다.


    - 1단계 : 코드내에 프로퍼티 설정 메소드에 @Required 어노테이션을 붙인다.

     import org.springframework.beans.factory.annotation.Required

     public class TestBean {

         private TestDao testDao;

  

         @Required

         public void setTestDao(TestDao testDao) {

            this.testDao = testDao;

         }

     }


    - 2단계 : 스프링 설정 파일에 RequiredAnnotationBeanPostProcessor 클래스를 빈으로 등록

    <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanpostProcessor"/>


    <bean name="testBean"  class="han.test.TestBean">

        <property name="testDao" ref="testDao"/>  

<!-- @Required 어노테이션을 적용하였으므로 설정하지 않으면 예외를 발생시킨다. -->

    </bean>


     RequiredAnnotationBeanPostProcessor 클래스는 스프링 컨테이너에 등록된 bean 객체를 조사하여 @Required 어노테이션으로 설정되어 있는 프로퍼티의 값이 설정되어 있는지 검사하고 설정되어있지 않으면 bean 생성시 예외를 발생시킨다.

 

     RequiredAnnotationBeanPostProcessor 클래스를 빈으로 등록하지 않고

     <context:annotation-config> 다음과 같이 태그를 이용할 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
           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
 
      <context:annotation-config/>
 
    </beans>



@Autowired

패키지 : org.springframework.beans.factory.annotation

버젼 : spring 2.5

개요 : 오토 와이어링 어노테이션은 의존관계를 자동설정할 때 사용하며 타입을 이용하여 의존하는 객체를 삽입해 준다. 그러므로 해당 타입의 빈객체가 존재하지 않거나 또는 2개 이상 존재할 경우 스프링은 예외를 발생시키게 된다.


설정 위치 : 생성자, 필드, 메소드(setter메소드가 아니여도 된다)

추가설정 : <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" /> 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.


옵션 : required - @Autowired어노테이션을 적용한 프로퍼티에 대해 설정할 필요가 없는 경우에 false값을 주며 이때 해당 프로퍼티가 존재하지 않더라도 스프링은 예외를 발생시키지 않는다.

       @Autowired(required=false)로 선언한다. (기본값은 true)


특징 : byType으로 의존관계를 자동으로 설정할 경우 같은 타입의 빈이 2개 이상 존재하게 되면 예외가 발생하는데, Autowired도 이러한 문제가 발생한다. 이럴 때 @Qualifier를 사용하면 동일한 타입의 빈 중 특정 빈을 사용하도록 하여 문제를 해결할 수 있다.

       @Autowired

       @Qualifier("test")

       private Test test;

 

● @Qualifier

패키지 : org.springframework.beans.factory.annotation

버젼 : spring 2.5

개요 : 콸리파이어 어노테이션은 @Autowired의 목적에서 동일 타입의 빈객체가 존재시 특정빈을 삽입할 수 있게 설정한다. @Qualifier("mainBean")의 형태로 @Autowired와 같이 사용하며 해당 <bean>태그에 <qualifire value="mainBean" /> 태그를 선언해주어야 한다. 메소드에서 두개이상의 파라미터를 사용할 경우는 파라미터 앞에 선언해야한다.


설정위치 : @Autowired 어노테이션과 함께 사용된다.

추가설정 : 동일타입의 빈객체 설정에서 <qualifier value="[alias명]" />를 추가해 준다.

옵션 : name - alias명


사용 예 :

1
2
3
4
5
6
7
<bean id="user2" class="com.sp4.UserImpl">
    <property name="name" value="스프링"/>
    <property name="age" value="20"/>
    <property name="tel" value="000-0000-0000"/>
</bean>
 
<bean id="userService1" class="com.sp4.UserService"/>


1
2
3
4
5
6
7
8
9
public class UserService {
    @Autowired
    @Qualifier("user2")
    private User user;
 
    public String result() {
        return user.getData();
    }
}



@Resource

개요 : 자바 6버전 및 JEE5 버전에 추가된 것으로 어플리케이션에서 필요로 하는 자원을 자동 연결할 때 사용 한다. 스프링 2.5 부터 지원하는 어노테이션으로 스프링에서는 의존하는 빈 객체를 전달할 때 사용하다.

              @Autowired 와 같은 기능을 하며 @Autowired와 차이점은 @Autowired는 타입으로(by type),  @Resource는 이름으로(by name)으로 연결시켜준다는 것이다.


설정위치 : 프로퍼티, setter 메소드


추가설정 : <bean class="org.springframework.beans.factory.annotation.CommonAnnotationBeanPostProcessor"/> 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.

옵션 : name 

      name속성에 자동으로 연결될 빈객체의 이름을 입력한다.

      @Resource(name="testDao")


CommonAnnotationBeanPostProcessor 클래스를 설정파일에 빈객체로 등록하여 어노테이션을 적용시킨다.

    <bean class="org.springframework.beans.factory.annotation.CommonAnnotationBeanPostProcessor"/>


    <bean name="testBean"  class="han.test.TestBean">

        <property name="testDao" ref="testDao"/>  

<!-- @Required 어노테이션을 적용하였으므로 설정하지 않으면 예외를 발생시킨다. -->

    </bean>


사용 예 :

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserService {
        @Resource(name="user2")
    private User user; 
        //UserImpl user2 = new UserImpl();
        //User user = user2;
 
    public void setUser(User user) {
        this.user = user;
    }
    public String result() {
        return user.getData();
    }
}



@Scope

패키지 : org.springframework.beans.factory.annotation

개요 : 스프링은 기본적으로 빈의 범위를 "singleton" 으로 설정한다. "singleton" 이 아닌 다른범위를 지정하고 싶다면 @Scope 어노테이션을 이용하여 범위를 지정한다.

설정 : prototype, singleton, request, session, globalSession


사용 예 - 1

1
2
3
4
5
@Component
@Scope(value="prototype")
public class Worker {
         :
}


사용 예 - 2

1
2
3
4
5
@Component
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class Worker {
        :
}



@PostConstruct

패키지 : javax.annotation

버젼 : jdk1.6, spring 2.5

개요 : 의존하는 객체를 설정한 이후에 초기화 작업을 수행하기 위해 사용

설정위치 : 초기화 작업 수행 메소드

추가설정 : CommonAnnotationBeanPostProcessor 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.


사용 예 :

1
2
3
4
@PostConstruct
public void init() {
    System.out.println("객체 생성 후 내가 먼저 실행된다.");
}



@PreDestroy

패키지 : javax.annotation

버젼 : jdk1.6, spring 2.5

개요 : 컨테이너에서 객체를 제거하기 전에 해야할 작업을 수행하기 위해 사용

설정위치 : 해당 작업 메소드

추가설정 : CommonAnnotationBeanPostProcessor 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.



@Inject

개요 : JSR-330 표준 Annotation으로 Spring 3 부터 지원하는 Annotation이다. 특정 Framework에 종속되지 않은 어플리케이션을 구성하기 위해서는 @Inject를 사용할 것을 권장한다. @Inject를 사용하기 위해서는 클래스 패스 내에 JSR-330 라이브러리인 javax.inject-x.x.x.jar 파일이 추가되어야 함에 유의해야 한다. 



@Service

개요 : @Service를 적용한 Class는 비지니스 로직이 들어가는 Service로 등록이 된다.


Controller에 있는 @Autowired는 @Service("xxxService")에 등록된 xxxService와 변수명이 같아야 하며

Service에 있는 @Autowired는 @Repository("xxxDao")에 등록된 xxDao와 변수명이 같아야 한다.


@Service("helloService")

public class HelloServiceImpl implements HelloService {


@Autowired

private HelloDao helloDao;


public void hello() {

System.out.println("HelloServiceImpl :: hello()");

helloDao.selectHello();

}

}

helloDao.selectHello(); 와 같이 @Autowired를 이용한 객체를 이용하여 Dao 객체를 호출한다.


사용 예 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service("test2.testService")
//괄호 속 문자열은 식별자를 의미한다.
//괄호를 생략할 경우 클래스명 그대로 사용한다.
//따라서 ,같은 클래스명이 존재 할 시 같은 식별자가 생성되기때문에 에러가 발생한다.
public class TestService {
    public String result(int num1, int num2, String oper) {
String str = null;
         
if(oper.equals("+")){
...
생략
...
        return str;
    }
}


@Resouce로 연결

1
2
3
4
5
6
7
8
9
@Resource(name="test2.testService")
//name에 필요한 것은 @Service("test2.testService") <- 여기서 괄호 속 문자열, 즉 식별자
private TestService service;
//TestService service = new TestService(); 라고 하는것과 같은 식
     
@RequestMapping(value="/test2/oper.action", method={RequestMethod.GET})
public String form() throws Exception {
    return "test2/write";
}



@Repository

패키지 : org.springframework.stereotype

버젼 : spring 2.0

개요 : 레포지 토리 어노테이션은 일반적으로 DAO에 사용되며 DB Exception을 DataAccessException으로 변환한다.


사용 예 :

1
2
3
4
5
6
7
8
@Repository("bbs.boardDAO")
public class BoardDAO {
    private SqlSession sqlSession;
     
    public int insertBoard(Board dto) throws Exception {
        ...
    }
}


1
2
3
4
5
6
public class BoardServiceImpl implements BoardService {
    @Resource(name="bbs.boardDAO")
    private BoardDAO dao;
 
    public int insertBoard(Board dto){}
}



@Controller

패키지 : org.springframework.stereotype

버젼 : spring 2.5

개요 : spring MVC의 Controller 클래스 선언을 단순화시켜준다. 스프링 컨트롤러, 

서블릿을 상속할 필요가 없으며, @Controller로 등록된 클래스 파일에 대한 bean을 자동으로 생성해준다.


Controller로 사용하고자 하는 클래스에 @Controller 지정해 주면 component-scan으로 자동 등록된다.


- xml 설정

<context:component-scan base-package="han.*"/>


- java

package han.test;


import org.springframework.stereotype.Controller;


@Controller

public class SpringC {


}


※ 컨트롤러 메서드의 파라미터 타입

 파라미터 타입설명 

 HttpServletRequest

 , HttpServletResponse, HttpSession     

 서플릿 API 

 java.util.Locale 

 현재 요청에 대한 Locale 

 InputStream, Reader

 요청 컨텐츠에 직접 접근할 때 사용 

 OutputStream, Writer

 응답 컨텐츠를 생성할 때 사용 

 @PathVariable 어노테이션 적용 파라미터

 URI 템플릿 변수에 접근할 때 사용 

 @RequestParam 어노테이션 적용 파라미터

 HTTP 요청 파라미터를 매핑 

 @RequestHeader 어노테이션 적용 파라미터

 HTTP 요청 헤더를 매핑 

 @CookieValue 어노테이션 적용 파라미터

 HTTP 쿠키 매핑 

 @RequestBody 어노테이션 적용 파라미터

 HTTP 요청의 몸체 내용에 접근할 때 사용, HttpMessage Converter를 이용해서 HTTP 요청 데이터를 해당 타입으로 변환한다. 

 Map, Model, ModelMap 

 뷰에 전달할 모델 데이터를 설정할 때 사용

 커맨드 객체

 HTTP 요청 파라미터를 저장한 객체. 기본적으로 클래스 이름을 모델명으로 사용. @ModelAttribute 어노테이션을 사용하여 모델명을 설정할 수 있다. 

 Errors, BindingResult

 HTTP 요청 파라미터를 커맨드 객체에 저장한 결과. 커맨드 객체를 위한 파라미터 바로 다음에 위치 

 SessionStatus

 폼 처리를 완료 했음을 처리하기 위해 사용. @SessionAttribute 어노테이션을 명시한 session 속성을 제거하도록 이벤트를 발생시킨다. 


설명 보기


※ 컨트롤러 메서드의 리턴 타입

 리턴타입설명 

 ModelAndView

 뷰 정보 및 모델 정보를 담고 있는 ModelAndView 객체 

 Model

 뷰에 전달할 객체 정보를 담고 있는 Model을 리턴한다. 이때 뷰 이름은 요청 URL로부터 결정된다. (RequestToViewNameTranslator를 통해 뷰 결정) 

 Map

 뷰에 전달할 객체 정보를 담고 있는 Map을 리턴한다. 이때 뷰 이름은 요청 URL로부터 결정된다. (RequestToViewNameTranslator를 통해 뷰 결정) 

 String

 뷰 이름을 리턴한다. 

 View 객체

 View 객체를 직접 리턴. 해당 View 객체를 이용해서 뷰를 생성한다. 

 void

 메서드가 ServletResponse나 HttpServletResponse 타입의 파라미터를 갖는 경우 메서드가 직접 응답을 처리한다고 가정한다. 그렇지 않을 경우 요청 URL로부터 결정된 뷰를 보여준다. (RequestToViewNameTranslator를 통해 뷰 결정) 

 @ResponseBody 어노테이션 적용

 메서드에서 @ResponseBody 어노테이션이 적용된 경우, 리턴 객체를 HTTP 응답으로 전송한다. HttpMessageConverter를 이용해서 객체를 HTTP 응답 스트림으로 변환한다. 


설명 보기



@RequestMapping

개요 : RequestMapping annotation은 url을 class 또는 method와 mapping 시켜주는 역활을 한다. annotation을 쓰지 않을때 지정했던 Controller등록을 위한 url bean 설정을 생략 할 수 있다. class에 하나의 url mapping을 할 경우, class위에 @RequestMapping("/url")을 지정하며

, GET 또는 POST 방식 등의 옵션을 줄 수 있다. 해당되는 method가 실행된 후, return 페이지가 따로 정의되어 있지 않으면 RequestMapping("/url")에서 설정된 url로 다시 돌아간다.

● 상세 속성 정보

- value 

 : "value='/getMovie.do'"와 같은 형식의 매핑 URL 값이다. 디폴트 속성이기 때문에 value만 정의하는 경우에는 'value='은 생략할 수 있다.

예 : @RequestMapping(value={"/addMovie.do", "/updateMovie.do" }) 

"/addMovie.do", "/updateMovie.do" 두 URL 모두 처리한다.


- method

 : GET, POST, HEAD 등으로 표현되는 HTTP Request method에 따라 requestMapping을 할 수 있다. 'method=RequestMethod.GET' 형식으로 사용한다. method 값을 정의하지 않는 경우 모든 HTTP Request method에 대해서 처리한다.

예 : @RequestMapping(method = RequestMethod.POST) 

value 값은 클래스 선언에 정의한 @RequestMapping의 value 값을 상속받는다.  


- params 

 : HTTP Request로 들어오는 파라미터 표현이다. 'params={"param1=a", "param2", "!myParam"}' 로 다양하게 표현 가능하다.

예 : @RequestMapping(params = {"param1=a", "param2", "!myParam"}) 

HTTP Request에 param1과 param2 파라미터가 존재해야하고 param1의 값은 'a'이어야하며, myParam이라는 파라미터는 존재하지 않아야한다. 또한, value 값은 클래스 선언에 정의한 @RequestMapping의 value 값을 상속받는다. 


- headers

 : HTTP Request의 헤더 값이다.'headers="someHader=someValue"', 'headers="someHader"', 'headers="!someHader"' 로 다양하게 표현 가능하다. Accept나 Content-Type 같은 헤더에 대해서 media type 표현 시 '*' 도 지원한다.

예 : @RequestMapping(value="/movie.do", headers="content-type=text/*") 

HTTP Request에 Content-Type 헤더 값이 "text/html", "text/plain" 모두 매칭이 된다. 또한, Type-Level, Method-Level에서 모두 사용할 수 있는데, Type-Level에 정의된 경우, 하위의 모든 핸들러 메소드에서도 Type-Level에서 정의한 헤더값 제한이 적용된다.


produces

 : 확인중. 명시하지 않아도 큰 문제는 발생하지 않는것으로 보인다. 다음처럼 설정할 경우 클라이언트는 json타입의 값을 받게 된다.

    @RequestMapping(value="...", produces = "application/json")

    @RequestBody

    public HashMap<String, String> testMethod(Model model) {

        HashMap<String, String> map = new HashMap<String, String>();

        map.put("code", "0");

        return map;

    }



사용 예 : 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Controller
@RequestMapping("/han/test/*")
public class HelloController {
 
@RequestMapping(method=RequestMethod.GET, value="go")
public returntype getMethodName(){
    :
}
 
@RequestMapping(method=RequestMethod.POST, value="go2")
public returntype getMethodName2(){
    :
}
 
@Controller("mainController")
@RequestMapping(value="/main.action")
public class MainController {
      
    @RequestMapping(method=RequestMethod.GET)
    public String method() {
        return ".mainLayout";
    }
}



@RequestParam

개요 : RequestParam annotation은 key=value 형태로 화면에서 넘어오는 파라미터를 맵핑 된 메소드의

파라미터로 지정해 준다. 주로 get 방식으로 들어오는 request에서 사용한다.


아래에서 xxx/editBlog.do?blogId=3 과 같이 접근할 때, editBlogHandler 메소드의 파라미터인 blogId에는 

3이 셋팅된다. 필수 요건이 아닐 경우, @RequestParam(value="id", required="false")와 같이 옵션을 

주고 사용할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public class BlogController {
    // 중간생략
     
    @RequestMapping("/editBlog")
    public ModelMap editBlogHandler(@RequestParam("blogId") int blogId) {
        blog = blogService.findBlog(blogId);
        return new ModelMap(blog);
    }
  
    // 중간생략
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RequestMapping(value="/...", method={RequestMethod.GET, RequestMethod.POST})
public String submit(HttpServletRequest req
        , @RequestParam(value="num1") int num1
        , @RequestParam(value="num2") int num2
        , @RequestParam(value="oper") String oper) throws Exception {
    ...  //value : form객체가 넘겨줄 값의 name
}
 
//@RequestParam 어노테이션이 적용된 파라미터는 기본적으로 필수 파라미터이다.
//따라서, 명시한 파라미터가 존재하지 않을 경우 400 에러가 발생한다.
//여기서 파라미터에 값이 있을수도 없을수도 있는 로직을 구현하려면 다음처럼 작성한다.
 
@RequestMapping(value="/...", method={RequestMethod.GET, RequestMethod.POST})
public String submit(HttpServletRequest req
        , @RequestParam(value="num1", defaultValue = "0") int num1
        , @RequestParam(value="num2", defaultValue = "0") int num2
        , @RequestParam(value="oper", required=false) String oper) throws Exception {
    ...
}



사용 예 :

파라미터를 각각 지정하는것이 번거롭다면 다음처럼 작성한다:

1
2
3
4
5
@RequestMapping("/faqDetail")
public String faqDetail(@RequestParam HashMap<String, String> map) {
     
    return "board/faq/faqDetail";
}

이렇게 작성하면 뷰에서 전달된 파라미터는 모두 하나의 컬렉션으로 전달된다. 



@SessionAttributes

개요 : SessionAttribute annotation은 세션상에서 model의 정보를 유지하고 싶을 경우 사용 할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
@SessionAttributes("blog")
public class BlogController {
    // 중간생략
  
    @RequestMapping("/createBlog")
    public ModelMap createBlogHandler() {
        blog = new Blog();
        blog.setRegDate(new Date());
        return new ModelMap(blog);
    }
  
    // 중간생략
}



@InitBinder

개요 : WebDataBinder를 초기화하는 method를 지정 할 수 있는 설정을 제공한다. 


일반적으로 WebDataBinder는 annotation handler 메소드의 command 와 form 객체 인자를 조작하는데 사용된다. 


InitBinder 메소드가 필수적으로 반환값을 가질 필요는 없으며, 일반적으로 이런 경우에 void를 선언한다. 특별한 인자는 WebdataBinder와 WebRequest또는 Locale의 조합으로 이루어지며, 이러한 조건이 만족되면 context-specific editors를 등록하는것이 허용된다.


WebdataBinder : WebDataBinder는 web request parameter를 javaBean 객체에 바인딩하는 특정한 DataBinder이다. WebDataBinder는 웹 환경이 필요하지만, Servlet API에 의존적이지 않다. servlet API에 의존적인 ServletRequestDataBinder와 같이 특정한 DaraBinder를 위한 더많은 base class를 제공한다.


RequestMapping : RequestMapping annotation은 web request를 특정한 handler class와 handler method에 mapping하는 역활을 수행한다. 대응하는 handlerMapping(for type level annotation)과 HandlerAdapter(for method level annotation)가 dispatch에 존재한다면, @RequestMapping이 처리된다.


WebRequest : WebRequest는 웹 요청에 대한 Generic interface이다. 주로 일반 request metadata에 generic web request interceptors의 접근을 허용하여 metadata에 대한 처리를 하기 위한 것이지 request 자체를 처리하기 위한 것은 아니다.



Annotation 기반 Controller 에서 ServletContext 구하기 :

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
@RequestMapping("/common/download")
public class DownloadController {
    @Autowired
    private ServletContext sc;
 
    @RequestMapping
    public ModelAndView download(@RequestParam("filePath") String filePath) throws Exception {
        String path = sc.getRealPath(filePath);
        return new ModelAndView("common.download", "downloadFile", new File(path));
    }
}



@ModelAttribute

● 파라미터에 지정하는 경우

 화면에서 전달된 model을 파라미터 객체의 프로퍼티와 매핑한다.

1
2
3
4
5
6
7
8
9
10
11
public class MyModel {
    private String title;
 
    public void setTitle(String title) {
        this.title = title;
    }
 
    public String getTitle() {
        return this.title;
    }
}

1
2
3
4
5
6
7
8
@RequestMapping(value = "/titleSubmit")
public String titleSubmit(@ModelAttribute MyModel params) throws Exception {
 
    params.getTitle();
 
    // 생략
    // ...
}


● 메서드 혹은  리턴타입에 지정하는 경우

 컨트롤러에서 뷰에 전달할 일종의 공통 모델을 설정한다.


예를 들어 다음과 같은 TestController가 존재할 때 :

1
2
3
4
5
6
7
8
9
10
11
@Controller
@RequestMapping("/sample")
public class TestController {
 
    @ModelAttribute("modelTest")
    public String[] refModelTest() {
        return new String[] {"하나", "둘", "셋"};
    }
 
    ...
}


@ModelAttribute 어노테이션이 적용된 메소드가 리턴하는 값은 Request객체에 전달된다. 

전달 범위는 해당 메소드가 존재하는 컨트롤러 전체에 해당된다. (전역변수가 되는 셈)


EL을 적용해 접근할 땐 다음처럼 작성한다 :

1
2
3
4
5
6
7
<table>
    <tr>
        <c:forEach var="entity" items="${modelTest}">
            <td>${entity}</td>
        </c:forEach>
    </tr>
</table>






@RequestBody

@RequestBody 어노테이션이 적용된 파라미터는 HTTP Request body의 내용이 전달된다.


참고페이지

http://java.ihoney.pe.kr/283


1
2
3
4
5
6
7
@RequestMapping(value="/test")
public void penaltyInfoDtlUpdate(@RequestBody String body,
        HttpServletRequest req, HttpServletResponse res,
        Model model, HttpSession session) throws Exception  {
 
    System.out.println(body);  
}



@ResponseBody

컨트롤러가 응답할 값 혹은 객체를 HTTP Response body로 전달한다.


참고페이지

http://ismydream.tistory.com/140


클라이언트로 JSON 객체를 전달할 때 유용하다. 메서드에 @ResponseBody를 적용한 후 객체를 리턴하면 그 값은 VIEW를 통해 전달되는 것이 아니라 HTTP ResponseBody에 직접 쓰여진다. 보통 AJAX를 처리할 메서드에 적용한다. 이 경우 클라이언트 스크립트의 AJAX 옵션 설정에 주의해야 한다. ($.ajax()의 경우라면 받는 데이터를 JSON으로 파싱하도록 dataType: "json" 옵션 추가)


1
2
3
4
5
6
7
@RequestMapping("/getVocTypeList")
@ResponseBody
public ArrayList<Object> getVocTypeList() throws Exception {
     
    HashMap<String, Object> vocData = gvocInf.searchVocTypeList();
    return (ArrayList<Object>) vocData.get("data");
}


HashMap 객체를 리턴하면 JSON은 Map형태로 변환되며, 위처럼 ArrayList로 리턴할 경우엔 배열의 형태로 변환된다.



@PathVariable

URL의 일부를 파라미터 혹은 변수로 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
package com.sp.ex;
 
@Controller("ex.exController")
public class ExController{
    @RequestMapping(value="/blog/{userId}/main.action", method=RequestMethod.GET)
    public String main(HttpServletRequest req
                       , @PathVariable String userId) throws Exception  {
 
        req.setAttribute("userId", userId);
        return "restful/result";
    }
}



출처 - http://noritersand.tistory.com/156

:

RequestDispatcher forward method

FRAMEWORK/SERVLET 2014. 11. 5. 17:59

RequestDispatcher forward :

Hello, we are going to learn about RequestDispatcher forward method in servlet API.

Why is it needed?

The need may arise such that when a request is made for some specific resource, and the resource cannot handle the operations those are needed, it can simply delegate those operations to another resource and another resource serves the request with its own response.

How to do it?

Servlet specification defines an interface called RequestDispatcherRequestDispatcher forward can be used for this purpose. As per javadoc,

Defines an object that receives requests from the client and sends them to any resource (such as a servlet, HTML file, or JSP file) on the server.
Forward method:
Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server. This method allows one servlet to do preliminary processing of a request and another resource to generate the response.

Step 1: Create a dynamic web project.

Create a new dynamic web project in eclipse named ServletJSPExamples .

Step 2: Create and configure DispatcherServlet.

Create a servlet named DispatcherServlet which will use RequestDispatcherforwardto some other resource, in this case,  Hello.jsp  .

Configure it in web.xml

 Step 3: Create Hello.jsp

Create Hello.jsp  in WebContent folder in eclipse.

 Step 4: Run application.

Deploy your application in tomcat and hit url as per your application. As our application name is ServletJSPExamples , the url will behttp://localhost:8080/ServletsJSPExamples/DispatcherServlet

You will see the output as  Hello page.

Explanation:

We got the output as Hello page. that’s fine, but what is happening here. Let us understand step by step.

  • In DispatcherServlet , we created a PrintWriter object so that response can be written.
  • After out.println();  statement, a RequestDispatcher is created for Hello.jsp  using RequestDispatcher rd =request.getRequestDispatcher("Hello.jsp");
  • rd.forward(request, response); i.e. RequestDispatcher forward method sends HttpServletRequest  and HttpServletResponse objects to the resource for which RequestDispatcher is created. So in this case, it’s Hello.jsp .
  • Control is forwarded to  Hello.jsp  and the contents are written to response. The response is committed and sent to the client.

The figure will elaborate the same.



'FRAMEWORK > SERVLET' 카테고리의 다른 글

RequestDispatcher include method  (0) 2014.11.05
서블릿이란  (0) 2014.11.05
jsp 와 servlet 의 차이  (0) 2014.11.05
서블릿  (0) 2014.11.05
:

RequestDispatcher include method

FRAMEWORK/SERVLET 2014. 11. 5. 17:57

RequestDispatcher include

Hello, continuing the previous article, we will learn about RequestDispatcherincludemethod.

Why is it needed?

The forward method redirects to another resource which is called through a resource. The response of calling resource is not sent to the client. It is lost. What should be done if we want to retain the response of calling resource and called resource? What if we want to show them both to the client?

How to do it?

RequestDispatcher include method comes to the rescue. This is what javadoc says about RequestDispatcher include .

Includes the content of a resource (servlet, JSP page, HTML file) in the response. In essence, this method enables programmatic server-side includes.

Let us see a practical example of  RequestDispatcher include  method.

Step 1: Create dynamic web project.

Create a new dynamic web project called ServletJSPExamples using eclipse IDE.

Step 2: Create and configure DispatcherServlet.

Create a new servlet called DispatcherServlet which will useRequestDispatcher includemethod to call another resource.

Configure it in web.xml

 Step 3: Create Hello.jsp

 Step 4: Run application.

Deploy the application under tomcat. Start it and hit the urlhttp://localhost:8080/ServletsJSPExamples/DispatcherServlet .

You will see the output as :

 

 Explanation:

  • PrintWriter is created in DispatcherServlet so that response can be written.
  • After  out.println();   statement, a RequestDispatcher is created for Hello.jsp   using RequestDispatcher rd =request.getRequestDispatcher("Hello.jsp");
  • rd.include(request, response);  i.e.  RequestDispatcher include  method sends HttpServletRequest and HttpServletResponse objects to the resource for which RequestDispatcher is created. So in this case, it’s Hello.jsp .
  • Control is forwarded to Hello.jsp   and the contents are written to response.
  • Now as we have used  RequestDispatcher include  method, the response fromDispatcherServlet is combined with the response of Hello.jsp . It acts as a dynamic include.
  • So both responses are printed onto the web page.

The diagram below will elaborate it clearly.




출처 - http://www.thejavageek.com/2013/12/11/requestdispatcher-include-method/

'FRAMEWORK > SERVLET' 카테고리의 다른 글

RequestDispatcher forward method  (0) 2014.11.05
서블릿이란  (0) 2014.11.05
jsp 와 servlet 의 차이  (0) 2014.11.05
서블릿  (0) 2014.11.05
:

서블릿이란

FRAMEWORK/SERVLET 2014. 11. 5. 17:43

1. Servlet의 이해

1.1 Servlet 이란

  • Servlet은 웹에서 JAVA 프로그래밍을 구현하기 위해 탄생 함.
  • JAVA 로 구현된 CGI(Common Gateway Interface) 라고들 흔히 말함
  • HTTP protocol 서비스를 지원하는 javax.servlet.http.HttpServlet 클래스를 상속하여 개발하며, Servlet은 Container에 의해서 실행되고, 관리된다.
  • HTML 변경시 Servlet을 재컴파일 해야 하는 단점이 있다.

1.2 Servlet Container란

  • HTTP 요청을 받아서 Servlet을 실행시키고, 그 결과를 사용자 브라우저에게 전달해주는 기능을 제공하는 컴포넌트 이다.
  • Servlet을 실행하고 생명주기를 관리하는 역할을 한다.
  • Servlet과 웹 서버(Apache, nginx..)가 서버 통신 할 수 있는 방법을 제공한다.
  • 멀티 스레딩을 지원하여 클라이언트의 다중 요청을 알아서 처리해준다.
  • 대표적인 Conatainer에는 Tomcat, jetty, jboss 등이 있다.

1.3 Servlet 동작과정



  • DD (배포서술자, Deployment Descriptor) = web.xml① 사용자가 URL을 클릭하면 HTTP Request를 Servlet Container에 보낸다.
  • ② Servlet Container는 HttpServletRequest, HttpServletResponse 두 객체를 생성한다.
  • ③ 사용자가 요청한 URL을 분석하여 어느 서블릿에 대한 요청인지 찾는다. (DD를 참조하여 분석)
  • ④ 컨테이너는 서블릿 service() 메소드를 호출하며, POST, GET여부에 따라 doGet() 또는 doPost()가 호출된다.
  • ⑤ doGet() or doPost() 메소드는 동적인 페이지를 생성한 후 HttpServletResponse객체에 응답을 보낸다.
  • ⑥ 응답이 완료되면 HttpServletRequest, HttpServletResponse 두 객체를 소멸시킨다.
  • Servlet, Error Page, Listener, Fillter, 보안 설정등 Web Application의 설정 파일이다.
  • URL과 실제 서블릿의 매핑 정보도 담고 있다.
  • 하나의 웹 어플리케이션에 하나만 존재한다.
  • 보통 Web Document Root 디렉토리에 WEB-INF 폴더 아래 web.xml 파일로 존재한다.

1.4 JSP의 동작구조



출처 - http://wiki.gurubee.net/pages/viewpage.action?pageId=26740202




서블릿(Servlet)
 - 자바 플랫폼에서 컴포넌트를 기반으로 한 웹 애플리케이션을 개발할 때 사용하는 핵심 기술.
   이러한 서블릿을 이용하면 웹 애플리케이션 확장이 용이하고 플랫폼에 독립적인 개발이 가능하다.
 - 쉽게 말하면 "클라이언트의 요구를 받아 그에 대한 처리를 한 후, 결과를 되돌려주는 서버 모듈"이다.


서블릿은 JSP에서 컨텐츠와 비즈니스 로직을 분리한다.
JSP가 텍스트 파일 구조인데 비해 서블릿은 자바 클래스 구조이다.





서블릿의 장점
1) 스레드를 기반으로 하기 때문에 웹 애플리케이션 운영에 효율적이다.
2) 자바를 기반으로 하므로 자바 API를 모두 사용할 수 있다.
3) 운영체제나 하드웨어에 영향을 받지 않으므로, 한번 개발된 애플리케이션은 다양한 서버 환경에서 실행 가능
4) 웹 애플리케이션에서 효율적인 자료 공유 방법을 제공한다.


웹 애플리케이션 개발시 서블릿 이용의 장점
1) 컨텐츠와 비즈니스 로직을 분리
2) 컨트롤러와 뷰 역할 분담으로 인해 웹 디자이너와 개발자간의 원활한 작업이 가능.
3) 유지보수가 수월, 기능 확장이 용이하다.
4) 현재와 같이 프로그래머가 HTML, 자바스크립트, 스타일 시트와 같은 복잡한 기술을 모두 알아야 할
    필요는 없다.
5) JSP에 HTML, CSS, 자바스크립트와 자바 소스 코드가 뒤섞이는 문제를 막는다.


서블릿 컨테이너(Servlet Container)

 - 서블릿 구조에서의 가장 큰 특징.
 - 서블릿을 서버에서 실행하기 위한 서버 프로그램. 
   웹 서버는 서블릿 자체를 실행하지 못하므로 JVM을 내장한 컨테이너라는 서블릿 실행환경이 필요하다. 


동작 과정
1) 웹 서버는 클라이언트 요청을 컨테이너로 전달한다. 컨테이너는 서블릿이 메모리에 있는지 확인하고 없을시
   클라이언트 요청을 처리할 스레드를 생성해서 처리한다.
2) 서블릿은 JDBC를 이용해 데이터베이스에 접근할 수 있고, 별도로 제작된 자바 클래스를 통해 다양한 기능을 구현한다.
3) 서블릿에서 수행된 결과는 HTML 형식으로 클라이언트에 전달된다.



아마 둘다 Java 언어로 만드는 것인데 이름이 비슷해서 헷갈릴 것이다.
둘 다 단순한 HTML 문서를 자바 프로그래밍을 이용하여 확장시킨 것으로 볼 수 있다.

애플릿(applet)은 하나의 완성된 자바프로그램이다. 자바가상머신(JVM)만 있으면 컴퓨터에서 실행시킬 수 있다.
쉬운 예로 들면, 플래시가 있는 웹 페이지를 생각해보자. 그 페이지를 접속하면 플래시가 실행된다. 브라우저 안에 플래시를 실행하는 기능이 플러그인

되어 있기 때문에 사용자는 이것이 어떠한 원리로 실행되는지 알 필요 없이 실행되는 것만 보면 된다.

애플릿도 동일하다. 웹 페이지 안에 들어있기 때문에, 그 페이지를 실행시키면, 자동적으로 실행되는 애플릿을 볼 수가 있다. 

애플릿은 완전한 자바 프로그래밍이기 때문에 확장성이 엄청나다. 프로그래밍에 따라서 무척 많은 것을 사용자에게 보여줄 수 있다.



서블릿(Servlet)도 하나의 자바프로그램이긴 한데, 서블릿의 주된 업무는 사용자에게 정보를 입력받아 적절한 HTML 문서를 돌려
주는 역할을 한다. 이러한 일을 하는 프로그램을 자바로 구현한 것이다. 사용자의 입력에 따라서 다른 HTML문서를 만들어내므로 동적인 HTML 문서를 구현할 수가 있게 된 것이다.


서블릿은 서블릿 규격에 맞게 구현해야 한다. 구현해야 하는 함수들이 미리 지정되어 있다. 그리고 서블릿은 서블릿 컨테이너라는 
기능이 있어야 실행이 된다. Tomcat이나 Resin등의 프로그램인데 이는 서버에 있어야하는 것이므로 사용자 입장에서는 신경을 쓰지 않아도 된다.


CGI라는 것이 사용자의 입력을 받아서 동적인 HTML 문서를 만드는 기능을 하는 것인데, 서블릿도 일종의 CGI라고 볼 수도 있다.

자바로 구현한 CGI 말이다. 하지만, 자바의 특성으로 인하여 기존의 CGI보다 훨씬 향상된 성능을 나타내게 된다.


서블릿 규칙은 꽤 복잡하다. 따라서 이를 좀 더 쉽게 프로그래밍하기 위하여 나온 것이 JSP이다. JSP 문서를 실행시켜면 이 문서가 
자동적으로 서블릿으로 컴파일되어 실행되게 된다.


출처 - http://warmz.tistory.com/524


'FRAMEWORK > SERVLET' 카테고리의 다른 글

RequestDispatcher forward method  (0) 2014.11.05
RequestDispatcher include method  (0) 2014.11.05
jsp 와 servlet 의 차이  (0) 2014.11.05
서블릿  (0) 2014.11.05
:

jsp 와 servlet 의 차이

FRAMEWORK/SERVLET 2014. 11. 5. 17:35

서블릿은 멀티쓰레딩에 의해 사용자 요구를 처리하고 가공해서 이에 대한 결과를 내보내게 된다. CGI가 클라이언트 프로세스로 처리하는데 반해 서블릿은 클라이언트를 쓰레드로 처리한다. 그래서 많은 클라이언트의 요구를 효과적으로 처리할 수 있다. 서블릿 객체는 쓰레드가 여러 개 돌아가면서 처리하기 때문에 서블릿 메소드들은 반드시 멀티쓰레드에 대한 고려를 해야한다.

 JSP와 서블릿은 자바 기반으로 만들어진 웹 프로그래밍 언어이다. 서블릿이 자바 코드에 의존적이라면 JSP는 덜 의존적이라 프로그래밍하기가 더 쉽고 편하다. JSP와 서블릿은 같은 처리 구조를 가진다. 엄밀히 말하면, JSP는 페이지 요청이 있을 시에는 최초에 한 번 자바 코드로 변환된 후 서블릿 클래스로 컴파일된다. 결론적으로 JSP는 실행시 서블릿으로 변환된다. 단 한번만 서블릿으로 변경되며 코드를 수정하기 전까지 재 변환 작업이 일어나지 않기 때문에 수행 속도는 JSP나 서블릿간에 별차이가 없다.
 
 서블릿과 JSP는 상호 연계되어 JSP에서 정적인 부분을 담당하고, 서블릿은 보다 동적인 처리를 위한 부분으로 사용되어 보다 효율적인 웹사이트를 구성할수 있다. JSP는 주로 사용자용 뷰(view)의 구현에 사용되고 서블릿은 사용자의 뷰와 프로그램 로저 사이를 제어해주는 역활을 주로 사용한다.

서블릿은 서버에서 실행되는 작은 프로그램이다. 이 용어는 웹 페이지와 함께 별도의 파일로 보내지는 작은 프로그램인 자바 애플릿의 맥락에서 만들어진 신조어이다. 자바 애플릿들은 사용자를 위해 간단한 계산업무를 수행하거나 사용자의 반응에 기반하여 이미지를 위치시키는 등과 같은 서비스를 위해 대개 클라이언트에서 실행되도록 만들어진다.

그러나, 사용자의 입력에 따라 데이터베이스와 연계되는 프로그램들은 서버에서 실행될 필요가 있다. 보통, 이러한 것들은 CGI를 이용하여 구현된다. 그러나, 서버에서 실행되는 자바 가상머신을 이용하면, 그러한 프로그램들을 자바 언어로 구현할 수 있다. 서버에 있는 자바 서블릿의 장점은 CGI 응용프로그램보다 더 빠르게 실행될 수 있다는 것이다. 서블릿은 각 사용자의 요청마다 별도의 프로세스가 생기는 대신, 단 하나의 데몬 프로세스 내에서 스레드로 호출되는데, 이는 각 요구에 따른 시스템 오버헤드가 적다는 것을 의미한다.


결국,
jsp 나 servlet 이나 결론적으로는 같음.
단, jsp는 서버에서 서블릿으로 변환되어 - 처음 한번만 - 실행됨
jsp 가 view단을 처리하기에 편리함. 서블릿의 경우엔 모두 프로그램적으로 처리해야함(out.println("<html>"); 이런 식으로)

MVC는 각각의 효율성을 봐서 V를 jsp, C를 서블릿에 맡긴 것.


출처 - http://lonelycat.tistory.com/467

'FRAMEWORK > SERVLET' 카테고리의 다른 글

RequestDispatcher forward method  (0) 2014.11.05
RequestDispatcher include method  (0) 2014.11.05
서블릿이란  (0) 2014.11.05
서블릿  (0) 2014.11.05
:

서블릿

FRAMEWORK/SERVLET 2014. 11. 5. 17:33

블릿이란?

게시판과 같은 프로그램을 만들기 위한 자바측 기술 중 하나이다.
java.sql 팩키지를 JDBC 라고 부르는 것처럼, javax.servlet 과 javax.servlet.http 팩키지를 서블릿이라 부른다.

서블릿은 네트워크 프로토콜과 무관하게 설계되었지만, 대부분 HTTP 프로토콜을 사용하는, 웹환경에서 동적인 컨텐츠를 만들 때에 사용된다.
참고로, JSP는 서블릿 이후에, 서블릿 기술을 기반으로 탄생했는데, 서블릿보다 동적인 웹 페이지 화면을 쉽게 만들 수 있다.

서블릿을 학습할 때는 javax.servlet, javax.servlet.http 팩키지에서 서블릿의 기본골격을 먼저 공략하는 것이 현명한 학습방법이다.

서블릿의 기본 골격

서블릿 기본골격은, 모든 서블릿이 구현해야 하는 javax.servlet.Servlet 인터페이스, 대부분의 모든 서블릿이 상속해야 하는 javax.servlet.GenericServlet 추상클래스, HTTP 프로토콜을 사용하는 서블릿이 상속해야 하는 javax.servlet.http.HttpServlet 클래스로 구성된다.
아래 그림1처럼 GenericServlet 은 프로그래머가 사용하기 편하도록 javax.servlet.ServletConfig 인터페이스를 구현하고 있다.


Servlet 인터페이스

javax.servlet.Servlet 인터페이스는 서블릿 아키텍처의 핵심이다.
모든 서블릿은 Servlet 인터페이스를 구현해야 한다.
이 인터페이스에는 서블릿의 라이프 사이클 메소드가 선언되어 있다.

  • init() : 서블릿 초기화
  • service() : 클라이언트 요청에 대한 서비스
  • destroy() : 서비스 중지, 자원반납

init() 메소드

서블릿 컨테이너는 서블릿 객체가 생성된 후, 단 한번 init() 메소드를 호출한다.
서블릿은 init() 메소드가 완료되어야 서비스할 수 있다. init() 메소드 완료 전의 요청은 블록킹된다.
init() 메소드가 호출될 때 ServletConfig 인터페이스 타입의 객체를 매개변수로 전달받는데, 만약 web.xml 에서 서블릿 초기화 파라미터를 설정했다면, 전달받은 ServletConfig 에는 web.xml에서 설정했던 서블릿 초기화 파라미터 정보를 가지고 있게 된다.
초기화 파라미터가 있다면 init() 메소드에 서블릿의 초기화 작업을 수행하는 코드가 있어야 할 것이다.

void init(ServletConfig config) throws ServletException;

service() 메소드

클라이언트가 서블릿에 요청을 보낼때마다, 서블릿 컨테이너는 서블릿의 service() 메소드를 호출한다.
전달받은 ServletRequest 타입의 객체를 통해서 요청정보와 클라이언트가 전달한 데이터를 읽을 수 있으며, 전달받은 ServletResponse 타입의 객체를 사용하여 클라이언트에게 응답할 수 있다.
여기서 주목해야 할 점은, 서블릿 컨테이너는 새로운 쓰레드에서 service()메소드를 실행한다는 것이다.
즉, 요청시마다 새로운 쓰레드에서 service() 메소드를 동시에 실행한다.
가각의 스레드에서 동시에 실행되기에 수많은 클라이언트의 요청에 대해 지체없이 응답을 할 수 있지만, 서블릿에서 공유되는 자원(파일이나 네트워크 커넥션, static 변수, 인스턴스 변수)은 "자바기초의 스레드"에서 보았던 문제가 발생할 수 있다.
문제가 발생하지 않도록 하려면, 서블릿에서 문제가 될 수 있는 static 이나 인스턴스 변수를 만들지 않는 것이 좋다.
서블릿이 공유하는 자원을 모두 동기화하는 것은 대부분의 경우에 옳은 코딩이 아니다.

void service(ServletRequest req, ServletResponse res) 
	throws ServletException, IOException;

destroy() 메소드

서블릿이 더이상 서비스를 하지 말아야 할 때 서블릿 컨테이너에 의해 호출된다.
이 메소드는 프로그래머가 호출하는게 아니다.
따라서 destroy() 를 예제로 확인하려면 "톰캣 매니저"를 사용하여 애플리케이션을 언로드하거나 톰캣을 셧다운시켜야 한다.
참고로 톰캣매니저는 http://localhost:port/manager 로 접근할 수 있는 웹 애플리케이션으로 웹 애플리케이션을 관리할 용도로 제공된다.
톰캣을 설치할 때 정해준 관리자와 관리자 비밀번호를 사용하여 로그인을 해야 화면을 볼 수 있다.
관리자와 관리자 비밀번호가 생각나지 않으면 {톰캣홈}/conf/tomcat-users.xml 파일을 열어보면 알 수 있다.

void destroy();

GenericServlet 추상클래스

GenericServlet 클래스는 부모 클래스로 쓰인다.
GenericServlet 클래스는 편의를 위해서 ServletConfig 인터페이스를 구현한다.
GenericServlet 는 Servlet 인터페이스를 불완전하게 구현한다.
Servlet 인터페이스의 service() 메소드를 구현하지 않았기 때문에, GenericServlet 의 service() 메소드는 abstract 가 붙어야 하고, 그래서 GenericServlet 은 추상클래스이다.
GenericServlet 를 상속하는 자식 클래스는 service() 메소드를 구현해야 한다.

public abstract void service(ServletRequest req, ServletResponse res) 
	throws ServletException, IOException;

init(ServletConfig config) 메소드는 다음과 같이 GenericServlt 에 구현되었다.

public void init(ServletConfig config) throws ServletException {
	this.config = config;
	this.init();
}

init(ServletConfig config) 구현부에 마지막에 매개변수가 없는 init() 메소드를 호출하고 있다.
매개변수가 없는 init() 메소드는 편의를 위해 GenericServlet 에 추가되었다.
자식 클래스에서 init(ServletConfig config) 메소드를 오버라이딩하는 것보다 이 메소드를 오버라이딩하는 것이 편리하다.
왜냐하면 ServletConfig 객체를 저장해야 한다는 걱정을 하지 않아도 되기 때문이다.
init(ServletConfig config) 메소드의 경우 자식 클래스에서 오버라이딩하려면 구현부에 super(config); 코드가 반드시 있어야 한다.
이 코드가 없으면 서블릿이 ServletConfig 객체를 저장하지 못하게 된다.
매개변수가 없는 init() 메소드는 다음과 같이 아무일도 하지 않는 것으로 구현되어 있다.

public void init() throws ServletException {

}

Servlet 인터페이스의 getServletConfig() 메소드는, init(ServletConfig config) 메소드에서 전달받아서 인스턴스 변수 config 에 저장된 ServletConfig 객체를 반환하도록 구현되어 있다.

public ServletConfig getServletConfig() {
	return config;
}

ServletConfig 인터페이스의 getServletContext() 메소드는 ServletContext 타입의 객체를 반환한다.

public ServletContext getServletContext() {
	return getServletConfig().getServletContext();
}

ServletConfig 인터페이스의 getInitParameter() 와 getInitParameterNames() 메소드는 GenericServlet 에서 다음과 같이 구현되어 있다.

public String getInitParameter(String name) {
	return getServletConfig().getInitParameter(name);
}
public Enumeration getInitParameterNames() {
	return getServletConfig().getInitParameterNames();
}   

편의를 위해서 GenericServlet이 ServletConfig를 구현했다는 말은 이런 것이다.
서블릿에서 ServletContext에 대한 레퍼런스를 얻기 위해서 this.getServletConfig().getServletContext(); 란 코드도 되지만 this.getServletContext(); 를 쓰는 것이 편리할 것이다.
초기화 파라미터 정보를 얻기 위해서 String driver = this.getServletConfig().getInitParameter("driver"); 와 같이 쓰기 보다는 String driver = this.getInitParameter("driver"); 쓰는 것이 편리할 것이다.

HttpServlet 클래스

HTTP 요청을 서비스하는 서블릿을 작성하는 경우에는 HttpServlet을 상속한다.
GenericServlet 추상 클래스를 상속하는 HttpServlet 클래스는 HTTP 프로토콜에 특화된 서블릿이다.
HttpServlet 클래스는 HTTP 요청을 처리하는 메소드를 제공한다.
클라이언트의 요청은 HttpServletRequest 객체 타입으로 서블릿에 전달되며, 서버는 HttpServletResponse 객체 타입으로 사용하여 응답한다.
HttpServlet 클래스는 GenericServlet 의 service() 추상 메소드를 구현했는데 구현 내용은 단지 클라이언트의 요청을 protected void service(HttpServletRequest req, HttpServletResponse resp) 메소드에 전달하는 것이다.

public void service(ServletRequest req, ServletResponse res) 
	throws ServletException, IOException {
	
	HttpServletRequest  request; 
	HttpServletResponse response;
	
	try {
		request = (HttpServletRequest) req;
		response = (HttpServletResponse) res;
	} catch (ClassCastException e) {
		throw new ServletException("non-HTTP request or response");
	}
	
	service(request, response);
}

결국, 다음 메소드가 HTTP 요청을 처리한다.

protected void service(HttpServletRequest req, HttpServletResponse resp) 
	throws ServletException, IOException {
	
	String method = req.getMethod();
	
	if (method.equals(METHOD_GET)) {
		long lastModified = getLastModified(req);
		if (lastModified == -1) {
			// servlet doesn't support if-modified-since, no reason
			// to go through further expensive logic
			doGet(req, resp);
		} else {
			long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
			if (ifModifiedSince < (lastModified / 1000 * 1000)) {
				// If the servlet mod time is later, call doGet()
				// Round down to the nearest second for a proper compare
				// A ifModifiedSince of -1 will always be less
				maybeSetLastModified(resp, lastModified);
				doGet(req, resp);
			} else {
				resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
			}
		}
	} else if (method.equals(METHOD_HEAD)) {
		long lastModified = getLastModified(req);
		maybeSetLastModified(resp, lastModified);
		doHead(req, resp);
	} else if (method.equals(METHOD_POST)) {
		doPost(req, resp);
	} else if (method.equals(METHOD_PUT)) {
		doPut(req, resp);
	} else if (method.equals(METHOD_DELETE)) {
		doDelete(req, resp);
	} else if (method.equals(METHOD_OPTIONS)) {
		doOptions(req,resp);
	} else if (method.equals(METHOD_TRACE)) {
		doTrace(req,resp);
	} else {
		//
		// Note that this means NO servlet supports whatever
		// method was requested, anywhere on this server.
		//
		
   		String errMsg = lStrings.getString("http.method_not_implemented");
   		Object[] errArgs = new Object[1];
   		errArgs[0] = method;
   		errMsg = MessageFormat.format(errMsg, errArgs);
   		
		resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
	}
}

HttpServlet 의 +service()는 단지 #service()메소드에게 제어권을 넘기는 역할만 한다.
HttpServlet 클래스의 #service() 메소드가 호출되면 이 메소드는 요청객체(HttpServletRequest타입의 객체)안에서 HTTP 메소드(대표적으로 POST, GET이 있다.)를 읽어내고 이 값에 따라 매칭되는 메소드를 호출한다.
이를 테면 HTTP 메소드가 "GET"이면 doGet(), "POST"이면 doPost() 메소드를 호출한다.
doGet(), doPost() 와 같은 메소드가 우리가 오버라이딩 해야 하는 메소드이다.

HttpServletRequest 인터페이스는 ServletRequest 인터페이스를 상속하고, HttpServletResponse 인터페이스는 ServletResponse 인터페이스를 상속한다.
서블릿 컨테이너는 클라이언트의 요청이 오면 HttpServletRequest타입의 객체와 HttpServletResponse 타입의 객체를 만들어서 서블릿의 +service(req:ServletRequest,res:ServletResponse)메소드를 호출하면서 전달한다.
HttpServletRequest, HttpServletResponse 인터페이스를 구현한 클래스는 서블릿 컨테이너를 제작하는 벤더의 몫이다.

서블릿 클래스, 인터페이스 요약

Servlet 인터페이스
init (config:ServletConfig)
service(req:ServletRequest, res:ServletResponse)
destroy()
getServletConfig() : ServletConfig
서블릿 초기화에 관련된 변수를 가지고 있는 ServletConfig 객체를 리턴
getServletInfo() : String
서블릿에 대한 간단한 정보를 리턴
ServletConfig 인터페이스
getInitParameter(name:String) : String
name에 해당하는 초기화 파라미터 값을 리턴
getInitParameterNames() : Enumeration
서블릿의 초기화 파라미터 이름들을 Enumeration 타입으로 리턴
getServletContext() : ServletContext
ServletContext 를 리턴
getServletName() : String
서블릿 인스턴스의 이름 리턴
+GenericServlet 추상 클래스
프로토콜에 무관한 기본적인 서비스 기능을 제공하는 클래스로 Servlet, ServletConfig 인터페이스를 구현
+init()
서블릿 초기화 메소드로, GenericServlet의 init(config:ServletConfig) 메소드의 의해 호출됨
<<abstract>> + service (req:ServletRequest, res:ServletResponse)
Servlet 인터페이스의 service() 메소드는 여전히 추상 메소드
HttpServlet 추상 클래스
GenericServlet 추상 클래스 상속
#doGet (req:HttpServletRequest, res:HttpServletResponse)
HTTP의 GET 요청을 처리하기 위한 메소드
#doPost (req:HttpServletRequest, res:HttpServletResponse)
HTTP의 POST 요청을 처리하기 위한 메소드
+service (req:ServletRequest, res:ServletResponse)
GenericServlet 추상클래스의 추상 메소드 service() 구현함. 구현내용은 아래 service() 메소드에 호출하는 것이 전부
#service (req:HttpServletRequest, res:HttpServletResponse)
클라이어트의 요청에 따라 doGet(), doPost() 메소드 호출
ServletContext 인터페이스
서블릿이 서블릿 컨테이너와 통신하기 위해서 사용하는 메소드 제공한다.
파일의 MIME 타입, 파일의 전체경로, RequestDispatcher 레퍼런스를 얻거나 로그파일에 기록할 수 있다.
ServletContext 객체는 웹 애플리케이션마다 하나씩 존재하며, 그래서 웹 애플리케이션 전체의 공동 저장소를 역할을 한다.
ServletContext 에 저장된 데이터는 같은 웹 애플리케이션내의 서블릿이나 JSP 에서 자유롭게 접근할 수 있다.
setAttribute (name:Strng, value:Object)
데이터를 이름과 값의 쌍으로 저장한다.
getAttribute (name:String) : Object
이름을 이용해서 저장된 데이터를 리턴
removeAttribute(name:String)
name에 해당하는 데이터를 삭제
getInitParameter(name:String) : String
웹 애플리케이션 전영역에 대한 초기화 파라미터 이름에 해당하는 값 반환
getRequestDispatcher(path:String) : RequestDispatcher
주어진 Path 를 위한 RequestDispatcher 를 리턴
getRealPath(path:String) : String
주어진 가상 Path의 실제 Path를 리턴
getResource(path:String) : URL
주어진 Path 에 해당되는 자원의 URL을 리턴
log(msg:String)
로그에 기록
log(String message, Throwable throwable)
로그에 기록
RequestDispatcher 인터페이스
클라이언트의 요청을 다른 자원(서블릿, JSP)으로 전달하거나 다른 자원의 내용을 응답에 포함하기 위해 사용한다.
forward(req:ServletRequest, res:ServletResponse)
클라이언트의 요청을 다른 자원으로 전달
include(req:ServletRequest, res:ServletResponse)
다른 자원의 내용을 응답에 포함
ServletRequest 인터페이스
서블릿에 대한 클라이언트의 요청 정보을 담고 있음.
setAttribute(name:String,o:Object)
객체를 주어진 이름으로 저장
getAttribute(name:String) : Object
주어진 이름의 저장된 데이터를 리턴
removeAttribute(name:String)
주어진 이름의 데이터를 삭제
getInputStream() : ServletInputStream
클라이언트 요청의 바디에 있는 바이너리 테이터를 읽기 위한 입력 스트림 리턴
getParameter(name:String) : String
클라이언트의 요청에 포함되어 있는 파라미터 이름에 해당하는 값 리턴
getParameterNames() : Enumeration
클라이언트의 요청에 포함되어 있는 모든 파라미터 이름을 Enumeration 타입으로 리턴
getParameterValues(name:String) : String[]
파라미터 name에 해당하는 모든 값을 String 배열로 리턴.
체크박스나 다중 선택 리스트와 같이 값이 여러 개 있을 때 이 메소드를 사용한다.
getServletPath() : String
"/" 로 시작하는 경로를 리턴. 경로에 쿼리스트링은 포함되지 않음.
getRemoteAddr() : String
클라이언트의 IP 주소를 리턴
HttpServletRequest 인터페이스
ServletReqeust 상속
getCookies() : Cookie[]
브라우저가 전달한 쿠키 배열을 리턴
getSession() : HttpSession
현재 세션(HttpSession)을 리턴
getSession(created:boolean) : HttpSession
현재 세션을 리턴, 만약 세션이 없는 경우 created 가 true 이면 세션을 생성후 리턴하고 created 가 false 면 null 리턴
getContextPath() : String
요청 URI 에서 컨텍스트를 지시하는 부분을 리턴한다.
http://localhost:port/ContextPath/board/list.do?curPage=1 를 요청하면 /ContextPath 를 리턴한다.
getRequestURI() : String
http://localhost:port/ContextPath/board/list.do?curPage=1 를 요청하면
/ContextPath/board/list.do 를 리턴한다.
getQueryString() : String
http://localhost:port/ContextPath/board/list.do?curPage=1 를 요청하면
curPage=1 를 리턴한다.
ServletResponse 인터페이스
클라이언트에 응답을 보내기 위해 사용
getOutputStream() : ServletOutputStream
응답으로 바이너리 데이터를 전송하기 위한 출력 스트림 리턴
getWriter() : PrintWriter
응답으로 문자 데이터를 전송하기 위한 출력 스트림 리턴
setContentType(type:String)
응답 데이터의 MIME 타입을 설정.
HTML은 text/html, 일반 텍스트는 text/plain, 바이너리 데이터는 application/octet-stream 으로 설정
이 메소드는 getWriter() 메소드 전에 호출되어야 한다.
getContentType() : String
setContentType()메소드에서 지정한 MIME 타입 리턴
만약 지정하지 않았다면 null 리턴
setCharacterEncoding(charset:String)
응답 데이터의 캐릭터셋을 설정
UTF-8로 설정하려면 setCharacterEncoding("UTF-8");
이것은 setContentType("text/html; charset=UTF-8"); 에서의 캐릭터셋 설정과 동일하다.
getWrite()메소드가 실행되기 전에 호출되어야 한다.
getCharacterEncoding() : String
설정된 응답 데이터의 캐릭터셋 리턴
캐릭터셋이 지정되지 않았다면 "ISO-8859-1" 을 리턴
setContentLength(length:int)
응답 데이터의 크기를 int형 값으로 설정
이 메소드는 클라이언트측에서 서버로부터의 응답 데이터를 어느 정도 다운로딩하고 있는지 표시하는데 응용될 수 있다.
HttpServletResponse 인터페이스
ServletResponse 인터페이스를 상속, HTTP 응답을 클라이언트에 보내기 위해 사용
addCookie(cookie:Cookie)
응답에 쿠키를 전송
sendRedirect(location:String)
주어진 URL로 리다이렉트
HttpSession 인터페이스
세션유지에 필요한 사용자의 정보를 서버측에서 저장할 때 사용한다.
getAttribute(name:String) : Object
setAttribute(name:String, value:Object)
removeAttribute(name:String)
invalidate()
Cookie
서블릿에서 만들어져서 웹브라우저로 보내는 작은 정보로 브라우저에 저장된 후에 다시 서버로 보내어진다.
이말은 세션 유지에 필요한 정보를 클라이언트측에서 저장한다는 것이다.
쿠키는 name 과 name 에 대해 하나의 value 를 가지며 경로, 도메인, 유효기간, 보안에 대한 옵션값을 가지기도 한다.
서블릿에서 쿠키를 만들면 쿠키에 대한 일정한 형식의 문자열이 응답헤더에 추가된다.
쿠키가 포함된 응답을 받은 웹브라우저는 이후 쿠키 정보를 모든 요청시 같이 보내게 된다.
서버 요소로 전달된 쿠키 정보는 HttpServletRequest.getCookie() 메소드를 이용하면 전송된 모든 쿠키의 배열을 얻을 수 있다.
쿠키나 세션은 응답후 접속을 끊는 HTTP 프로토콜의 한계를 극복하기 위한 기술이다.
+ Cookie(name:String, value:String)
+ getName() : String
+ getValue() : String
+ setValue(newValue:String)
+ getPath() : String
+ setPath(uri:String)
+ getDomain() : String
+ setDomain(pattern:String)
+ getMaxAge() : int
+ setMaxAge(expiry:int)
+ getSecure() : boolean
+ setSecure(flag:boolean)

서블릿 예제

아래 나오는 모든 예제는 ROOT 애플리케이션에 작성한다.
웹 애플리케이션 작성 실습에서 도큐먼트베이스가 C:/www/myapp 인 애플리케이션을 ROOT 애플리케이션으로 변경했었다.
JSP는 C:/www/myapp 아래에, 자바는 C:/www/myapp/WEB-INF/src 아래 자바 팩키지 이름의 서브디렉토리에 생성한다.
이클립스를 사용하지 않고 에디트플러스와 같은 일반 에디터를 사용한다.

SimpleServlet.java
package example;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleServlet extends HttpServlet {

	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		doPost(req,resp);
	}
	
	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
			
		resp.setContentType("text/html; charset=UTF-8");
		PrintWriter out = resp.getWriter();
    	
		out.println("<html>");
		out.println("<body>");
    	
		//요청한 클라이언트의 IP를 출력
		out.println("당신의 IP 는 " + req.getRemoteAddr() + "입니다.\n");
		out.println("</body></html>");
		out.close();
	}
  
}

SimpleServlet은 서블릿 라이프 사이클 메소드 중 init()과 destroy()는 구현하지 않았다.
이 메소드들은 GenericServlet 에서 이미 구현되어 있고, 또 특별히 오버라이딩할 이유가 없기 때문에 위의 소스에서 보이지 않는 것이다.
/WEB-INF/web.xml 파일을 열고 web-app 엘리먼트의 자식 엘리먼트로 servlet 엘리먼트와 내용을 아래와 같이 추가한다.

web.xml
/* .. 중간 생략 .. */

    <servlet>
      <servlet-name>SimpleServlet</servlet-name>
      <servlet-class>example.SimpleServlet</servlet-class>
    </servlet>

    <servlet-mapping>
      <servlet-name>SimpleServlet</servlet-name>
      <url-pattern>/simple</url-pattern>
    </servlet-mapping>

/* .. 중간 생략 .. */

명령 프롬프트에서 SimpleServlet.java 가 있는 소스 폴더로 이동하여 아래와 같이 컴파일한다.

javac -d C:/www/myapp/WEB-INF/classes ^
-cp "C:/Program Files/Apache Software Foundation/Tomcat 7.0/lib/servlet-api.jar" ^
SimpleServlet.java

package javax.servlet.http does not exist

SimpleServlet.java 를 컴파일할 때 위와 같은 컴파일 에러가 나온다는 것은 자바 컴파일러가 javax.servlet.http 팩키지를 찾지 못한다는 의미로, javac 의 classpath 옵션의 값으로 servlet-api.jar 파일의 전체경로를 잘못 적었기 때문이다.
classpath 옵션값으로 주는 경로가 중간에 공백이 있다면 "" 으로 묶어주어야 한다.
다른 방법으로는 CLASSPATH 란 환경변수에 servlet-api.jar 의 전체경로를 추가해주면 아래와 같이 classpath 옵션을 사용하지 않고도 컴파일을 할 수 있다.
javac -d C:/www/myapp/WEB-INF/classes SimpleServlet.java (CLASSPATH 환경변수의 값은 반드시 현재 디렉토리를 나타내는 . 를 포함해야 한다.
그래서 CLASSPATH를 설정하면 아래와 같다.
.;C:\Program Files\Apache Software Foundation\Tomcat 7.0\lib\servlet-api.jar

톰캣을 재시작한 후 http://localhost:8989/simple 로 방문하여 테스트한다.

SimpleServlet.java 소스설명

public class SimpleServlet extends HttpServlet {

HttpServlet 클래스를 상속받은 서블릿은 public class으로 선언해야 한다.

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) 
	throws ServletException, IOException {
	
   doPost(req,res);
}

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) 
	throws ServletException, IOException {
	
   ..생략..
   
}

doGet()과 doPost()메소드는 HttpServlet의 doGet()와 doPost()메소드를 오버라이드 한 메소드이다.
모든 비즈니스 로직이 이 메소드에 존재한다.
HTTP METHOD가 GET으로 요청해오면 doGet() 메소드를 오버라이딩한다.
웹브라우저의 주소창에서 웹서버의 자원을 요청하는 것은 GET방식의 요청이다.
예제에서는 doGet()메소드는 단순히 doPost()메소드를 호출하는 것으로 구현하여 GET이든 POST요청이든 같은 코드가 실행된다.
doGet()과 doPost()메소드는 HttpServletRequest와 HttpServletResponse타입의 파라미터를 가진다.
이 메소드는 예외가 발생할 수 있으므로 throws ServletException, IOExcepiton가 메소드 선언부에 추가되어 있다.

resp.setContentType("text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();

resp.setContentType("text/html; charset=UTF-8");은 응답(HttpServletResponse)의 컨텐츠타입을 셋팅하는 작업을 한다.
웹브라우저에 응답으로 출력될 문서의 MIME2타입을 설정하는 것이다.
이 코드는 서블릿에서 단 한번만 사용이 가능하며 PrintWriter를 획득하기 전에 실행되야 한다.
; charset=UTF-8 부분이 빠지면 한글이 깨진다.
PrintWriter의 획득은 HttpServletResponse의 getWriter()을 호출함으로써 이루어진다.
PrintWriter out = resp.getWriter();
out 은 웹브라우저가 목적지인 문자 출력 스트림이다.

out.println("<html>");
out.println("<body>");
//요청한 클라이언트의 IP를 출력
out.println("당신의 IP 는 " + req.getRemoteAddr() + "입니다.\n");

PrintWrtier는 클라이언트로의 응답으로 보내는 스트림에 글을 쓸 수 있게 한다.
PrintWriter의 plintln()메소드안에 문자열을 넣으면 클라이언트의 웹브라우저에 출력된다.
위에서 보듯이 SimpleServlet는 클라이언트에게 HTML을 보내기 위해 PrintWriter의 println()메소드를 사용하고 있다.
req.getRemoteAddr()은 클라이언트의 IP주소값을 반환하는 메소드이다.
이와같이 HttpServeltRequest 는 클라이언트가 보내거나 클라이언트에 관한 정보를 담고 있다.

SimpleServlet 서블릿이 응답을 보내기까지 과정을 살펴보자.
클라이언트가 웹브라우저를 이용해서 서버의 SimpleSerlvet을 요청한다.
서블릿 컨테이너인 톰캣은 SimpleServlet의 +service(ServletRequest req, ServletResponse res) 메소드를 호출하면서 클라이언트의 요청을 캡슐화한 객체(HttpSerlvetRequest 인터페이스 구현체)와 응답을 위한 객체(HttpSerlvetResponse 인터페이스 구현체)를 메소드의 인자로 전달한다.
+service(ServletRequest req, ServletResponse res) 메소드는 단지 #service(HttpServletRequest req,HttpServletResponse res) 메소드를 호출하도록 구현되어 있다.
#service(HttpServletRequest req,HttpServletResponse res) 메소드는 HTTP 메소드타입(GET,POST 등)에 따라 doGet() 또는 doPost()메소드를 호출한다.
테스트에서는 웹브라우저의 주소창에서 서블릿 자원을 요청했기 때문에 GET 방식의 요청이다.
따라서 doGet() 메소드가 호출된다.

사용자가 문자열 데이터를 서버측 자원으로 전송하는 방법과 이 데이터를 서버측 자원에서 수신하는 방법

웹 환경에서 동적인 요소라 하면 클라이언트가 보낸 문자열 데이터에 따라 응답을 하는 요소를 말한다.
웹에서 동적인 요소를 만들어야 하는 웹 프로그래머는 클라이언트가 웹 브라우저를 통해서 문자열 데이터를 보내는 방법과 그 데이터를 획득하는 방법을 알아야 한다.
클라이언트 사이드에서 서버 사이드로 문자열 데이터를 전송하기 위해서는 주로 form 엘리먼트와 form의 서브 엘리먼트를 사용한다.3
클라이언트가 서버로 전달하는 데이터는 form 엘리먼트의 action 속성의 값으로 지정된 자원으로 전달된다.

파라미터 전송 방법과 전송된 파라미터의 값 얻기

아래 표에서 폼 작성은 사용자로부터 값을 받기 위한 form 양식을 출력하기 위한 HTML 태그를 보여주고 있다.
표에서 서블릿은 form 양식에서 입력받은 파라미터 값을 서블릿에서 가져오기 위한 코드 조각이다.

폼 작성서블릿
<input type="text" name="addr" />req.getParameter("addr");
<input type="radio" name="os" value="Windows" />
<input type="radio" name="os" value="Linux" />
req.getParameter("os");
<input type="hidden" name="curPage" value="1" />req.getParameter("curPage");
<input type="password" name="passwd" />req.getParamter("passwd");
<textarea name="content" cols="60" rows="12">
</textarea>
req.getParamter("content");
<select name="grade">
  <option value="A">A</option>
  <option value="B">B</option>
  <option value="C">C</option>
  <option value="D">D</option>
  <option value="F">F</option>
</select>
req.getParameter("grade");
<input type="checkbox" name="hw" value="Intel" />
<input type="checkbox" name="hw" value="AMD" />
req.getParameterValues("hw");
<select name="sportsmultiple="multiple">
<option value="soccer">축구</option>
<option value="basketball">농구</option>
<option value="baseball">야구</option>
</select>
req.getParameterValues("sports");

getParameter(String name)

ServletRequest 의 getParameter(String name) 메소드는 사용자가 보낸 데이터를 얻기 위해 사용하는 가장 보편적인 메소드이다.
전달되는 데이터는 파라미터 이름과 값의 쌍으로 서버 요소에 전달된다.
|name|value| 이때 파라미터의 이름은 form 의 서브 엘리먼트(input, textarea, select)의 name 속성값과 같고 value 는 사용자가 입력한 값이다.
getParameter(String name) 인자값으로 파라미터명(input, textarea, select 엘리먼트의 name 속성값)을 전달하면 사용자가 입력하거나 선택한 값을 얻을 수 있다.

input 엘리먼트의 type 속성값이 radio(라디오 버튼)인 의 경우, name 속성값이 같은 라디오 버튼들은 그룹을 형성한다.
그룹내의 라디오 버튼은 그 중 하나만 선택된다.

getParameterValues(String name)

클라이언트 사이드에서 하나의 파라미터명에 여러 개의 값이 전송할 때 서블릿에서 이 데이터를 수신하려면 HttpServletRequest 의 getParamterValues(String name) 메소드를 사용한다.
이 메소드의 리턴 타입은 사용자가 선택한 값들만으로 구성된 String 배열이다.

클라이언트 사이드에서 하나의 파라미터에 값을 여러개 보내려면 체크박스나 multiple 속성값이 "multiple" 인 select 엘리먼트가 사용된다.
체크박스는 input 엘리먼트의 type 속성값이 checkbox 인 것을 말한다.
체크박스는 라디오 버튼과 마찬가지로 name 속성값을 같게 하면 그룹화되는데 라디오 버튼과 다른 점은 그룹내에서 선택을 다중으로 할 수 있다는 것이다.
select 엘리먼트는 일반적으로 값을 하나만 선택할 수 있지만 multiple 속성값이 "multiple" 인 select 엘리먼트는 Ctrl 이나 Shift 버튼을 이용해서 값을 여러개 선택할 수 있다.

getParamterNames()

클라이언트 사이드에서 전송되는 데이터가 어떤 파라미터에 담겨 오는지 알 수 있는 메소드가 HttpServletRequest 의 getParamterNames() 메소드이다.
getParameterNames() 메소드는 파라미터 이름을 접근할 수 있는 Enumeration4 타입을 반환한다.

input type="file" 은 파일 업로드에 쓰인다.

type 속성값이 file 인 input 엘리먼트는 이미지와 같은 바이너리 데이터를 서버 사이드로 전송할 때 쓰인다.
이때 form 엘리먼트의 method 속성값은 post 이고 동시에 enctype 속성(속성값이 "multipart/form-data")이 반드시 있어야 한다.
<form action="서버요소" method="post" enctype="multipart/form-data">
업로드하는 파일뿐만 아니라 다른 부가 정보(예를 들면 이름,제목,내용 등등)을 전송해야 한다면 이들 엘리먼트 역시 input type="file" 이 있는 form 안에 같이 위치시키면 된다.
submit 버튼을 누르면 이제까지와는 다른 전송 규약에 의해 서버 요소로 전송된다.
따라서 서버 요소에서는 getParameter() 메소드를 사용하여 데이터에 접근할 수 없다.
서블릿 JSP API를 가지고 바이너리 데이터를 수신하는 방법을 서블릿에 구현할 수 있겠지만 대부분의 경우 외부 라이브러리를 있고 이용한다.
이에 대한 예제는 곧 다룬다.

문자열 전송 예제

회원가입 예제를 통해 사용자가 보낸 데이터를 서블릿에서 수신하는 방법을 실습한다.
이 예제의 목적은 사용자가 보낸 데이터를 확인하는 것이다.
회원가입 양식(form)을 보여주는 HTML 파일을 작성한다.

/example/join.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입 폼</title>
</head>
<body>
<h3>회원가입</h3>
<form id="joinForm" action="../RegisterServlet" method="post">
아이디 <input type="text" name="id" /><br />
별명 <input type="text" name="nickname" /><br />
비밀번호 <input type="password" name="passwd" /><br />
이름 <input type="text" name="name" /><br />
<br />
성별<br />
남 <input type="radio" name="gender" value="M" /> 
여 <input type="radio" name="gender" value="F" /><br />

<br />
생년월일 <input type="text" name="birthyear" style="width: 30px" /> 년
 
<select name="birthmon">
   <option value="0" selected="selected">-월-</option>
   <option value="1">1</option>
   <option value="2">2</option>
   <option value="3">3</option>
   <option value="4">4</option>
   <option value="5">5</option>
   <option value="6">6</option>
   <option value="7">7</option>
   <option value="8">8</option>
   <option value="9">9</option>
   <option value="10">10</option>
   <option value="11">11</option>
   <option value="12">12</option>
</select>
월

<select name="birthday">
   <option value="0" selected="selected">-일-</option>
   <option value="1">1</option>
   <option value="2">2</option>
   <option value="3">3</option>
   <option value="4">4</option>
   <option value="5">5</option>
   <option value="6">6</option>
   <option value="7">7</option>
   <option value="8">8</option>
   <option value="9">9</option>
   <option value="10">10</option>
   <option value="11">11</option>
   <option value="12">12</option>
   <option value="13">13</option>
   <option value="14">14</option>
   <option value="15">15</option>
   <option value="16">16</option>
   <option value="17">17</option>
   <option value="18">18</option>
   <option value="19">19</option>
   <option value="20">20</option>
   <option value="21">21</option>
   <option value="22">22</option>
   <option value="23">23</option>
   <option value="24">24</option>
   <option value="25">25</option>
   <option value="26">26</option>
   <option value="27">27</option>
   <option value="28">28</option>
   <option value="29">29</option>
   <option value="30">30</option>
   <option value="31">31</option>		
</select>
일
<br />

양력 <input type="radio" name="solar" value="Y" checked="checked" />
음력 <input type="radio" name="solar" value="N" /><br />
<br />

휴대전화 <input type="text" name="mobile" /><br />
일반전화 <input type="text" name="tel" /><br />
<br />

주소<br />
<input type="text" name="zipcode1" style="width: 30px" /> -
<input type="text" name="zipcode2" style="width: 30px" /><br />
<input type="text" name="add" style="width: 300px" /><br />
<br />
이메일 <input type="text" name="email" /><br />
이메일수신여부<br />
수신 <input type="radio" name="emailyn" value="Y" checked="checked" />
수신안함 <input type="radio" name="emailyn" value="N" /><br />
<br />
좋아하는 운동<br />
<input type="checkbox" name="sports" value="soccer" />축구<br />
<input type="checkbox" name="sports" value="baseball" />야구<br />
<input type="checkbox" name="sports" value="basketball" />농구<br />
<input type="checkbox" name="sports" value="tennis" />테니스<br />
<input type="checkbox" name="sports" value="tabletennis" />탁구<br />
<br />
복수선택<br />
<select name="focus" multiple="multiple">
	<option value="">-- 복수선택 --</option>
	<option value="java">JAVA</option>
	<option value="jdbc">JDBC</option>
	<option value="jsp">JSP</option>
	<option value="css-layout">CSS Layout</option>
	<option value="jsp-prj">JSP 프로젝트</option>
	<option value="spring">Spring</option>
	<option value="javascript">자바스크립트</option>
</select>
<br />
<br />
자기소개<br />
<textarea name="aboutMe" cols="40" rows="7"></textarea><br />
<input type="submit" value="전송" />
</form>
</body>
</html>

RegisterServlet 서블릿 작성 : 회원 등록처리

데이터를 처리하는 서블릿을 아래와 같이 작성한다.

RegisterServlet.java
package example;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class RegisterServlet extends HttpServlet {
	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws IOException,ServletException {
		
		resp.setContentType("text/html; charset=UTF-8");
		PrintWriter out = resp.getWriter();
		req.setCharacterEncoding("UTF-8");
		
		String id = req.getParameter("id");
		
		out.println("<html><body>");
		out.println("id : " + id);
		
		String[] sports = req.getParameterValues("sports");
		int len = sports.length;
		
		out.println("<ol>");
		for (int i = 0; i < len; i++) {
			out.println("<li>" + sports[i] + "</li>");
		}
		
		out.println("</ol>");
		
		String path = req.getContextPath();
		out.println("<a href=" + path + "/example/join.html>Join</a>");
		out.println("</body></html>");
	}
}

명령 프롬프트를 열고 위 소스코드가 있는 /WEB-INF/src/exampe 로 이동하여 아래와 같이 컴파일한다.

javac -d C:/www/myapp/WEB-INF/classes ^
-cp "C:/Program Files/Apache Software Foundation/Tomcat 7.0/lib/servlet-api.jar" ^
RegisterServlet.java

다음으로 web.xml 파일을 열고 아래를 추가한다.

web.xml
<servlet>			
    <servlet-name>RegisterServlet</servlet-name>
    <servlet-class>example.RegisterServlet</servlet-class>
</servlet>

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

톰캣을 재시작 한 후 http://localhost:8989/example/join.html로 방문하여 테스트한다.
ID와 좋아하는 운동외에 사용자가 입력하거나 선택한 값을 확인하는 소스를 서블릿에 추가하고 테스트해 보자.

RequestDispatcher 인터페이스

RequestDispathcer는 include()와 forward() 2개의 메소드가 있다.
include()메소드는 요청을 다른 자원으로 보냈다가 다른 자원에서 실행이 끝나면 다시 요청을 가져오는 메소드로 요청을 전달한 자원의 결과를 포함해서 클라이언트에게 보내게 된다.
forward()메소드는 이름 그대로 클라이언트의 요청을 서버상의 다른 자원에게 넘기는 메소드이다.
다음은 RequestDispatcher 의 forward() 메소드의 예로 모델 2 컨트롤러에 대한 이해에 도움이 될 것이다.

ControllerServlet.java
package example;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ControllerServlet extends HttpServlet {

	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		doPost(req,resp);
	}

	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {

		req.setCharacterEncoding("UTF-8");
		
		String uri = req.getRequestURI();
		String contextPath = req.getContextPath();
		String command = null;
		String view = null;
		boolean isRedirect = false;
		
		command = uri.substring(contextPath.length());
		
		if (command.equals("/example/join.action")) {
			view = "/example/join.html";
		}
		if (isRedirect == false) {
			ServletContext sc = this.getServletContext();
			RequestDispatcher rd = sc.getRequestDispatcher(view);
			rd.forward(req,resp);
		} else {
			resp.sendRedirect(view);
		}
		
	}
	
}

명령 프롬프트를 열고 위 소스코드가 있는 /WEB-INF/src/exampe 로 이동하여 컴파일한다.

javac -d C:/www/myapp/WEB-INF/classes ^
-cp "C:/Program Files/Apache Software Foundation/Tomcat 7.0/lib/servlet-api.jar" ^
ControllerServlet.java

다음으로 web.xml 파일을 열고 아래를 추가한다.

web.xml
<servlet>
    <servlet-name>Controller</servlet-name>
    <servlet-class>example.ControllerServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>Controller</servlet-name>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

톰캣을 재시작한 다음 http://localhost:8989/example/join.action 방문하여 /example/join.html 이 응답하는지 확인한다.
ControllerServlet 에서 isRedirect 를 true 로 변경한 다음 테스트한다.

소스 설명

web.xml에서 모든 action 확장자의 요청을 ControllerServlet 담당하도록 설정했다.
확장자가 action 인 요청이 오면 톰캣은 web.xml 의 매핑정보를 해석해서 ControllerServlet 서블릿의 +service(req:ServletRequest, res:ServletResponse) 메소드를 호출한다.
+service(req:ServletRequest, res:ServletResponse) 메소드는 #service(req:HttpServletRequest, resp:HttpServletResponse) 메소드를 호출한다.
#service(req:HttpServletRequest, resp:HttpServletResponse) 메소드에서는 요청의 HTTP METHOD가 무엇인지 판단하고 그에 맞는 메소드를 호출한다.
웹브라우저의 주소창에서 http://localhost:8989/example/join.action 를 요청했으므로 GET 방식의 요청이다.
따라서 이 경우는 doGet()메소드를 호출된다.
ControllerServlet서블릿의 doGet()메소드는 단순히 doPost()을 호출한다.
doPost()의 구현부에서 사용된 HttpServletRequest 의 다음 메소드를 다음과 같이 정리한다.

getRequestURI()
  웹브라우저로 http://localhost:8989/example/join.action 을 요청시 "/example/join.action" 을 리턴한다.
getContextPath()
  컨텍스트 파일의 path 속성값을 얻을 수 있다.(이를 Context Path 라 한다.)
  우리는 ROOT 애플리케이션에서 작업하므로 이 메소드를 통해 얻을 수 있는 값은 "" 이다.
req.getRequestURI().substring(req.getContextPath().length())
  결과는 "/example/join.action" 를 리턴한다.
  이 값이 getRequestURI() 의 리턴값이 같은 이유는 ROOT 애플리케이션이기 때문이다.
  ROOT 애플리케이션이 아니라도 이 값은 달라지지 않는다.

/example/join.html 에 대한 RequestDispatcher 를 얻은 후 forward() 메소드를 이용해서 사용자의 요청을 /example/join.html 로 전달한다.
제어권이 ControllerServlet 에서 /example/join.html 로 이동한 것이다.
결과적으로 /example/join.action 을 요청한 사용자는 /example/join.html 의 응답을 받게 된다.

데이터베이스 연동

JDBC 메뉴에서 실습했던 오라클 JDBC 연동 테스트 파일인 GetEmp.java 를 서블릿으로 변환해보자.
이번 예제는 순수 자바 애플리케이션을 서블릿으로 바꾸는 예제인 것이다.
ROOT 애플리케이션의 /WEB-INF/src/example 디렉토리에 GetEmpServlet.java 파일을 생성한다.

GetEmpServlet.java
package example;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GetEmpServlet extends HttpServlet {
	
	private String DB_URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
	private String DB_USER = "scott";
	private String DB_PASSWORD = "tiger";
	
	/*
	 * GenericServlet의 init() 메소드
	 * init(ServletConfig config) 메소드 구현부에서 이 메소드를 호출하도록 구현되어 있다.
	 * 따라서 굳이 init(ServletConfig config) 메소드를 오버라이딩하지 않아도 된다.
	 */
	@Override
	public void init() throws ServletException {
		try {
			Class.forName( "oracle.jdbc.driver.OracleDriver" );
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		
		resp.setContentType("text/html; charset=UTF-8");
		PrintWriter out = resp.getWriter();
		
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;
		
		String sql = "select * from emp";
		
		try {
			// Connection 레퍼런스를 획득
			con = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
			// Statement를 생성
			stmt = con.createStatement();
			// SQL문 실행
			rs = stmt.executeQuery(sql);
			
			while (rs.next()) {
				String empno = rs.getString(1);
				String ename = rs.getString(2);
				String job = rs.getString(3);
				String mgr = rs.getString(4);
				String hiredate = rs.getString(5);
				String sal = rs.getString(6);
				String comm = rs.getString(7);
				String depno = rs.getString(8);
				
				out.println( empno + " : " + ename + " : " + job + " : " + mgr + 
				" : " + hiredate + " : " + sal + " : " + comm+" : " + depno + "<br />" );
			}

		} catch (SQLException e) {
			e.printStackTrace(out);
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {

					e.printStackTrace();
				}
			}
			if (con != null) {
				try {
					con.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		
	}

}

명령 프롬프트를 이용해서 ROOT 애플리케이션의 /WEB-INF/src/example 디렉토리로 이동 한 후 컴파일한다.

javac -d C:/www/myapp/WEB-INF/classes ^ 
-cp "C:/Program Files/Apache Software Foundation/Tomcat 7.0/lib/servlet-api.jar" ^
GetEmpServlet.java

컴파일 후에는 web.xml 파일을 편집한다.
web.xml 열고 아래와 같이 서블릿 선언과 매핑을 추가한다.

web.xml
<servlet>
    <servlet-name>GetEmpServlet</servlet-name>
    <servlet-class>example.GetEmpServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>GetEmpServlet</servlet-name>
    <url-pattern>/empList</url-pattern>
</servlet-mapping>

이미 설명한 대로 톰캣 클래스로더는 환경변수 CLASSPATH 를 참조하지 않는다.
JDBC 드라이버 파일은 특별한 이유로 반드시 {톰캣홈}/lib 두어야 한다.

톰캣을 재시작한 후, http://localhost:8989/empList 를 방문하여 확인한다.

7369 : SMITH : CLERK : 7902 : 1980-12-17 00:00:00.0 : 800 : null : 20
7499 : ALLEN : SALESMAN : 7698 : 1981-02-20 00:00:00.0 : 1600 : 300 : 30
7521 : WARD : SALESMAN : 7698 : 1981-02-22 00:00:00.0 : 1250 : 500 : 30
7566 : JONES : MANAGER : 7839 : 1981-04-02 00:00:00.0 : 2975 : null : 20
7654 : MARTIN : SALESMAN : 7698 : 1981-09-28 00:00:00.0 : 1250 : 1400 : 30
7698 : BLAKE : MANAGER : 7839 : 1981-05-01 00:00:00.0 : 2850 : null : 30
7782 : CLARK : MANAGER : 7839 : 1981-06-09 00:00:00.0 : 2450 : null : 10
7788 : SCOTT : ANALYST : 7566 : 1987-04-19 00:00:00.0 : 3000 : null : 20
7839 : KING : PRESIDENT : null : 1981-11-17 00:00:00.0 : 5000 : null : 10
7844 : TURNER : SALESMAN : 7698 : 1981-09-08 00:00:00.0 : 1500 : 0 : 30
7876 : ADAMS : CLERK : 7788 : 1987-05-23 00:00:00.0 : 1100 : null : 20
7900 : JAMES : CLERK : 7698 : 1981-12-03 00:00:00.0 : 950 : null : 30
7902 : FORD : ANALYST : 7566 : 1981-12-03 00:00:00.0 : 3000 : null : 20
7934 : MILLER : CLERK : 7782 : 1982-01-23 00:00:00.0 : 1300 : null : 10

원하는 결과가 나오지 않을 때에는 아래 리스트를 체크한다.

  • web.xml 파일에서 서블릿 선언과 서블릿 매핑정보가 올바른지 확인한다.
  • /WEB-INF/classes/example 에 GetEmpServlet 바이트코드 있는지 확인한다.
  • {톰캣홈}/lib 에 오라클 JDBC 드라이버 파일(ojdbc6.jar)이 있는지 확인한다.
  • 웹 애플리케이션이 성공적으로 로드되었는지 확인한다.

ServletConfig 초기화 파라미터를 이용하는 예제

위 예제에서 서블릿의 메소드 구현부에서 JDBC 코드가 있었다.
이번 예제는 JDBC 설정을 ServletConfig 초기화 파라미터를 이용하도록 만든다.
아래 서블릿을 /WEB-INF/src/example 폴더에 만든다.

InitParamServlet 서블릿
package example;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class InitParamServlet extends HttpServlet {
	
	private String url;
	private String user;
	private String passwd;
	private String driver;
	
	@Override
	public void init() throws ServletException {
		url = this.getInitParameter("url");
		user = this.getInitParameter("user");
		passwd = this.getInitParameter("passwd");
		driver = this.getInitParameter("driver");
		
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException, ServletException {
		
		resp.setContentType("text/html; charset=UTF-8");
		PrintWriter out = resp.getWriter();
		
		Connection con = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		
		String sql = "SELECT * FROM emp";
		
		try {
			con = DriverManager.getConnection(url, user, passwd);
			stmt = con.prepareStatement(sql);
			rs = stmt.executeQuery();
			
			while (rs.next()) {
				String empno = rs.getString(1);
				String ename = rs.getString(2);
				String job = rs.getString(3);
				String mgr = rs.getString(4);
				String hiredate = rs.getString(5);
				String sal = rs.getString(6);
				String comm = rs.getString(7);
				String depno = rs.getString(8);
				
				out.println(empno + " : " + ename + " : " + job + " : " + mgr + 
				  " : " + hiredate + " : " + sal + " : " + comm+" : " + depno + "<br />");
			}
		} catch (SQLException e) {
			e.printStackTrace(out);
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (con != null) {
				try {
					con.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	
}

ServletConfig 초기화 파라미터 선언은 web.xml 파일에서 아래와 같이 servlet 엘리먼트의 자식 엘리먼트 init-param 를 이용한다.

web.xml
<servlet>
  <servlet-name>InitParamServlet</servlet-name>
  <servlet-class>example.InitParamServlet</servlet-class>

  <init-param>
    <param-name>driver</param-name>
    <param-value>oracle.jdbc.driver.OracleDriver</param-value>
  </init-param>
  <init-param>
    <param-name>url</param-name>
    <param-value>jdbc:oracle:thin:@127.0.0.1:1521:XE</param-value>
  </init-param>
  <init-param>
    <param-name>user</param-name>
    <param-value>scott</param-value>
  </init-param>
  <init-param>
    <param-name>passwd</param-name>
    <param-value>tiger</param-value>
  </init-param>
</servlet>

<servlet-mapping>
  <servlet-name>InitParamServlet</servlet-name>
  <url-pattern>/initParam</url-pattern>
</servlet-mapping>

위에서 설정한 ServletConfig 의 초기화 파라미터의 값은 ServletConfig 의 getInitParameter(String name) 메소드를 이용하면 얻어진다. 톰캣을 재시작한 후에
http://localhost:8989/initParam 으로 방문하여 테스트한다.

ServletContext 초기화 파라미터를 이용하는 예제

위에서 ServletConfig 의 초기화 파라미터는 해당 서블릿에서만 참조 할 수 있다.
ServletContext 초기화 파라미터는 웹 애플리케이션 내 모든 서블릿과 JSP에서 참조할 수 있다.
ServletContext 초기화 파라미터는 context-param 엘리먼트를 이용한다.
엘리먼트의 위치는 web.xml 엘리먼트 순서를 참조한다.
기존의 servlet 선언부보다 먼저 있어야 한다.
web.xml 열고 아래 내용을 추가한다.

web.xml
<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:oracle:thin:@127.0.0.1:1521:XE</param-value>
</context-param>

ServletContext 객체의 레퍼런스는 서블릿에서 getServletContext() 메소드를 이용하면 얻을 수 있다.
위에서 선언한 ServletContext 의 초기화 파라미터 url 의 값은 ServletContext 의 getInitParameter(String name) 메소드를 이용하여 구한다.
따로 예제를 만들지 않고 위에서 작성한 SimpleSerlvet 에 다음과 같이 코드를 적당한 위치에 추가한 후 다시 컴파일한다.

SimpleServlet 서블릿 편집
ServletContext sc = getServletContext();
String url = sc.getInitParameter("url");
out.println(url);

톰캣을 재실행하고
http://localhost:8989/simple
으로 방문하여 테스트한다.
InitParamServlet 에서 url 을 이 ServletContext 초기화 파라미터를 이용해서 설정하도록 코드를 수정하고 테스트해 보자.

리슨너

리슨너는 웹 애플리케이션의 이벤트에 실행된다.
웹 애플리케이션 이벤트는 서블릿 스펙 2.3 이후 등장했다.
웹 애플리케이션 이벤트는 다음과 같이 나뉜다.

  • 애플리케이션 스타트업과 셧다운
  • 세션 생성 및 세션 무효

애플리케이션 스타트업 이벤트는 톰캣과 같은 서블릿 컨테이너에 의해 웹 애플리케이션이 처음 로드되어 스타트될 때 발생한다.
애플리케이션 셧다운 이벤트는 웹 애플리케이션이 셧다운될 때 발생한다.

세션 생성 이벤트는 새로운 세션이 생성될 때 발생한다.
세션 무효 이벤트는 세션이 무효화 될때 매번 발생한다.

이벤트를 이용하기 위해서는 리슨너라는 클래스를 작성해야 한다.
리슨너 클래스는 순수 자바 클래스로 다음의 인터페이스를 구현한다.

  • javax.servlet.ServletContextListener
  • javax.servlet.http.HttpSessionListener

애플리케이션 스타트업 또는 셧다운 이벤트를 위한 리슨너를 원한다면 ServletContextListener 인터페이스를 구현한다.
세션 생성 및 세션 무효 이벤트를 위한 리슨너를 원한다면 HttpSessionListener 인터페이스를 구현한다.

ServletContextListener 인터페이스는 다음 2개의 메소드로 구성되어 있다.

  • public void contextInitialized(ServletContextEvent sce);
  • public void contextDestroyed(ServletContextEvent sce);


HttpSessionListener 인터페이스는 다음 2개의 메소드로 구성되어 있다.

  • public void sessionCreated(HttpSessionEvent se);
  • public void sessionDestroyed(HttpSessionEvent se);

다음은 애플리케이션 스타트업과 셧다운 이벤트를 이용하는 예제이다.
우리의 웹 애플리케이션이 스타트업되면 OracleConnectionManager 객체를 생성하여 ServletContext 에 저장하기 위해 아래와 같은 클래스를 작성한다.

MyServletContextListener.java
package net.java_school.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import net.java_school.db.dbpool.OracleConnectionManager;

public class MyServletContextListener implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		ServletContext sc = sce.getServletContext();
		OracleConnectionManager dbMgr = new OracleConnectionManager();
		sc.setAttribute("dbMgr", dbMgr);
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		ServletContext sc = sce.getServletContext();
		sc.removeAttribute("dbMgr");
	}

}

이 클래스가 제 기능을 다하도록 하려면 web.xml 에 아래를 web-app 엘리먼트의 자식 엘리먼트로 추가한다.
이때 다음 http://java.sun.com/dtd/web-app_2_3.dtd 를 참고하여 엘리먼트의 순서에 맞게 추가해야 한다.
context-param 보다는 아래에 servlet 보다는 위에 추가해야 함을 알 수 있다.

<!ELEMENT web-app (icon?, display-name?, description?, distributable?, context-param*, filter*, filter-mapping*, listener*servlet*, servlet-mapping*, session-config?, mime-mapping*, welcome-file-list?, error-page*, taglib*, resource-env-ref*, resource-ref*, security-constraint*, login-config?, security-role*, env-entry*, ejb-ref*, ejb-local-ref*)>
web.xml
<listener>
    <listener-class>net.java_school.listener.MyServletContextListener</listener-class>
</listener>

JDBC 메뉴의 ConnectionPool 소스를 WEB-INF/src 에 추가한다.
net.java_school.util.Log.java 파일에서 다음 부분을 수정한다.
public String dbgFile = "C:/www/myapp/WEB-INF/debug.txt";
orcale.properties 파일은 WEB-INF 폴더에 위치시킨다.
ConnectionManager.java 파일에서 다음 부분을 수정한다.
configFile = "C:/www/myapp/WEB-INF/"+poolName+".properties";
이제 우리의 웹 애플리케이션이 시작될 때 OracleConnectionManager 객체를 생성되고 그 레퍼런스가 ServletContext 에 저장될 것이다.
테스트를 위해 GetEmpServlet.java 파일을 아래와 같이 수정한다.

GetEmpServlet.java
package example;

import java.sql.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

import net.java_school.db.dbpool.*;

public class GetEmpServlet extends HttpServlet {

	private OracleConnectionManager dbMgr;
	
	@Override
	public void init() throws ServletException {
		ServletContext sc = getServletContext();
		dbMgr = (OracleConnectionManager) sc.getAttribute("dbMgr");
	}
	
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException, ServletException {
			
		resp.setContentType("text/html; charset=UTF-8");
		PrintWriter out = resp.getWriter();
		
		Connection con = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		
		String sql = "SELECT * FROM emp";
		
		try {
			con = dbMgr.getConnection();
			stmt = con.prepareStatement(sql);
			rs = stmt.executeQuery();
			
			while (rs.next()) {
				String empno = rs.getString(1);
				String ename = rs.getString(2);
				String job = rs.getString(3);
				String mgr = rs.getString(4);
				String hiredate = rs.getString(5);
				String sal = rs.getString(6);
				String comm = rs.getString(7);
				String depno = rs.getString(8);
				
				out.println( empno + " : " + ename + " : " + job + " : " + mgr +
					" : " + hiredate + " : " + sal + " : " + comm+" : " + depno + "<br>" );
			}
		} catch (SQLException e) {
			e.printStackTrace(out);
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (con != null) {
				try {
					con.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	
	}
	
}

GetEmpServlet를 컴파일하고 톰캣을 재가동 후 http://localhost:8989/empList 를 방문한다.
(GetEmpServlet 서블릿에 대한 선언과 매핑은 이미 이전 예제 테스트에서 설정했다.)

다음은 HttpSessionListener 에 대한 설명과 예제이다.
HttpSessionListener 인터페이스 역시 2개의 메소드로 구성되어 있는데 하나는 세션 생성되는 이벤트와 세션이 무효화 되는 이벤트를 위한 것이다.

  • public void sessionCreated(HttpSessionEvent se);
  • public void sessionDestroyed(HttpSessionEvent se);
SessionCounterListener.java
package net.java_school.listener;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionCounterListener implements HttpSessionListener {
	public static int totalCount;
	
	@Override
	public void sessionCreated(HttpSessionEvent event) {
		totalCount++;
		System.out.println("세션증가 총세션수:" + totalCount);
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent event) {
		totalCount--;
		System.out.println("세션감수 총세션수:" + totalCount);
	}

}

web.xml 에 다음을 추가한다.

<listener>
    <listener-class>net.java_school.listener.SessionCounterListener</listener-class>
</listener>

설정파일인 web.xml이 변경되었으므로 톰캣을 재실행한 후
http://localhost:8989/simple 을 방문한다.
다른 웹브라우저로 http://localhost:8989/simple 을 방문한다.
톰캣 로그 파일에서 로그 메시지를 확인한다.

Filter

필터란 사용자의 요청이 서버 자원에 전달되기 전에 언제나 수행되어야 하는 코드 조각이 있을 때 사용한다.
필터는 web.xml 에서 선언과 매핑을 해야 한다.
web.xml 에 필터1 다음에 필터2 가 순서대로 선언되고 매핑되었다면
필터1-필터2-서버 자원 순으로 실행될 것이다.
필터는 응답이 사용자의 웹브라우저에 도달되기 전에도 필터링 할 수 있다.
필터2-필터1-웹브라우저 순서로 응답이 도달된다.

여기서 서버 자원은 서블릿,JSP는 물론이고 HTML페이지와 이미지와 같은 정적인 자원를 포함하며, web.xml 에서 필터 관련 매핑 설정에서 URL 패턴에 부합하는 자원을 말한다.
필터 클래스를 작성하기 위해서는 javax.servlet.Filter 인터페이스를 구현해야 한다.

Filter 인터페이스

  • init (FilterConfig filterConfig) throws ServletException
    서블릿 컨테이너에 의해 호출되면 필터는 서비스 상태가 됨
  • doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    서블릿 컨테이너에 의해 호출되어 필터링 작업을 수행
  • destroy()
    서블릿 컨테이너에 의해 호출되면 해당 필터는 더 이상 서비스를 할 수 없음. 주로 자원반납을 위해 사용

doFilter 메소드의 아규먼트를 보면, 필터가 요청이나 응답을 가로챌 때 ServletRequest 와 ServletResponse 그리고 javax.servlet.FilterChain 객체를 접근할 수 있음을 알 수 있다.
여기서 FilterChain 객체는 순서대로 호출되어야 하는 필터의 리스트를 담고 있다.
필터의 doFilter 메소드에서 FilterChain 의 doFilter 메소드를 호출하기 전까지가 요청전에 실행되는 필터링 코드이며 FilterChain의 doFilter 메소드 호출다음은 응답전에 호출되는 필터링 코드이다.
이와 같이 작동하는 이유가 궁금하면 아래 필터를 흉내낸 자바 순수 애플리케이션을 실행해 본다.

필터 매커니즘을 흉내낸 자바 순수 애플리케이션

ChainFilter.java
package net.java_school.filter;

import java.util.ArrayList;
import java.util.Iterator;

public class ChainFilter {
	private ArrayList<Filter> filters;
	private Iterator<Filter> iterator;

	public void doFilter() {
		if (iterator.hasNext()) {
			iterator.next().doFilter(this);
		} else {
			System.out.println("서버 자원을 호출한다.");
		}
	}

	public ArrayList<Filter> getFilters() {
		return filters;
	}

	public void setFilters(ArrayList<Filter> filters) {
		this.filters = filters;
		this.iterator = filters.iterator();
	}
	
}
Filter.java
package net.java_school.filter;

public interface Filter {
	
	public void doFilter(ChainFilter chain);

}
Filter1.java
package net.java_school.filter;

public class Filter1 implements Filter {

	@Override
	public void doFilter(ChainFilter chain) {
		System.out.println("서버 자원 실행전에 필터1 실행부");
		chain.doFilter();
		System.out.println("서버 자원 실행후에 필터1 실행부");
	}

}
Filter2.java
package net.java_school.filter;

public class Filter2 implements Filter {

	@Override
	public void doFilter(ChainFilter chain) {
		System.out.println("서버 자원 실행전에 필터2 실행부");
		chain.doFilter();
		System.out.println("서버 자원 실행후에 필터2 실행부");
	}

}
Tomcat.java
package net.java_school.filter;

import java.util.ArrayList;

public class Tomcat {

	public static void main(String[] args) {
		ChainFilter chain = new ChainFilter();
		ArrayList<Filter> filters = new ArrayList<Filter>();
		Filter f1 = new Filter1();
		Filter f2 = new Filter2();
		filters.add(f1);
		filters.add(f2);
		chain.setFilters(filters);
		chain.doFilter();
	}

}
서버 자원 실행전에 필터1 실행부
서버 자원 실행전에 필터2 실행부
서버 자원을 호출한다.
서버 자원 실행후에 필터2 실행부
서버 자원 실행후에 필터1 실행부

Filter 예제

다음은 모든 요청에 대해 ServletRequest 의 setCharacterEncoding("UTF-8"); 가 먼저 수행되도록 하려 한다.
아래와 같이 CharsetFilter.java 파일을 작성한다.

/WEB-INF/src/net/java_school/filter/CharsetFilter.java
package net.java_school.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class CharsetFilter implements Filter {

	private String charset = null;
	
	@Override
	public void init(FilterConfig config) throws ServletException {
		this.charset = config.getInitParameter("charset");
	}
	
	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
		throws IOException, ServletException {
		
		if (req.getCharacterEncoding() == null) {
			req.setCharacterEncoding(charset);
			chain.doFilter(req,resp);
		}
	}

	@Override
	public void destroy() {
		//반납할 자원이 있다면 작성한다.
	}

}

/WEB-INF/src/net/java_school/filter/ 폴더로 이동해서 아래와 같이 컴파일한다.

javac -d C:/www/myapp/WEB-INF/classes ^
-cp "C:/Program Files/Apache Software Foundation/Tomcat 7.0/lib/servlet-api.jar" ^
CharsetFilter.java


다음 web.xml 파일을 열고 아래를 추가한다. 이때 기존 엘리먼트와의 순서에 주의한다.
context-param 엘리먼트와 listener 엘리먼트 사이에 아래 코드가 위치해야 한다.

web.xml
<filter>
   <filter-name>CharsetFilter</filter-name>
   <filter-class>net.java_school.filter.CharsetFilter</filter-class>
   <init-param>
      <param-name>charset</param-name>
      <param-value>UTF-8</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>CharsetFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

테스트

위에서 실행했던 회원가입 예제의 RegisterServlet.java 소스에서
req.setCharacterEncoding( "UTF-8" ); 을 주석처리 한 후 RegisterServlet 서블릿을 재컴파일한다.
http://localhost:8989/example/join.jsp 를 방문하여 아이디에 한글을 입력하고 좋아하는 운동을 선택한 후 전송을 클릭한다.
이때 RegisterServlet 이 사용자가 입력한 한글 아이디 값을 제대로 출력하는지 테스트한다.

소스 설명

필터의 초기화 파라미터를 읽기 위해서는 FilterConfig의 getInitParameter()메소드나 getInitParameters()메소드를 이용한다.
filter-mapping엘리먼트를 이용해서 URL패턴이나 서블릿 이름으로 이들 자원에 앞서 필터링 작업을 수행할 필터를 정의한다.
필터는 배치 정의자에 나와 있는 순으로 FilterChain에 추가된다.
이때 서블릿 이름과 매핑된 필터는 URL 패턴에 매칭되는 필터 다음에 추가된다.
필터 클래스 코드내에서 FilterChain.doFilter() 메소드를 이용하면 FilterChain의 다음 아이템을 호출하게 된다.

파일 업로드

MultipartRequest

MultipartRequest 팩키지는 파일 업로드에 널리 이용되고 있는 팩키지이다.
http://www.servlets.com/cos/index.html 에서 가장 최신 버전인 cos-26Dec2008.zip 를 다운로드 하여 압축을 푼 후 서브 디렉토리 lib 에 있는 cos.jar 파일을 /WEB-INF/lib 디렉토리에 복사한다.

MultipartRequest 클래스의 생성자는 아래 링크에서 확인할 수 있듯이 8개가 있다.
http://www.servlets.com/cos/javadoc/com/oreilly/servlet/MultipartRequest.html 
그 중 아래의 생성자는 한글 인코딩 문제를 해결할 수 있고 업로드하는 파일이 중복될 때 파일명을 변경해서 업로드한다. 예제에서는 이 생성자를 이용한다.

public MultipartRequest(
	HttpServletRequest request,
	String saveDirectory,
	int maxPostSize,
	String encoding,
	FileRenamePolicy policy) throws IOException

MultipartRequest 메소드

MultipartRequest 객체가 성공적으로 생성되었다면 이미 업로드는 성공한 것이다.
아래 메소드는 서버의 파일 시스템에 파일을 업로드 된 후 이용하는 MultipartRequest 멤버 메소드를 소개하고 있다.
<input type="file" name="photo"/>를 이용해서 logo.gif 를 업로드했다고 가정하에 설명한다.

메소드설명
getContentType("photo");업로드된 파일의 MIME 타입 리턴, 예를 들어 확장자가 gif 이미지라면 "image/gif" 가 반환
getFile("photo");업로드되어 서버에 저장된 파일의 File 객체 리턴
getFileNames();폼 요소 중 input 태그 속성이 file 로 된 파라미터의 이름을 Enumeration 타입으로 리턴
getFilesystemName("photo");업로드되어 서버 파일시스템에 존재하는 실제 파일명을 리턴
getOriginalFileName("photo");원래의 파일명을 리턴
HttpServletRequest 구현체와 같은 인터페이스를 제공하기 위한 메소드
getParameter(String name);name 에 해당하는 파라미터의 값을 String 타입으로 리턴
getParameterNames();모든 파라미터의 이름을 String 으로 구성된 Enumeration 타입으로 리턴
getParameterValues(String name);name 에 해당하는 파라미터의 값들을 String[] 타입으로 리턴

MultipartRequest 예제

사용자로 부터 업로드 파일을 선택하도록 유도하는 페이지를 ROOT 애플리케이션의 최상위 디렉토리의 서브 디렉토리 example 에 작성한다.

/example/MultipartRequest.html
<!DOCTYPE html 
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
   <title>MultipartRequest 서블릿 예제</title>
   <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
</head>
<body>
<h2>MultipartRequest 를 이용한 파일 업로드 테스트</h2>
<form action="../servlet/UploadTest" method="post" enctype="multipart/form-data">
  <p>
    이름 : <input type="text" name="name" /><br />
    파일1 : <input type="file" name="file1" /><br />
    파일2 : <input type="file" name="file2" /><br />
  <input type="submit" value="전송" />
  </p>  
</form>
</body></html>

업로드를 실행할 서블릿을 아래와 같이 작성하고 컴파일한다.
컴파일 할 때에 자바 컴파일러는 cos.jar 파일에 대한 경로를 알아야 한다.

UploadTest.java
package example;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;

public class UploadTest extends HttpServlet {
	
	@Override	
	public void doPost(HttpServletRequest req, HttpServletResponse resp) 
			throws IOException, ServletException {
			
		resp.setContentType("text/html; charset=UTF-8");
		PrintWriter out = resp.getWriter();
		
		ServletContext cxt = getServletContext();
		String dir = cxt.getRealPath("/upload");
		
		try {
			MultipartRequest multi = 
				new MultipartRequest(
					req,
					dir,
					5*1024*1024,
					"UTF-8",
					new DefaultFileRenamePolicy());
					
			out.println("<html>");
			out.println("<body>");
			out.println("<h1>사용자가 전달한 파라미터들</h1>");
			out.println("<ol>");
			Enumeration<?> params = multi.getParameterNames();
			
			while (params.hasMoreElements()) {
				String name = (String) params.nextElement();
				String value = multi.getParameter(name);
				out.println("<li>" + name + "=" + value + "</li>");
			}
			
			out.println("</ol>");
			out.println("<h1>업로드된 파일</h1>");
			
			
			Enumeration<?> files = multi.getFileNames();
			
			while (files.hasMoreElements()) {
				out.println("<ul>");
				String name = (String) files.nextElement();
				String filename = multi.getFilesystemName(name);
				String orginalname =multi.getOriginalFileName(name);
				String type = multi.getContentType(name);
				File f = multi.getFile(name);
				out.println("<li>파라미터 이름 : "  + name + "</li>");
				out.println("<li>파일 이름 : " + filename + "</li>");
				out.println("<li>원래 파일 이름 : " + orginalname + "</li>");
				out.println("<li>파일 타입 : " + type + "</li>");
				
				if (f != null) {
					out.println("<li>크기: " + f.length() + "</li>");
				}
				out.println("</ul>");
			}
		} catch(Exception e) {
			out.println("<ul>");
			e.printStackTrace(out);
			out.println("</ul>");
		}
		out.println("</body></html>");
	}
}

다음으로 web.xml 파일을 열고 작성한 서블릿을 등록하고 매핑한다.

web.xml
<servlet>
    <servlet-name>UploadTest</servlet-name>
    <servlet-class>example.UploadTest</servlet-class>
</servlet>

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

예제를 실행하기 전에 ROOT 웹 애플리케이션의 최상위 디렉토리에 upload 라는 서브 디렉토리를 만든다.
톰캣을 재가동하고 http://localhost:8989/example/upload.html 를 방문하여 테스트한다.
중복된 파일을 업로드한 후 upload 폴더에서 파일명을 확인한다.
중복된 파일을 업로드하면 확장자를 제외한 파일이름의 끝에 숫자가 붙어서 업로드 되고 있음을 확인할 수 있다.
만약 테스트가 실패했다면 아래 리스트를 체크한다.

  1. UploadTest 서블릿의 바이트 코드가 생성되어 있는지 확인한다.
  2. ROOT 애플리케이션의 최상위 디렉토리에 upload 라는 서브 디렉토리가 있는지 확인한다.
  3. 톰캣 클래스로더가 찾을 수 있도록 cos.jar 파일이 /WEB-INF/lib 에 위치하는지 확인한다.
  4. web.xml 파일에 UploadTest 서블릿이 제대로 등록되고 매핑되어 있는지 확인한다.
  5. 웹 애플리케이션이 로드되었는지 확인한다.

commons-fileupload

http://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi
http://commons.apache.org/proper/commons-io/download_io.cgi
에서 최신 바이너리 파일을 다운로드 한 후
/WEB-INF/lib에 commons-fileupload-x.x.jar 와 commons-io-x.x.jar 를 위치시킨다.

/example/commons-fileupload.html
<!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=UTF-8">
<title>commons-fileupload 테스트</title>
</head>
<body>
<h1>파일 업로드 테스트</h1>
<form action="../CommonsUpload" method="post" enctype="multipart/form-data">
파일 : <input type="file" name="upload" /><br />
<input type="submit" value="전송" />
</form>
</body>
</html>
CommonsUpload.java 서블릿
package example;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

import java.util.Iterator;
import java.io.File;
import java.util.List;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class CommonsUpload extends HttpServlet {

	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse resp) 
			throws IOException, ServletException {
			
		resp.setContentType("text/html; charset=UTF-8");
		PrintWriter out = resp.getWriter();
		//Check that we have a file upload request
		boolean isMultipart = ServletFileUpload.isMultipartContent(req);
		//Create a factory for disk-based file items
		DiskFileItemFactory factory = new DiskFileItemFactory();
		
		//Configure a repository (to ensure a secure temp location is used)
		ServletContext servletContext = this.getServletConfig().getServletContext();
		File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
		factory.setRepository(repository);
		
		//Create a new file upload handler
		ServletFileUpload upload = new ServletFileUpload(factory);
		upload.setHeaderEncoding("UTF-8");//한글파일명 처리위해 추가
		try {
			//Parse the request
			List<FileItem> items = upload.parseRequest(req);
			//Process a file upload
			Iterator<FileItem> iter = items.iterator();
			while (iter.hasNext()) {
				FileItem item = iter.next();
				String fileName = null;
				if (!item.isFormField()) {
					String fieldName = item.getFieldName();
					out.println(fieldName);
					fileName = item.getName();
					out.println(fileName);
					String contentType = item.getContentType();
					out.println(contentType);
					boolean isInMemory = item.isInMemory();
					out.println(isInMemory);
					long sizeInBytes = item.getSize();
					out.println(sizeInBytes);
				}
				// Process a file upload
				ServletContext cxt = getServletContext();
				String dir = cxt.getRealPath("/upload");
				File uploadedFile = new File(dir + "/" + fileName);
				item.write(uploadedFile);
			}
		} catch (Exception e) {
			out.println("<ul>");
			e.printStackTrace(out);
			out.println("</ul>");
		}
		out.println("<a href=\"example/commons-fileupload.html\">파일업로드폼으로</a>");
	}
}

web.xml 파일을 열고 작성한 서블릿을 등록하고 매핑한다.

web.xml
<servlet>
    <servlet-name>commonsUpload</servlet-name>
    <servlet-class>example.CommonsUpload</servlet-class>
</servlet>

<servlet-mapping>
	<servlet-name>commonsUpload</servlet-name>
	<url-pattern>/CommonsUpload</url-pattern>
</servlet-mapping>

톰캣을 재가동하고 http://localhost:8989/example/commons-fileupload.html 를 방문하여 테스트한다.
중복된 파일을 업로드한 후 upload 폴더에서 파일명을 확인한다.
중복된 파일을 업로드하면 cos.jar 와는 달리 기존 파일을 덮어쓴다는 것을 확인한다.
업로드된 파일을 확인하는 예제는 JSP에서 다룬다.

쿠키

HTTP 프로토콜은 상태를 유지할 수 없는 프로토콜이다.
쿠키는 HTTP 프로토콜의 특징상 각각의 웹 브라우저가 서버와의 통신에서 세션을 유지하지 못하는 것을 보완하기 위한 기술이다.
서버가 쿠키를 전송하면 웹 브라우저는 그 다음 요청마다 쿠키 값을 서버로 전달하여 사용자 정보를 유지할 수 있게 한다.

서버 -> 웹 브라우저

쿠키가 작동하려면 서버에서 쿠키값을 클라이언트로 전송해야 한다.
이것을 쿠키를 굽는다고 표현하는데 아래와 같은 정보가 서버로부터 웹 브라우저에 전달되고 웹 브라우저가 관리하는 폴더에 파일로 저장된다.

Set-Cookie : name = value ; expires = date ; path = path ; domain = domain ; secure

웹 브라우저 -> 서버

쿠키가 웹브라우저에 셋팅되면, 웹브라우저는 쿠기를 전달해준 서버로 요청시마다 아래와 같은 문자열을 서버로 보낸다.

Cookie ; name = value1 ; name2 = value2 ;

쿠키 이름과 값에는 []()="/?@:; 와 같은 문자는 올 수 없다.
다음은 쿠키를 설정하는 절차이다.

쿠키 설정 절차

1) Cookie 객체를 만든다. Cookie(String name, String value)
2) 다음 메소드를 이용해 쿠키에 속성을 부여한다.

메소드설명
setValue(String value)생성된 쿠키의 값을 재설정할 때 사용한다.
setDomain(String pattern)쿠키는 기본적으로 쿠키를 생성한 서버에만 전송된다.
같은 도메인을 사용하는 서버에 대해서 같은 쿠키를 보내기 위해서 setDomain()을 사용한다.
주의할 점은 쿠키를 생성한 서버와 관련이 없는 도메인을 setDomain()에 값으로 설정하면 쿠키가 구워지지 않는다는 것이다.
setMaxAge(int expiry)쿠키의 유효기간을 초단위로 설정한다.
음수 입력시에는 브라우저가 닫으면 쿠키가 삭제된다.
setPath(String uri)쿠키가 적용될 경로 정보를 설정한다.
경로가 설정되면 해당되는 경로로 방문하는 경우에만 웹브라우저가 쿠키를 웹서버에 전송한다.
setSecure(boolean flag)flag가 true이면 보안채널을 사용하는 서버의 경우에 한해 쿠키를 전송한다.

3) 웹브라우저에 생성된 쿠키를 전송 : resp.addCookie(cookie);

구워진 쿠키 이용

위에서 쿠키를 설정했다면 이제 서블릿에서 쿠키 이용하는 방법에 대해 알아본다.

Cookie[] cookie = req.getCookies();

HttpServletRequest 의 getCookies() 메소드를 사용해서 쿠키배열을 얻는다.
만약 구워진 쿠키가 없다면 getCookies() 메소드는 null 을 리턴한다.
다음 메소드를 이용하면 쿠키에 대한 정보를 얻을 수 있다.
이중 getName()과 getValue()가 주로 쓰인다.

Cookie 메소드설명
getName()쿠키의 이름을 구한다.
getValue()쿠키의 값을 구한다.
getDomain()쿠키의 도메인을 구한다.
getMaxAge()쿠키의 유효시간을 구한다.

다음은 서버 사이드에서 쿠키값을 알아내는 코드조각이다.
예에서는 쿠키 이름이 id에 해당하는 쿠키값을 반환하는 예이다.

String id = null;
Cookie[] cookies = request.getCookies();
if (cookies != null) {
  for (int i = 0; i < cookies.length; i++) {
    String name = cookies[i].getName();
    if (name.equals("id")) {
      id = cookies[i].getValue();
      	break;
    }
  }
}

아래는 쿠키를 삭제하는 예이다.
방법은 삭제하고자 하는 쿠키와 같은 이름의 쿠키를 생성하고 setMaxAge(0) 을 호출한다.

Cookie cookie = new Cookie("id","");
cookie.setMaxAge(0);
resp.addCookie(cookie);

쿠키에 대한 실습은 JSP에서 다룬다.

세션

세션은 쿠키 기반 기술로 쿠키의 보안상 약점을 극복하기 위한 기술이다.
쿠키와 다른 점(즉, 보안상 개선된 점)은 웹브라우저는 서버가 정해준 세션ID 만을 쿠키값으로 저장한다는 것이다.
세션이 생성되면 서버에서는 세션ID에 해당하는 HttpSession 객체를 서블릿 컨테이너가 연결시켜 준다.
아래는 세션 생성하는 코드이다.

HttpSession session = req.getSession(true); //세션이 없으면 생성
HttpSession session = req.getSession(false); //세션이 없다면 null리턴

세션 객체가 생성되었으면 세션에 정보를 아래코드처럼 저장할 수 있다.

User user = new User("홍길동","1234");
session.setAttribue("user", user); //user 이름으로 user 객체 저장

세션에 대한 실습은 JSP에서 한다.

주석


  1. 그림(서블릿 기본골격 클래스 다이어그램)이 GenericServlet, HttpServlet 의 모든 속성과 메소드를 모두 나타내고 있지는 않다. 이어지는 설명을 쉽게 이해하려면 클래스 다이어그램에서 나오는 인퍼페이스,추상클래스,클래스 이름은 기억해야 한다.
  2. MIME(Multipurpose Internet Mail Extensions)
    .html 또는 .htm 은 text/html, .txt 는 text/plain .gif 는 image/gif 이다.
  3. 쿼리 스트링(Query tring)이라 하는 URL 뒤 ? 다음에 이어지는 문자열은 URL에 해당하는 서버측 자원에 전달된다. 정보가 1개 이상일 때는 두번째부터는 &를 사용한다.
  4. Enumeration 인터페이스는 hasMoreElements() 와 nextElement() 2개의 메소드를 이용하여 데이터를 순서대로 접근할 수 있다.
참고





출처 - http://www.java-school.net/jsp/Servlet.php

'FRAMEWORK > SERVLET' 카테고리의 다른 글

RequestDispatcher forward method  (0) 2014.11.05
RequestDispatcher include method  (0) 2014.11.05
서블릿이란  (0) 2014.11.05
jsp 와 servlet 의 차이  (0) 2014.11.05
:

뷰에 모델(Model) 전달

FRAMEWORK/SPRING 2014. 11. 5. 10:28


※ @RequestMapping 어노테이션이 적용된 메소드의 파라미터나 리턴 타입으로 ModelAndView, Model, ModelMap, Map, 커맨드 객체 등을 이용해서 모델을 뷰어 전달

 

◆ @RequestMapping 메소드가 ModelAndView, Model, Map을 리턴하는 경우 이들에 담긴 모델 데이터가 뷰에 전달

  ● 추가적으로 다음의 항목도 뷰에 함께 전달

    => 커맨드 객체

    => @ModelAttribute 어노테이션이 적용된 메소드가 리턴한 객체

    => 메서드의 Map, Model, ModelMap 타입의 파라미터를 통해 설정된 모델

 



 

 

UserVO.java

package sp.mvc.vo;

 

public class UserVO {
 private String userName;
 private String address;
 private String fruit;
 
 public String getUserName() {
  return userName;
 }
 public void setUserName(String userName) {
  this.userName = userName;
 }
 public String getAddress() {
  return address;
 }
 public void setAddress(String address) {
  this.address = address;
 }
 public String getFruit() {
  return fruit;
 }
 public void setFruit(String fruit) {
  this.fruit = fruit;
 }
 
 @Override
 public String toString() {
  return "UserVO [userName=" + userName + ", address=" + address + ", fruit=" + fruit + "]";
 }
} 

 

UserController.java

package sp.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import sp.mvc.vo.UserVO;

 

@Controller
public class UserController {

 

 @ModelAttribute("popularFruit")
 public String[] refPopularFruit(){
  return new String[]{"사과", "포도", "수박", "참외"};
 }
 
 @RequestMapping(value="/userForm.sp", method=RequestMethod.GET)
 public String userForm(){
  System.out.println("----- UserController.userForm() : GET -----");
  return "user/userForm";
 }

 
 @RequestMapping(value="/userSave.sp", method=RequestMethod.POST)
 public ModelAndView userSave(UserVO userVo, Model model){
// public ModelAndView userSave(@ModelAttribute("userVo") UserVO userVo, Model model){ 
  System.out.println("----- UserController.userSave() : POST -----");
  System.out.println("userInfo : " + userVo.toString());
  
  model.addAttribute("msg", "SUCCESS");
  
  ModelAndView modelAndView = new ModelAndView();
  modelAndView.setViewName("user/userInfo");
  modelAndView.addObject("userVo", userVo);
  
  return modelAndView;
 }
 
 @RequestMapping("/userView.sp")
 public Model userView(){
  System.out.println("----- UserController.userView() -----");
  
  Model model = new ExtendedModelMap();
  model.addAttribute("msg", "member info");
  
  return model;
 }

}

▦ Map, Model, ModelMap을 통한 모델 설정 방법

  ■ Map, Model, ModelMap 중 한 가지를 파라미터로 전달

  ■ Map, Model을 리턴

    => Map, Model은 인터페이스 이므로 인터페이스를 구현한 클래스의 객체를 생성해서 리턴

 

● @ModelAttribute("popularFruit") : @ModelAttribute 어노테이션이 적용된 메소드가 리턴한 객체

● public ModelAndView userSave(UserVO userVo, Model model: Model 파라미터, ModelAndView 리턴 타입

● public ModelAndView userSave(@ModelAttribute("userVo") UserVO userVo, Model model: @ModelAttribute 어노테이션, ModelAndView 리턴 타입

● public Model userView() : Model 리턴 타입

 

 

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
 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  
  <display-name>spring303</display-name>

 <filter>
  <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>


 <!-- 공통 빈 설정
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/applicationContext.xml</param-value>
 </context-param>
   
 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 -->
 
 <servlet>
  <servlet-name>spring303</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>
    /WEB-INF/main_config.xml
   </param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 <servlet-mapping>
  <servlet-name>spring303</servlet-name>
  <url-pattern>*.sp</url-pattern>
 </servlet-mapping>

</web-app>

 

main_config.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context
 xmlns:p="http://www.springframework.org/schema/p"
 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/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 <context:annotation-config/>
 <context:component-scan base-package="sp.mvc.controller"/>
 
 <!-- ViewResolver 설정 -->
 <bean id="internalResource" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="jsp/"></property>
  <property name="suffix" value=".jsp"></property>
 </bean>


 <!-- 각종 bean 설정 -->

 
</beans>

 

userForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
<!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=UTF-8">
<title>회원정보 입력</title>
</head>
<body>

<form action="userSave.sp" method="post">
 <table border="1">
  <tr>
   <td>이름</td>
   <td><input type="text" name="userName" value=""/></td>
  </tr>
  <tr>
   <td>주소</td>
   <td><input type="text" name="address" value=""/></td>
  </tr>
  <tr>
   <td>과일</td>
   <td>
   <c:forEach var="fruit" items="${popularFruit}">
    ${fruit},&nbsp;
   </c:forEach> 

   </td>
  </tr>
 </table>
 
 <input type="submit" name="submit" value="전송"/>
</form>
</body>
</html>

 

userInfo.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<!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=UTF-8">
<title>회원정보 출력</title>
</head>
<body>
<h2>${msg}</h2>
<form action="userView.sp" method="post">
 <table border="1">
  <tr>
   <td>이름</td>
   <td>${userVo.userName}</td>
  </tr>
  <tr>
   <td>주소</td>
   <td>${userVo.address}</td>
  </tr>
  <tr>
   <td>과일</td>
   <td>
   <c:forEach var="fruit" items="${popularFruit}">
    ${fruit},&nbsp;
   </c:forEach>

   </td>
  </tr>
 </table>
 <input type="submit" name="submit" value="뷰"/>
</form>
</body>
</html>

 

userView.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<!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=UTF-8">
<title>회원정보 출력</title>
</head>
<body>
<h2>${msg}</h2>
<c:forEach var="fruit" items="${popularFruit}">
${fruit} &nbsp;
</c:forEach>
</body>
</html>

 

 

로그 결과

----- UserController.userView() -----
----- UserController.userSave() : POST -----
userInfo : UserVO [userName=스누피, address=제주도 456, fruit=null]
----- UserController.userView() -----

 

 



 

 


출처 - http://snoopy81.tistory.com/311

'FRAMEWORK > SPRING' 카테고리의 다른 글

<util:properties/> 와 Spring EL 로 값 가져오기  (0) 2014.11.11
Spring annotation  (0) 2014.11.10
@ModelAttribute 어노테이션  (0) 2014.11.05
@ModelAttribute와 @RequestParam의 차이점  (0) 2014.11.05
Bean 정의시 id vs name  (0) 2014.09.26
: