java框架之Struts2(1)-简介及入门

时间:2021-11-30 20:47:38

简介

Struts2 是一个基于 MVC 设计模式的 Web 应用框架,它本质上相当于一个 servlet,在 MVC 设计模式中,Struts2 作为控制器 (Controller) 来建立模型与视图的数据交互。

Struts2 是 Struts1 的下一代产品,是在 struts1和 WebWork 的技术基础上进行了合并的全新的 Struts2 框架。其全新的 Struts2 的体系结构与 Struts1 的体系结构差别巨大。

Struts2 以 WebWork 为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以 Struts2 可以理解为 WebWork 的更新产品。

虽然从 Struts1到 Struts2 有着太大的变化,但是相对于 WebWork,Struts2 的变化很小。

开发环境官网下载,下面示例使用版本为 struts-2.3.37,点击可直接下载 。

快速开始

导包

这里可以直接导入解压目录下 apps/struts2-blank.war 中所有 jar 。

Hello Struts2

代码

1、编写 Action 类:

package com.zze.action;

public class HelloAction {
    public String execute() {
        System.out.println("hello Struts 2");
        return "hello";
    }
}

com.zze.action.HelloAction

2、新建要跳转到的 jsp 页:

<%--
  Created by zze.
  Date: 2019/1/22
  Time: 10:48
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello</title>
</head>
<body>
<h3>hello Struts2</h3>
</body>
</html>

WEB-INF/hello.jsp

3、在 src 下新建如下配置文件:

<?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>
    <!--配置 Strut 2 的包-->
    <package name="test1" extends="struts-default" namespace="/">
        <!--配置 Action-->
        <action name="hello" class="com.zze.action.HelloAction">
            <result name="hello">/WEB-INF/hello.jsp</result>
        </action>
    </package>
</struts>

struts.xml

4、在 web.xml 中配置核心过滤器:

<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>

web.xml

5、部署到 tomcat,运行:

java框架之Struts2(1)-简介及入门

执行流程

1、首先浏览器请求 localhost:8080/hello 。

2、在 web.xml 中配置的核心过滤器会拦截到请求。

3、拦截器会解析 url,在 struts.xml 中根据 package 标签上的 namespace 属性和  action 标签上的 name 找到对应请求路径的 action。

4、执行对应 action 标签对应 Action 类对象的 execute 方法。

5、execute 方法返回字符串,这个字符串与 action 标签下的 result 标签的 name 匹配。

6、返回匹配到的 result 标签中定义的路径对应的 jsp。

7、浏览器接收、渲染。

java框架之Struts2(1)-简介及入门

配置文件的加载顺序

已经知道 Struts2 的入口就是核心过滤器 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter ,在程序启动时会执行过滤器的 init 方法,及会执行核心过滤器的 init 方法:

 public void init(FilterConfig filterConfig) throws ServletException {
     InitOperations init = new InitOperations();
     Dispatcher dispatcher = null;
     try {
         FilterHostConfig config = new FilterHostConfig(filterConfig);
         init.initLogging(config);
         dispatcher = init.initDispatcher(config);
         init.initStaticContentLoader(config, dispatcher);

         prepare = new PrepareOperations(dispatcher);
         execute = new ExecuteOperations(dispatcher);
         this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);

         postInit(dispatcher, filterConfig);
     } finally {
         if (dispatcher != null) {
             dispatcher.cleanUpAfterInit();
         }
         init.cleanup();
     }
 }

org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter#init

而 Struts2 相关的配置文件就是在第 7 行的 init.initDispatcher(config) 中加载的:

 public Dispatcher initDispatcher( HostConfig filterConfig ) {
     Dispatcher dispatcher = createDispatcher(filterConfig);
     dispatcher.init();
     return dispatcher;
 }

org.apache.struts2.dispatcher.ng.InitOperations#initDispatcher

