JSP e tag files: criando templates em 10 minutos
Este mês têm sido bastante curioso pelo fato de três pessoas terem me feito a mesma pergunta: “- Você largou o Java de vez?”. A resposta é não, mas, para não deixar dúvidas, eu resolvi voltar a escrever sobre Java. O assunto foge um pouco do que costumo escrever, mas é algo que considero importante.
Ultimamente, tenho visto a forma como certos desenvolvedores escrevem seus JSPs, e o que me preocupou foi ver o quanto eles desconhecem a tecnologia. Não é raro achar páginas cheias de scriptlets, includes malucos, código du-tri-quadri-plicado e nenhuma taglib além da fornecida pelo framework web em uso.
É muito fácil escrever JSPs da forma errada, mas fazer as coisas de um jeito melhor é mais fácil ainda. Ao contrário do que muitos pensam, é possível sim criar JSPs limpos sem o uso de qualquer framework de layout (como Sitemesh) ou biblioteca (com exceção da JSTL, é claro).
Um exemplo
O primeiro passo é definir o template base para as páginas da aplicação. Considere o seguinte exemplo de XHTML e sua representação gráfica:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> <meta name="keywords" content="keyword1, keyword2, keyword3" /> <meta name="description" content="Page description" /> <title>Page title</title> <!-- Essential scripts --> <link rel="stylesheet" type="text/css" href="css/style.css"/> <script type="text/javascript" src="js/query.js"></script> <!-- Extra header --> <script type="text/javascript" src="js/util.js"></script> </head> <body> <!-- Main --> <div id="wrapper"> <div id="header"></div> <div id="content"></div> <div id="footer"></div> </div> <!-- Main --> <script type="text/javascript"> // Other scripts (e.g. Google Analytics) </script> </body> </html>
O que costuma acontecer, mesmo em um layout simples como esse, é o alto índice de duplicação de código entre as diferentes páginas, principalmente em função do uso de Copy and Paste Programming. Muitos desenvolvedores por aí escreveriam o seguinte JSP para representar o layout proposto:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> <meta name="keywords" content="keyword1, keyword2, keyword3" /> <meta name="description" content="Page description" /> <title>Page title</title> <!-- Essential scripts --> <jsp:include page="scripts.jsp"/> <!-- Extra header --> <script type="text/javascript" src="js/util.js"></script> </head> <body> <!-- Main --> <div id="wrapper"> <jsp:include file="header.jsp"/> <div id="content">Page content here</div> <jsp:include page="footer.jsp"/> </div> <!-- Main --> <script type="text/javascript"> // Other scripts </script> </body> </html>
Viu o problema? De nada adianta separar os pedaços com
<%@ taglib tagdir="/WEB-INF/tags/layout" prefix="layout" %> <layout:page title="Title" description="Page description" keywords="keyword1, keyword2, keyword3"> <jsp:attribute name="extraHeader"> <script type="text/javascript" src="js/util.js"></script> </jsp:attribute> <jsp:attribute name="extraBottom"> // Other scripts (e.g. Google Analytics) </jsp:attribute> <jsp:body> Page content here </jsp:body> </layout:page>
A boa notícia é que isso é possível, através de tag files.
Reúso de conteúdo com tag files
Existem pelo menos três formas de se criar custom tags em JSP, cada qual com seu propósito: classic tag handlers, simple tag handlers e tag files. Neste post tratarei somente das tag files, mas saber como e quando utilizar simple tag handlers também é extremamente recomendado a todos que usam JSP no dia-a-dia.
A primeira coisa a fazer é criar o diretório WEB-INF/tags/layout. É nesse diretório que as tag files referentes ao template devem ficar. Agora, crie dois arquivos neste diretório, um para o cabeçalho e outro para o rodapé:
header.tag
<%@ tag body-content="empty" description="Header tag file" %> <div id="header"> <!-- Header section here --> </div>
footer.tag
<%@ tag body-content="empty" description="Footer tag file" %> <div id="footer"> <!-- Footer section here --> </div>
Como podemos ver, as tag files são como JSPs, mas com algumas propriedades especiais.
A última tag file, page.tag, define o esqueleto das páginas da aplicação:
<%@ tag description="Page layout" %> <%@ attribute name="title" required="true" description="Page title" %> <%@ attribute name="keywords" required="true" description="Page keywords to improve SEO" %> <%@ attribute name="description" required="true" description="Page description" %> <%@ attribute name="extraHeader" fragment="true" description="Extra code to put before </head>" %> <%@ attribute name="extraBottom" fragment="true" description="Extra code to put before </body>" %> <%@ taglib tagdir="/WEB-INF/tags/layout" prefix="layout" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> <!-- Displaying the attributes using EL --> <meta name="keywords" content="${keywords}" /> <meta name="description" content="${description}" /> <title>${title}</title> <!-- Essential scripts --> <link rel="stylesheet" type="text/css" href="css/style.css"/> <script type="text/javascript" src="js/query.js"></script> <!-- Process the given input fragment --> <jsp:invoke fragment="extraHeader"/> </head> <body> <!-- Main --> <div id="wrapper"> <!-- Renders the page header --> <layout:header/> <!-- Renders the tag body inside a DIV --> <div id="content"><jsp:doBody/></div> <!-- Renders the page footer --> <layout:footer/> </div> <!-- Main --> <!-- Process the given input fragment --> <jsp:invoke fragment="extraBottom"/> </body> </html>
Esta última tag file é mais complexa que as anteriores, pois recebe dados e blocos de código (através de fragments). Além disso, ela aceita um corpo, que é executado com <jsp:doBody/>.
O exemplo usado aqui é razoavelmente simples e, por isso, não precisamos apelar para outros recursos mais avançados como variables e dynamic attributes. De qualquer forma, procure conhecer bem a tecnologia, pois você nunca sabe quando um ou outro recurso lhe pode ser útil.
Exemplo de uso
Novamente, um exemplo de uso das tags que criamos:
<%@ taglib tagdir="/WEB-INF/tags/layout" prefix="layout" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <layout:page title="Index page" description="This is the index page of my amazing app" keywords="amazing, app"> <jsp:attribute name="extraHeader"> <script type="text/javascript" src="js/util.js"></script> <script type="text/javascript" src="js/some_jquery_plugin.js"></script> </jsp:attribute> <jsp:attribute name="extraBottom"> $(function() { // Call some JQuery function here, specific for this page }); </jsp:attribute> <jsp:body> Welcome to my amazing app!<br/> 1+1 = ${1+1} <ul> <c:forEach items="${objects}" var="obj"> <li>${obj.attr}</li> </c:forEach> </ul> </jsp:body> </layout:page>
Embora a aplicação como um todo siga um mesmo padrão visual, cada página pode necessitar de recursos adicionais (JavaScript, CSS, etc). Isso não chega a ser um problema, pois nosso template contempla a inclusão de conteúdo extra, se necessário.
Outra vantagem é que as tag files permitem o uso de custom tags e unified EL, mas proíbem o uso de scriptlets, o que já é meio caminho andado rumo a um JSP mais limpo.


