Servlet-JSP相关

EL表达式
    EL 全名为Expression Language , EL表达式语言
    目的:
        使用EL 和 JSTL 取代jsp中的脚本 --> jsp页面中没有java代码(底层还需要执行相应的java代码)
    作用:
        获得域中的数据
        执行运算
        获得web开发常用对象
        调用java对象方法
    格式:${el表达式}
        注意:如果数据不存在,将返回空字符串""
    EL内置对象(11个)
        作用域:pageScope 、requestScope、sessionScope、applicationScope
            示例:${requestScope.属性名}
            注意:如果没有显示说明从哪个作用域取,则底层调用pageContext.findAttribute(name)方法.从小到大取
        JSP上下文对象:pageContext
            示例:<%((HttpServletRequest)pageContext.getRequest()).getContextPath()%>
                el:${pageContext.request.contextPath}
        请求参数:
            param.name      --> request.getParameter(name)
            paramValues.name    -->request.getParameterValues(name)
        请求头信息:
            header.name     --> request.getHeader(name)
            headerValues.name   --> request.getHeaders(name)
        获得cookie : cookie
            获得指定cookie名称的值:${cookie.cookie名称.value}
        web项目的初始化参数:
            initParam.name --> servletContext.getInitParameter(name)
    访问集合
        访问List中的元素: list[0]
        访问Map中的元素: map.key 或 map['key']
    运算符
        算术运算符 + - / % (5/2=2.5)
        关系运算符 == != < <= > >=  (可以用对应英文缩写表示 eq ne lt le gt ge)
        逻辑运算符 && || !  (非也可以用not)
        其他
            empty 表示是否为空 (null , "" , size()==0)  ${empty 变量名}
            ${表达式 ? true : false}    三目运算符
    注意
        访问属性eWaiJiFen时,要写 对象.EWaiJiFen



自定义标签库(了解)
    概述
        在jsp中书写自定义标签,在生成java代码时要被指定java代码替换,底层还是java代码
        要关联自定义标签和替换的java代码,需要有配置文件(tld文件)
    步骤
        编写实现类
            传统标签:必须实现Tag接口  --> 继承实现类 TagSupport ,如果需要标签体 继承BodyTagSupport (了解)
            简单标签:必须实现SimpleTag接口 --> 继承实现类 SimpleTagSupport(源码tomcat中有)
        编写配置文件 tld
        使用
            引入标签库 taglib
            使用:<前缀:标签名称>
    tld文件
        标签库描述符(taglib description)
        tld文件基于xml文件 ,扩展名为tld,但内容是xml
        存放位置
            1 WEB-INF目录下,但classes和lib目录除外
            2 WEB-INF/lib目录下的jar文件中的META-INF目录下
        tomcat将自动加载tld文件
    示例(自定义foreach标签,用来遍历集合)
        编写实现类
            package tagImpl;
            import java.io.IOException;
            import java.util.ArrayList;
            import javax.servlet.jsp.JspException;
            import javax.servlet.jsp.PageContext;
            import javax.servlet.jsp.tagext.SimpleTagSupport;
            public class MyTagImpl extends SimpleTagSupport{
                //页面的属性必须对应一个set方法
                ArrayList<String> list;
                String item;
                public void setList(ArrayList<String> list){
                    this.list=list;
                }
                public void setItem(String item){
                    this.item=item;
                }
                public void doTag() throws JspException,IOException{
                    PageContext page=(PageContext)this.getJspContext();
                    for(String str : list){
                        page.setAttribute(item, str);//存入page作用域,方便页面取出
                        this.getJspBody().invoke(page.getOut());//把标签体中的内容输出到浏览器
                    }
                }
            }
        编写tld文件(位置:WEB-INF目录,名称任意)
            <?xml version="1.0" encoding="UTF-8" ?>
            <taglib xmlns="http://java.sun.com/xml/ns/javaee"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
                version="2.1">
                <!-- 版本 -->   
                <tlib-version>1.0</tlib-version>
                <!-- 建议前缀 -->
                <short-name>s</short-name>
                <!-- 引用此文件的标识 -->
                <uri>http://localhost:8080/demo/tagImpl/MyTagImpl</uri>
                
                <!-- 定义标签 -->
                <tag>
                    <!-- 标签名称 -->
                    <name>foreach</name>
                    <!-- 标签的实现类 -->
                    <tag-class>tagImpl.MyTagImpl</tag-class>
                    <!-- 标签体
                        取值:
                            JSP:不会用
                            empty:空,没有标签体
                            scriptless:支持EL表达式,不支持jsp脚本
                            tagdependent:不会用
                    -->
                    <body-content>scriptless</body-content>
                    <!-- 标签的属性 -->
                    <attribute>
                        <!-- 属性名称 -->
                        <name>list</name>
                        <!-- 是否是必须的 -->
                        <required>true</required>
                        <!-- 是否支持运行时表达式(EL) -->
                        <rtexprvalue>true</rtexprvalue>
                    </attribute>
                    <attribute>
                        <!-- 属性名称 -->
                        <name>item</name>
                        <!-- 是否是必须的 -->
                        <required>true</required>
                        <!-- 是否支持运行时表达式(EL) -->
                        <rtexprvalue>true</rtexprvalue>
                    </attribute>
                </tag>
            </taglib>
        使用
            在页面引用标签库 
                <%@ taglib uri="http://localhost:8080/demo/tagImpl/MyTagImpl" prefix="s"%>
            在页面使用标签
                <s:foreach list="${list}" item="i">
                    ${i}<br>
                </s:foreach>
    自定义标签细节(其实是自定义标签的生命周期)
        1 父类SimpleTagSupport会缓存PageContext对象。可以PageContext page=(PageContext)this.getJspContext();获取jsp上下文对象
        2 通过标签的属性attribute进行数据的传递,实现类需要提供相应的属性property的setter方法接受传递的数据
        3 缓存了标签体,类型是JspFragment(代码片段),可以通过this.getJspBody()获得标签体.
            可以执行invoke将标签体的内容输出到指定流中this.getJspBody().invoke(page.getOut());如果invoke参数为null,则默认输出到浏览器
        4 doTag()方法,定义自定义标签功能的方法
            如果在doTag标签体中,throw new SkipPageException(),表示当前标签执行之后,页面之后的内容将不输出。