再看到 dispatcher.init() :

 public void init() {

     if (configurationManager == null) {
         configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
     }

     try {
         init_FileManager();
         init_DefaultProperties(); // [1]
         init_TraditionalXmlConfigurations(); // [2]
         init_LegacyStrutsProperties(); // [3]
         init_CustomConfigurationProviders(); // [5]
         init_FilterInitParameters() ; // [6]
         init_AliasStandardObjects() ; // [7]

         Container container = init_PreloadConfiguration();
         container.inject(this);
         init_CheckWebLogicWorkaround(container);

         if (!dispatcherListeners.isEmpty()) {
             for (DispatcherListener l : dispatcherListeners) {
                 l.dispatcherInitialized(this);
             }
         }
         errorHandler.init(servletContext);

     } catch (Exception ex) {
         if (LOG.isErrorEnabled())
             LOG.error("Dispatcher initialization failed", ex);
         throw new StrutsException(ex);
     }
 }

org.apache.struts2.dispatcher.Dispatcher#init

在上述 9-14 行就会加载  Struts2 相关配置文件,如下:

init_DefaultProperties(); // [1] 加载 struts2-core-2.3.37.jar!/org/apache/struts2/default.properties
init_TraditionalXmlConfigurations(); // [2] 加载 struts2-core-2.3.37.jar!/struts-default.xml、src:struts-plugin.xml、src:struts.xml
init_LegacyStrutsProperties(); // [3] 加载 src:struts.properties
init_CustomConfigurationProviders(); // [5] 加载配置提供类
init_FilterInitParameters() ; // [6] 加载 web.xml中 过滤器初始化参数
init_AliasStandardObjects() ; // [7] 加载 Bean 对象
总结上述,配置文件的加载顺序为:

-> default.properties

-> struts-default.xml

-> struts-plugin.xml

-> struts.xml

-> struts.properties

-> web.xml

标绿的是我们可配置的。

注意:后配置的常量会覆盖之前配置的常量。

配置相关

struts.xml

package标签

package 标签称为包,这个包与 Java 中的包概念不一致,它是为了更好地管理 action 的配置。

属性:

  • name:包的名称,只要在一个项目中不重名即可。
  • extends:继承一个包,通常值为 "struts-default"。
  • namespace:名称空间,与 action 标签的 name 属性共同组成访问路径。
    名称空间匹配的优先级从高到低有如下三种:

    1、带名称的名称空间:namespace="/aaa"。

    2、根名称空间:namespace="/"。

    3、默认名称空间:namespace=""。

  • abstract:标识当前包是用来被继承的。

action标签

配置 action 类。

属性:

  • name:与 package 标签上的 namespace 属性共同组成访问路径。
  • class:Action 类的全路径。
  • method:方法名,标识执行 Action 中哪个方法,默认值为 "execute" 。
  • converter:用于配置类型转换器。  

constant标签

配置常量。

属性:

  • name:常量名。
  • value:常量值。

include标签

分模块开发时使用,用来引入其它配置文件。

属性:

  • file:要引入的配置文件路径,如:"com/zze/config/demo1/struts.xml"。

常量配置

默认常量

在 Struts2 中提供了非常多默认的常量,在 "struts2-core-2.3.37.jar!/org/apache/struts2/default.properties" 中,如下:

#
# $Id$
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#
### START SNIPPET: complete_file

### Struts default properties
###(can be overridden by a struts.properties file in the root of the classpath)
###

### This can be used to set your default locale and encoding scheme
# struts.locale=en_US
struts.i18n.encoding=UTF-8

### if specified, the default object factory can be overridden here
### Note: short-hand notation is supported in some cases, such as "spring"
###       Alternatively, you can provide a com.opensymphony.xwork2.ObjectFactory subclass name here
# struts.objectFactory = spring

### specifies the autoWiring logic when using the SpringObjectFactory.
### valid values are: name, type, auto, and constructor (name is the default)
struts.objectFactory.spring.autoWire = name

### indicates to the struts-spring integration if Class instances should be cached
### this should, until a future Spring release makes it possible, be left as true
### unless you know exactly what you are doing!
### valid values are: true, false (true is the default)
struts.objectFactory.spring.useClassCache = true

