大家都知道Struts2是一个MVC框架,我们javaWeb中学习的servlet也是一个mvc框架,那我要问大家一个问题,为什么要用struts2开发呢?怎么不使用serlet来开发呢?
那我们先来讨论一下servlet的缺点,它的缺点有哪些呢?
1. Servlet回顾
1.1. 第一步:创建一个Servlet类,继承HttpServlet
1.2. 第二步:编写doGet方法,传递request和response参数
1.3. 第三步:转向index.jsp页面
package com.dn.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{
String method = (String)request.getParameter("method");
if("hello".equals(method)){
System.out.println("hello world.");
}else if("hey".equals(method)){
System.out.println("hey world.");
}
request.getRequestDispatcher("/index.jsp").forward(request, response);
}
}
1.1. 第四步:在web.xml中配置servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>springmvc</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.dn.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/servlet/helloServlet</url-pattern>
</servlet-mapping>
</web-app>
1.2. 第五步:发布,测试
2. Servlet的缺点
1)写一个Servlet要在web.xml中配置8行代码,当项目中有很多servlet会导致web.xml比较庞大,杂乱,不易维护
2)在项目中多人编辑同一个web.xml,容易导致版本冲突问题
3)在servlet中,一个方法的入口只有一个,如果在servlet中写很多方法,这些方法应该传递参数,根据每次请求的参数不同来判断具体执行哪个方法。
4)如果在表单中的元素很多,在servlet中要想获取表单中的数据,那么在servlet的方法中必要有大量的request.getParameter()方法。码农的最佳实践,和业务逻辑毫无关系。字符串还简单,强转;对应int,float,data还需要写额外的null判断。
5)Servlet中有两个参数request和response,这两个参数有严重容器依赖性。所以在servlet中写的代码不能单独测试的。
6)如果在servlet中的一个方法中,有很多功能,这个时候会导致该方法比较复杂,以至于不利于维护
doGet(){
权限的验证
用户注册
用户头像图片上传
验证表单
方法的执行效率
}
7)在servlet中声明成员变量,会存在线程安全问题。
重构:
1)更有利于团队开发,提高开发效率
2)克服上面这些缺点。其实对照上面的问题,你看看struts2怎么解决,也就会使用struts2了。
把servlet说的一无是处,那它有优点吗?
1)Servlet能让你看到mvc框架的本质,它是不最底层的东西啊。实际struts2就是对它的封装。
2)越底层的东西执行效率越高。也就是现在欧美很多企业仍然直接使用servlet,因为什么啊,它的掌控性比较强,自己可以一层层的重构,所有东西都是你自己写的,那看起来会怎样?是不容易读懂啊。那你要使用struts2,是不还得研究它的源代码啊。Struts2高度封装,可不是那么容易研究。
那struts2怎么样对servlet重构呢?下节来说。
3. Servlet的重构
怎么做这个事呢?
需求:
用户IE输入URL,解析URL找到具体的业务执行,然后转向页面展现结果。
有什么?(资源)
URL,action的名字;
Action,execute方法,转向的页面名称
Jsp页面
缺什么?(如何整合资源实现需求)
当前有哪些action? (通过监听器进行初始化对应关系)
url怎么定位action? (过滤器实现,struts2就用过滤器实现)
Action的方法怎么执行? (通过反射)
怎么完成转向? (request的转向方法)
1)监听器
a)准备一个map
b)把所有的action都以key,value的形式放到map中
c)把map放到application域
2)过滤器
a)从application中获取map
b)解析url
c)根据解析的url从map中获取value值
d)根据java的反射机制动态调用action中的值
e)根据action返回的字符串进行跳转
3)执行action的execute的方法,返回字符串
1. Servlet重构的实现
1.1. 第一步:创建web工程 superservlet
1.1. 第二步:创建监听器
配置URL对应的实现类
package com.dn.listener;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ServletListener implements ServletContextListener {
//摧毁,释放资源
public void contextDestroyed(ServletContextEvent arg0) {
arg0.getServletContext().removeAttribute("mappings");
}
//当tomcat容器启动时,进行初始化
public void contextInitialized(ServletContextEvent arg0) {
Map<String,Object> map = new HashMap<String,Object>();
map.put("loginAction", "com.dn.action.LoginAction");
//将mapping放入application域
arg0.getServletContext().setAttribute("mappings", map);
}
}
1.1. 第三步:创建filter
package com.dn.filter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.dn.utils.ServletUtils;
public class DispatcherFilter implements Filter{
//摧毁
public void destroy() {
}
//执行体,处理业务逻辑
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
//获取用户的请求
HttpServletRequest req = (HttpServletRequest) request; //重载,HttpServletRequest为ServletRequest的子接口
String actionName = ServletUtils.getActionName(req.getRequestURI());
ServletContext sc = request.getServletContext(); //获取上下文
//从上下文中拿出map
Map<String,Object> mappings = (Map)sc.getAttribute("mappings");
for(Entry entry : mappings.entrySet()){
if("loginAction".equals(entry.getKey())){
//利用反射得到执行的action和method
Class clazz = Class.forName((String)entry.getValue());
Method method = clazz.getMethod("execute", HttpServletRequest.class, HttpServletResponse.class);
//执行指定的action的指定方法,并获取返回值
String jspName = (String)method.invoke(clazz.newInstance(), request, response);
//根据action的返回值跳转
request.getRequestDispatcher(jspName).forward(request, response);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
//初始化
public void init(FilterConfig arg0) throws ServletException {
}
}
1.1. 第四步:工具类,解析action名称
package com.dn.utils;
/**
* @Description:
* @Author: 达内集团 教学研发部 大数据方向 陈子枢
* @Company: http://www.tarena.com.cn
* @CreateDate: 2015年3月9日
*/
public class ServletUtils {
static public String getActionName(String url){
url.split("/"); // /loginAction.action
String[] array = url.split("/");
String _str = array[array.length-1];
return _str.substring(0, _str.indexOf("."));
}
}
1.2. 第五步:创建userAction
package com.dn.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description:
* @Author: 达内集团 教学研发部 大数据方向 陈子枢
* @Company: http://www.tarena.com.cn
* @CreateDate: 2015年3月9日
*/
public class LoginAction {
public String execute(HttpServletRequest request, HttpServletResponse response){
System.out.println("login action execute");
return "index.jsp"; //转向index.jsp页面
}
}
1.3. 第六步:web.xml配置filter
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>springmvc</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>com.dn.listener.ServletListener</listener-class>
</listener>
<filter>
<filter-name>DispatcherFilter</filter-name>
<filter-class>com.dn.filter.DispatcherFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>DispatcherFilter</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
</web-app>
小结
Action中是不跟原来servlet一样,它不就要request,response吗,都有。
这样比起来
web.xml中就一个声明,再有新的action,只需在监听器中配置即可
Action方法动态调用
这就是重构,是不是比原来方便很多,代码结果非常清晰。
当然这里有很多不足
在ServletListener中是不要有新的servlet还得再这写;
是不如果进一步的优化,还可以把它写入配置文件中啊,那样是不就更完美了。