一、引入
1、servlet中的缺点
- 一种请求对应一个servlet,代码比较拥挤
- 在web.xml中,配置比较麻烦,并且不适合团队开发
- servlet中doGet方法和doPost方法中的两个参数reqeust,response拥有严重的容器依赖性(服务器不启动,程序就无法测试)
- 如果页面上表单中的元素比较复杂,则在servlet的方法中获取表单元素的数据比较繁琐
- servlet是单线程的,只要在servlet中的属性中声明一个数据,该数据就是全局数据了,有线程安全问题。
- 在servlet中处理异常,如果servlet中有5个方法,则这5个方法必须都要try catch
- 如果一个servlet中有很多个方法,则必须采用传递参数的形式,分解到每一个方法中。
2、重构
1.基本原理
在web.xml中,配置一个servlet或者配置一个过滤器(在这里用过滤器),过滤器完成的内容:
1、过滤.do的所有的请求
2、利用java的反射机制动态的调用方法
3、service的方法返回一个jsp,然后利用转发或者重定向到相应的页面
2.具体实现
1.访问
http://localhost:8080/struts2_st1/userService.do?method=insert
2.分析
如果采用servlet(userService)方法,需要在doXXX方法中通过判断方法method来调用相应的service方法
public void doPost(request,response){
if(method.equals("insert")){
//调用service相应的方法
}if else(method.equals("update"))
{}
}
如果通过反射机制来获取方法需要知道的内容为:方法名称,类的全名称,传递的参数为reqeust,response
3.实现
首先要配置一个过滤器来拦截.do的url(这里是userService.do)
<filter>
<filter-name>servletFilter</filter-name>
<filter-class>com.shen.web.filter.ServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>servletFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
这里的service为:
public class UserService {
public void insert(HttpServletRequest request,HttpServletResponse response){
System.out.println("UserService insert");
}
}
通过map集合来通过url传递的”userService“ 获取相对应的service(全名)
public class ServletConfig {
//userService?method=insert key与userService相对应,value为其全名
public static Map<String,String> serviceMap=new HashMap<String, String>();
static{
serviceMap.put("userService", "com.shen.service.UserService");
}
}
其中过滤器如下:
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
/**
* 1、获取Request,Response,然后通过方法的调用传递到service层
* 2、获取service的全名和方法名称
* 3、根据service的方法的名称利用java的反射机制创建service层
* 4、根据method的invoke方法动态的调用service
*/
HttpServletRequest request=(HttpServletRequest) res;
HttpServletResponse response=(HttpServletResponse) req;
String uri=request.getRequestURI();// :/struts2_st1/userService.do
String[] strings=uri.split("/");// :userService.do
String doString=strings[strings.length-1];
String url=doString.substring(0,doString.indexOf("."));// :userService
String serviceName=ServletConfig.serviceMap.get(url);//根据userService来获取全名
String methodName=request.getParameter("method");//获取方法的名称
try {
Object obj=Class.forName(serviceName).newInstance();
Method method=obj.getClass().getMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
method.invoke(obj, request,response);//调用该方法
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
4.总结
如果在UserService中添加一个方法
public class UserService {
public void insert(HttpServletRequest request,HttpServletResponse response){
System.out.println("UserService insert");
}
public void update(HttpServletRequest request,HttpServletResponse response){
System.out.println("UserService update");
}
}
那么访问http://localhost:8080/struts2_st1/userService.do?method=update 即可调用该方法。
二、入门
1、第一个案例
新建一个struts2的web工程,其自动生成的web.xml文件如下:
<!-- 在浏览器中只要发送.action就拦截 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
说明:该过滤器过滤所有的.action请求
现在先写一个HelloWorldAction
public class HelloWorldAction {
public String execute(){
return "index";
}
}
在struts.xml文件中对其进行配置:
<struts>
<!-- package为包,name为包名(是唯一的)namespace与url相对应 -->
<package name="helloWorld" namespace="/" extends="struts-default">
<!-- action是用来描述一个类的,name为名称(与url相对应[即访问该action的url地址,与servlet的映射类似]) -->
<action name="helloWorldAction" class="com.shen.action.HelloWorldAction">
<!-- result为结果,name中的内容和当前请求action方法的返回值对应 -->
<result name="index">index.jsp</result>
</action>
</package>
</struts>
说明:即如果返回值是"index"就执行index.jsp
有test.jsp页面:
<body>
测试struts2,有命名空间helloWorld<br/>
<a href="${pageContext.request.contextPath}/helloWorldAction.action">测试</a>
</body>
注意:该处地址与action对应,点击超链接转发到index页面
2、分析案例
1.struts.xml文件的加载
- 当tomcat服务器启动的时候,会去classpath下加载struts-default.xml,struts-plugin.xml,struts.xml,并且按照先后顺序进行加载,其中struts-default.xml,struts-plugin.xml文件会在jar包中。
- 这三个xml文件的dtd约束都一样,数据结构一样
- 如果在这三个配置文件中,出现了后者和前者一样,那么后者覆盖前者
- struts2容器与其他框架是以插件的形式结合的,而struts-plugin.xml文件就是结合的入口点,所以将来会有很多个
struts-plugin.xml
2.包的继承
现将struts.xml文件改动如下:
<!-- package为包,name为包名(是唯一的) -->
<package name="helloWorld" namespace="/" extends="struts-default">
<!-- action是用来描述一个类的,name为名称(与url相对应[即访问该action的url地址,与servlet的映射类似]) -->
<action name="helloWorldAction" class="com.shen.action.HelloWorldAction">
<!-- result为结果,name中的内容和当前请求action方法的返回值对应 -->
<result name="index">index.jsp</result>
</action>
</package>
<package name="hello" namespace="/base" extends="helloWorld"></package>
说明:即包hello继承helloWorld,此处访问http://localhost:8080/mystruts2/base/helloWorldAction.action (会报错,因为命名空间)时同样可以执行。因为包hello继承于helloWorld,所以helloWorld中的所有功能也被继承过来了(包struts-default是最基础的包)
3.继承ActionSupport类
现令action继承ActionSupport类:
public class ActionSupportAction extends ActionSupport {
@Override
public String execute() throws Exception {
System.out.println("action support");
return SUCCESS;
}
}
其配置如下:
<action name="actionSupportAction" class="com.shen.action.ActionSupportAction">
<result name="success">index.jsp</result>
</action>
说明:在action中返回的是SUCCESS,在struts2中是"success",是因为在ActionSupport中定义了常量SUCCESS=“success”(还有一些其他常量,如ERROR)。并且在ActionSupport中还实现了Validateable(验证)TextProvider(国际化)等功能。
4.不配class属性
action标签中的class属性是可以不写的,因为action标签肯定在一个包中,该包继承了struts-defualt,在struts-default包中有这么一段配置:
<default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
如果在action标签中没有class属性,则默认查找ActionSupport,并且必须返回success
<action name="defaultAction">
<result name="success">index.jsp</result>
</action>
同样跳转到index.jsp中
5.配置文件的包含
即一个struts配置文件是可以包含其他的文件的。现除了src下有struts.xml文件,在com.shen.include包下有IncludeAction.java和struts-include.xml。其中struts-include.xml内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="include" namespace="/" extends="struts-default">
<action name="includeAction" class="com.shen.include.IncludeAction">
<result name="success">index.jsp</result>
</action>
</package>
</struts>
则在struts.xml文件要包含struts-include.xml需要:
<include file="com/shen/include/struts-include.xml"></include>
6.命名空间
namespace是package标签中的属性,在写namespace的时候,一定要加上"/",namespace的值要体现在url中:
【<package namespace="/base">】那么在访问一个action的时候,在action前面要加上/base:
<package name="ns" namespace="/ns" extends="struts-default">
<action name="nsAction" class="com.shen.action.ns.NameSpaceAction">
<result name="success">../index.jsp</result>
</action>
</package>
说明:在访问http://localhost:8080/struts2_st1/ns/nsAction.action 时action将会被执行,返回结果跳转到index页面时【index.jsp在webroot下】,因为有命名空间ns,故会在webroot下找文件夹ns中的index.jsp,但是并未该文件,所以要在index前面加../(其实改为/index.jsp也行(该“/”代表web根目录),但是不能写为index.jsp)
注意:
1.如果在一个url访问中有好几层命名空间:http://localhost:8080/sturts2/a/b/c/d/AAction.action 在配置文件中如下:
<package name="ns" namespace="/a/b" extends="struts-default">
<action name="AAction" class="com.shen.action.ns.NameSpaceAction">
<result name="success">/index.jsp</result>
</action>
</package>
其查找步骤:
1、会去a/b/c/d下查找有没有相对应的AAction,如果找到,则执行action
2、如果找不到,则会去a/b/c下查找,如果找到,则执行action
3、按照这样的查找方法,直到查找到根目录下,如果还没有找到,则报错,没有找到相应的action
显然会在/a/b下找到,并执行NameSpaceAction
7.返回结果
标签result表示对action在执行方法后返回的结果进行处理,其中name表示返回的值,type表示要对该返回结果的操作类型。其中type的值有:
<result-types>
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
<result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
</result-types>
举例:
对返回结果进行转发:
<!-- name的值默认为success type默认为转发(dispatcher)方法默认为execute方法-->
<result>/index.jsp</result>
对结果进行重定向:
<!-- 重定向-->
<result type="redirect">/index.jsp</result>
对结果重定向到action:
<result type="redirectAction">
<param name="actionName">nsAction</param><!--重定向到的action-->
<param name="namespace">/ns</param><!--重定向到的action的命名空间-->
<param name="method">execute</param><!--重定向到的action的执行方法-->
</result>
此外还有一个方法:
<!-- 方法默认是execute方法 -->
<result type="redirectAction">/ns/nsAction.action</result>
注意:结果集是可以自定义的如果自定义的,结果集需要获取result标签中的内容,则继承抽象类,如果自定义的结果集不需要获取result标签中的内容,则实现接口。自定义结果集的步骤:
- 写一个类,该类有两种方式:1、实现Result接口 2、继承StrutsResultSupport
- 在配置文件中
<package name="myresult" namespace="/" extends="struts-default">
<result-types>
<result-type name="myresult" class="cn.itheima02.struts2.result.MyDispatcher"></result-type>
</result-types>
</package>
- 使用
<package name="testMyResult" namespace="/" extends="myresult">
<action name="resultAction_*" method="{1}" class="cn.itheima02.struts2.result.ResultAction">
<result type="myresult" name="aaa">index.jsp</result>
</action>
</package>
8.调用的方法
如果没有配置action执行的方法时默认执行execute,当然也可以自己配置(在PatternAction中有方法pattern):
<action name="patternAction" class="com.shen.action.patter.PatterAction" method="pattern">
<result>/index.jsp</result>
</action>
除了在文件中配置方法外还可以在url中指定调用哪个方法:http://localhost:8080/struts2_st2/patternAction!pattern.action 也会执行方法 pattern