IT_Programming/Dev Libs & Framework

[펌] JSTL Custom tag에 Spring Bean 주입하기

JJun ™ 2015. 11. 27. 02:03



 * 출처

 : http://redutan.github.io/2015/11/25/jstl-customtag-with-springbean

 : http://www.mungchung.com/xe/spring/36064

 : http://egloos.zum.com/ultteky/v/10471170





JSTL Custom tag에 Spring Bean 주입하기

어떻게든 ApplicationContext만 가져오면 되는데 이미 SpringMVC에서 다 제공을 해주더군요

  1. RequestContextAwareTag을 상속한다.
    • 재미있는 부분은 해당 Tag클래스를 Spring Tag Library 구현체 모두 상속받아서 사용한다는 점입니다. 당연한 이야기 겠네요.
    • 예를 들면 MessageTag도 위 클래스를 상속받는데 jsp tag로 보면 <spring:message> 입니다.
  2. WebApplicationContext를 얻는다.
  3. 스프링 빈 주입

예제코드를 확인해봅시다.

// 1. RequestContextAwareTag 상속
public class CodeTag extends RequestContextAwareTag {
    @Autowired
    CodeService codeService;
...
    @Override
    protected int doStartTagInternal() throws Exception {
        if (codeService == null) {
            // 2. WebApplicationContext를 얻는다.
            WebApplicationContext wac = getRequestContext().getWebApplicationContext();
            AutowireCapableBeanFactory beanFactory = wac.getAutowireCapableBeanFactory();
            // 3. 스프링 빈 주입
            beanFactory.autowireBean(this);
        }
        // TODO working
        return SKIP_BODY;
    }
}

참 쉽죠 ~

참조 : http://stackoverflow.com/questions/3445908/is-there-an-elegant-way-to-inject-a-spring-managed-bean-into-a-java-custom-simpl







custom taglib 만들때 spring bean 사용하기




custom taglib 만들면서 User 객체 사용하려고 @Autowired 어노테이션이용해서 User 객체를 DI 받았는데

User 객체 값을 찍어보면 계속 null이 나왔다.

public class UserTag extends TagSupport {
    @Autowired
    private User user;
  
    public int doStartTag() throws Exception {
         // user 객체는 null 이 된다.
         System.out.println(user);
        ...
     }
}

 

왜 그럴까 검색에 검색을 해보니 너무도 당연한 거였다 -_-

Spring 기초내공이 부족하니 이런 단순한 개념도 이해를 못했던 것이다.

 

@Autowired 어노테이션 이용한 객체는 Spring 컨테이너에서 생성된 빈이기 때문에 Spring이 관리하는 빈이지만

tag library는 서블릿에서 생성되기때문에 Spring 관리대상이 아니다.

그렇기 때문에 custom taglib 만드는 자바코드에서 @Autowired를 사용해도 객체를 DI 받을수 없었던것이다.

 

 

이거에 대한 해결 방법은 아래와 같이 2가지 방법이 있다. (더 있을수도 있다 -_-)

해결 방법역시 간단하다. WebApplicationContext에 현재 등록된 빈을 찾아서 가져오는거다.

Spring 컨테이너내에서는 Spring 알아서 찾아주지면 그렇지 않은 곳에서는 아래와 같은 방법으로 직접 찾아서 이용하면 된다.

 


1. RequestContextAwareTag를 이용하는 방법

public class UserTag extends RequestContextAwareTag {
    private User user;
 
    public int doStartTagInternal() throws Exception {
        user = (User) getRequestContext().getWebApplicationContext().getBean(User.class);
 
        ...
    }
}



2. TagSupport를 이용하는 방법 

public class UserTag extends TagSupport {
    private User user;
 
    public int doStartTag() throws Exception {
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(pageContext.getServletContext());
        user = ctx.getBean(User.class);
 
        ...
    }
}

 

 

추가로,

@Configurable 과 Aspect를 이용해서 Spring관리대상이 아닌 빈들은 Spring관리대상으로 포함시켜서 DI 하는 방법이 있다는데 그건 확인 안해봤다.





[JSP] Custom Tag 사용하기




Custom Tag(사용자 정의 태그)

