Struts入门

补充
Struts核心jar包的获取,在Struts2的源文件的apps下,解压struts2-blank.war,在其lib下对应的就是核心jar包。
其它jar包:
struts2-convention-plugin-2.3.28.jar struts2的注解开发的jar包
struts2-spring-plugin-2.3.28.jar struts2用于整合spring的jar包

Struts入门

简介

Struts是流行和成熟的基于MVC设计模式的Web应用程序框架。
MVC模式
MVC模式

Struts2的工作原理及文件结构

工作原理
工作原理
FilterDispatcher是struts的2.0-2.1.2的核心过滤器。
最新的过滤器是StrutsPrepareAndExecuteFilter
例如在web.xml中配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>04_13_HelloStruts</display-name>

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

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

核心文件

web.xml

web.xml
任何MVC框架都需要与Web应用整合,这就不得不借助于web.xml文件,只要配置在web.xml文件中的Servlet才会被应用加载。
通常,所有的MVC框架都需要web应用加载一个核心控制器,对于Struct2框架而言,需要加载 StrutsPrepareAndExecuteFilter,只要web应用负责加载StrutsPrepareAndExecuteFilterStrutsPrepareAndExecuteFilter将会加载Struts2框架。

struts.xml

struts.xml是struts2的核心配置文件,在开发过程中利用率最高。该文件主要负责管理应用中的Action映射,以及该Action包含的Result定义等。

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

    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.devMode" value="true" />

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

        <default-action-ref name="index" />

        <global-results>
            <result name="error">/WEB-INF/jsp/error.jsp</result>
        </global-results>

        <global-exception-mappings>
            <exception-mapping exception="java.lang.Exception" result="error"/>
        </global-exception-mappings>

        <action name="index">
            <result type="redirectAction">
                <param name="actionName">HelloWorld</param>
                <param name="namespace">/example</param>
            </result>
        </action>
    </package>

    <include file="example.xml"/>

    <!-- Add packages here -->

</struts>

package元素
package元素用来配置包。

属性说明
name必需属性,标识包的名字,以便在其它包中被引用
extends可选属性,指定包继承自其它包
namespace可选属性,指定命名空间,标识包下的action的访问路径
abstract可选属性,指定包为抽象包,抽象包不能有Action的定义

action元素
Struts2框架通过Action对象来处理HTTP请求,该请求的URL地址对应的Action即配置在Action元素中。

属性说明
name必需属性,标识Action
class可选属性,指定Action对象对应的实现类
method可选属性,指定请求Action调用的方法
converter可选属性,指定类型转换器的类

result元素
result元素用来设置返回给浏览器的视图。配置result元素需指定name和type两个属性。

name属性对应Action方法返回的值,success为其默认值。
type属性指定结果类型,默认的类型是dispatcher。

结果类型说明
dispatcher将请求forward转发到指定的JSP页面
redirect将请求重定向到指定的视图资源
chain处理Action链
freemarker指定使用freemarker模板作为视图
httpheader控制特殊的HTTP行为
redirect-action直接跳转打其他Action
stream向浏览器返回一个InputStream(一般用户文件下载)
velocity指定使用velocity模板作为视图
xslt用于XML/XSLT整合
plainText显示某个页面的原始代码

include元素
global-results元素
配置包中的全局结果。当一个Action处理用户请求后返回时,会首先在该Action本身的局部结果中进行搜索,如果局部结果中没有对应的结果,则会查找全局结果。

default-action-ref元素
用来配置默认的Action,即如果Struts2找不到对应的Action时,就会使用默认的Action来处理用户请求。

struts.properties

struts.properties
struts2框架的全局属性文件,自动加载。该文件包好很多key-value对。
该文件可以完全配置在struts.xml文件中,使用constant元素。

### When set to true, Struts will act much more friendly for developers
struts.devMode = true

### Enables reloading of internationalization files
struts.i18n.reload = true

### Enables reloading of XML configuration files
struts.configuration.xml.reload = true

### Sets the port that the server is run on
struts.url.http.port = 8080