### ensures the autowire strategy is always respected.
### valid values are: true, false (false is the default)
struts.objectFactory.spring.autoWire.alwaysRespect = false

### By default SpringObjectFactory doesn't support AOP
### This flag was added just temporally to check if nothing is broken
### See https://issues.apache.org/jira/browse/WW-4110
struts.objectFactory.spring.enableAopSupport = false

### if specified, the default object type determiner can be overridden here
### Note: short-hand notation is supported in some cases, such as "tiger" or "notiger"
###       Alternatively, you can provide a com.opensymphony.xwork2.util.ObjectTypeDeterminer implementation name here
### Note: By default, com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer is used which handles type detection
###       using generics. com.opensymphony.xwork2.util.GenericsObjectTypeDeterminer was deprecated since XWork 2, it's
###       functions are integrated in DefaultObjectTypeDeterminer now.
###       To disable tiger support use the "notiger" property value here.
#struts.objectTypeDeterminer = tiger
#struts.objectTypeDeterminer = notiger

### Parser to handle HTTP POST requests, encoded using the MIME-type multipart/form-data
# struts.multipart.parser=cos
# struts.multipart.parser=pell
# struts.multipart.parser=jakarta-stream
struts.multipart.parser=jakarta
# uses javax.servlet.context.tempdir by default
struts.multipart.saveDir=
struts.multipart.maxSize=2097152

### Load custom property files (does not override struts.properties!)
# struts.custom.properties=application,org/apache/struts2/extension/custom

### How request URLs are mapped to and from actions
#struts.mapper.class=org.apache.struts2.dispatcher.mapper.DefaultActionMapper

### Used by the DefaultActionMapper
### You may provide a comma separated list, e.g. struts.action.extension=action,jnlp,do
### The blank extension allows you to match directory listings as well as pure action names
### without interfering with static resources, which can be specified as an empty string
### prior to a comma e.g. struts.action.extension=, or struts.action.extension=x,y,z,,
struts.action.extension=action,,

### Used by FilterDispatcher
### If true then Struts serves static content from inside its jar.
### If false then the static content must be available at <context_path>/struts
struts.serve.static=true

### Used by FilterDispatcher
### This is good for development where one wants changes to the static content be
### fetch on each request.
### NOTE: This will only have effect if struts.serve.static=true
### If true -> Struts will write out header for static contents such that they will
###             be cached by web browsers (using Date, Cache-Content, Pragma, Expires)
###             headers).
### If false -> Struts will write out header for static contents such that they are
###            NOT to be cached by web browser (using Cache-Content, Pragma, Expires
###            headers)
struts.serve.static.browserCache=true

### Set this to false if you wish to disable implicit dynamic method invocation
### via the URL request. This includes URLs like foo!bar.action, as well as params
### like method:bar (but not action:foo).
### An alternative to implicit dynamic method invocation is to use wildcard
### mappings, such as <action name="*/*" method="{2}" class="actions.{1}">
struts.enable.DynamicMethodInvocation = false

### Set this to true if you wish to allow slashes in your action names.  If false,
### Actions names cannot have slashes, and will be accessible via any directory
### prefix.  This is the traditional behavior expected of WebWork applications.
### Setting to true is useful when you want to use wildcards and store values
### in the URL, to be extracted by wildcard patterns, such as
### <action name="*/*" method="{2}" class="actions.{1}"> to match "/foo/edit" or
### "/foo/save".
struts.enable.SlashesInActionNames = false

### Disables support for action: prefix
struts.mapper.action.prefix.enabled = false

### Blocks access to actions in other namespace than current with action: prefix
struts.mapper.action.prefix.crossNamespaces = false

### use alternative syntax that requires %{} in most places
### to evaluate expressions for String attributes for tags
struts.tag.altSyntax=true