[1]. 사용자가 임의로 만들어 쓸 수 있는 태그

[2]. 자주 사용되는 기능을 태그로 만들어 사용할 수 있음

[3]. 태그의 이름과 속성, 접두어 등은 사용자가 임의로 정할 수 있음

 

[1]. 구성요소

1) JSP - 태그를 사용하는 파일

2) Tag Handler(java) - 실제로 동작을 수행해 주는 자바 파일 태그 하나당 하나의 Tag Handler가 있어야 함

3) TLD - custom 태그와 Tag Handler 간의 관계를 나타내는 파일. 모든 사용자 정의 태그에 대해 기술하고 있어야 함

 

▶ 커스텀 태그 동작 구조

     ① JSP에 커스텀 태그가 나오면 taglib 지시자에 지정된 tld에서 해당 태그 핸들러를 찾아서 연결

     ② 태그가 열릴 때 태그 핸들러의 doStart() 메소드가 실행

     ③ 여는 태그와 닫는 태그 사이에 몸체가 있을 경우 doAfterBody() 메소드가 실행

        ※ tld에서 body-content 태그에 empty 가 아닌 다른 것이 있고 doStartTag() 메소드에서는 SKIP_BODY가 반환되지 않는 조건

     ④  EVAL_PAGE가 호출된 경우 doEndTag() 메소드가 실행된다.

     ⑤  EVAL_BODY_INCLUDE와 EVAL_BODY_BUFFERED 차이

          ㉠ EVAL_BODY_INCLUDE

              - 기존 출력 스트림을 사용 함.

              - 호출된 후 바로 doAfterBody()와 doEndTag()가 차례로 실행됨.

          ㉡ EVAL_BODY_BUFFERED

              - 새로운 버퍼를 만들어서 사용함.

             - 호출된 후 setBodyContent() → doInitBody() → doAfterBody() → doEndTag()가 차례로 실행됨.

     ⑥ EVAL_BODY_AGAIN

          body 부분을 반복하여 실행하려면 doAfterBody() 메소드를 실행한 뒤 리턴값으로 사용한다. 반복을 종료시에는

          SKIP_BOD를 반환한다.

     ⑦ 태그 핸들러 메소드의 반환 필드 종류와 기능

          ㉠ javax.servlet.jsp.Tag 클래스

                    return 필드

                    SKIP_BODY                        [사용하는 메소드 : doStartTag(), doAfterBody()]

                    EVAL_BODY_INCLUDE         [사용하는 메소드 : doStartTag()]

                    SKIP_PAGE                        [사용하는 메소드 : doEndTag()]

                    EVAL_PAGE                       [사용하는 메소드 : doEndTag()]

          ㉡ javax.servlet.jsp.BodyTag        

                   return 필드

                   EVAL_BODY_BUFFERED       [사용하는 메소드 : doStartTag() , 조건 : implements BodyTag]

          ㉢ javax.servlet.jsp.IterationTag

                   return 필드

                   EVAL_BODY_AGAIN             [doAfterBody()]



[2]. 사용법(작동 순서)

1) JSP

  1. <%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>
    <%@ page import="tag.DateTag" %>
    <html>
    <head>
    <title>Insert title here</title>
    </head>
        <body>
            <h2>DateTag</h2>
            <hr>
            <%@ taglib prefix="honey" uri="/WEB-INF/taglib.tld" %>
           
            현재시각은 <honey:date /> 입니다.
           
            <honey:date></honey:date><br>
            <honey:date></honey:date><br>
            <honey:date></honey:date><br>
        </body>
    </html>

 

2) Tag Handler

2.1) TagSupport/BodyTagSupport를 상속 받음

2.2) 함수 호출순서

doStartTag() -> { setBodyContent() -> doInitBody() -> doAfterBody() } -> doEndTag() -> release()

 

3) TLD

  1. <?xml version="1.0" encoding="EUC-KR" ?>
    <!DOCTYPE taglib PUBLIC "-//SUN Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
    "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

    <taglib>
    <tlibversion>1.1</tlibversion>
    <jspversion>1.2</jspversion>
    <shortname>honey</shortname>
    <uri>"/WEB-INF/taglib.tld"</uri>
    <info>custom tag</info>
    <tag>
        <name>hello</name>
        <tagclass>tag.HelloTag</tagclass>
        <info>say hello</info>
    </tag>
    <tag>
        <name>date</name>
        <tagclass>tag.DateTag</tagclass>
        <info>Show Date</info>
    </tag>
    </taglib>

 

