前言:
在前面的学习过程中,写的代码都是一个操作operator对应着一个Servlet,这只是一个小的管理系统,所以看着代码不是很多,但是在一个大的系统中,显然有点不合适,所以进行了优化,一个JavaBean对应一个servlet,使用的是switch-case解决,但是随着项目的业务规模扩大,那么会有很多的Servlet,也就意味着会有很多的switch-case,这是一种代码冗余
因此,我们在servlet中使用了反射技术,我们规定operate的值和方法名一致,那么接收到operate的值是什么就表明我们需要调用对应的方法进行响应,如果找不到对应的方法,则抛异常
但是:每一个servlet中都有类似的反射技术的代码,因此继续抽取,设计了中央控制器类:DispatcherServlet。
说明:
在这里仍然使用的是水果管理系统,在这个项目的基础上做出相关优化,学习中央处理器的实现。
中央处理器的使用是的FruitServlet进行了转变,变成一个普通的Java类,Fruitcontroller,DiapatcherServlet继承ViewBaseServlet,进行接收和响应用户,将核心逻辑放在FruitController(调用底层具体的实现更新的方法)中。
①首先进行编码的设置,这是最基本的,浏览器传来一个中文字符时可以识别,实现写入数据库时不会出现乱码的现象。
//设置编码
request.setCharacterEncoding("UTF-8");
②调用getServletPath()方法从url中获取到servletPath:
例如:
url------------------------------------------------>servletPath
http://localhost:8080/pro15/hello.do----->/hello.do
String servletPath = request.getServletPath();
③实现对servletPath的截取,获取到“hello”;【通过调用String类的subString()方法实现截取】
操作过程:
servletPath = servletPath.substring(1);
int lastDotIndex = servletPath.lastIndexOf(".do") ;
servletPath = servletPath.substring(0,lastDotIndex);
文件的作用:实现servletPath中设置的名字(假设为fruit),可能映射到FruitController来处理(具体的JavaBean)【也即是说在中央处理器中建立一种映射关键字,从前端获取到要处理的类,可以找到对应的Controller】
<beans>
<bean id="fruit" class="com.lei.fruit.dao.impl.controllers.FruitController"/>
beans>
以键值对的形式进行存储,方便以后在使用的使用通过get(servletPath)来获取key对应的value,通过DOM技术我们去解析XML文件,在中央控制器中形成一个beanMap容器,用来存放所有的Controller组件
实现存储的过程:
①创建一个输入流,将硬盘上的文件读入到内存,方便创建Document时使用
②创建DocumentBuilderFactory
③创建DocumentBuilder对象
④创建Document对象通过创建的输入流解析xml配置文件
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
//1.创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//2.创建DocumentBuilder对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder() ;
//3.创建Document对象
Document document = documentBuilder.parse(inputStream);
⑤获取所有的bean节点存储到集合
NodeList beanNodeList = document.getElementsByTagName("bean");
⑥通过一个循环逐个获取单个节点beanNode,对单个节点进行判断,是否是元素节点,是的话进行强制转化,得到beanElement,调用getAttribute()获取bean节点中对应的字符串
for(int i = 0 ; i<beanNodeList.getLength() ; i++){
Node beanNode = beanNodeList.item(i);
if(beanNode.getNodeType() == Node.ELEMENT_NODE){
Element beanElement = (Element)beanNode ;
String beanId = beanElement.getAttribute("id");
String className = beanElement.getAttribute("class");
Class controllerBeanClass = Class.forName(className);
Object beanObj = controllerBeanClass.newInstance() ;
beanMap.put(beanId , beanObj) ;
}
}
由于map集合中的value需要存储的是一个Class实例,所以在这是通过反射创建Class对象
Class controllerBeanClass = Class.forName(className);
Object beanObj = controllerBeanClass.newInstance() ;
由于第三步已经将对应的关系存储到Map集合中,所以需要在这里通过获得的servletPath,利用get()方法,找到对应的Controller。
获取方法如下:
Object controllerBeanObj = beanMap.get(servletPath);
说明:
调用Controller组件的三个步骤,实际上实现的就是对原有的FruitServlet的优化,把里面的参数,重定向,processTemplate等进行的优化,减少了代码的冗余度。
在实现获取参数之前,需要有一些事情要做:
(1)获得需要执行操作的名称
(2)通过反射获得所有的方法集合
String operate = request.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index" ;
}
由于在前面通过Map集合的get()方法获得的Class实例是controllerBeanObj,所以在获取所有的方法使用的是:
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
通过判断operator和对应的操作方法名称来确定需要执行的方法,然后切入正题,获取参数
if(operate.equals(method.getName()))
实现具体过程的说明:
首先通过调用getParameters()得到当前调用的方法,需要使用的参数集合,然后创建一个Object类型的集合来承载对应的数据,(例如现在执行的是添加操作,所以会有对应的fid、fname、price…等等),利用parameters[i],获取到当前的一个参数,然后通过parameter.getName() 获得这个参数的名称,之后再分类:
①如果参数名是request,response,session,将对应的参数直接赋值给parameterValues
②普通的需要获取参数的值,通过request.getParameter(parameterName);获得前端传递过来的数值
又因为在②中传递的类型有不同需要再次做出出理
//1-1.获取当前方法的参数,返回参数数组
Parameter[] parameters = method.getParameters();
//1-2.parameterValues 用来承载参数的值
Object[] parameterValues = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
String parameterName = parameter.getName() ;
//如果参数名是request,response,session 那么就不是通过请求中获取参数的方式了
if("request".equals(parameterName)){
parameterValues[i] = request ;
}else if("response".equals(parameterName)){
parameterValues[i] = response ;
}else if("session".equals(parameterName)) {
parameterValues[i] = request.getSession();
}else{
//从请求中获取参数值
String parameterValue = request.getParameter(parameterName);
String typeName = parameter.getType().getName();
Object parameterObj = parameterValue ;
if(parameterObj!=null) {
if ("java.lang.Integer".equals(typeName)) {
parameterObj = Integer.parseInt(parameterValue);
}
}
parameterValues[i] = parameterObj ;
}
}
执行方法比较简单,就是利用反射获得实例来调用invoke()方法,实现实际的操作方法调用,例如:update…
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj,parameterValues);
主要实现的就是对重定向sendRedirect以及processTemplate的处理,将Controller的操作进行优化,统一处理
这里比较简单,主要实现的就是使用String类的方法startsWith()方法判断需要执行的不同操作。
String methodReturnStr = (String)returnObj ;
if(methodReturnStr.startsWith("redirect:")){ //比如: redirect:fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
response.sendRedirect(redirectStr);
}else{
super.processTemplate(methodReturnStr,request,response); // 比如: "edit"
}
最大的不同就是对FruitController实现类的优化,从对比中,我们可以看出使用中央处理器来做出优化很有必要
FruitController
public class FruitController extends ViewBaseServlet {
//之前FruitServlet是一个Sevlet组件,那么其中的init方法一定会被调用
//之前的init方法内部会出现一句话:super.init();
private ServletContext servletContext ;
public void setServletContext(ServletContext servletContext) throws ServletException {
this.servletContext = servletContext;
super.init(servletContext);
}
private FruitDAO fruitDAO = new FruitDAOImpl();
private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.设置编码
request.setCharacterEncoding("utf-8");
//2.获取参数
String fidStr = request.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
int price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
//3.执行更新
fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));
//4.资源跳转
//super.processTemplate("index",request,response);
//request.getRequestDispatcher("index.html").forward(request,response);
//此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
response.sendRedirect("fruit.do");
}
private void edit(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
String fidStr = request.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
Fruit fruit = fruitDAO.getFruitByFid(fid);
request.setAttribute("fruit",fruit);
super.processTemplate("edit",request,response);
}
}
private void del(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
String fidStr = request.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
fruitDAO.deleteFruit(fid);
//super.processTemplate("index",request,response);
response.sendRedirect("fruit.do");
}
}
private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String fname = request.getParameter("fname");
Integer price = Integer.parseInt(request.getParameter("price")) ;
Integer fcount = Integer.parseInt(request.getParameter("fcount"));
String remark = request.getParameter("remark");
Fruit fruit = new Fruit(0,fname , price , fcount , remark ) ;
fruitDAO.addFruit(fruit);
response.sendRedirect("fruit.do");
}
private void index(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
HttpSession session = request.getSession() ;
// 设置当前页,默认值1
Integer pageNo = 1 ;
String oper = request.getParameter("oper");
//如果oper!=null 说明 通过表单的查询按钮点击过来的
//如果oper是空的,说明 不是通过表单的查询按钮点击过来的
String keyword = null ;
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
//说明是点击表单查询发送过来的请求
//此时,pageNo应该还原为1 , keyword应该从请求参数中获取
pageNo = 1 ;
keyword = request.getParameter("keyword");
//如果keyword为null,需要设置为空字符串"",否则查询时会拼接成 %null% , 我们期望的是 %%
if(StringUtil.isEmpty(keyword)){
keyword = "" ;
}
//将keyword保存(覆盖)到session中
session.setAttribute("keyword",keyword);
}else{
//说明此处不是点击表单查询发送过来的请求(比如点击下面的上一页下一页或者直接在地址栏输入网址)
//此时keyword应该从session作用域获取
String pageNoStr = request.getParameter("pageNo");
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr); //如果从请求中读取到pageNo,则类型转换。否则,pageNo默认就是1
}
//如果不是点击的查询按钮,那么查询是基于session中保存的现有keyword进行查询
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String)keywordObj ;
}else{
keyword = "" ;
}
}
// 重新更新当前页的值
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword , pageNo);
session.setAttribute("fruitList",fruitList);
//总记录条数
int fruitCount = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (fruitCount+5-1)/5 ;
/*
总记录条数 总页数
1 1
5 1
6 2
10 2
11 3
fruitCount (fruitCount+5-1)/5
*/
session.setAttribute("pageCount",pageCount);
//此处的视图名称是 index
//那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
//逻辑视图名称 : index
//物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是: / index .html
super.processTemplate("index",request,response);
}
}
FruitController
public class FruitController {
private FruitDAO fruitDAO = new FruitDAOImpl();
private String update(Integer fid,String fname,Integer price,Integer fcount,String remark, HttpServletRequest request) {
fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));
return "redirect:fruit.do";
}
private String edit(Integer fid ,HttpServletRequest request ) {
if(fid != null){
Fruit fruit = fruitDAO.getFruitByFid(fid);
request.setAttribute("fruit",fruit);
return "edit";
}
return "error";
}
private String del(Integer fid, HttpServletRequest request ){
if(fid!=null){
fruitDAO.deleteFruit(fid);
return "redirect:fruit.do";
}
return "error";
}
private String add(String fname,Integer price,Integer fcount,String remark, HttpServletRequest request) {
Fruit fruit = new Fruit(0,fname , price , fcount , remark ) ;
fruitDAO.addFruit(fruit);
return "redirect:fruit.do";
}
private String index(String oper, String keyword,Integer pageNo,HttpServletRequest request ) {
if (pageNo==null){
pageNo=1;
}
HttpSession session = request.getSession() ;
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
pageNo = 1 ;
if(StringUtil.isEmpty(keyword)){
keyword = "" ;
}
session.setAttribute("keyword",keyword);
}else{
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String)keywordObj ;
}else{
keyword = "" ;
}
}
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword , pageNo);
session.setAttribute("fruitList",fruitList);
int fruitCount = fruitDAO.getFruitCount(keyword);
int pageCount = (fruitCount+5-1)/5 ;
session.setAttribute("pageCount",pageCount);
return "index";
}
}