Struts
概念:
是一个MVC框架;
Servlet的缺点
1.在web.xml中文件中需要配置很多行代码,维护起来很不方便呢,不利于团队合作。
2.一个servlet的入口只有一个doPost或者doGet方法,如果在一个servlet在写好几个方法 ,怎么办?
这样会导致代码结构很乱
3.servlet类与servlet容器高度耦合,每个方法中都有两个参数,request、response、如果服务器不启动,这两个参数没有办法初始化(单元测试)。
4.如果在servlet中的一个方法中,有很多功能,这个时候会导致该方法比较复杂、以至于不利于维护。
用户注册完成四件事情、所以整个方法比较杂乱。
5.如果一个servlet类中有很多方法、浏览器对这些方法进行请求、URL写起来麻烦
6.在servlet中如果要获取页面表单中的数据,那么在方法中会写很多行代码。
Servlet的重构
目的:
1.在web.xml文件中只写一个过滤器
2.用action处理业务逻辑
3.在过滤器中动态的调用action中的方法处理业务逻辑。
类的设计:
1.监听器:
1.准备一个map
2.把所有的action的key,value放到map中
3.把map放到application域中;
2过滤器:
1.获取application域中的map
2.解析URL
3.根据解析的URL从map中把value提取出来
4.根据java的反射机制动态调用action
5.根据action返回的方法跳转到相应的页面
3.执行action的execute方法,该方法返回一个字符串
实现:
1.写监听器:
1 import java.util.HashMap; 2 import java.util.Map; 3 4 import javax.servlet.ServletContextEvent; 5 import javax.servlet.ServletContextListener; 6 7 public class ServletListener implements ServletContextListener{ 8 /** 9 * 在tomcat销毁的时候执行 10 */ 11 @Override 12 public void contextDestroyed(ServletContextEvent arg0) { 13 arg0.getServletContext().setAttribute("mappings", null); 14 } 15 /** 16 * 在tomcat启动的时候执行 17 */ 18 @Override 19 public void contextInitialized(ServletContextEvent arg0) { 20 Map<String, String> map = new HashMap<String, String>(); 21 map.put("userAction", "com.itheima09.action.UserAction"); 22 arg0.getServletContext().setAttribute("mappings", map); 23 } 24 }
2.写过滤器:
1 import java.io.IOException; 2 import java.lang.reflect.Method; 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import javax.servlet.Filter; 7 import javax.servlet.FilterChain; 8 import javax.servlet.FilterConfig; 9 import javax.servlet.ServletContext; 10 import javax.servlet.ServletException; 11 import javax.servlet.ServletRequest; 12 import javax.servlet.ServletResponse; 13 import javax.servlet.http.HttpServletRequest; 14 import javax.servlet.http.HttpServletResponse; 15 16 import com.itheima09.servlet.utils.ServletUtils; 17 18 public class DispatcherFilter implements Filter{ 19 20 private ServletContext servletContext; 21 22 @Override 23 public void destroy() { 24 // TODO Auto-generated method stub 25 26 } 27 28 @Override 29 public void doFilter(ServletRequest arg0, ServletResponse arg1, 30 FilterChain arg2) throws IOException, ServletException { 31 /** 32 * 1、从 application域中获取map 33 */ 34 HttpServletRequest request = (HttpServletRequest)arg0; 35 HttpServletResponse response = (HttpServletResponse)arg1; 36 Map<String, String> map = (HashMap<String, String>)this.servletContext.getAttribute("mappings"); 37 /** 38 * 2、获取浏览器中的url,把url解析出来 39 * http://localhost:8080/itheima09_servlet_super/userAction.action 40 * ---->userAction 41 */ 42 //mapping = userAction 43 String mapping = ServletUtils.parse(request.getRequestURI()); 44 String value = map.get(mapping); //value就是action的类的全名 45 try { 46 Class class1 = Class.forName(value); 47 Method method = class1.getMethod("execute", HttpServletRequest.class,HttpServletResponse.class); 48 //调用了action中的方法 49 String jspName = (String)method.invoke(class1.newInstance(), request,response); 50 request.getRequestDispatcher(jspName).forward(request, response); 51 } catch (Exception e) { 52 // TODO Auto-generated catch block 53 e.printStackTrace(); 54 } 55 } 56 57 @Override 58 public void init(FilterConfig arg0) throws ServletException { 59 // TODO Auto-generated method stub 60 this.servletContext = arg0.getServletContext(); 61 } 62 63 }
3.工具类:
1 import org.junit.Test; 2 3 public class ServletUtils { 4 /** 5 * http://localhost:8080/itheima09_servlet_super/userAction.action 6 * @param url 7 * @return 8 */ 9 public static String parse(String url){ 10 String[] array = url.split("/"); 11 String mapping = array[array.length-1].substring(0,array[array.length-1].indexOf(".")); 12 return mapping; 13 } 14 15 @Test 16 public void test(){ 17 System.out.println(ServletUtils.parse("http://localhost:8080/itheima09_servlet_super/userAction.action")); 18 } 19 }
4.action类:
1 import javax.servlet.http.HttpServletRequest; 2 import javax.servlet.http.HttpServletResponse; 3 4 public class UserAction { 5 public String execute(HttpServletRequest request,HttpServletResponse response){ 6 return "index.jsp"; 7 } 8 }
5.配置文件:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 5 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 6 <listener> 7 <listener-class>com.itheima09.servlet.listener.ServletListener</listener-class> 8 </listener> 9 10 <filter> 11 <filter-name>actionFilter</filter-name> 12 <filter-class>com.itheima09.servlet.filter.DispatcherFilter</filter-class> 13 </filter> 14 <filter-mapping> 15 <filter-name>actionFilter</filter-name> 16 <url-pattern>*.action</url-pattern> 17 </filter-mapping> 18 <welcome-file-list> 19 <welcome-file>login.jsp</welcome-file> 20 </welcome-file-list> 21 </web-app>
Struts2的历史
1.servlet
2.struts1
1,写action
2,写了一个中控的servlet
3,actionForm和页面表单中的内容一致
3.webwork(框架MVC):
1.使得action和servlet容器完全松耦合
2.属性驱动和模型驱动获取页面上表单中的数据
3.利用拦截器的概念把servlet容器的第四个缺点克服掉了
4.struts1+webwork=struts2
Struct2的第一个例子:
1.创建一个web project
2.导入jar包
3.编写web.xml文件
4.写一个action
5.编写struts.xml文件;(放在classpath根目录下)
6.运行访问:格式:http://localhost:8080/ProjectName/[namespace]/ActionName.action
解析:
1.structs.xml文件内容:
上图为加载流程
注意:
1、 struts.xml文件必须放在classpath的根目录下
2、 名字必须为struts.xml文件
3、 因为整个加载过程写在了过滤器中的init方法中,所以tomcat启动的时候就把该文件加载了
2.Package:
1.目的:用来管理action的;
从上图可以看出system模块下有三个action
2.name属性:
为包名称,是惟一的;
3.namespace:命名空间,针对URL的
上述的命名空间针对的是:itheima09_struts2_helloworld/hello
当浏览器提交一个url:
上述的url直接从项目的根目录查找,所以找不到
该路径和上述的url是对应的
先找hello/a下的action,如果找不到,则查找上一层,再找不到再找上一层,直到找到,如果最上层找不到,则报错
如果我们的namespace设的是/a 那么:工程名/a/xx/xx/xx.action可以由右向左找到此目录,但是如果在工程名后面写不存在的目录则会报错:工程名xx/xx/a/xx/xx/xx.action这个样就报错了,可以理解为在url中工程名与namespace地址是紧密关联的,
上述的命名空间针对的是:itheima09_struts2_helloworld
只要命名空间加一层,最后跳转到相应的jsp以后,也会加上相应的路径
这样的体系不好。
4.extends:
1.在Tomcat启动的时候,不仅仅加载了struts.xml文件,还加载了struts-default.xml文件,而这个文件在classpath下,针对该文件的路径在:
在一个配置文件中:
说明helloworld拥有package的名称为struts-default包的所有功能;
案例:
Action:
action元素代表一个类
class为action的类的全名,可以写,也可以不写
如果不写class属性,则默认执行ActionSupport中的execute方法
该方法什么都没有做,仅仅返回了一个success字符串
如图:
Result
代表一种结果集
Type 为结果集的类型
Name 属性的值和action中某一个方法的返回值一致
type属性不写,则默认和struts-default中的结果集中的default="true"的结果集保持一致
为dispatcher,转发
result标签中的内容就是要转发到的页面
在struts-default.xml文件中
Name属性也可以不写,如果不写,则默认值为”success”
Struts2基本用法的其他方法
Include
在struts.xml文件中
就可以把struts-helloworld.xml文件包括进来了
Action的写法
1.简单的javabean
2.实现action接口
3.继承ActionSupport
.Action和.do
structs2默认的是以.action为后缀,springmvc是以.do为后缀。structs1也是.do。两者并没什么区别,名字不同而已。
structs2修改为.do后缀的方法如下:
可以在struts.xml中costant标签中,设置“struts.action.extension”的值为do即可。。
<constant name="struts.action.extension" value="do"/>
并且value可以设置成任意值。比如.hello,.haha。你开心就好
springmvc我现在还不知道怎么改。但是controller可以接收.html和.do。在web.xml里配置servlet-mapping映射吧
Action的模式
在action的构造器中输出一句话,在浏览器中多次请求,可以看到构造器执行了好几次,所以action是多例的。
结果集:
转发
重定向
重定向到action
通配符映射
第一种:
将执行UrlPatternAction中的execute方法
第二种:
缺点:action中有几个方法就得在配置文件中写几个action元素
第三种
第四种
第五种
标识符:*写什么 {1}就代表什么 <action name=类名_* method={1} class=类所在路径
该模型的好处:如果在action中增加了一个方法,配置文件是不需要改变的,在写url时
urlPatternAction_后面的内容变成要请求的方法的名称就可以了
第六种
针对不同action中的同名方法;
格式:<action name=类名_* method=方法名 class=类所在路径{1}
第七种(不推荐)
这么写不好,覆盖范围太大,很有可能出现和其他的action 的配置冲突的情况
第八种
格式:<action name=类名_* method={1} class=类所在路径
<result>{1}.jsp<result>
强制让url中的_后面的内容和方法保持一致,跳转到的jsp页面的名称和方法的名称也保持一致。这么写带有一定的规范性
Struts2与servlet容器的交互
这种方法可以交互,但是这种方法把ServletAction与servlet容器耦合性变高了,不利于测试。
可以通过ServletActionContext把servlet容器相关的类调出来
该写法使得action与servlet容器的耦合性不是很强。
总结
1、 sturts2的配置文件中用了package的机制,这样可以分模块
name是唯一的名称,extends采用了继承的机制
2、 写的action与servlet容器完全松耦合了
3、 通配符映射解决:很容器就把一个url映射到一个action的方法中了
4、 Include保证了可以写多个配置文件
5、 结果集的封装
struts内核流程图: