Filter+Servlet+反射 采用Filter+Servlet+反射的设计模式, 把原本后台需要20多个Servlet的经典Servlet设计方式,精简到了7个。 把原本前台需要20多个Servlet的经典Servlet设计方式,精简到了2个。 web.xml的配置文件也大大减少,降低了开发和维护的工作量,减少了出错的几率。

 

通过观察

http://127.0.0.1:8080/tmall/admin_category_list

可以发现,分类管理需要:增加,删除,编辑,修改,查询5个服务端功能。 
那么按照传统的在web.xml中配置Servlet的方式,一个路径对应一个Servlet的思路,就需要设计5个Servlet类,并且在web.xml中配置5个路径

 
AddCategoryServlet
DeleteCategoryServlet
EditCategoryServlet
UpdateCategoryServlet
ListCategoryServlet
 
但是观察已经实现了分类管理的可运行项目下载包里的代码,却发现Servlet只有一个即CategoryServlet,web.xml里,也只有一个CategoryServlet的映射,并没有5个映射。

如图是对于最后完工了的项目的servlet包里类的截图,可以发现,每种实体类,对应了一个Servlet,而不是对应了5个,这样首先从Servlet数量上来讲,就大大的减少了
 

而后台需要做分类,产品,属性,产品图片,用户,订单这么6种管理,那么就一共需要30个Servlet,以及在web.xml中对应的配置,那么配置文件就会变得臃肿,并且容易出错

 

那么是如何做到一个CategoryServlet类,就能完成本来需要5个Servlet类才能完成的功能的呢?

让我们借助流程图来分析,为什么访问admin_category_list的时候,CategoryServlet的list()方法会被调用

1.假设访问路径是 http://127.0.0.1:8080/tmall/admin_category_list
2. 过滤器BackServletFilter进行拦截,判断访问的地址是否以/admin_开头
3. 如果是,那么做如下操作
3.1 取出两个下划线之间的值 category
3.2 取出最后一个下划线之后的值 list
3.3 然后根据这个值,服务端跳转到categoryServlet,并且把list这个值传递过去
4. categoryServlet 继承了BaseBackServlet,其service方法会被调用。 在service中,借助反射技术,根据传递过来的值 list,调用对应categoryServlet 中的方法list()
5. 这样就实现了当访问的路径是 admin_category_list的时候,就会调用categoryServlet.list()方法这样一个效果

换句话说:
如果访问的路径是admin_category_add,就会调用categoryServlet.add()方法
如果访问的路径是admin_category_delete,就会调用categoryServlet.delete()方法
如果访问的路径是admin_category_edit,就会调用categoryServlet.edit()方法
如果访问的路径是admin_category_update,就会调用categoryServlet.update()方法
如此这般,一个categoryServlet类,就完成了本来需要5个Servlet类才能完成的功能。

那么BackServletFilter类到底是如何工作的呢? 接下来我们此类进行代码讲解。