<속성 없는 태그의 경우>

[1]. JSP 파일에서

  1. ① <%@ page import="package.classFile" %>
  2. ② <%@ taglib uri="/WEB-INF/taglib.tld" prefix="honey" %>

    ③ <honey:printName />

[2]. Tag Handler(.java)에서 -> extends TagSupport  추가

doStartTag() / doEndTag()  등에서

  1. public int doEndTag() throws JspException {
  2. try {

  3. out = pageContext.getOut();

  4. out.write(" 제 이름은 HoneyMon 입니다.");

  5. } catch ( IOException e) {}

  6. return EVAL_PAGE;

  7. }

 

[3]. TLD 파일에서 속성 내용 추가

  1. <tag>
  2. <name>printName</name>

  3. <tagclass>tag.Print</tagclass>

  4. </tag>

ex) 속성이 없는 태그

1.taglib.tld

  1. <?xml version="1.0" encoding="EUC-KR" ?>
    <!DOCTYPE taglib PUBLIC "-//SUN Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
    "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

    <taglib>
    <tlibversion>1.1</tlibversion>
    <jspversion>1.2</jspversion>
    <shortname>honey</shortname>
    <uri>"/WEB-INF/taglib.tld"</uri>
    <info>custom tag</info>
    <tag>
        <name>hello</name>
        <tagclass>tag.HelloTag</tagclass>
        <info>say hello</info>
    </tag>
    </taglib>

 

2.HelloTag.java

  1. package tag;

    import java.io.IOException;
    import javax.servlet.jsp.*;
    import javax.servlet.jsp.tagext.*;


    public class HelloTag extends TagSupport{
       
        public int doStartTag() throws JspException {
            return EVAL_BODY_INCLUDE;
            //return SKIP_BODY;
        }
       
        public int doEndTag() throws JspException {
            try {
                pageContext.getOut().write("Hello, World!!!");
            } catch(IOException e){
                throw new JspException("IO Error : " + e.getMessage() );
            }
           
            return EVAL_PAGE;
    //        reutrn SKIP_PAGE: 이 경우에는 하나만 나오기도 한다. 첫 실행 태그 이후는 실행안됨
        }

    }

 

3.HelloTag.jsp

  1. <%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>
    <%@ page import="tag.HelloTag" %>
    <html>
    <head>
    <title>Insert title here</title>
    </head>
        <body>
            <h2>HelloTag</h2>
            <hr>
            <%@ taglib prefix="honey" uri="/WEB-INF/taglib.tld" %>
           
            <honey:hello> Why Do you laugh?</honey:hello>
            <honey:hello> Puhahahahahahah!!!</honey:hello>
       
        </body>
    </html>

 

<속성 있는 태그의 경우>

[1]. JSP 파일에서

  1. <honey:printName name="name"/>

[2]. Tag Handler(.java)에서 -> extends TagSupport  추가

1) 속성(name)과 동일한 이름으로 Member Field 추가

: String name;

2) 속성 name에 값을 설정하는 setter 메소드 만들기

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

 

[3]. TLD 파일에서 속성 내용 추가

  1. <tag>
       <name>printName</name>
       <tagclass>tag.Print</tagclass>
       <attribute>
          <name>name</name>
          <required>true</required>
          <rtexpravalue>true</rtexprvalue>
       </attribute>

    </tag>

ex) 속성있는 태그의 설정

1.ADD.jsp

  1. <%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>
    <%@ page import="tag.DateTag, tag.Add" %>
    <html>
    <head>
    <title>Insert title here</title>
    </head>
        <body>
            <h2>DateTag</h2>
            <hr>
            <%@ taglib prefix="honey" uri="/WEB-INF/taglib.tld" %>
            <honey:add num1="30" num2="20" />
        </body>
    </html>

 

