struts2的配置

本文详细介绍了Struts2框架的基本概念、配置方法、核心组件及常用功能,包括控制器配置、Struts配置文件解析、请求后缀修改、Action方法调用等,并深入探讨了跳转类型、服务端验证、拦截器机制等内容。

Struts2 是一个web应用框架。它不是一个Struts的新的发布版本,而是一个全新的框架。Struts2 是 第二代基于Model-View-Controller (MVC)模型的web应用框架。 Struts2是java企业级web应用的可扩 展性的框架。 它是WebWork和Struts社区合并后的产物。 struts 2没有继承struts 1的血统,而是继 承了webwork 的血统。

配置Struts2的中央控制器

<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>/*</url-pattern> </filter-mapping>

Struts配置文件 struts.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts> <package name="mypackage" extends="struts-default"> <action name="hello" class="com.kaishengit.action.HelloAction"> <result name="success">/WEB-INF/views/hello.jsp</result> </action> </package> </struts>

修改默认的请求后缀 • struts.properties struts.action.extension=do,jspx • struts.xml <constant name="struts.properties" value="do,jspx"></constant>

调用Action中指定的方法 • 调用HelloAction类中的add方法 <action name="hello" class="com.kaishengit.action.HelloAction" method="add"> <result name="success">/WEB-INF/views/hello.jsp</result> </action> • 通过URL来指定调用的方法 http://localhost:8080/struts2/hello!add.action • 使用通配符来调用指定方法 <action name="hello*" class="com.kaishengit.action.HelloAction" method="{1}"> <result name="success">/WEB-INF/views/hello.jsp</result> </action>

跳转类型 • struts-default.xml <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>

• 请求转发跳转到另一个Action中 <result name="input" type="chain">

<param name="actionName">Home</param> <param name="method">index</param> </result> • 重定向跳转到另一个Action中 <result name="input" type="redirectAction"> <param name="actionName">Home</param> </result>

服务端验证

  1. Action继承自com.opensymphony.xwork2.ActionSupport类
  2. 重写父类的validate()方法
  3. 通过addActionError()方法添加错误信息
  4. 如果发生错误,则会跳转到名称为input的视图中
  5. 在JSP中通过Struts2的自定义标签<s:actionerror/>来获取Action中的错误信息

Session 方法1:通过ActionContext对象获取 Map<String, Object> session = ActionContext.getContext().getSession(); session.put("sessionKey", "Hello,Session"); 方法2:实现org.apache.struts2.interceptor.SessionAware接口 public class HomeAction extends ActionSupport implements SessionAware{ private Map<String, Object> session; public String execute() { session.put("sessionKey", "Hello,Session"); return SUCCESS; } public void setSession(Map<String, Object> session) { this.session = session; }

HttpServletRequest和HttpServletResponse 方法1:通过ServletActionContext获取 HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); 方法2:通过Aware接口获取 public class HomeAction implements ServletRequestAware,ServletResponseAware{ public void setServletResponse(HttpServletResponse arg0) {} public void setServletRequest(HttpServletRequest arg0) {}

Application 方法1:通过ActionContext或ServletActionContext获取 Map<String, Object> map = ActionContext.getContext().getApplication(); ServletContext application = ServletActionContext.getServletContext(); 方法2:通过Aware接口获取 public class HomeAction implements ServletContextAware,ApplicationAware{ public void setApplication(Map<String, Object> arg0) {} public void setServletContext(ServletContext arg0) {}

NameSpace <package name="mypackage" namespace="/book" extends="struts-default"> </package>

内置拦截器 • struts-default.xml <interceptors> <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/> <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/> <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/> <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/> <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/> <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" /> <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" /> <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" /> <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/> <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/> <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/> • 多个拦截器可以组成拦截器栈

<!-- Basic stack -->

<interceptor-stack name="basicStack"> <interceptor-ref name="exception"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*,^struts\..*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> </interceptor-stack>

• 默认拦截器栈 <default-interceptor-ref name="defaultStack"/> • 修改package的默认拦截器栈 <package name="mypackage" namespace="/book" extends="struts-default"> <interceptors> <interceptor-stack name="mystack"> <interceptor-ref name="timer"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <default-interceptor-ref name="mystack"/> • 为Action添加拦截器 <action name="home" class="com.kaishengit.action.HomeAction"> <result>/WEB-INF/views/home.jsp</result> <result name="input">/WEB-INF/views/error.jsp</result> <interceptor-ref name="timer"></interceptor-ref> </action> 自定义拦截器 public class MyInterceptor implements Interceptor { @Override public void destroy() { System.out.println("interceptor销毁"); } @Override public void init() { System.out.println("interceptor初始化"); } @Override public String intercept(ActionInvocation invocation) throws Exception { return null; } } AbstractInterceptor public class LoginInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { return null; } } 配置自定义拦截器 <interceptors> <interceptor name=“myTimer” class=“com.kaishengit.interceptor.MyInterceptor”/> <interceptor-stack name="myInterceptor"> <interceptor-ref name="myTimer"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> 拦截器属性 <interceptor name="mytimer" class="com.kaishengit.interceptor.MyTimerInterceptor">

<param name="par">hello</param> </interceptor> public class MyTimerInterceptor extends AbstractInterceptor{ private static final long serialVersionUID = 1L; private String par; @Override public String intercept(ActionInvocation actionInvocation) throws Exception { return null; } public String getPar() { return par; } public void setPar(String par) { this.par = par; } }

Struts2文件上传

<form action="uploadfile.action" method="post" enctype="multipart/form-data"> <input class="input-file" id="fileInput" name="file" type="file”> <button type="submit" class="btn btn-primary">上传</button> </form>

public class FileUploadAction implements Action{ private File file; private String fileFileName; private String fileContentType; public String execute() throws Exception { System.out.println("文件类型:" + fileContentType); System.out.println("文件名称:" + fileFileName); String savePath = ServletActionContext.getServletContext().getRealPath("/") + "upload/"; InputStream inputStream = new FileInputStream(file); OutputStream outputStream = new FileOutputStream(new File(savePath+fileFileName)); byte[] buffer = new byte[1024]; int len = -1; while((len = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, len); } outputStream.flush(); outputStream.close(); inputStream.close(); return SUCCESS; }

多文件上传 public class MoreFileUploadAction implements Action{ private List<File> file; private List<String> fileFileName; private List<String> fileContentType; public String goPage() { return SUCCESS; } public String execute() throws Exception { for(String str : fileFileName) { System.out.println("fileName:" + str); } System.out.println("----------------"); for(String str : fileContentType) { System.out.println("contentType:" + str); } return SUCCESS; }

文件下载 <action name="downloadfile" class="com.kaishengit.web.download.FileDownloadAction" method="download">

<!-- 配置下载文件的名称 -->

<param name="fileName">less.pdf</param> <result name="success" type="stream"> <!-- 下载文件的mime类型 --> <param name="contentType">application/pdf</param> <!-- 下载文件的描述 --> <param name="contentDisposition">attachment;filename="document.pdf"</param> <!-- 缓冲区大小 --> <param name="bufferSize">1024</param> <!-- 获取文件的流getxxx方法的名字(不包含get) --> <param name="inputName">inputStream</param> </result> </action> public class FileDownloadAction implements Action{ private String fileName; public String download() throws Exception { return Action.SUCCESS; } public InputStream getInputStream() throws Exception { String filePath = “C:/download/”+ fileName; InputStream stream = new FileInputStream(new File(filePath)); return stream; } JFreechart <package name="jfreechart" extends="jfreechart-default"> <action name="showjfreechart" class="com.kaishengit.web.jfreechart.JFreeChartAction"> <result type="chart"> <param name="width">400</param> <param name="height">300</param> </result> </action> </package> public class JFreeChartAction implements Action{ private JFreeChart chart; public String execute() throws Exception { StandardChartTheme theme = new StandardChartTheme("mytheme"); //设置标题字体 theme.setExtraLargeFont(new Font("黑体",Font.BOLD,32)); //设置图例字体 theme.setRegularFont(new Font("黑体",Font.BOLD,14)); //设置轴向字体 theme.setLargeFont(new Font("黑体",Font.BOLD,14)); ChartFactory.setChartTheme(theme); DefaultPieDataset dataSet = new DefaultPieDataset(); dataSet.setValue("Java", 300); dataSet.setValue("PHP", 240); dataSet.setValue(".NET", 140); chart = ChartFactory.createPieChart3D("技术统计", dataSet, true,false,false); return SUCCESS; } public JFreeChart getChart() { return this.chart; } } JSON <package name="ajax" extends="json-default"> <action name="calljson" class="com.kaishengit.web.ajax.JSONAction" method="showJson"> <result type="json"> <param name="root">user</param> <param name="noCache">true</param> <param name="enableGZIP">true</param> <param name="excludeNullProperties">true</param> </result> </action> </package> public class JSONAction implements Action{ private User user; public String execute() throws Exception { return SUCCESS; } public String showJson() throws Exception { user = new User(); user.setAddress("China"); user.setAge(23); user.setName("Kay"); return SUCCESS; } Convention Plugin http://struts.apache.org/release/2.3.x/docs/convention-plugin.html

OGNL OGNL的全称为Object Graph Navigation Language(对象图导航语言) OGNL中的对象 User user = new User(12,"Alex"); Address address = new Address("Shanghai"); user.setAddress(address); System.out.println(Ognl.getValue("id", user)); System.out.println(Ognl.getValue("name", user)); System.out.println(Ognl.getValue("address.city", user)); OGNL中的Map User user = new User(12,"Alex"); Address address = new Address("Shanghai"); user.setAddress(address); Book book = new Book("Head First Java","Tom"); Map<String, Object> map = new HashMap<String, Object>(); map.put("u", user); map.put("b", book); System.out.println(Ognl.getValue("address.city", user)); System.out.println(Ognl.getValue("author", book)); System.out.println(Ognl.getValue("address.city", map,user)); System.out.println(Ognl.getValue("author", map,book)); System.out.println(Ognl.getValue("#u.address.city", map,map)); System.out.println(Ognl.getValue("#b.author", map,map)); System.out.println(Ognl.getValue("#root.address.city", user)); System.out.println(Ognl.getValue("#root.author", map,book)); OGNL中的List User user1 = new User(12,"Alex"); User user2 = new User(13,"Rose"); User user3 = new User(14,"Hanks"); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); list.add(user3); System.out.println(Ognl.getValue("#root[0].name", list)); System.out.println(Ognl.getValue("#root[1].name", list)); OGNL调用方法 User user1 = new User(12,"Alex"); User user2 = new User(13,"Rose"); User user3 = new User(14,"Hanks"); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); list.add(user3); System.out.println(Ognl.getValue("size()", list)); System.out.println(Ognl.getValue("sayHello()", user1));

ValueStack 值栈 ValueStack ActionContext CompoundRoot Push pop Request Session Application ActionContext.getContext().put() 访问值需要加#号 Action ActionContext.getContext().getValueStack().push() 默认取出栈顶的值即#root[0] public class OgnlAction { private String name; private String password; @Action("ognl") public String execute() { name = "Tom"; password = "09876"; ActionContext.getContext().put("a1", "aa1"); ActionContext.getContext().put("a2", "aa2"); User user = new User(); user.setId(11); user.setName("Vivi"); ActionContext.getContext().getValueStack().push(user); return "success"; } ActionContext a1 aa1 a2 aa2 OgnlAction user #root[0] #root[1] CompoundRoot 使用Struts2标签取值

<h3>访问ActionContext中的值</h3> <s:property value="#a1"/> <s:property value="#a2"/> <h3>访问Action中的值</h3> <s:property value="name"/><!-- 显示Vivi的姓名 --> <s:property value="#root[1].name"/><!-- 显示Tom的姓名 --> <s:property value="password"/> <h3>访问Request的值</h3> <s:property value="#request.root[1].name"/> <s:property value="#request.a1"/> 更多操作 userList:<s:property value="userList"/> list size:<s:property value="userList.size"/> 第一个List对象:<s:property value="userList[0]"/> 投影 取得List中所有的姓名:<s:property value="userList.{userName}"/> 获取List第一个对象的姓名:<s:property value="userList.{userName}[0]"/> 获取List第一个对象的密码:<s:property value="userList.{userPwd}[0]"/> 输出List中姓名为aa的所有用户对象: <s:property value="userList.{?#this.userName == 'aa'}"/> 输出List中姓名为aa的第一个用户对象: <s:property value="userList.{^#this.userName == 'aa'}.{id}"/> 输出List中姓名为aa的最后一个用户对象: <s:property value="userList.{$#this.userName == 'aa'}.{id}"/> 输出Map中的key:<s:property value="userMap.keys"/> 输出Map中的value:<s:property value="userMap.values"/> 输出Map中的数量:<s:property value="userMap.size"/> 输出key(tom)对应的value:<s:property value="userMap['tom'].{id}[0]"/> 判断Map是否为empty:<s:property value="userMap.isEmpty"/> 动态创建List和Map 动态创建List:<s:property value="{'aa','bb','cc'}"/> 动态创建Map:<s:property value="#{'aa':'aa','bb':'bb' }"/> 获取request session Application中的值 获取Session中的值: <s:property value="#session.sessionKey"/> 获取reuqst中的值: <s:property value="#request.requestKey"/> 获取Application中的值: <s:property value="#application.applicationKey"/> 自动搜寻requset、session、application中的值: <s:property value="#attr.sessionKey"/> Struts2 Tag Struts2 Tag 数据标签 逻辑标签 UI标签 逻辑标签 <s:if test="address == '河南焦作'"> 河南焦作 </s:if> <s:elseif test="address == '河南'"> 河南 </s:elseif> <s:else> 未知区域 </s:else> 逻辑标签 <s:iterator value="userList" var="u"> <s:property value="#u.userName"/> </s:iterator> 属性 描述 value 被迭代的集合或数组 status 迭代状态对象,该值被放入值栈中 var 当前迭代对象,该值被放入值栈中

<s:iterator value="userList" var="u" status="status"> <s:if test="#status.odd">

<div style="color:red"> <s:property value="#u.userName"/> </div> </s:if> <s:else> <div style="color:blue"> <s:property value="#u.userName"/> </div> </s:else> </s:iterator> 属性 描述 count 当前迭代对象是集合中的第几个对象 index 当前迭代对象在集合中的索引值 even 是否是偶数行 odd 是否是奇数行 first 是否是第一个对象 last 是否是最后一个对象 UI标签 form textfield password textarea checkbox select radio 基础属性 方法名称 描述 name 表单元素映射的名字 value 表单元素的值 label 显示在元素前的文字 id HTML的id属性,可以和JavaScript进行集 成 form标签 <s:form action="ognl" method="post"> </s:form> 属性名称 描述 action 要提交到的action名称 method post和get enctype 进行文件上传时设置为multipart/form-data textfield标签 <s:form action="ognl" method="post" theme="simple"> 用户名:<s:textfield name=“userName” theme=“simple/> </s:form> 属性名称 描述 maxlength 这个字段可以输入的最大长度 readonly 当设置为true,用户不能在这个表当元素中 修改值 size 这个字段的宽度 password标签 <s:form action="ognl" method="post" theme="simple"> 用户名:<s:textfield name="userName" theme="simple"/> 密码:<s:password theme="simple"/> </s:form> 属性名称 描述 showPassword 是否预置显示password,默认为true。 maxlength 这个字段可以输入的最大长度 readonly 当设置为true,用户不能在这个表当元素中修改值 size 这个字段的宽度 textarea标签 <s:form action="ognl" method="post" theme="simple"> 用户名:<s:textfield name="userName" theme="simple"/> 密码:<s:password theme="simple"/> 留言:<s:textarea theme="simple" name="msg“/> </s:form> 属性名称 描述 cols 文本区域的列数 rows 文本区域的行数 readonly 当设置为true,用户不能在这个表当元素中修改 值 wrap 指定文本区域内的内容是否该换行 checkbox 爱好:<s:checkbox name="fav" fieldValue="football" theme="simple"/> football <s:checkbox name="fav" fieldValue="pingpang" theme="simple"/> pingpang 属性名称 描述 fieldValue 复选框被选中时提交给Action的值 select <s:select list="userList" listKey="id" listValue="userName“/> 属性名称 描述 list 用来显示在下拉框中的集合 listKey 如果集合中是对象,指定对象中哪个属性作为 选项的key listValue 如果集合中是对象,指定对象中哪个属性作为 选项的value emptyOption 当设置为true时,下拉框中第一项为空选项 size 指定下拉框中可以看到多少个选项 radio <s:radio list="#{0:'男',1:'女' }" name="sex"/> 属性名称 描述 list 用来显示单选项的集合 listKey 如果集合中是对象,指定对象中哪个属性作为选 项的key listValue 如果集合中是对象,指定对象中哪个属性作为选 项的value ModelDriven log4j Logger log = Logger.getLogger(Log4jTest.class); log.debug("debug message"); log.info("info message"); log.warn("warn message"); log.error("error message"); log.fatal("fatal message"); log4j.properties log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=[%p](%d)---%l--%m\r\n log4j.rootLogger=DEBUG, A1 %m 输出代码中指定的消息 %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL %r 输出自应用启动到输出该log信息耗费的毫秒数 %c 输出所属的类目,通常就是所在类的全名 %t 输出产生该日志事件的线程名 %n 输出一个回车换行符,Windows平台为“/r/n”,Unix平台为“/n” %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss , SSS} %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。 log4j.properties log4j.appender.fout=org.apache.log4j.FileAppender log4j.appender.fout.layout=org.apache.log4j.PatternLayout log4j.appender.fout.layout.ConversionPattern=[%p](%d)---%l--%m\r\n log4j.appender.fout.file=C:/logs/system.log log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=[%p](%d)---%l--%m\r\n log4j.rootLogger=DEBUG, A1,fout log4j.properties 以天为单位生成日志文件 log4j.appender.fout=org.apache.log4j.DailyRollingFileAppender log4j.appender.fout.layout=org.apache.log4j.PatternLayout log4j.appender.fout.layout.ConversionPattern=[%p](%d)---%l--%m\r\n log4j.appender.fout.DatePattern='.'yyyy-MM-dd log4j.appender.fout.file=C:/logs/x.log 根据日志级别存放的不同的位置 log4j.appender.fout=org.apache.log4j.DailyRollingFileAppender log4j.appender.fout.layout=org.apache.log4j.PatternLayout log4j.appender.fout.layout.ConversionPattern=[%p](%d)---%l--%m\r\n log4j.appender.fout.DatePattern='.'yyyy-MM-dd log4j.appender.fout.Threshold=INFO log4j.appender.fout.file=C:/logs/x.log log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=[%p](%d)---%l--%m\r\n log4j.rootLogger=DEBUG, A1,fout 根据包输出日志信息 log4j.appender.fout=org.apache.log4j.DailyRollingFileAppender log4j.appender.fout.layout=org.apache.log4j.PatternLayout log4j.appender.fout.layout.ConversionPattern=[%p](%d)---%l--%m\r\n log4j.appender.fout.DatePattern='.'yyyy-MM-dd log4j.appender.fout.file=C:/logs/x.log log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=[%p](%d)---%l--%m\r\n log4j.logger.com.google=ERROR,fout log4j.rootLogger=DEBUG, A1 SiteMesh https://github.com/sitemesh/sitemesh2 web.xml <filter> <filter-name>sitemesh</filter-name> <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class> </filter> <filter-mapping> <filter-name>sitemesh</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 配置文件 放到/WEB-INF/文件夹中,名称为decorators.xml <?xml version="1.0" encoding="UTF-8"?> <decorators defaultdir="/WEB-INF/views/layout"> <decorator name="basic-theme" page="layout.jsp"> <pattern>/*</pattern> </decorator> <excludes> <pattern>/img/*</pattern> </excludes> </decorators> layout.jsp <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Hello base Layout-<decorator:title/></title> <decorator:head/> </head> <body> <h3>Base top</h3> <decorator:body></decorator:body> <hr/> <h3>Base bottom</h3> </body> </html> home.jsp <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Home page</title> <style type="text/css"> .error{ color:red; } </style> </head> <body> Hello,Struts2! <img alt="" src="img/1.png"> </body> </html>

转载于:https://my.oschina.net/u/1388978/blog/220450

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值