### when set to true, Struts will act much more friendly for developers. This
### includes:
### - struts.i18n.reload = true
### - struts.configuration.xml.reload = true
### - raising various debug or ignorable problems to errors
###   For example: normally a request to foo.action?someUnknownField=true should
###                be ignored (given that any value can come from the web and it
###                should not be trusted). However, during development, it may be
###                useful to know when these errors are happening and be told of
###                them right away.
struts.devMode = false

### when set to true, resource bundles will be reloaded on _every_ request.
### this is good during development, but should never be used in production
### struts.i18n.reload=false

### Standard UI theme
### Change this to reflect which path should be used for JSP control tag templates by default
struts.ui.theme=xhtml
struts.ui.templateDir=template
### Change this to use a different token to indicate template theme expansion
struts.ui.theme.expansion.token=~~~
#sets the default template type. Either ftl, vm, or jsp
struts.ui.templateSuffix=ftl

### Configuration reloading
### This will cause the configuration to reload struts.xml when it is changed
### struts.configuration.xml.reload=false

### Location of velocity.properties file.  defaults to velocity.properties
struts.velocity.configfile = velocity.properties

### Comma separated list of VelocityContext classnames to chain to the StrutsVelocityContext
struts.velocity.contexts =

### Location of the velocity toolbox
struts.velocity.toolboxlocation=

### used to build URLs, such as the UrlTag
struts.url.http.port = 80
struts.url.https.port = 443
### possible values are: none, get or all
struts.url.includeParams = none

### Load custom default resource bundles
# struts.custom.i18n.resources=testmessages,testmessages2

### workaround for some app servers that don't handle HttpServletRequest.getParameterMap()
### often used for WebLogic, Orion, and OC4J
struts.dispatcher.parametersWorkaround = false

### configure the Freemarker Manager class to be used
### Allows user to plug-in customised Freemarker Manager if necessary
### MUST extends off org.apache.struts2.views.freemarker.FreemarkerManager
#struts.freemarker.manager.classname=org.apache.struts2.views.freemarker.FreemarkerManager

### Enables caching of FreeMarker templates
### Has the same effect as copying the templates under WEB_APP/templates
### struts.freemarker.templatesCache=false

### Enables caching of models on the BeanWrapper
struts.freemarker.beanwrapperCache=false

### See the StrutsBeanWrapper javadocs for more information
struts.freemarker.wrapper.altMap=true

### maxStrongSize for MruCacheStorage for freemarker, when set to 0 SoftCacheStorage which performs better in heavy loaded application
### check WW-3766 for more details
struts.freemarker.mru.max.strong.size=0

### configure the XSLTResult class to use stylesheet caching.
### Set to true for developers and false for production.
struts.xslt.nocache=false

### Whether to always select the namespace to be everything before the last slash or not
struts.mapper.alwaysSelectFullNamespace=false

### Whether to allow static method access in OGNL expressions or not
struts.ognl.allowStaticMethodAccess=false

### Whether to throw a RuntimeException when a property is not found
### in an expression, or when the expression evaluation fails
struts.el.throwExceptionOnFailure=false

### Logs as Warnings properties that are not found (very verbose)
struts.ognl.logMissingProperties=false

### Caches parsed OGNL expressions, but can lead to memory leaks
### if the application generates a lot of different expressions
struts.ognl.enableExpressionCache=true

### Indicates if Dispatcher should handle unexpected exceptions by calling sendError()
### or simply rethrow it as a ServletException to allow future processing by other frameworks like Spring Security
struts.handle.exception=true
### END SNIPPET: complete_file

struts2-core-2.3.37.jar!/org/apache/struts2/default.properties

下面描述部分配置的含义:

struts.i18n.encoding=UTF-8 # 处理了 POST 请求中文乱码
struts.multipart.saveDir= # 上传文件默认保存位置
struts.multipart.maxSize=2097152 # 上传文件最大大小
struts.action.extension=action,, # 请求路径默认后缀为 action 或空白

修改常量