自定义函数库(了解)
    概述
        在jsp中使用 ${fn:函数名(参数...)} 执行固定的java方法完成操作
        要关联固定的java代码,需要有配置文件(tld文件)
    示例
        编写自定义类,类中编写要调用的方法,必须是静态的,例:
            package function;
            public class MyFunction {
                public static String join(String i,String j){
                    return i + "/" + j;
                }
            }
        编写tld文件(位置:WEB-INF目录,名称任意)
            <?xml version="1.0" encoding="UTF-8" ?>
            <taglib xmlns="http://java.sun.com/xml/ns/j2ee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
              version="2.0">
                
              <tlib-version>1.0</tlib-version>
              <short-name>myfn</short-name>
              <uri>http://localhost:8080/myFunctionTag/myFunction</uri>
              <function>
                <!-- 使用时的名称 -->
                <name>join</name>
                <!-- 对应的类 -->
                <function-class>function.MyFunction</function-class>
                <!-- 函数签名 -->
                <function-signature>java.lang.String join(java.lang.String ,java.lang.String)</function-signature>
              </function>
            </taglib>
        使用
            引入标签库
                <%@ taglib uri="http://localhost:8080/myFunctionTag/myFunction" prefix="myfn" %>
            调用
                ${myfn:join("q","w")}



JSTL
    JavaServer Pages Standard Tag Library, JSP标准标签库。
    第三方实现apache
        导入jar包(/jstl/)
            jstl.jar            --> myeclipse自动添加的jar名称javax.servlet.jsp.jstl.jar
            standard.jar        --> myeclipse自动添加的jar名称jstl-impl.jar
        注意:myeclipse已经提供了相应的jar,并在将web项目发布到tomcat时,自动的将两个jar添加到web/WEB-INF/lib目录中
    JSTL提供5种 标签库
        核心库 : http://java.sun.com/jsp/jstl/core ,默认前缀:c
        函数库 : http://java.sun.com/jsp/jstl/functions ,默认前缀:fn
        国际化标签库,数据库标签库,XML标签库
    核心库
        先引入标签库
            <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
        域操作
            <c:set> 给指定作用域设置属性
                var         属性的名称
                value       属性的值
                scope       作用域(page/request/session/application)
                例:<c:set var="名称" value="值" scope="page"></c:set>
            <c:remove/> 移除属性
                var         要移除的属性的名称
                scope       作用域,默认移除所有域中的指定名称的数据(page/request/session/application)
                例:<c:remove var="username" />
            <c:out> 向页面输出内容(可以进行html或js转义)
                value       要输出的内容,支持EL表达式
                escapeXml   是否进行标签内容转义(如:html代码在页面中原样输出,还是解析为html代码)默认为true
                例:<c:out value="${username}"></c:out>
        逻辑操作
            <c:if> 相当于java中的if
                test        判断条件
                例:<c:if test="${empty username}">如果是真则输出</c:if>
                注意:没有<c:else>
            <c:choose> 相当于java中的switch
                test        条件
                子标签<c:when> 相当于java中的case
                子标签<c:otherwise> 相当于java中的default
                例:
                    <c:choose>
                        <c:when test="${param.num<10}">
                            num小于10
                        </c:when>
                        <c:when test="${param.num<20}">
                            num大于等于10且小于20
                        </c:when>
                        <c:otherwise>
                            num大于等于20
                        </c:otherwise>
                    </c:choose>
        循环操作
            <c:forEach> 循环
                items       需要遍历的数据,支持EL表达式
                var         循环时每一项数据存放的变量名称,可以从域中获取当前项.只能在标签体内使用
                begin       循环初始值
                end         循环结束值
                例:
                    遍历集合或数组
                        <c:forEach items="${list}" var="obj">
                            ${obj} <br/>
                        </c:forEach>
                    遍历Map
                        <c:forEach items="${map}" var="entry">
                            ${entry.key} --> ${entry.value} <br/>
                        </c:forEach>
                    指定范围的循环
                        <c:forEach begin="1" end="9" var="i">
                            ${i} <br/>
                        </c:forEach>
                    注:访问对象的属性,就是调用对象的get方法
        其它常用操作
            <c:url> 将url添加项目名称后添加到域中
                var         添加域中的属性的名称
                value       要添加到域中的url
                子标签<c:param> 给url后添加参数,此标签在<c:url>标签体中使用
                    name        参数名称
                    value       参数值,可以自动进行中文编码(base64)
                例:
                    <c:url var="oneURL" value="/oneServlet">
                        <c:param name="username" value="sss"></c:param>
                    </c:url>
            <c:redirect> 重定向
                url     要重定向的url,特别注意:当以/开头时不需要加项目名称
                例:<c:redirect url="/index.jsp"></c:redirect>
    函数库
        先引入标签库
            <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
        用法:
            ${fn:函数名(参数...)}
        包含了一下处理字符串的常用方法



JSP模式
    模式1 : JSP + JavaBean
        小型项目,开发速度快。不好处扩展差,不易维护
    模式2 : JSP + Servlet + JavaBean (推荐)
        模式2 基于MVC思想



MVC设计模式
    MVC 软件设计模式,思想:一种分离业务逻辑与显示界面的设计方法 (业务逻辑 与 显示页面 相分离)
    模块
        Model模型 , 负责管理程序的业务数据
        View视图 ,负责显示界面
        Controller控制器 ,负责与用户进行交互(接收请求和选择响应视图)