2.taglib.tld

  1. <?xml version="1.0" encoding="EUC-KR" ?>
    <!DOCTYPE taglib PUBLIC "-//SUN Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
    "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

    <taglib>
    <tlibversion>1.1</tlibversion>
    <jspversion>1.2</jspversion>
    <shortname>honey</shortname>
    <uri>"/WEB-INF/taglib.tld"</uri>
    <info>custom tag</info>
    <tag>
        <name>add</name>
        <tagclass>tag.Add</tagclass>
        <attribute>
            <name>num1</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>num2</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

    </taglib>

 

3.Add.java

  1. package tag;

    import java.io.*;
    import java.util.Date;
    import javax.servlet.jsp.*;
    import javax.servlet.jsp.tagext.*;


    public class Add extends TagSupport {
        int num1;
        int num2;

       
        public void setNum1(int num1){
            this.num1 = num1;
        }

       
        public void setNum2(int num2){
            this.num2 = num2;
        }


        public int doStartTag() throws JspException {
            return EVAL_BODY_INCLUDE;
        }
           
        public int doEndTag() throws JspException {
            try{
                // out = pageContext.getOut();
                pageContext.getOut().write(num1 +" + "+ num2 + " = " +(num1+num2) );           
            } catch ( Exception e ) {
                throw new JspException("IO Error : " + e.getMessage());
            }
            return EVAL_PAGE;
        }
    }

<바디 있는 태그의 경우>

[1]. JSP 파일에서

  1. <honey:query>
       SELECT * FROM customer
    </honey:query>

[2]. Tag Handler(.java)에서 -> extends BodyTagSupport  추가

1) doStartTag() -> setBodyContent() -> doInitBody() - > doAfterBody() -> doEndTag() -> release()

 

2) doStartTag() : 바디내용을 Tag Handler에서 읽어가기 위해

  1. EVAL_BODY_BUFFERED를 return / or 함수 기술하지 않음
    return EVAL_BODY_BUFFERED

    public void doStartTag() throws JspException {
       return EVAL_BODY_BUFFERED;
    }

 

3) doAfterBody() 등 기술

  1. public int doAfterBody() throws JspException {
       BodyContent bc = getBodyContetn(); // body 내용 가져오기
       out = bc.getEnclosingWriter();
       String query = bc.getString().trim();
       .
       .
       .
       return SKIP_BODY;//이 리턴값 이후에는 다른 tag들은 사용되지가 않는다.
    }

 

ex) 바디 있는 Custom Tag

1.QueryTag.jsp

  1. <%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>
    <%@ page import="tag.QueryTag" %>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>Custom Tag use Query</title>
    </head>
        <body>
        <%@ taglib prefix="honey" uri="/WEB-INF/taglib.tld" %>
        <honey:query>
            SELECT * FROM customer
        </honey:query>
       
        </body>
    </html>

 