常量详解
请参考Struts2学习(二):struts2配置详解!

<struts>  
    <!-- 指定Web应用的默认编码集,相当于调用HttpServletRequest的setCharacterEncoding方法 -->  
    <constant name="struts.i18n.encoding" value="UTF-8" />  

    <!--  
        该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。  
        如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。  
    -->  
    <constant name="struts.action.extension" value="do" />  

    <!-- 设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 -->  
    <constant name="struts.serve.static.browserCache" value="false" />  

    <!-- 当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 -->  
    <constant name="struts.configuration.xml.reload" value="true" />  

    <!-- 开发模式下使用,这样可以打印出更详细的错误信息 -->  
    <constant name="struts.devMode" value="true" />  

    <!-- 默认的视图主题 -->  
    <constant name="struts.ui.theme" value="simple" />  

    <!-- spring 托管 -->  
    <constant name="struts.objectFactory" value="spring" />  

    <!--  
        指定加载struts2配置文件管理器,默认为org.apache.struts2.config.DefaultConfiguration  
        开发者可以自定义配置文件管理器,该类要实现Configuration接口,可以自动加载struts2配置文件。  
    -->  
    <constant name="struts.configuration"  
        value="org.apache.struts2.config.DefaultConfiguration" />  

    <!-- 设置默认的locale和字符编码 -->  
    <constant name="struts.locale" value="zh_CN" />  
    <constant name="struts.i18n.encoding" value="GBK" />  

    <!-- 指定Struts的工厂类 -->  
    <constant name="struts.objectFactory" value="spring"></constant>  

    <!--  
        指定spring框架的装配模式,装配方式有: name, type, auto, and constructor (name  
        是默认装配模式)  
    -->  
    <constant name="struts.objectFactory.spring.autoWire" value="name" />  

    <!-- 该属性指定整合spring时,是否对bean进行缓存,值为true or false,默认为true -->  
    <cosntant name="struts.objectFactory.spring.useClassCache" />  

    <!-- 指定类型检查,包含tiger和notiger -->  
    <cosntant name="struts.objectTypeDeterminer" value="tiger" />  

    <!-- 该属性指定处理 MIME-type multipart/form-data,文件上传 -->  
    <constant name="struts.multipart.parser" value="cos" />  
    <constant name="struts.multipart.parser" value="pell" />  
    <constant name="struts.multipart.parser" value="jakarta" />  

    <!-- 指定上传文件时的临时目录,默认使用 javax.servlet.context.tempdir -->  
    <constant name="struts.multipart.saveDir" value="/tmpuploadfiles" />  

    <!-- 该属性指定Struts 2文件上传中整个请求内容允许的最大字节数 -->  
    <constant name="struts.multipart.maxSize" value="2097152" />  

    <!--  
        该属性指定Struts2应用加载用户自定义的属性文件,该自定义属性文件指定的属性不会覆盖  
        struts.properties文件中指定的属性。如果需要加载多个自定义属性文件,多个自定义属性文  
        件的文件名以英文逗号(,)隔开。(也就是说不要改写struts.properties!)  
    -->  
    <constant name="struts.custom.properties"  
        value="application,org/apache/struts2/extension/custom" />  

    <!-- 
        指定请求url与action映射器,默认为org.apache.struts2.dispatcher.mapper.DefaultActionMapper 
    -->  
    <constant name="struts.mapper.class"  
        value="org.apache.struts2.dispatcher.mapper.DefaultActionMapper" />  

    <!-- 指定action的后缀,默认为action -->  
    <constant name="struts.action.extension" value="do" />  

    <!-- 被 FilterDispatcher使用指定浏览器是否缓存静态内容,测试阶段设置为false,发布阶段设置为true. -->  
    <constant name="struts.serve.static.browserCache" value="true" />  

    <!-- 设置是否支持动态方法调用,true为支持,false不支持. -->  
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />  

    <!-- 设置是否可以在action中使用斜线,默认为false不可以,想使用需设置为true. -->  
    <constant name="struts.enable.SlashesInActionNames" value="true" />  

    <!-- 是否允许使用表达式语法,默认为true. -->  
    <constant name="struts.tag.altSyntax" value="true" />  

    <!-- 设置当struts.xml文件改动时,是否重新加载 -->  
    <cosntant name="struts.configuration.xml.reload" value="true" />  

    <!-- 设置struts是否为开发模式,默认为false,测试阶段一般设为true. -->  
    <cosntant name="struts.devMode" value="true" />  

    <!-- 设置是否每次请求,都重新加载资源文件,默认值为false. -->  
    <cosntant name="struts.i18n.reload" value="false" />  

    <!-- 标准的UI主题,默认的UI主题为xhtml,可以为simple,xhtml或ajax -->  
    <cosntant name="struts.ui.theme" value="xhtml" />  

    <!-- 模板目录 -->  
    <cosntant name="struts.ui.templateDir" value="template" />  

    <!-- 设置模板类型. 可以为 ftl, vm, or jsp -->  
    <cosntant name="struts.ui.templateSuffix" value="ftl" />  

    <!-- 定位velocity.properties 文件. 默认velocity.properties -->  
    <cosntant name="struts.velocity.configfile" value="velocity.properties" />  

    <!-- 设置velocity的context. -->  
    <cosntant name="struts.velocity.contexts" value="...." />  

    <!-- 定位toolbox -->  
    <cosntant name="struts.velocity.toolboxlocation" value="...." />  

    <!-- 指定web应用的端口 -->  
    <cosntant name="struts.url.http.port" value="80" />  

    <!-- 指定加密端口 -->  
    <cosntant name="struts.url.https.port" value="443" />  

    <!-- 设置生成url时,是否包含参数.值可以为: none,get or all -->  
    <cosntant name="struts.url.includeParams" value="get" />  

    <!-- 设置要加载的国际化资源文件,以逗号分隔. -->  
    <cosntant name="struts.custom.i18n.resources" value="application" />  

    <!--  
        对于一些web应用服务器不能处理HttpServletRequest.getParameterMap(), 像  
        WebLogic,Orion, and OC4J等,须设置成true,默认为false.  
    -->  
    <cosntant name="struts.dispatcher.parametersWorkaround" value="false" />  

    <!-- 指定freemarker管理器 -->  
    <cosntant name="struts.freemarker.manager.classname"  
        value="org.apache.struts2.views.freemarker.FreemarkerManager" />  

    <!-- 设置是否对freemarker的模板设置缓存,效果相当于把template拷贝到 WEB_APP/templates. -->  
    <cosntant name="struts.freemarker.templatesCache" value="false" />  

    <!-- 通常不需要修改此属性. -->  
    <cosntant name="struts.freemarker.wrapper.altMap" value="true" />  

    <!-- 指定xslt result是否使用样式表缓存.开发阶段设为true,发布阶段设为false. -->  
    <cosntant name="struts.xslt.nocache" value="false" />  

    <!-- 设置struts自动加载的文件列表. -->  
    <cosntant name="struts.configuration.files"  
        value="struts-default.xml,struts-plugin.xml,struts.xml" />  

    <!-- 设定是否一直在最后一个slash之前的任何位置选定namespace. -->  
    <cosntant name="struts.mapper.alwaysSelectFullNamespace"  
        value="false" />  
