前言
熟悉JAVA语法很久后,迟迟才开始学习JSP。而学习JSP时,却只学了基本的用法就去学Struts和Hibernate,以致对JSP掌握得很不够。后来发现所学习的Struts框架实际上是“包装”了的JSP。所以,我在学习框架的时候也回头看看JSP。
以后应该不会再去专门学习JSP了。现在把一些JSP的相关知识总结下,记录下来,以防来日忘了。
说明:以下所描述的环境是
jdk1.5
、
tomcat5.5
、
jsp2.0
、
servlet2.4
、
JSTL1.1.2
一、基本配置
基本的重要的配置在web.xml 文件中。
1
、
Jsp
属性组
<
jsp-property-group
>
< url-pattern > /pages/* </ url-pattern >
< el-ignore > true </ el-ignore >
< page-encoding > UTF-8 </ page-encoding >
< include-prelude > /include/header.jspf </ include-prelude >
< include-coda > /include/copyright.jspf </ include-coda >
</ jsp-property-group >
< url-pattern > /pages/* </ url-pattern >
< el-ignore > true </ el-ignore >
< page-encoding > UTF-8 </ page-encoding >
< include-prelude > /include/header.jspf </ include-prelude >
< include-coda > /include/copyright.jspf </ include-coda >
</ jsp-property-group >
这个设置可以指定页面编码,页头页脚等等。
设置
<page-encoding>UTF-8</page-encoding> 的好处是不用在每个页面像这样指定编码:
<%@page contentType="html/text;charset=UTF-8" %>
而设置
<include-prelude>/include/header.jspf</include-prelude> 使得每个页面都在头部包含
header.jspf文件(通常把对标签的包含放在这里)。
2
、数据库资源的引用
<
resource-ref
>
< description > CourseDesign JDNI datasource </ description >
< res-ref-name > jdbc/test </ res-ref-name >
< res-type > javax.sql.DataSource </ res-type >
< res-auth > Container </ res-auth >
</ resource-ref >
< description > CourseDesign JDNI datasource </ description >
< res-ref-name > jdbc/test </ res-ref-name >
< res-type > javax.sql.DataSource </ res-type >
< res-auth > Container </ res-auth >
</ resource-ref >
前提是要在TOMCAT的中配置
<
Context
path
="/Course"
docBase
="Course"
debug
="0"
crosscontext
="true"
reloadable
="true"
>
< Resource name ="jdbc/test" auth ="Container" type ="javax.sql.DataSource"
maxActive ="100" maxIdle ="30" maxWait ="10000"
username ="root" password ="123456"
driverClassName ="com.mysql.jdbc.Driver"
url ="jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=UTF-8" />
</ Context >
< Resource name ="jdbc/test" auth ="Container" type ="javax.sql.DataSource"
maxActive ="100" maxIdle ="30" maxWait ="10000"
username ="root" password ="123456"
driverClassName ="com.mysql.jdbc.Driver"
url ="jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=UTF-8" />
</ Context >
在程序中可以这样获取连接
public
static
Connection getConnection()
... {
Connection conn=null;
try
...{
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/test");
conn = ds.getConnection();
}catch(Exception e)...{ }
return conn;
}
... {
Connection conn=null;
try
...{
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/test");
conn = ds.getConnection();
}catch(Exception e)...{ }
return conn;
}
3、过滤器
一般来说,字符编码的处理,我们会写一个过滤器。这个过滤器的JAVA类在TOMCAT的例子中有提供,可以按需来更改再拿来用。只要在配置文件中设置:
<
filter-name
>
setCharacterEncoding
</
filter-name
>
< filter-class > powerwind.filter.SetCharacterEncodingFilter </ filter-class >
< init-param >
< param-name > encoding </ param-name >
< param-value > UTF-8 </ param-value >
</ init-param >
</ filter >
< filter-mapping >
< filter-name > setCharacterEncoding </ filter-name >
< url-pattern > /pages/* </ url-pattern >
</ filter-mapping >
< filter-class > powerwind.filter.SetCharacterEncodingFilter </ filter-class >
< init-param >
< param-name > encoding </ param-name >
< param-value > UTF-8 </ param-value >
</ init-param >
</ filter >
< filter-mapping >
< filter-name > setCharacterEncoding </ filter-name >
< url-pattern > /pages/* </ url-pattern >
</ filter-mapping >
4、标签的URI
JSTL是个东西,里面提供了很好用的标签(Tag),但也不一定满足我们的要求,就自己写标签了。把 *.tld 文件直接放到WEB-INF下,在自己定义的tld文件中加上<uri>元素,如:
<uri>http://powerwind/course</uri> 。
5、日志
只用过log4j这个日志包。首先是配置文件 log4j.properties (比较完整的配置,应根据情况选择):
log4j.rootLogger
=
DEBUG
,
INFO
,
A1
,
A2
,
A3
log4j.appender.A1 = org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout = org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern = %4p [ %t ] (%F:%L) - %m%n
log4j.appender.A2 = org.apache.log4j.RollingFileAppender
log4j.appender.A2.File = ../../log/test.log
log4j.appender.A2.MaxFileSize = 1KB
log4j.appender.A2.MaxBackupIndex = 3
log4j.appender.A2.layout = org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern = %d{yyyy-MM-dd hh:mm:ss}:%p %t %c - %m%n
log4j.appender.A3 = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.A3.URL = jdbc:mysql://localhost: 3306 /log4jTest
log4j.appender.A3.driver = com.mysql.jdbc.Driver
log4j.appender.A3.user = root
log4j.appender.A3.password = 123456
log4j.appender.A3.layout = org.apache.log4j.PatternLayout
log4j.appender.A3.layout.ConversionPattern = INSERT INTO log4j (createDate , thread , level , class , message) values('%d' , '%t' , '%-5p' , '%c' , '%m')
log4j.appender.A1 = org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout = org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern = %4p [ %t ] (%F:%L) - %m%n
log4j.appender.A2 = org.apache.log4j.RollingFileAppender
log4j.appender.A2.File = ../../log/test.log
log4j.appender.A2.MaxFileSize = 1KB
log4j.appender.A2.MaxBackupIndex = 3
log4j.appender.A2.layout = org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern = %d{yyyy-MM-dd hh:mm:ss}:%p %t %c - %m%n
log4j.appender.A3 = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.A3.URL = jdbc:mysql://localhost: 3306 /log4jTest
log4j.appender.A3.driver = com.mysql.jdbc.Driver
log4j.appender.A3.user = root
log4j.appender.A3.password = 123456
log4j.appender.A3.layout = org.apache.log4j.PatternLayout
log4j.appender.A3.layout.ConversionPattern = INSERT INTO log4j (createDate , thread , level , class , message) values('%d' , '%t' , '%-5p' , '%c' , '%m')
接着写个Servlet来加载log4j:
package
powerwind.servlet;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import javax.servlet. * ;
import javax.servlet.http. * ;
public class Log4jInit extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
super .init(config);
String prefix = getServletContext().getRealPath( " / " );
String file = getInitParameter( " log4j " );
System.out.println( " init log4j... " );
if (file != null ){
PropertyConfigurator.configure(prefix + file);
} else
{
PropertyConfigurator.configure(prefix + " log4j.properties " );
}
}
}
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import javax.servlet. * ;
import javax.servlet.http. * ;
public class Log4jInit extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
super .init(config);
String prefix = getServletContext().getRealPath( " / " );
String file = getInitParameter( " log4j " );
System.out.println( " init log4j... " );
if (file != null ){
PropertyConfigurator.configure(prefix + file);
} else
{
PropertyConfigurator.configure(prefix + " log4j.properties " );
}
}
}
然后同时要在web.xml下配置:
<
servlet
>
< servlet-name > log4jInit </ servlet-name >
< servlet-class > powerwind.servlet.Log4jInit </ servlet-class >
< init-param >
< param-name > log4j </ param-name >
< param-value > WEB-INF/classes/log4j.properties </ param-value >
</ init-param >
< load-on-startup > 1 </ load-on-startup >
</ servlet >
< servlet-name > log4jInit </ servlet-name >
< servlet-class > powerwind.servlet.Log4jInit </ servlet-class >
< init-param >
< param-name > log4j </ param-name >
< param-value > WEB-INF/classes/log4j.properties </ param-value >
</ init-param >
< load-on-startup > 1 </ load-on-startup >
</ servlet >
6、国际化
#
test_zh_CN.properties
#login page
login.title = 登录页面
#login page
login.title = 登录页面
小型的应用中,我们并不常需要国际化。但是,如果网站要中文版和英文版的话,这个就不错啦。使用时很简单,把资源
test_zh_CN.properties文件放到classes目录下,然后用JSTL的fmt标签调用。
<
fmt:setLocale
value
="zh_CN"
scope
=”session”
/>
< fmt:setBundle basename ="test" scope =”session” var =”hehe” />
< fmt:message key ="login.title" bundle =”${hehe}” scope =”session” />
< fmt:setBundle basename ="test" scope =”session” var =”hehe” />
< fmt:message key ="login.title" bundle =”${hehe}” scope =”session” />
其中var和scope属性不是必需的。三者结合,就可以实现国际化了。
二、极限与安全
资源放在WEB-INF下是安全的,因为这个目录对于客户端是不存在的。权限控制并不是仅仅这样就可以了。如果只是简单地判断用户是否登录,可用一个过滤器检查Session对象即可。若需要级别控制的话,就在Session中保存级别信息,然后加以判断。
一般把权限的控制做成一个标签(tag)。如:
public
int
doEndTag()
throws
JspException {
HttpSession session = pageContext.getSession();
if ((session != null ) && (session.getAttribute( " user " ) != null )) {
String t = ((UserBean) session.getAttribute( " user " )).getType();
if (t == null || role == null ) {
invalid();
return (SKIP_PAGE);
}
String[] roles = role.split(delimiter);
for ( int i = 0 ; i < roles.length; i ++ ) {
if (roles[i].equalsIgnoreCase(role))
return (EVAL_PAGE);
}
} else {
invalid();
return (SKIP_PAGE);
}
return (EVAL_PAGE);
}
HttpSession session = pageContext.getSession();
if ((session != null ) && (session.getAttribute( " user " ) != null )) {
String t = ((UserBean) session.getAttribute( " user " )).getType();
if (t == null || role == null ) {
invalid();
return (SKIP_PAGE);
}
String[] roles = role.split(delimiter);
for ( int i = 0 ; i < roles.length; i ++ ) {
if (roles[i].equalsIgnoreCase(role))
return (EVAL_PAGE);
}
} else {
invalid();
return (SKIP_PAGE);
}
return (EVAL_PAGE);
}
三、上传与下载
上传的话,一般使用已有的组件,如commons-fileupload 或者欧莱礼的cos (可能会遇到中文编码的问题)。而下载,比较简单,就自己写了个Servlet。
public
void
handleRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
String name = request.getParameter( " name " );
String type = request.getParameter( " type " );
String dir = request.getParameter( " dir " );
if (name == null || name.length() < 2 || dir == null || dir.length() < 1 || type == null || type.length() < 1 ) {
throw new ServletException( " Sorry,error occured " );
}
char ch = dir.charAt(dir.length() - 1 );
if (ch != ' / ' || ch != ' / ' )
dir = dir + " / " ;
ServletOutputStream os = null ;
BufferedInputStream bis = null ;
try {
File file = new File(dir + name);
if ( ! file.exists() || file.length() >= Integer.MAX_VALUE) {
logger.error( " Invalid file or file to large,file: " + name);
throw new ServletException(
" Invalid file or file to large,file: " + name);
}
response.setContentType( " application/ " + type);
response.addHeader( " Content-Disposition " , " attachment; filename= " + name);
response.setContentLength(( int ) file.length());
os = response.getOutputStream();
bis = new BufferedInputStream( new FileInputStream(file));
int size = - 1 ;
while ((size = bis.read()) != - 1 )
os.write(size);
} catch (IOException ioe) {
throw new ServletException(ioe.getMessage());
} finally {
if (os != null )
os.close();
if (bis != null )
bis.close();
}
}
HttpServletResponse response) throws IOException, ServletException {
String name = request.getParameter( " name " );
String type = request.getParameter( " type " );
String dir = request.getParameter( " dir " );
if (name == null || name.length() < 2 || dir == null || dir.length() < 1 || type == null || type.length() < 1 ) {
throw new ServletException( " Sorry,error occured " );
}
char ch = dir.charAt(dir.length() - 1 );
if (ch != ' / ' || ch != ' / ' )
dir = dir + " / " ;
ServletOutputStream os = null ;
BufferedInputStream bis = null ;
try {
File file = new File(dir + name);
if ( ! file.exists() || file.length() >= Integer.MAX_VALUE) {
logger.error( " Invalid file or file to large,file: " + name);
throw new ServletException(
" Invalid file or file to large,file: " + name);
}
response.setContentType( " application/ " + type);
response.addHeader( " Content-Disposition " , " attachment; filename= " + name);
response.setContentLength(( int ) file.length());
os = response.getOutputStream();
bis = new BufferedInputStream( new FileInputStream(file));
int size = - 1 ;
while ((size = bis.read()) != - 1 )
os.write(size);
} catch (IOException ioe) {
throw new ServletException(ioe.getMessage());
} finally {
if (os != null )
os.close();
if (bis != null )
bis.close();
}
}
以上只是个示例程序,灵活与方便的做法应该是在Servlet初始化参数(<init-param>)设置下载文件所在目录,当然也可以在页面中设置参数。甚至可以做成一个下载标签,方便使用。