2.QueryTag.java

  1. package tag;

    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.Statement;

    import javax.naming.InitialContext;
    import javax.servlet.jsp.JspWriter;
    import javax.servlet.jsp.tagext.BodyContent;
    import javax.servlet.jsp.tagext.BodyTagSupport;
    import javax.sql.DataSource;

    public class QueryTag extends BodyTagSupport {
        DataSource ds;
        Connection con;
        Statement stat;
        JspWriter out;
        String query;

        public void doInitBody(){ // DB 풀을 이용하기 위한 설정용으로 사용
            try {
                InitialContext context = new InitialContext();
                ds = (DataSource)context.lookup("java:comp/env/jdbc/oracle");
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
       
        public int doAfterBody(){ //화면 출력용으로 사용
            BodyContent bc = getBodyContent();
            out = bc.getEnclosingWriter();
            query = bc.getString().trim();
            try {
                con = ds.getConnection();
                stat = con.createStatement();
               
                ResultSet rs = stat.executeQuery(query);
                out.write("<html>");
                out.write("<body><center>");
                out.write("<h1>Custom Tag use to Print DB table</h1>");
                out.write("<table border='1' align='center'>");
                out.write("<tr><th>번호</th><th>이름</th><th>주소</th></tr>");
                while( rs.next() ){
                    String num = rs.getString(1);
                    String name = rs.getString(2);
                    String address = rs.getString(3);
                    out.write("<tr>");
                    out.write("<td>" + num + "</td>");
                    out.write("<td>" + name + "</td>");
                    out.write("<td>" + address + "</td>");
                    out.write("</tr>");               
                }
                out.write("</table>");
                } catch ( Exception e ) {
                    e.printStackTrace();
                }
            return SKIP_BODY;
        }
       
        public void release(){ //파이프 연결 종료
            try {
                stat.close();
                con.close();
            } catch ( Exception e ) {
                e.printStackTrace();
            }
        }

    }

 

3.taglib.tld 설정

  1. <?xml version="1.0" encoding="EUC-KR" ?>
    <!DOCTYPE taglib PUBLIC "-//SUN Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
    "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

    <taglib>
    <tlibversion>1.1</tlibversion>
    <jspversion>1.2</jspversion>
    <shortname>honey</shortname>
    <uri>"/WEB-INF/taglib.tld"</uri>
    <info>custom tag</info>
    <tag>
        <name>query</name>
        <tagclass>tag.QueryTag</tagclass>
    </tag>
    </taglib>

CustomTag.JPG

 

 과제 : 하나의 Custom Tag 이용해서 처리해주기

1.Sql2.java

  1. package tag;

    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    import java.sql.Statement;

    import javax.naming.InitialContext;
    import javax.servlet.jsp.JspWriter;
    import javax.servlet.jsp.tagext.BodyContent;
    import javax.servlet.jsp.tagext.BodyTagSupport;
    import javax.sql.DataSource;

    public class Sql2 extends BodyTagSupport {
        DataSource ds;
        Connection con;
        Statement stat;
        JspWriter out;
        String query;

        public void doInitBody(){
            try {
                InitialContext context = new InitialContext();
                ds = (DataSource)context.lookup("java:comp/env/jdbc/oracle");
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
       
        public int doAfterBody(){
            BodyContent bc = getBodyContent();
            out = bc.getEnclosingWriter();
            query = bc.getString().trim();
           
            try {
                con = ds.getConnection();
                stat = con.createStatement();
               
                boolean flag = stat.execute(query);
               
                    if( flag == true ){
                        ResultSet rs = stat.executeQuery(query);
                        ResultSetMetaData rsmd = rs.getMetaData();
                        int count = rsmd.getColumnCount();
                        out.println("<html>");
                        out.println("<table border='1'>");
                       
                        while( rs.next() ){
                            out.println("<tr>");
                            for (int i = 1; i <= count; i++) {
                                String tmp = rs.getString(i);
                                out.println("<td>" + tmp + "</td>");
                            }
                            out.println("</tr>");
                        }
                        out.println("</table></html>");
                       
                    } else {
                        out.println(query);
                        out.println("퀴리가 정상적으로 처리되었습니다.");
                    }
               
                } catch ( Exception e ) {
                    e.printStackTrace();
                }
            return SKIP_BODY;
        }
       
        public void release(){
            try {
                stat.close();
                con.close();
            } catch ( Exception e ) {
                e.printStackTrace();
            }
        }
    }

 

2.taglib.tld

  1. <?xml version="1.0" encoding="EUC-KR" ?>
    <!DOCTYPE taglib PUBLIC "-//SUN Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
    "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

    <taglib>
    <tlibversion>1.1</tlibversion>
    <jspversion>1.2</jspversion>
    <shortname>honey</shortname>
    <uri>"/WEB-INF/taglib.tld"</uri>
    <info>custom tag</info>

    <tag>
        <name>sql2</name>
        <tagclass>tag.Sql2</tagclass>
    </tag>
    </taglib>

 

3.SqlTag.jsp

  1. <%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>
    <%@ page import="tag.Sql2" %>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>Custom Tag use Query</title>
    </head>
        <body>
        <%@ taglib prefix="honey" uri="/WEB-INF/taglib.tld" %>
        <honey:sql2>
            SELECT * from customer
        </honey:sql2>
        <hr>
        <honey:sql2>
            INSERT INTO customer VALUES(10003, 'kim', 'Earth')
        </honey:sql2>
        <hr>
        <honey:sql2>
            SELECT * from customer
        </honey:sql2>
        </body>
    </html>