</struts>

深入

访问Servlet API

Web应用中通常需要访问的Servlet API就是HttpServletRequest、HttpSession、ServletContext这3个类,分别代表JSP内置对象中的request、session、application。

Struts2提供了三种方式来访问Servlet API

  1. ActionContext
  2. 实现***Aware接口
  3. ServletActionContext

ActionContext
常用方法

  • public Object get(Object key):取得HttpServletRequest中key的值;

  • void put(String key,Object value):设置HttpServletRequest中key的值为value。模拟Servlet API中的HttpServletRequest 的 setAttribute() 方法。

  • public Map getApplication():获取封装了ServletContext的Map对象;

  • void setApplication(Map application):设置ServletContext实例;

  • static ActionContext getContext():静态方法,获取系统的ActionContext实例;

  • Map getParameters():类似于HttpServletRequest中的getParametersMap方法;

  • public Map getSession():获取封装了HttpSession的Map对象;

  • void setSession(Map session):直接传入一个Map实例,将该Map实例里的key-value对转换为session的属性名和属性值;

如下:

    ActionContext context = ActionContext.getContext();
    //request中放入("name", "tom")
    context.put("name", "request_tom");
    //application中放入("name", "tom")
    context.getApplication().put("name", "application_tom");
    //session中放入("name", "tom")
    context.getSession().put("name", "session_tom");