经典三层体系架构
    在程序业务更加复杂的时候,会将Controller控制层继续划分为三层.
        表示层(web层)
        业务逻辑层(service层)
        数据访问层(dao层)



监听器Listener
    servlet 三种技术 : servlet,listener(监听器),filter(过滤器)
    web提供8接口,分别对ServletContext、HttpSession、ServletRequest 对象自身、属性(域)进行监听
        域对象自身(3个)
            ServletContextListener ,对ServletContext对象进行监听 (对象创建,与对象销毁)
            HttpSessionListener ,对HttpSession对象进行监听 (对象创建,与对象销毁)
            ServletRequestListener ,对ServletRequest对象进行监听 (对象创建,与对象销毁)
            以上三个监听器,都提供了事件对象 xxxEvent,获得被监听对象。
            需要在web.xml文件中进行注册。tomcat自动触发响应的方法。
        域内容(对象属性)(3个)
            ServletContextAttributeListener  对ServletContext属性进行监听 (属性添加,移除,替换)
            HttpSessionAttributeListener 对HttpSession属性进行监听  (属性添加,移除,替换)
            ServletRequestAttributeListener 对ServletRequest属性进行监听 (属性添加,移除,替换)
            需要在web.xml文件中进行注册。tomcat自动触发响应的方法。
        session中javabean(2个)
            HttpSessionBindingListener : 对实现此接口的javabean在session域中的状态进行监听(绑定状态)
                valueBound(HttpSessionBindingEvent event) ,当javabean对象添加到session属性触发此方法 (绑定)
                void valueUnbound(HttpSessionBindingEvent event) ,当javabean对象从session属性移除时,触发此方法 (取消绑定)
                应该以被监听的javaBean继承此接口,并且不需要再web.xml中注册
            HttpSessionActivationListener : 对实现此接口的javabean在session域中的状态进行监听(持久化状态)
                void sessionDidActivate(HttpSessionEvent se) ,在服务器启动时,如果已经钝化了javabean,则将javabean重新加载进内存。(活化 :硬盘 --> 内存 )
                void sessionWillPassivate(HttpSessionEvent se) ,如果javabean在session中,当服务器正常关闭时,进行持久化此对象。(钝化 : 内存 --> 硬盘)
                钝化时数据保持位置:%tomcat%\work\Catalina\localhost\day14_listener\SESSIONS.ser
                应该以被监听的javaBean继承此接口,并且不需要在web.xml中注册
                javabean必须实现java.io.Serializable接口
        注册监听器(web.xml)
            <listener>
                <listener-class>类的完全限定名</listener-class>
            </listener>
        HttpSessionAttributeListener 和 HttpSessionBindingListener 区别:
            HttpSessionAttributeListener ,对所有javabean生效
            HttpSessionBindingListener ,对实现了此接口javabean生效
            对同一件事的监听角度不同,一个从session角度进行监听,一个从javaBean角度进行监听



国际化(i18n)
    程序的界面可以支持多国语言。 internationalization ,i18n
    国际化资源文件
        文件名称:baseName_语言_国家|地区.properties
            baseName,基名,自定义
            后面的语言和国家不能乱写,可参见IE浏览器的internet选项-->常规-->语言 中的选项
            例如:
                message_zh_CN.properties ,简体中文
                message_zh_TW.properties ,繁体中文
                message_en.properties ,英语
                message.properties ,默认配置文件
        默认位置:src,ResourcBundle.getBundle("baseName");
        Locale 对象表示了特定的地理、政治和文化地区
            Locale(String language, String country) 指定 语言,国家|地区
                例如 new Locale("zh", "CN")
    使用时
        文本
            ResourceBundle.getBundle("message" ,request.getLocale()); 对资源文件进行解析,可以指定 Locale对象设置不同的语言
            getString("usernameMsg") 获得指定名称的对象
        数字
            数字 NumberFormat.getNumberInstance(request.getLocale()).format(double)
            百分比 NumberFormat.getPercentInstance(request.getLocale()).format(double)
            货币 NumberFormat.getCurrencyInstance(request.getLocale()).format(double)
        日期时间
            DateFormat.getDateTimeInstance ,处理日期时间
            DateFormat.getDateInstance  处理日期
            DateFormat.getTimeInstance  处理时间
            样式:FULL 、LONG 、MEDIUM (默认) 、 SHORT
        消息格式化
            MessageFormat.format ,将字符串中{n}进行内容自动的填充。
    示例
        在src下添加message_zh_CN.properties
            usernameMsg=用户名
            passwordMsg=密码
        在src下添加message_en.properties
            usernameMsg=username
            passwordMsg=password
        jsp页面
            <%
                ResourceBundle bundle = ResourcBundle.getBundle("message",request.getLocale());
            %>
            <%=bundle.getString("usernameMsg") %> : <input name="username"/> <br/>
            <%=bundle.getString("passwordMsg") %> : <input type="password" name="password"/>
        在IE浏览器的internet选项-->常规-->语言 中选择不同的语言,则网页显示效果不同