1. 首先在web.xml配置文件中,让所有的请求都会经过BackServletFilter

 
<url-pattern>/*</url-pattern>
 



2. 还是假设访问的路径是:

 
http://127.0.0.1:8080/tmall/admin_category_list
 


3. 在BackServletFilter 中通过request.getRequestURI()取出访问的uri: /tmall/admin_category_list
4. 然后截掉/tmall,得到路径/admin_category_list
5. 判断其是否以/admin开头
6. 如果是,那么就取出两个_之间的字符串,category,并且拼接成/categoryServlet,通过服务端跳转到/categoryServlet
7. 在跳转之前,还取出了list字符串,然后通过request.setAttribute的方式,借助服务端跳转,传递到categoryServlet里去

注: 需要导入右上角下载的jar包
注: 有的同学使用myeclipse,会提示request.getServletContext().getContextPath(); 这一行编译错误。这是因为myeclipse默认会导入一个j2ee的系列jar包,里面有可能是旧版本的servlet jar包,需要去掉才能正常编译通过。

 

package tmall.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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.lang.StringUtils;
 
public class BackServletFilter implements Filter {
 
    public void destroy() {
         
    }
 
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
         
        String contextPath=request.getServletContext().getContextPath();
        String uri = request.getRequestURI();
        uri =StringUtils.remove(uri, contextPath);
        if(uri.startsWith("/admin_")){     
            String servletPath = StringUtils.substringBetween(uri,"_", "_") + "Servlet";
            String method = StringUtils.substringAfterLast(uri,"_" );
            request.setAttribute("method", method);
            req.getRequestDispatcher("/" + servletPath).forward(request, response);
            return;
        }
         
        chain.doFilter(request, response);
    }
 
    public void init(FilterConfig arg0) throws ServletException {
     
    }
}

接着流程就到了categoryServlet这里。

根据web.xml中的配置

<servlet-name>CategoryServlet</servlet-name>
<url-pattern>/categoryServlet</url-pattern>

服务端跳转/categoryServlet就到了CategoryServlet这个类里

1. 首先CategoryServlet继承了BaseBackServlet,而BaseBackServlet又继承了HttpServlet
2. 服务端跳转过来之后,会访问CategoryServlet的doGet()或者doPost()方法
3. 在访问doGet()或者doPost()之前,会访问service()方法
4. BaseBackServlet中重写了service() 方法,所以流程就进入到了service()中
5. 在service()方法中有三块内容
5.1 第一块是获取分页信息
5.2 第二块是根据反射访问对应的方法
5.3 第三块是根据对应方法的返回值,进行服务端跳转、客户端跳转、或者直接输出字符串。
6. 第一块和第三块放在后面讲解,这里着重讲解第二块是根据反射访问对应的方法
6.1 取到从BackServletFilter中request.setAttribute()传递过来的值 list
6.2 根据这个值list,借助反射机制调用CategoryServlet类中的list()方法
这样就达到了CategoryServlet.list()方法被调用的效果

package tmall.servlet;
 
import java.awt.image.BufferedImage;   
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import tmall.bean.Category;
import tmall.util.ImageUtil;
import tmall.util.Page;
 
public class CategoryServlet extends BaseBackServlet {
     
    public String add(HttpServletRequest request, HttpServletResponse response, Page page) {
        Map<String,String> params = new HashMap<>();
        InputStream is = super.parseUpload(request, params);
         
        String name= params.get("name");
        Category c = new Category();
        c.setName(name);
        categoryDAO.add(c);
         
        File  imageFolder= new File(request.getSession().getServletContext().getRealPath("img/category"));
        File file = new File(imageFolder,c.getId()+".jpg");
         
        try {
            if(null!=is && 0!=is.available()){
                try(FileOutputStream fos = new FileOutputStream(file)){
                    byte b[] = new byte[1024 * 1024];
                    int length = 0;
                    while (-1 != (length = is.read(b))) {
                        fos.write(b, 0, length);
                    }
                    fos.flush();
                    //通过如下代码,把文件保存为jpg格式
                    BufferedImage img = ImageUtil.change2jpg(file);
                    ImageIO.write(img, "jpg", file);       
                }
                catch(Exception e){
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  
        return "@admin_category_list";
    }
 
    public String delete(HttpServletRequest request, HttpServletResponse response, Page page) {
        int id = Integer.parseInt(request.getParameter("id"));
        categoryDAO.delete(id);
        return "@admin_category_list";
    }
 
    public String edit(HttpServletRequest request, HttpServletResponse response, Page page) {
        int id = Integer.parseInt(request.getParameter("id"));
        Category c = categoryDAO.get(id);
        request.setAttribute("c", c);
        return "admin/editCategory.jsp";       
    }
 
    public String update(HttpServletRequest request, HttpServletResponse response, Page page) {
        Map<String,String> params = new HashMap<>();
        InputStream is = super.parseUpload(request, params);
         
        System.out.println(params);
        String name= params.get("name");
        int id = Integer.parseInt(params.get("id"));
 
        Category c = new Category();
        c.setId(id);
        c.setName(name);
        categoryDAO.update(c);
         
        File  imageFolder= new File(request.getSession().getServletContext().getRealPath("img/category"));
        File file = new File(imageFolder,c.getId()+".jpg");
        file.getParentFile().mkdirs();
         
        try {
            if(null!=is && 0!=is.available()){
                try(FileOutputStream fos = new FileOutputStream(file)){
                    byte b[] = new byte[1024 * 1024];
                    int length = 0;
                    while (-1 != (length = is.read(b))) {
                        fos.write(b, 0, length);
                    }
                    fos.flush();
                    //通过如下代码,把文件保存为jpg格式
                    BufferedImage img = ImageUtil.change2jpg(file);
                    ImageIO.write(img, "jpg", file);       
                }
                catch(Exception e){
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "@admin_category_list";
 
    }
 
    public String list(HttpServletRequest request, HttpServletResponse response, Page page) {
        List<Category> cs = categoryDAO.list(page.getStart(),page.getCount());
        int total = categoryDAO.getTotal();
        page.setTotal(total);
         
        request.setAttribute("thecs", cs);
        request.setAttribute("page", page);
         
        return "admin/listCategory.jsp";
    }
}
package tmall.servlet;
 
import java.io.InputStream;
 
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
 
import tmall.dao.CategoryDAO;
import tmall.dao.OrderDAO;
import tmall.dao.OrderItemDAO;
import tmall.dao.ProductDAO;
import tmall.dao.ProductImageDAO;
import tmall.dao.PropertyDAO;
import tmall.dao.PropertyValueDAO;
import tmall.dao.ReviewDAO;
import tmall.dao.UserDAO;
import tmall.util.Page;
 
public abstract class BaseBackServlet extends HttpServlet {
 
    public abstract String add(HttpServletRequest request, HttpServletResponse response, Page page) ;
    public abstract String delete(HttpServletRequest request, HttpServletResponse response, Page page) ;
    public abstract String edit(HttpServletRequest request, HttpServletResponse response, Page page) ;
    public abstract String update(HttpServletRequest request, HttpServletResponse response, Page page) ;
    public abstract String list(HttpServletRequest request, HttpServletResponse response, Page page) ;
     
    protected CategoryDAO categoryDAO = new CategoryDAO();
    protected OrderDAO orderDAO = new OrderDAO();
    protected OrderItemDAO orderItemDAO = new OrderItemDAO();
    protected ProductDAO productDAO = new ProductDAO();
    protected ProductImageDAO productImageDAO = new ProductImageDAO();
    protected PropertyDAO propertyDAO = new PropertyDAO();
    protected PropertyValueDAO propertyValueDAO = new PropertyValueDAO();
    protected ReviewDAO reviewDAO = new ReviewDAO();
    protected UserDAO userDAO = new UserDAO();
 
    public void service(HttpServletRequest request, HttpServletResponse response) {
        try {
             
            /*获取分页信息*/
            int start= 0;
            int count = 5;
            try {
                start = Integer.parseInt(request.getParameter("page.start"));
            } catch (Exception e) {
                 
            }
            try {
                count = Integer.parseInt(request.getParameter("page.count"));
            } catch (Exception e) {
            }
            Page page = new Page(start,count);
             
            /*借助反射,调用对应的方法*/
            String method = (String) request.getAttribute("method");
            Method m = this.getClass().getMethod(method, javax.servlet.http.HttpServletRequest.class,
                    javax.servlet.http.HttpServletResponse.class,Page.class);
            String redirect = m.invoke(this,request, response,page).toString();
             
            /*根据方法的返回值,进行相应的客户端跳转,服务端跳转,或者仅仅是输出字符串*/
             
            if(redirect.startsWith("@"))
                response.sendRedirect(redirect.substring(1));
            else if(redirect.startsWith("%"))
                response.getWriter().print(redirect.substring(1));
            else
                request.getRequestDispatcher(redirect).forward(request, response);
             
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public InputStream parseUpload(HttpServletRequest request, Map<String, String> params) {
            InputStream is =null;
            try {
                DiskFileItemFactory factory = new DiskFileItemFactory();
                ServletFileUpload upload = new ServletFileUpload(factory);
                // 设置上传文件的大小限制为10M
                factory.setSizeThreshold(1024 * 10240);
                  
                List items = upload.parseRequest(request);
                Iterator iter = items.iterator();
                while (iter.hasNext()) {
                    FileItem item = (FileItem) iter.next();
                    if (!item.isFormField()) {
                        // item.getInputStream() 获取上传文件的输入流
                        is = item.getInputStream();
                    } else {
                        String paramName = item.getFieldName();
                        String paramValue = item.getString();
                        paramValue = new String(paramValue.getBytes("ISO-8859-1"), "UTF-8");
                        params.put(paramName, paramValue);
                    }
                }
                  
            } catch (Exception e) {
                e.printStackTrace();
            }
            return is;
        }
}

通过这样一种模式,一个Servlet类就能满足CRUD一系列业务要求
如果访问的路径是admin_category_list,就会调用categoryServlet.list()方法
如果访问的路径是admin_category_add,就会调用categoryServlet.add()方法
如果访问的路径是admin_category_delete,就会调用categoryServlet.delete()方法
如果访问的路径是admin_category_edit,就会调用categoryServlet.edit()方法
如果访问的路径是admin_category_update,就会调用categoryServlet.update()方法

 

posted @ 2019-04-02 18:07  Ideaway  阅读(157)  评论(0)    收藏  举报