在JSP页面中输出:

<center>
    ${applicationScope.name}<br/>
    ${sessionScope.name}<br/>
    <%=request.getAttribute("name") %><br/>
    ${name}<br/>
</center>

注意:

${param.name} 等价于 request.getParamter("name"),这两种方法一般用于服务器从页面或者客户端获取的内容。

${requestScope.name} 等价于 request.getAttribute("name"),一般是从服务器传递结果到页面,在页面中取出服务器保存的值。

Action搜索顺序

例如:

http://localhost:8080/struts2/path1/path2/path3/student.action

第一步:判断package是否存在,如:path/path2/path3/

  • 存在
    • 第二步:判断action是否存在,如果不存在则去默认namespace的package里面寻找action
    • 第三步:如果没有,报错
  • 不存在
    • 第二步:检查上一级路径的package是否存在(直到默认namespace),重复第一步。
    • 第三步:如果没有,报错

动态方法调用

动态方法调用是为了解决一个Action对应多个请求的处理,以免Action太多。
例如,在HelloWorldAction定义了多个方法,如下:

package com.imooc.action;

import com.opensymphony.xwork2.ActionSupport;

public class HelloWorldAction extends ActionSupport {

    @Override
    public String execute() throws Exception {

        System.out.println("执行execute");
        return SUCCESS;

    }

    public String add()
    {
        return SUCCESS;
    }

    public String update()
    {
        return SUCCESS;
    }
}

哪么如何访问add、update方法?
struts2提供了3种方式:
指定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="default" namespace="/" extends="struts-default">
        <action name="helloworld" class="com.imooc.action.HelloWorldAction">
            <result>/result.jsp</result>
        </action>
        <action name="addAction" method="add" class="com.imooc.action.HelloWorldAction">
            <result>/add.jsp</result>
        </action>
        <action name="updateAction" method="update" class="com.imooc.action.HelloWorldAction">
            <result>/update.jsp</result>
        </action>
    </package>

</struts>

如上配置,通过http://localhost:8080/04_13_HelloStruts/updateAction.action即可访问到update.jsp页面。

感叹号方式
这种方式不太推荐。如果要使用,必须开启struts.enable.DynamicMethodInvocation

<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>

HelloWorldAction文件,如下:

    public String add()
    {
        return "add";
    }

    public String update()
    {
        return "update";
    }

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="default" namespace="/" extends="struts-default">
        <action name="helloworld" class="com.imooc.action.HelloWorldAction">
            <result>/result.jsp</result>
            <result name="add">/add.jsp</result>
            <result name="update">/update.jsp</result>
        </action>
    </package>

    <constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>

</struts>

通过http://localhost:8080/04_13_HelloStruts/helloworld!add.action即可访问到add方法。

通配符方式
官方推荐,通配符说明

通配符说明
*匹配0或者多个字符除了”/”
**匹配0或者多个字符包含”/”
\character转移字符,”\”匹配”\”,”*”匹配”*”

通配符”*”通常用在<action>标签的name属性中,而在class、name属性及result元素中使用{N}的形式来代表前面第N*所匹配的字符串,{0}来代表URL请求的整个Action字符串。

<?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="default" namespace="/" extends="struts-default">
        <action name="helloworld_*"  method="{1}" class="com.imooc.action.HelloWorldAction">
            <result>/result.jsp</result>
            <result name="add">/{1}.jsp</result>
            <result name="update">/{1}.jsp</result>
        </action>
    </package>

    <constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
</struts>