过滤器Filter
    对tomcat的请求会优先由过滤器进行处理
    编写流程
        1 编写实现类 ,实现Filter接口 (javax.servlet.Fitler)
        2 在web.xml配置
            格式
                <filter>
                    <filter-name>随便起个名,注意不要重复,一般为类名</filter-name>
                    <filter-class>实现类的全限定名称</filter-class>
                </filter>
                <filter-mapping>
                    <filter-name>上面的filter-name</filter-name>
                    <url-pattern>/*</url-pattern>
                    <!-- <dispatcher>request</dispatcher> -->
                </filter-mapping>
            注意
                <url-pattern>和servlet的<url-pattern>用法相同,可参加<Servlet访问路径配置>
                url-pattern和dispatcher标签可以有多个



Filter接口(javax.servlet)
    所有过滤器需要实现的父接口
    方法
        void init(FilterConfig filterConfig) 初始化
        void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 过滤到请求时执行的方法
        void destroy() 销毁
    注意 chain.doFilter(request,response);为放行



过滤器的生命周期
    init ,初始化
        时间:tomcat启动时。只要注册了过滤器,则tomcat就进行实例化
        参数:
            FilterConfig,当前过滤器配置对象
                getFilterName() ,获得过滤器配置的名称<filter-name>的值
                getInitParameter(java.lang.String name) 获得指定名称,过滤器初始化参数的值
                getInitParameterNames() 获得所有的初始化参数的名称
                getServletContext() 获得servlet的上下文对象
    doFilter ,过滤执行
        时间:接收到请求时。
        参数:
            FilterChain对象 过滤器链对象
                服务器tomcat将所有符合要求的过滤器生成了一个链。
                过滤器使用 FilterChain 调用链中的下一个过滤器,如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。 
                方法
                     void doFilter(ServletRequest request, ServletResponse response) 放行
                注意:
                    过滤器链中的所有过滤器的顺序,取决于web.xml配置文件<filter-mapping>配置顺序
                    tomcat6.0 web.xml文件中<filter> 必须在<filter-mapping>之前
    destroy,销毁
        时间:服务器正常关闭时



配置Filter的初始化参数
    在web.xml中<filter>中的<filter-class>后添加<init-param>
    例:
        <filter>
            <filter-name>
            <filter-class>
            <init-param>
                <param-name>参数名</param-name>
                <param-value>参数值</param-value>
            </init-param>
        </filter>



dispatcher标签(过滤器的过滤类型)
    位置:<filter-mapping>中的末尾
    作用:设置当前过滤器的过滤类型
    取值:
        REQUEST,request : 对请求进行过滤(默认)
        FORWARD,forward : 在进行请求转发时进行过滤
        INCLUDE,include : 在进行包含操作时进行过滤
        ERROR,error : 当页面出现错误,执行友好页面(web.xml <error-page>)时进行过滤
        ASYNC,async :异步,Servlet3.0提供,用servlet3.0新技术异步时进行过滤。
    注意
        取值为FORWARD,INCLUDE,ERROR时,url-pattern应该写要转发到的页面或要包含的页面的url
        当取值为ERROR时,可以不能看到自定义的错误页面,而是浏览器的错误页面.可在过滤器放行前将状态码设置为200(先强转).例:response.setStatus(200);



过滤器案例
    处理中文乱码(post)
        添加过滤器,<url-pattern>为/*
        在过滤器的doFilter方法放行前设置
            request.setCharacterEncoding("utf-8");
            response.setContentType("text/html;charset=UTF-8");
    设置图片缓存时间
        http响应提供三个头,进行页面缓存设置
            pragma : no-cache
            cache-control : no-cache
            expires : 0 
        添加过滤器,<url-pattern>为*.jpg
        在过滤器的doFilter方法放行前设置 
            response.setDateHeader("expires",System.currentTimeMillis() + 60 * 60 * 1000 * 24 * 30L); //一个月,加L是为了防止溢出



处理中文乱码工具类
    思想:
        对request的getParameter和getParameterMap方法进行增强,在其中处理get中文乱码.然后将增强后的自定义request对象传递给servlet
        tomcat提供了HttpServletRequestWrapper作为HttpServletRequest接口的装饰类,无任何增强的方法,供编程人员使用
    代码:   
        package filter;
        import java.io.IOException;
        import java.io.UnsupportedEncodingException;
        import java.util.HashMap;
        import java.util.Map;
        import javax.servlet.Filter;
        import javax.servlet.FilterChain;
        import javax.servlet.FilterConfig;
        import javax.servlet.ServletException;
        import javax.servlet.ServletRequest;
        import javax.servlet.ServletResponse;
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletRequestWrapper;
        import javax.servlet.http.HttpServletResponse;
        public class EncodingFilter implements Filter {
            public void init(FilterConfig filterConfig) throws ServletException {

            }
            public void doFilter(ServletRequest req, ServletResponse res,
                    FilterChain chain) throws IOException, ServletException {
                //强转
                HttpServletRequest request = (HttpServletRequest) req;
                HttpServletResponse response = (HttpServletResponse) res;
                //处理post请求乱码
                request.setCharacterEncoding("utf-8");
                //实例化自己的类(装饰者模式,增强了request的getParameter和getParameterMap方法,处理get请求乱码)
                MyRequest myRequest = new MyRequest(request);
                //放行
                chain.doFilter(myRequest, response);//传递自己的类对象
            }
            public void destroy() {
                
            }
        }
        /**
         * 自定义的用于增强tomcat的request对象的类
         * @author Administrator
         * HttpServletRequestWrapper为HttpServletRequest接口的装饰类,不过无任何增强的方法,供编程人员使用
         */
        class MyRequest extends HttpServletRequestWrapper{
            public MyRequest(HttpServletRequest request) {
                super(request);//父类缓存起来了
            }
            //增强方法
            public String getParameter(String name){
                //获取原来的值
                String value = super.getParameter(name);
                if(value == null){
                    return null;
                }
                //对get请求乱码问题处理
                if(super.getMethod().equalsIgnoreCase("get")){
                    //get请求
                    try {
                        value = new String(value.getBytes("ISO8859-1"),super.getRequest().getCharacterEncoding());
                    } catch (UnsupportedEncodingException e) {
                        throw new RuntimeException(e);
                    }
                }
                return value;
            }
            public Map<String, String[]> getParameterMap(){
                Map<String,String[]> map = super.getParameterMap();
                try {
                    if(super.getMethod().equalsIgnoreCase("get")){
                        Map<String,String[]> map2 = new HashMap<String,String[]>();
                        for(String key : map.keySet()){
                            String[] value = map.get(key);
                            for(int i=0;i<value.length;i++){
                                value[i] = new String(value[i].getBytes("ISO8859-1"),super.getCharacterEncoding());
                            }
                            map2.put(key, value);
                        }
                        map = map2;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return map;
            }
        }
    web.xml配置
        <filter>
            <filter-name>EncodingFilter</filter-name>
            <filter-class>filter.EncodingFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>EncodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>



页面静态化
    原因
        每次请求会查询数据库,动态生成静态网页,速度很慢.对于一些固定的或访问量很大,很少更新的页面,可以使用静态页面
    思想
        首次访问Servlet将数据保存为静态html页面(而不是发送到浏览器),然后将html页面发送到浏览器.以后的访问不用查询数据库,直接返回第一次访问生成的html页面
        Servlet(JSP)的响应内容默认通过request.getWriter方法输出到浏览器,增强response的getWriter方法修改目标输出位置
        tomcat提供了HttpServletResponseWrapper作为Response接口的装饰类,无任何增强的方法,供编程人员使用
    代码
        package web.filter;
        import java.io.File;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.io.OutputStreamWriter;
        import java.io.PrintWriter;
        import javax.servlet.Filter;
        import javax.servlet.FilterChain;
        import javax.servlet.FilterConfig;
        import javax.servlet.ServletException;
        import javax.servlet.ServletRequest;
        import javax.servlet.ServletResponse;
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;
        import javax.servlet.http.HttpServletResponseWrapper;
        public class PageStaticFilter implements Filter {
            public void init(FilterConfig filterConfig) throws ServletException {

            }
            public void doFilter(ServletRequest req, ServletResponse res,
                    FilterChain chain) throws IOException, ServletException {
                //强转
                HttpServletRequest request = (HttpServletRequest) req;
                HttpServletResponse response = (HttpServletResponse) res;
                //获取文件名(这里根据自己的需求写)
                String id = request.getParameter("id");
                String fileName = "/html/" + id + ".html";
                //获取全路径
                String filePath = request.getSession().getServletContext().getRealPath(fileName);
                //判断文件是否存在
                File htmlFile = new File(filePath);
                if(!htmlFile.exists()){
                    //不存在
                    MyResponse myResponse = new MyResponse(response,htmlFile);
                    //放行
                    chain.doFilter(request, myResponse);//通过增强方法,会将数据保存为静态页面
                }
                //将文件发送给浏览器
                request.getRequestDispatcher(fileName).forward(request, response);
            }
            public void destroy() {

            }
        }
        /**
         * 自定义的response类,修改getWriter方法的功能
         * @author Administrator
         * HttpServletResponseWrapper为tomcat的Response对象的装饰类,没有修改任何方法,供开发人员使用
         */
        class MyResponse extends HttpServletResponseWrapper{
            private File htmlFile;//缓存的文件
            PrintWriter writer;//缓存的打印流
            public MyResponse(HttpServletResponse response,File htmlFile) {
                super(response);
                this.htmlFile = htmlFile;
            }
            //增强方法(将目的地改为指定文件)
            public PrintWriter getWriter() throws IOException {
                //此方法会调用两次,第一次tomcat获得流用来输出,第二次tomcat获得流是为了关闭流
                if(writer == null){
                    //第一次调用,实例化打印流
                    String encoding = super.getResponse().getCharacterEncoding();//编码
                    OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(htmlFile),encoding);//转换流
                    writer = new PrintWriter(out);//此类编码为操作系统默认编码,要使用转换流指定编码,否则将出现乱码
                }
                return writer;
            }
        }
    web.xml配置
        <filter>
            <filter-name>PageStaticFilter</filter-name>
            <filter-class>web.filter.PageStaticFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>PageStaticFilter</filter-name>
            <url-pattern>路径自己写,某个servlet</url-pattern>
        </filter-mapping>



javamail
    应用场景
        注册账号时,进行账号激活
        邮件订阅
        生日祝福
    名词
        邮件服务器:安装有接收邮件、发送邮件功能的软件的计算机。
        电子邮箱email:邮件服务器上申请的账号。
    协议
        SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,发送邮件服务器称为SMTP服务器,默认端口号:25
        POP3(Post Office Protocol 3)即邮局协议,接收邮件服务器称为POP3服务器。默认端口:110
        IMAP(Internet Mail Access Protocol)交互邮件访问协议,接收邮件服务器。默认端口:143
            与POP3协议的主要区别是用户可以不用把所有的邮件全部下载,可以通过客户端直接对服务器上的邮件进行操作
    发送邮件过程
        xxx@163.com 发送到 xxx@126.com
            首先发送到163邮件服务器的SMTP发送端,然后发送到126邮件服务器的SMTP发送端,并保存.
            当用户读取时,通过126邮件服务器的POP3接收端读取邮件,返回给用户
    手动发送邮件示例
        命令行
            telnet smtp.163.com 25      --连接163邮件服务器的smtp发送端(一般都为smtp.服务器.com)
            ehlo itcast                 --随便说点啥
            auth login                  --需要登陆
            aXRjYXN0X2x0                --用户名(base64编码)
            MXFhejJ3c3g=                --密码(base64编码)
            mail from:<itcast_lt@163.com>   --发件人
            rcpt to:<itcast_lt@126.com>     --收件人
            data                            --需要书写邮件内容
            from:<lkplovelyf@163.com>   --显示的发件人
            to:<itcast_lt@126.com>      --显示的收件人
            subject:good luck!!!        --主题
            [空行]                      --与正文的分割
            baidu welcome you!!!        --正文
            .                           --结束邮件主体
            quit                        --断开连接
    base64编码与解码
        解码:
            sun.misc.BASE64Decoder类
            sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
            byte[] bytes = decoder.decodeBuffer("要解码的字符串");
            System.out.println(bytes);
        编码:
            sun.misc.BASE64Encoder类
            sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
            String str = encoder.encode("要编码的字符串".getBytes());
            System.out.println(str);
        注意:
            myeclipse默认没有上面两个类,换个jre库就好了(没搞明白为什么)
            项目右键-->Properties-->Java Build Path-->Libraries-->删除原来的-->Add Library-->JRE System Library
    javamail
        使用java程序发送电子邮件。
        sun公司提供
            核心jar:mail.jar           --> javamail1_4_5.zip
            依赖jar:activation.jar     --> jaf-1_1_1.zip (编写复杂邮件时使用,现阶段不用)
    javamail 编写流程
        1 获得会话 Session  -->相当于连接
        2 编写消息 Message  -->相当于邮件
        3 发送邮件 Transport
    工具类
        import java.util.Properties;
        import javax.mail.Authenticator;
        import javax.mail.Message;
        import javax.mail.Message.RecipientType;
        import javax.mail.PasswordAuthentication;
        import javax.mail.Session;
        import javax.mail.Transport;
        import javax.mail.internet.InternetAddress;
        import javax.mail.internet.MimeMessage;
        /**
         * 邮件工具包
         * @author Administrator
         *
         */
        public class MailUtils {
            private static String serverHost = "localhost";//邮箱服务器地址(例:smtp.163.com)
            private static String mail_username = "123";//用户
            private static String mail_password = "123";//密码
            private static String sendHost = "123@ss.cn";//发送人地址
            /**
             * 向用户发送邮件
             * @param sendToUrl 目标邮箱
             * @param subject 主题
             * @param content 正文
             */
            public static void sendMail(String sendToHost,String subject,String content){
                try {
                    //获得会话
                    // * 准备参数
                    // ** 1 准备配置信息
                    Properties props = new Properties();
                    props.setProperty("mail.host", serverHost);//邮箱服务器地址
                    props.setProperty("mail.smtp.auth", "true");//进行登录验证
                    // ** 2 准备用户和密码
                    Authenticator authenticator = new Authenticator() {
                        protected PasswordAuthentication getPasswordAuthentication() {
                            return new PasswordAuthentication(mail_username, mail_password);
                        }
                    };
                    Session session = Session.getDefaultInstance(props, authenticator);
                    //session.setDebug(true);// 设置debug模式 在控制台看到交互信息
                    //编写消息
                    Message message = new MimeMessage(session);
                    // * 1 设置发件人
                    message.setFrom(new InternetAddress(sendHost));
                    // * 2 设置收件人
                    message.setRecipient(RecipientType.TO, new InternetAddress(sendToHost));
                    //TO:接收者;CC:抄送;BCC:暗送;(接收者知道所有被抄送的人,但不知道被暗送的人)
                    // * 3 设置主题
                    message.setSubject(subject);
                    // * 4 设置正文
                    message.setContent(content, "text/html;charset=utf-8");
                    //发送
                    Transport.send(message);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
            public static void main(String[] args) {
                sendMail("456@ss.cn","主题1","正文");
            }
        }



MIME类型
    概述
        多用途互联网邮件扩展(MIME,Multipurpose Internet Mail Extensions)
        规定了各种各样数据类型的符号化方法,使邮件能够支持非ASCII字符,二进制格式附件等多种格式的消息
        HTTP协议中也使用了MIME的框架,标准被扩展为互联网媒体类型
    格式:content-type : type/subtype;parameter
        例如:text/html;charset=UTF-8
        type的所有取值
            Text:用于标准化地表示的文本信息,文本消息可以是多种字符集和或者多种格式的;
            Multipart:用于连接消息体的多个部分构成一个消息,这些部分可以是不同类型的数据;
            Application:用于传输应用程序数据或者二进制数据;
            Message:用于包装一个E-mail消息;
            Image:用于传输静态图片数据;
            Audio:用于传输音频或者音声数据;
            Video:用于传输动态影像数据,可以是与音频编辑在一起的视频数据格式。
        subtype常见取值
            text/plain(纯文本)
            text/html(HTML文档)
            image/gif(gif图像)
            image/jpeg(jpeg图像)
            image/png(png图像)
            application/msword(Microsoft Word文件)
            application/x-www-from-urlencoded(HTTP的POST方式提交表单)
            multipart/form-data (用于文件上传)
        %tomcat%/conf/web.xml 提供常见的MIME类与扩展名的映射关系。



案例:使用本地服务器发送邮件
    软件介绍
        易邮邮件服务器:邮件服务器软件,可以注册账号发送和接收邮件
        Foxmail:使用客户端管理邮箱
    安装易邮邮件服务器
    设置易邮邮件服务器
        工具-->服务器设置-->作为局域网的邮件服务器,单域名:ss.cn
        新建账号
            账号123     账号123     联系邮件地址123@ss.cn
            账号456     账号456     联系邮件地址456@ss.cn
    安装Foxmail
    设置Foxmail
        第一次启动时创建用户:手动设置
            邮件账号    123@ss.cn
            密码        123
            POP服务器   localhost
            SMTP服务器  localhost
        启动后创建用户:在已有用户上右击-->设置-->新建-->手动设置
            邮件账号    456@ss.cn
            密码        456
            POP服务器   localhost
            SMTP服务器  localhost
    可以试试用Foxmail用两个账号互相发送邮件
    使用java代码发送邮件
        设置工具类MailUtils中的成员变量的值
            private static String serverHost = "localhost";//邮箱服务器地址(例:smtp.163.com)
            private static String mail_username = "123";//用户
            private static String mail_password = "123";//密码
            private static String sendHost = "123@ss.cn";//发送人地址
        发送邮件
            MailUtils.sendMail("456@ss.cn","主题","正文");



文件上传
    概述
        将用户本地的资源,通过浏览器发送给服务器,服务器保存到服务器端
        浏览器:选择文件,提交
        服务器:获得数据,保存
    步骤
        1 通知浏览器,可以选择文件、可以将文件进行提交
            提交: <form> <input type="submit"/>
            选择:<input type="file" name=""/>
            发送上传内容:给form表单添加属性enctype="multipart/form-data" ,设置method="post". 注意:默认情况只发送文件的名称
                enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码
                    取值:
                        application/x-www-form-urlencoded 在发送前编码所有字符(默认) 
                        multipart/form-data 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
            注意:
                上传文件时,request.getParameter方法不能获取方法体传递的参数
                    可以在表单的action后面使用?追加参数
                    可以在当前servlet上添加servlet3.0的注解@MultipartConfig
        2 服务器接收数据,并保存文件。
            第一种:使用servlet的api解析上传内容,需要手动对内容进行分析。比较繁琐
                request提供的getInputStream() 用来获得请求体的所有内容。
            第二种:使用第三方工具 -- apache commons组件--fileupload
                jar
                    核心jar:commons-fileupload-1.2.2.jar   --> 来自commons-fileupload-1.2.2-bin.zip
                    依赖jar:commons-io-2.3.jar             --> 来自commons-io-2.3-bin.zip
                核心类:ServletFileUpload
                api见工具类
            第三种:Servlet3.0支持文件上传,参见下面的<servlet3.0>
            第四种: struts2的文件上传,参见<struts>中的<文件上传>
    工具类FileUploadApacheUtils
        package util;
        import java.io.File;
        import java.io.FileOutputStream;
        import java.io.InputStream;
        import java.io.OutputStream;
        import java.util.HashMap;
        import java.util.List;
        import java.util.Map;
        import java.util.UUID;
        import javax.servlet.http.HttpServletRequest;
        import org.apache.commons.fileupload.FileItem;
        import org.apache.commons.fileupload.disk.DiskFileItemFactory;
        import org.apache.commons.fileupload.servlet.ServletFileUpload;
        public class FileUploadApacheUtils {
            /**
             * 处理文件上传
             * @param request 请求对象
             * @return Map:普通表单字段的name-value,上传文件的name-保存名称
             */
            @SuppressWarnings("unchecked")
            public static Map<String,String> fileUpload(HttpServletRequest request){
                //判断是否表单是否是文件上传 ,即判断enctype是否为"multipart/form-data"
                boolean isMultipart = ServletFileUpload.isMultipartContent(request);
                if(!isMultipart){
                    throw new RuntimeException("表单不是文件上传类型");
                }
                int sizeThreshold = 1024 * 1024;//设置临界点大小,当文件大小大于临界点大小时,将会产生临时文件
                String tempPath = "/WEB-INF/tempFile";//临时文件存放位置
                String savePath = "/WEB-INF/uploadFile";//保存路径
                Map<String,String> map = new HashMap<String,String>();
                try {
                    //准备工厂,进行配置(出于安全性考虑,当文件过大时每接收一点先缓存到临时文件中)
                    DiskFileItemFactory factory = new DiskFileItemFactory();
                    factory.setSizeThreshold(sizeThreshold);//设置临界点大小,当文件大小大于临界点大小时,将会产生临时文件
                    String tempPath2 = request.getSession().getServletContext().getRealPath(tempPath);
                    factory.setRepository(new File(tempPath2));//设置临时文件存放位置
                    //准备解析文件
                    ServletFileUpload upload = new ServletFileUpload(factory);//核心类
                    upload.setHeaderEncoding("utf-8");//设置文件名的编码,否则文件名含有中文时会出现乱码.如果不设置则使用request的编码
                    List<FileItem> allFileItem = upload.parseRequest(request);//解析request,获得文件项
                    for(FileItem fileItem : allFileItem){
                        if(fileItem.isFormField()){
                            //表单字段
                            String name = fileItem.getFieldName();
                            String value = fileItem.getString("utf-8");//参数为编码方式,不设置可能出现中文乱码
                            map.put(name, value);
                        }else{
                            //上传的文件
                            String fieldName = fileItem.getFieldName();//name属性
                            String fileName = fileItem.getName();//文件名称
                            fileName = getFileName(fileName);//处理文件名称
                            InputStream in = fileItem.getInputStream();//获得文件流
                            //将文件流保存到文件
                            String path = request.getSession().getServletContext().getRealPath(savePath);//保存路径
                            File file = new File(path,fileName);
                            OutputStream out = new FileOutputStream(file);
                            byte[] bytes = new byte[128 * 1024];
                            int len = 0;
                            while( (len = in.read(bytes)) > 0 ){
                                out.write(bytes,0,len);
                            }
                            in.close();
                            out.close();
                            fileItem.delete();//删除临时文件(如果有的话)
                            map.put(fieldName, fileName);
                        }
                    }
                    return map;
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
            /**
             * 返回经过处理的文件名称,保证不重复
             * @param fileName 要处理的文件名称
             * @return 经过处理的文件名称
             */
            private static String getFileName(String fileName){
                fileName = fileName.substring(fileName.lastIndexOf("/") + 1);//处理兼容,因为ie8在xp下返回的是全路径
                fileName = UUID.randomUUID().toString().replace("-", "") + "_" + fileName;//避免重复
                return fileName;
            }
        }
    上传文件示例
        导入jar包和工具类
            核心jar:commons-fileupload-1.2.2.jar   --> 来自commons-fileupload-1.2.2-bin.zip
            依赖jar:commons-io-2.3.jar             --> 来自commons-io-2.3-bin.zip
            工具类:自定义工具类FileUploadApacheUtils
        在WEB-INF下创建tempFile和uploadFile文件夹
        index.jsp
            <form action="${pageContext.request.contextPath }/userServlet" method="post" enctype="multipart/form-data">
                <input name="username" value="jack"><br>
                <input type="file" name="uploadFile"><br>
                <input type="submit" value="提交">
            </form>
        userServlet
            public void doPost(HttpServletRequest request, HttpServletResponse response)
                    throws ServletException, IOException {
                Map<String,String> map = FileUploadApacheUtils.fileUpload(request);
                System.out.println(map);
            }
    上传文件请求
        请求头
            Content-Type: multipart/form-data; boundary=---------------------------7de32138c0540
        请求体
            -----------------------------7de32138c0540
            Content-Disposition: form-data; name="username"
            jack
            -----------------------------7de32138c0540
            Content-Disposition: form-data; name="password"
            123
            -----------------------------7de32138c0540
            Content-Disposition: form-data; name="uploadFile1"; filename="123.txt"
            Content-Type: text/plain
            上传文件内容...
            -----------------------------7de32138c0540
            Content-Disposition: form-data; name="uploadFile2"; filename="456.txt"
            Content-Type: text/plain
            上传文件内容...
        注意:上面上传了两个普通表单字段和两个文件.(请求体中的空行我去掉了)
        请求头生成一串随机字符串,然后用此字符串将请求体的多个键-值分隔



文件下载
    概述
        服务器提供下载资源,当浏览器发送请求时,通知浏览器保存到本地
        服务器:读取资源,发送到浏览器,通知浏览器下载
        浏览器:下载
    浏览器下载文件时,如果浏览器可以解析将直接显示,如果不能解析则提供下载。
    通知浏览器总是进行下载:response.setHeader("content-disposition", "attachment;filename=文件名称");
    文件中文显示乱码处理:value = new String(value.getBytes("GBK") , "ISO-8859-1");
    示例
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String fileName = "图片.jpg";
            //处理fileName中文乱码问题(响应头不支持中文)
            //http协议进行iso8859-1编码,客户端进行gbk(操作系统默认编码)解码,所以要反着来一下
            fileName = new String(fileName.getBytes("gbk"),"iso8859-1");
            //通知浏览器内容类型为附件
            response.setHeader("content-disposition", "attachment;filename=" + fileName);
            //获得文件
            InputStream in = request.getSession().getServletContext().getResourceAsStream("/图片.jpg");
            //发送
            OutputStream out = response.getOutputStream();
            byte[] bytes = new byte[128 * 1024];
            int len = 0;
            while( (len = in.read(bytes))>0 ){
                out.write(bytes,0,len);
            }
            in.close();
        }
    注意:处理文件名称中文乱码的代码通常放在过滤器中,处理图片名称含有中文的问题(向tomcat请求不到名称含有中文的图片)



servlet3.0
    servlet3.0提供注解
        @WebServlet, 用于取代servlet的配置web.xml <servlet>...配置内容
            属性
                name用来配置servlet名称,不写的话默认为当前类名
                urlPatterns 和 value 作用一样,都用来配置servlet访问路径
                例: @WebServlet("/one") 或 @WebServlet({"/one","/two"}) 
        @WebFilter ,用于取代过滤器的配置 web.xml <filter>...配置内容,属性同上
        @WebListener ,用于取代监听器的配置 <listener><listener-class>
        @MultipartConfig,通知tomcat当前servlet将开启文件上传的处理,无属性
    servlet3.0对文件上传进行支持
        通过request.getPart(名称)获得上传内容,Part对象等价 commons FileItem
        默认情况servlet不支持上传,需要在当前servlet上添加注解@MultipartConfig,无属性
        注意:当使用@MultipartConfig注解时,request.getParameter()可以获得普通表单字段的值
        工具类FileUploadServlet3Utils
            public class FileUploadServlet3Utils {
                /**
                 * 保存上传文件
                 * @param request 请求对象
                 * @param name input的name值
                 * @return 保存路径
                 */
                public static String saveUploadFile(HttpServletRequest request,String name){
                    String fileDir = "/WEB-INF/uploadFile";//保存文件夹
                    try {
                        // 获得上传文件内容对象
                        Part part = request.getPart(name);
                        if(part==null){
                            throw new RuntimeException("没有找到上传的文件");
                        }
                        // * 获得上传文件名称
                        String fileName = getFileName(part);
                        String savePath = fileDir+"/"+fileName;
                        // * 获得文件内容 --流
                        InputStream is = part.getInputStream();
                        // * 保存
                        String realSavePath = request.getServletContext().getRealPath(savePath);
                        FileOutputStream out = new FileOutputStream(new File(realSavePath));
                        byte[] buf = new byte[128*1024];
                        int len = -1;
                        while( (len = is.read(buf)) != -1 ){
                            out.write(buf, 0, len);
                        }
                        out.close();
                        is.close();
                        return savePath;
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException("保存上传文件失败");
                    }
                }
                /**
                 * 获得文件名称,保证不重复
                 * @param part
                 * @return
                 */
                private static String getFileName(Part part){
                    // * 获取文件名称
                    String headerValue = part.getHeader("Content-Disposition");
                    int beginIndex = headerValue.indexOf("filename=\"") + "filename=\"".length();
                    int endIndex = headerValue.length() -1;
                    String fileName = headerValue.substring(beginIndex, endIndex);
                    // * 处理文件名称 --浏览器兼容
                    fileName = fileName.substring(fileName.lastIndexOf("/") + 1);//处理兼容,因为ie8在xp下返回的是全路径
                    // * 处理文件名称 --不重复
                    fileName = UUID.randomUUID().toString().replace("-", "") + "_" + fileName;//避免重复
                    return fileName;
                }
            }
        使用示例
            在WEB-INF下创建uploadFile文件夹
            index.jsp
                <form action="${pageContext.request.contextPath }/userServlet" method="post" enctype="multipart/form-data">
                    <input name="username" value="jack"><br>
                    <input type="file" name="uploadFile"><br>
                    <input type="submit" value="提交">
                </form>
            userServlet
                @MultipartConfig
                public class UserServlet extends HttpServlet {
                    public void doGet(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
                        String savePath = FileUploadServlet3Utils.saveUploadFile(request, "uploadFile");
                        System.out.println(savePath);
                    }
                    public void doPost(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
                        doGet(request, response);
                    }
                }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值