可以在三个位置修改常量的值:

  • struts.xml

    直接通过 constant 标签修改常量。

    <constant name="struts.action.extension" value="do"/>

    struts.xml

  • struts.properties

    需在 src 下新建一个 struts.properties 文件,在其中以 key=value 的形式修改常量,例:

    struts.action.extension=do

    struts.properties

  • web.xml

    需在过滤器中通过配置过滤器的初始化参数修改常量,例:

    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
        <init-param>
            <param-name>struts.action.extension</param-name>
            <param-value>do</param-value>
        </init-param>
    </filter>

    web.xml

Action相关

Action的编写

Action 类的编写可有三种方式如下:

POJO

Action 类可以是一个 POJO(简单的 Java 类),例:

package com.zze.action;

public class HelloAction1 {
    public String execute() {
        System.out.println("hello Struts2");
        return "hello";
    }
}

com.zze.action.HelloAction1

实现Action接口

Action 类也可以实现 Action 接口,例:

package com.zze.action;

import com.opensymphony.xwork2.Action;

/**
 * 实现接口 Action
 * Action 接口中提供了五个逻辑视图名称常量:
 * public static final String SUCCESS = "success";
 * public static final String NONE = "none";
 * public static final String ERROR = "error";
 * public static final String INPUT = "input";
 * public static final String LOGIN = "login";
 */
public class HelloAction2 implements Action {
    @Override
    public String execute() throws Exception {
        System.out.println("hello Struts2");
        return SUCCESS;
    }
}

com.zze.action.HelloAction2

继承ActionSupport(推荐)

Action 类还可以继承  ActionSupport 类,例:

package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;

public class HelloAction3 extends ActionSupport {

    @Override
    public String execute() throws Exception {
        System.out.println("hello Struts2");
        return super.execute();
    }
}

com.zze.action.HelloAction3

Action的访问

Action 访问配置也有三种方式,下面通过三种配置来访问下面 Action。

package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;

public class TestAction extends ActionSupport {
    public String save() {
        System.out.println("from save");
        return NONE;
    }

    public String delete(){
        System.out.println("from delete");
        return NONE;
    }

    public String select(){
        System.out.println("from select");
        return NONE;
    }

    public String update(){
        System.out.println("from update");
        return NONE;
    }
}

com.zze.action.TestAction

method配置

<?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="test" extends="struts-default" namespace="/">
        <!--localhost:8080/save-->
        <action name="save" class="com.zze.action.TestAction" method="save"></action>
        <!--localhost:8080/delete-->
        <action name="delete" class="com.zze.action.TestAction" method="delete"></action>
        <!--localhost:8080/select-->
        <action name="select" class="com.zze.action.TestAction" method="select"></action>
        <!--localhost:8080/update-->
        <action name="update" class="com.zze.action.TestAction" method="update"></action>
    </package>
</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="test" extends="struts-default" namespace="/">
        <!--localhost:8080/save-->
        <!--localhost:8080/delete-->
        <!--localhost:8080/update-->
        <!--localhost:8080/select-->
        <!--method:{1} 代表 name 中第一个通配符的取值-->
        <action name="*" class="com.zze.action.TestAction" method="{1}"></action>

        <!--localhost:8080/Test_save-->
        <!--localhost:8080/Test_delete-->
        <!--localhost:8080/Test_update-->
        <!--localhost:8080/Test_select-->
        <!--通配符更抽象写法-->
        <!--<action name="*_*" class="com.zze.action.{1}Action" method="{2}"></action>-->
    </package>
</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>
    <!--
    设置常量,开启动态方法访问
    开启之后可通过 <host>:<port>/<actionName>!<methodName> 访问:
    host : ip
    port : 端口
    actionName : action 名称
    methodName : 要访问的方法名称
    -->
    <constant name="struts.enable.DynamicMethodInvocation" value="true"/>
    <package name="test" extends="struts-default" namespace="/">
        <!--localhost:8080/test!save-->
        <!--localhost:8080/test!delete-->
        <!--localhost:8080/test!select-->
        <!--localhost:8080/test!delete-->
        <action name="test" class="com.zze.action.TestAction" method="{1}"></action>
    </package>
</struts>

struts.xml