通过http://localhost:8080/04_13_HelloStruts/helloworld_add.action即可访问到add.jsp页面。

指定多个配置文件

action可能会有非常多,成百上千,如果都配置到一个struts.xml文件中,文件就会非常大。就可以用包含多个配置文件的方法来解决这个问题,每个模块可以有自己的配置文件,通过一个总的struts.xml来进行添加包含。
指定多个配置文件

例如创建一个helloworld.xml文件,在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="default" namespace="/" extends="struts-default">
        <action name="helloworld_*" method="{1}"
            class="com.imooc.action.HelloWorldAction">
            <result>/result.jsp</result>
            <result name="add">/{1}.jsp</result>
            <result name="update">/{1}.jsp</result>
        </action>
    </package>

</struts>
<?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>

    <include file="helloworld.xml"></include>
    <constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>

</struts>

默认Action

默认Action是为了解决,当访问某一个Action,别人找不到对应的Action。默认的Action就会起到作用。

        <!-- 默认的action -->
        <default-action-ref name="index"></default-action-ref>
        <action name="index">
            <result>/error.jsp</result>
        </action>

Struts后缀

比如对于http://localhost:8080/04_13_HelloStruts/abc.action,想把后面的action改为do或者html。
在struts中加入如下的语句。

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

http://localhost:8080/04_13_HelloStruts/helloworld.html即可访问到页面。

接收参数

如何在action中接收参数?
例子:
新建一个login.jsp的页面,如下:

<form action="LoginAction.action" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="password">
    <input type="submit" value="提交">
</form>

那么如何获取到用户名和密码呢?

使用Action的属性接收参数
新建一个LoginAction类,如下,有username和password两个属性。

package com.wz.action;

import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport  {

    private String username;
    private String password;

    public String login(){

        System.out.println(username);

        return SUCCESS;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

查看输出,控制台可以输出username,说明已经获取到了数据。

使用Domain Model接收参数
如果参数很多,如何获取?
新建一个User类,用来保存提交的信息,如下:

package com.wz.model;

public class User {

    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

对应的LoginAction类如下,添加一个User属性:

package com.wz.action;

import com.opensymphony.xwork2.ActionSupport;
import com.wz.model.User;

public class LoginAction extends ActionSupport  {

    private User user;

    public String login(){

        System.out.println(user.getUsername());

        return SUCCESS;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

}

最后,还要修改login.jsp页面,修改后的结果如下:

<form action="LoginAction.action" method="post">
    用户名:<input type="text" name="user.username">
    密码:<input type="password" name="user.password">
    <input type="submit" value="提交">
</form>

使用ModelDriven接收参数
要实现ModelDriven接口,要注意的是:

  • user属性要实例化
  • 不需要重写user的getter和setter方法

如下:

package com.wz.action;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.wz.model.User;

public class LoginAction extends ActionSupport implements ModelDriven<User>  {

    private User user = new User();

    public String login(){

        System.out.println(user.getUsername());

        return SUCCESS;
    }

    @Override
    public User getModel() {
        return user;
    }

}

并且将login.jsp页面,修改为原来的样式:

<form action="LoginAction.action" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="password">
    <input type="submit" value="提交">
</form>

数据集合处理
假设User中有bookList字符串数组该如何处理呢?

private List<String> bookList;

要在login.jsp页面进行如下的设置:

<form action="LoginAction.action" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="password">
    书籍1:<input type="text" name="bookList[0]">
    书籍2:<input type="text" name="bookList[1]">
    <input type="submit" value="提交">
</form>

处理结果类型

Struts2处理流程
Struts2处理流程

这里写图片描述

内置属性
内置属性

处理结果类型
处理结果是通过在struts.xml使用<result/>标签配置结果。
根据位置不同,分为两种结果:

  • 局部结果:将<result/>作为<action/>元素的子元素配置
  • 全局结果:将<result/>作为<global-result/>元素的子元素配置
CCF大数据与计算智能大赛-面向电信行业存量用户的智能套餐个性化匹配模型联通赛-复赛第二名-【多分类,embedding】.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值