MVC案例之通过配置切换底层存储源(面向接口)
1.深入理解面向接口编程:
在类中调用接口的方法,而不必关心具体的实现。这将有利于代码的解耦。使程序有更好的可移植性
和可扩展性
动态修改 Customer 的存储方式:通过修改类路径下的 switch.properties 文件的方式来实现
switch.properties
①. CustomerServlet 中不能在通过 private CustomerDAO customerDAO =
new CustomerDAOXMLImpl(); 的方式来写死实现类
②. 需要通过一个类的一个方法来获取具体的实现类的对象
2.实现步骤
①
当前 WEB 应用才启动的时候,InitServlet 被创建,并由 Servlet 容器调用其 init() 方法:
读取类路径下的 switch.properties 文件
获取 switch.properties 的 type 属性值
赋给了 CustomerDAOFactory 的 type 属性值
InitServlet
package com.aff.mvcapp.servlet; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import com.aff.mvcapp.dao.factory.CustomerDAOFactory; @WebServlet("/initServlet") public class InitServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void init() throws ServletException { CustomerDAOFactory.getInstance().setType("jdbc"); InputStream is = getServletContext().getResourceAsStream("/WEB-INF/classes/switch.properties"); Properties properties = new Properties(); try { properties.load(is); String type = properties.getProperty("type"); CustomerDAOFactory.getInstance().setType(type); } catch (IOException e) { e.printStackTrace(); } } }
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>CustomerServlet</servlet-name> <servlet-class>com.aff.mvcapp.servlet.CustomerServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CustomerServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <servlet> <servlet-name>InitServlet</servlet-name> <servlet-class>com.aff.mvcapp.servlet.InitServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> </web-app>
②
创建 CustomerServlet 时,为 customerDAO 属性赋值是通过 CustomerDAOFactory 的
getCustomerDAO() 方法完成的 。
此时的 type 已经在 InitServlet 中被赋值了。
CustomerServlet
package com.aff.mvcapp.servlet; import java.io.IOException; import java.lang.reflect.Method; import java.util.List; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.aff.mvcapp.dao.CriteriaCustomer; import com.aff.mvcapp.dao.CustomerDAO; import com.aff.mvcapp.dao.factory.CustomerDAOFactory; import com.aff.mvcapp.domian.Customer; @WebServlet("/customerServlet") public class CustomerServlet extends HttpServlet { private CustomerDAO customerDAO = CustomerDAOFactory.getInstance().getCustomerDAO(); //private CustomerDAO customerDAO = new CustomerDAOImpl(); private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取ServletPath: /edit.do 或 addCustomer.do String servletPath = request.getServletPath(); // 2.去除 / 和 .do 得到类似于 edit 或 addCustomer 这样的字符串 String methodName = servletPath.substring(1); methodName = methodName.substring(0, methodName.length() - 3); try { // 3.利用反射获取 methodName 对应的方法 Method method = getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); // 4.利用反射调用对应的方法 method.invoke(this, request, response); } catch (Exception e) { // e.printStackTrace(); // 可以有一些响应 response.sendRedirect("error.jsp"); } } private void addCustomer(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1.获取表单参数:name address phone String name = request.getParameter("name"); String address = request.getParameter("address"); String phone = request.getParameter("phone"); // 2.检验 name 是否被占用 // 2.1调用CustomerDAO的getCountWithName(String name) 获取 name 在数据库中是否存在 long count = customerDAO.getCountWithName(name); // 2.2若返回值大于0,则响应 newcustomer.jsp 页面, 通过转发的方式响应newcustomer.jsp if (count > 0) { // 2.2.1要求页面显示一个错误消息: 用户名 name已经被占用,请重新选择, // 在request中放入一个属性message :用户名 name 已经被占用,请重新选择! // 在页面通过request.getAttribute("message")的方式显示 request.setAttribute("message", "用户名 " + name + "已经被占用,请重新选择"); // 2.2.2 newcustomer.jsp 的表单可以回显 // 通过value="<%=request.getParameter("name") == null ? "" : // request.getParameter("name") %>"进行回显 // 2.2.3结束方法:return request.getRequestDispatcher("/newcustomer.jsp").forward(request, response); return; } // 3.若验证通过,把表单参数封装为一个Customer 对象 customer Customer customer = new Customer(name, address, phone); // 4.调用CustomerDAO的 save(Customer customer) 执行保存操作 customerDAO.save(customer); // 5.重定向的 success.jsp 页面:使用重定向可以避免表单的重复提交问题 response.sendRedirect("success.jsp"); } private void edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String forwardPath = "/error.jsp";// 初始值 // 1.获取请求参数 id String idStr = request.getParameter("id"); // 2.调用CustomerDAO 的 customerDAO.get(id) 获取 id 对应的Customer 对象customer try { Customer customer = customerDAO.get(Integer.parseInt(idStr)); if (customer != null) {// 当customer为null是转到 error.jsp forwardPath = "/updatecustomer.jsp"; // 3.将customer放入request 中 request.setAttribute("customer", customer); } } catch (Exception e) { } // 4.响应updatecustomer.jsp 页面: 转发 request.getRequestDispatcher(forwardPath).forward(request, response); } private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1.获取表单参数:id,name,addres, phone, oldName String id = request.getParameter("id"); String name = request.getParameter("name"); String address = request.getParameter("address"); String phone = request.getParameter("phone"); String oldName = request.getParameter("oldName"); // 2.检查name是否被占用 // 2.比较name和oldName 是否相同,若相同说明 name可用 // 2.1若不相同, 则调用CustomerDAO 的 getCountWithName(String name) 获取 name在数据库中是否存在 if (!oldName.equalsIgnoreCase(name)) { long count = customerDAO.getCountWithName(name); if (count > 0) { // 2.2若返回值大于0,则响应 updatecustomer.jsp 页面, // 通过转发的方式响应updatecustomer.jsp // 2.2.1要求页面显示一个错误消息: 用户名 name已经被占用,请重新选择, // 在request中放入一个属性message :用户名 name 已经被占用,请重新选择! // 在页面通过request.getAttribute("message")的方式显示 request.setAttribute("message", "用户名" + name + "已经被占用,请重新选择!"); // 2.2.2 updatecustomer.jsp 的表单可以回显 // address, phone 显示提交表单的新的值,而 name 显示oldName, 而不是新提交的 name // 2.2.3结束方法:return request.getRequestDispatcher("/updatecustomer.jsp").forward(request, response); return; } } //3.若验证通过,则把表单参数封装为一个Customer 对象 customer Customer customer = new Customer(name, address, phone); customer.setId(Integer.parseInt(id));//表单过来的 安全的 //4.调用CustomerDAO 的 update(Customer customer) 执行更新操作 customerDAO.update(customer); //5.重定向到 query.do response.sendRedirect("query.do"); } private void query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); String address = request.getParameter("address"); String phone = request.getParameter("phone"); CriteriaCustomer cc = new CriteriaCustomer(name, address, phone); // 1.调用 CustomerDAO 的 getForListWithCriteriaCustomer() 得到 Customer 的集合 List<Customer> customers = customerDAO.getForListWithCriteriaCustomer(cc); // 2.把 Customer 的集合放入 request 中 request.setAttribute("customers", customers); // 3.转发页面到 index.jsp 中( 不能使用重定向) request.getRequestDispatcher("/index.jsp").forward(request, response); } private void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String idstr = request.getParameter("id"); int id = 0; // try-catch的作用 , 防止恶意的输入, idStr 不能转为int类型,若出异常 id直接为0 try { id = Integer.parseInt(idstr); customerDAO.delete(id); } catch (Exception e) { } response.sendRedirect("query.do"); } }
③
CustomerDAOFactory 的 getCustomerDAO() 方法为 customerDAO 属性赋值
CustomerDAOFactory
package com.aff.mvcapp.dao.factory; import java.util.HashMap; import java.util.Map; import com.aff.mvcapp.dao.CustomerDAO; import com.aff.mvcapp.dao.impl.CustomerDAOImpl; import com.aff.mvcapp.dao.impl.CustomerDAOXMLImpl; public class CustomerDAOFactory { private Map<String, CustomerDAO> daos = new HashMap<>(); private static CustomerDAOFactory instance = new CustomerDAOFactory(); public static CustomerDAOFactory getInstance() { return instance; } public String type = null; public void setType(String type) { this.type = type; } private CustomerDAOFactory() { daos.put("jdbc", new CustomerDAOImpl()); daos.put("xml", new CustomerDAOXMLImpl()); } public CustomerDAO getCustomerDAO() { return daos.get(type); } }
All that work will definitely pay off