Struts2学习总结(二)

一、 OGNL

1、OGNL,Object-Graph Navigation Language,对象图导航语言,比EL表达式强大很多倍的语言;
	EL:从域对象中获得数据,从EL的11个对象中获取;
	OGNL:调用对象的方法,获取Struts2的值栈的数据,OGNL其实是第三方的表达式语言。
	
2、OGNL在java环境下的使用
   a、OGNL调用对象的方法:
@Test
public void demo1() throws OgnlException {
		// 获得context
		OgnlContext context = new OgnlContext();
		// 获得跟对象
		Object root = context.getRoot();
		// 执行表达式
		Object obj = Ognl.getValue("'helloworld'.length()", context, root);

		System.out.println(obj);
	}
输出结果:10

	b、OGNL访问对象的静态方法:@类名@方法名
@Test
public void demo2() throws OgnlException {
		// 获得context
		OgnlContext context = new OgnlContext();
		// 获得跟对象
		Object root = context.getRoot();
		// 执行表达式:@类名@方法名
		Object obj = Ognl.getValue("@java.lang.Math@random()", context, root);
		System.out.println(obj);
	}
输出结果:0.9202230352601020-1的随机数)
	
	c、访问Root中的数据 不需要加#
@Test
public void demo3() throws OgnlException {
		// 获得context
		OgnlContext context = new OgnlContext();

		// 执行表达式:
		User user = new User("张三", "123456");
		context.setRoot(user);

		// 获得跟对象
		Object root = context.getRoot();
		Object userName = Ognl.getValue("userName", context, root);
		Object password = Ognl.getValue("password", context, root);
		System.out.println("用户名:" + userName + "   	" + "密码:" + password);
	}
输出结果:用户名:张三   	密码:123456

	d、访问context中的数据 需要加#
@Test
public void demo4() throws OgnlException {
		// 获得context
		OgnlContext context = new OgnlContext();
		// 获得跟对象
		Object root = context.getRoot();
		// 向context中存入数据
		context.put("name", "张三");
		// 执行表达式:
		Object name = Ognl.getValue("#name", context, root);

		System.out.println("用户名:" + name);
	}
输出结果:用户名:张三


public class User{
	private String userName;
	private String password;
	public User() {
		super();
	}
	public User(String userName, String password) {
		super();
		this.userName = userName;
		this.password = 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;
	}
}

二、值栈

	Struts2将XWork对OGNL的扩展这一套机制封装起来,这个对象就被叫做ValueStack。
1、什么是值栈
		ValueStack是Struts2的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实
	现类,客户端发起一个请求Struts2架构会创建一个action实例同时创建一个OgnlValueStack值
	栈实例,OgnlValueStack贯穿整个action的生命周期,Struts2中使用OGNL将请求Action的参数
	封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性值。
		ValueStack其实类似于一个数据中转站,(Struts2的框架当中的数据就都保存到了ValueStack中)。

2、值栈的内部结构
	ValueStack中有两个主要的区域:
		root区域:其实就是一个ArrayList,里面一般放置对象
		context区域:其实就是一个Map,里面放置的是Web开发的常用的对象数据的引用(root、request、session、application、
		parameters、attr)(attr指的就是从小到大范围去搜索数据)。
		注:获取root区域的数据不需要加#;
		    获取context区域的数据需要加#。
所说的操作值栈,通常指的就是操作ValueStack中的root区域。

3、值栈与ActionContext的关系
	ServletContext:Servlet的上下文
	ActionContext:Action的上下文
		通过源码查看到:当请求过来的时候,执行过滤器中doFilter方法,在这个方法中创建ActionContext,在创建ActionContext的
	过程中,创建ValueStack对象,将ValueStack对象传递给ActionContext对象,所以可以通过ActionContext获取值栈的对象。
		ActionContext对象之所以能够访问Servlet的API(访问时域对象的数据),是因为在其内部有值栈的引用。

4、获得值栈
	a:通过ActionContext对象获取值栈
	b:在Struts2内部,将值栈存入到request中一份,所以可以通过request对象获得
	
public class ValueStackDemo extends ActionSupport {
	@Override
	public String execute() {
		// 一种:通过ActionContext获得:
		ValueStack valueStack1 = ActionContext.getContext().getValueStack();
		// 二种:通过request对象获得:
		ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest()
				.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
		// 一个Action的实例,只会创建一个ValueStack的对象
		System.out.println(valueStack1 == valueStack2);
		return NONE;
	}
}
输出结果:true

5、操作值栈---向值栈中存入数据

	第一种:在Action中提供属性的get方法的方式
		默认情况下,将Action对象压入到值栈,Action的属性也会在值栈中(继承自ActionSupport)。
		
public class ValueStackDemo extends ActionSupport {
	private User user;
	public User getUser() {
		return user;
	}
	@Override
	public String execute() {
		// 向ValueStack中存值
		user = new User("张三", "123456");
		return SUCCESS;
	}
}

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>查看值栈的内部结构</h1>
	<s:debug></s:debug>
	<!--方式一的获取:利用Action在值栈的特性  -->
	<s:property value="user.userName"/>
	<s:property value="user.password"/>
</body>
</html>


	第二种:调用值栈中的方法实现
		push(Object obj):针对对象
		set(String key,Object obj):针对集合

public class ValueStackDemo4 extends ActionSupport {
	@Override
	public String execute() {
		// 向ValueStack中保存数据:
		// 获得值栈对象:
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		// 使用push(Object obj); set(String key,Object obj);push一般保存对象,set一般保存集合
		User user = new User("李四", "12");
		// 压栈,现在user在栈顶的位置
		valueStack.push(user);
		valueStack.set("name", "王五");//创建一个Map集合、将Map压入到栈中,但是在debug中看不到,因为没有提供属性的get、set方法	
		
		// 向值栈中存入一个集合
		List<User> list = new ArrayList<User>();
		list.add(new User("aaa", "111"));
		list.add(new User("bbb", "222"));
		list.add(new User("ccc", "333"));
		ActionContext.getContext().getValueStack().set("list", list);
		
		/**
		 * 向context中存入数据
		 */
		ServletActionContext.getRequest().setAttribute("name", "request张三");
		ServletActionContext.getRequest().getSession().setAttribute("name", "session李四");
		ServletActionContext.getServletContext().setAttribute("name", "application王五");
		return SUCCESS;
	}
}

<!--方式二的获取:调用值栈中的方法实现 push/set 方法 -->
	<s:property value="userName" />
	<s:property value="password" />
	<s:property value="name" />

	<!--获取root中的数据  -->
	<s:property value="list[0].userName" />
	<s:property value="list[0].password" />
	<br />

	<s:property value="list[1].userName" />
	<s:property value="list[1].password" />
	<br />

	<s:property value="list[2].userName" />
	<s:property value="list[2].password" />
	<br />

	<s:iterator value="list">
		<s:property value="userName" />
		<s:property value="password" />
	</s:iterator>
	<hr>

	<!--获取context中的数据  -->
	<s:property value="#request.name" />
	<br />
	<s:property value="#session.name" />
	<br />
	<s:property value="#application.name" />
	<br />
	<s:property value="#attr.name" />
	<br />
	<!-- parameters获取的是页面传过来的值,在valueStackDemo5.action后+“?id=5”则获取的值是5,并打印在页面上 -->
	<s:property value="#parameters.id" />

6、EL也可以访问值栈数据
	因为Struts2的框架的底层对request.getAttribute(String name)的加强。

三、OGNL中的特殊字符

1、#号

	1、获取context的数据
	<h3>获取context的数据</h3>
	<%
		request.setAttribute("name", "张三");
	%>
	<s:property value="#request.name" />
	
	2、构建Map集合
	<h3>构建Map集合</h3>
	<s:iterator var="i" value="{'aa','bb','cc'}">
		<s:property value="i" />--<s:property value="#i" />
		<br />
	</s:iterator>
	<hr>
	<s:iterator var="entry" value="#{'aa':'11','bb':'22','cc':'33' }">
		<s:property value="key" />--<s:property value="value" />
		<br />
		<s:property value="#entry.key" />--<s:property value="#entry.value" />
		<br />
	</s:iterator>
	<hr>
	
	<!-- 单选框 -->
	性别:
	<input type="radio" name="sex1" value="男"><input type="radio" name="sex1" value="女"><br />
	<s:radio list="{'男','女'}" name="sex2" label="性别"></s:radio>
	<hr>
	性别:
	<input type="radio" name="sex3" value="1"><input type="radio" name="sex3" value="2"><br />
	<s:radio list="#{ '1':'男','2':'女' }" name="sex4" label="性别"></s:radio>

2、%号

	1、强制解析OGNL
		在不属于<s:property  />的标签获取解析OGNL的权利
	<h1>%号的用法</h1>
	<%
		request.setAttribute("name", "马东");
	%>
	姓名:
	<s:textfield name="name" value="%{ #request.name}"></s:textfield>

	2、强制不解析OGNL
	在<s:property />标签中也可放弃解析OGNL的权利
	<h1>%号的用法</h1>
	<%
		request.setAttribute("name", "马东");
	%>
	姓名:
	<s:property value="%{'#request.name'}" />

	3、$号
	在配置文件中使用OGNL
	属性文件:
		国际化地方:
			message_zh_CN.properties
			user.login=登录
			user.welcome=欢迎,${#session.user.username}

			message_en_US.properties
			user.login=login
			user.welcome=Welcome,${#session.user.username}

	XML文件:
		文件下载:
		<action name="download" class="xxx.ActionDemo">
			<result type="stream">
				<param name="Content-Type">文件类型</param>
				<param name="Content-Disposition">attachment;filename=${文件名}</param>
			</result>
		</action>

四、拦截器

1、什么是拦截器

	Interceptor:拦截器,起到拦截Action的作用。
		Filter:过滤器,过滤从客户端向服务器发送的请求。
		Interceptor:拦截器,拦截是客户端对Action的访问。更细粒度化的拦截。(拦截Action中的具体的方法)。
	Struts2框架核心的功能都是依赖拦截器实现。

2、Struts2的执行流程

		客户端向服务器发送一个Action的请求,执行核心过滤器(doFilter)方法。在这个方法中,调用executeAction()方法,
	在这个方法内部调用dispatcher.serviceAction();在这个方法内部创建一个Action代理,最终执行的是Action代理中的execute(),
	在代理中执行的execute方法中调用ActionInvocation的invoke方法。在这个方法内部递归执行一组拦截器(完成部分功能),
	如果没有下一个拦截器,就会执行目标Action,根据Action的返回的结果进行页面跳转。

在这里插入图片描述

3、拦截器入门

1、编写拦截器类
	编写一个类实现Interceptor接口或者继承AbstractInterceptor类。
public class InterceptorDemo1 extends AbstractInterceptor {
	@Override
	public String intercept(ActionInvocation invocation) throws Exception {
		System.out.println("InterceptorDemo1执行了...");
		String obj = invocation.invoke();
		System.out.println("InterceptorDemo1执行结束了...");
		return obj;
	}
}
public class InterceptorDemo2 extends AbstractInterceptor {
	@Override
	public String intercept(ActionInvocation invocation) throws Exception {
		System.out.println("InterceptorDemo2执行了...");
		String obj = invocation.invoke();
		System.out.println("InterceptorDemo2执行结束了...");
		return obj;
	}
}

2、对拦截器进行配置
	定义拦截器进行配置
<package name="demo1" extends="struts-default" namespace="/">
		<!--第一种方式:定义拦截器 -->
	<interceptors>
		<interceptor name="interceptorDemo1" class="interceptor.InterceptorDemo1"></interceptor> 
		<interceptor name="interceptorDemo2" class="interceptor.InterceptorDemo2"></interceptor> 
	</interceptors>

	<action name="actionDemo1" class="action.ActionDemo1">
		<result>/demo1/demo1.jsp</result>
	</action>

		<!--第一种方式引入拦截器 -->
	<interceptor-ref name="interceptorDemo1"></interceptor-ref> 
	<interceptor-ref name="interceptorDemo2"></interceptor-ref> 
	<!-- 一旦引入自定义拦截器,默认拦截器栈的拦截器就不执行了,这里手动配置 -->
	<interceptor-ref name="defaultStack"></interceptor-ref> 
</package>
	
	定义一个拦截器栈的方式
	<!--第二种方式:定义一个拦截器栈 -->
	<interceptors>
		<interceptor name="interceptorDemo1"
			class="interceptor.InterceptorDemo1"></interceptor>
		<interceptor name="interceptorDemo2"
			class="interceptor.InterceptorDemo2"></interceptor>

	<!-- 定义拦截器栈 -->
		<interceptor-stack name="myStack">
			<interceptor-ref name="defaultStack"></interceptor-ref>
			<interceptor-ref name="interceptorDemo1"></interceptor-ref>
			<interceptor-ref name="interceptorDemo2"></interceptor-ref>
		</interceptor-stack>
	</interceptors>

	<action name="actionDemo1" class="com.itheima.web.action.ActionDemo1">
		<result>/demo1/demo1.jsp</result>
		<interceptor-ref name="myStack"></interceptor-ref>
	</action>

public class ActionDemo1 extends ActionSupport {
	@Override
	public String execute() throws Exception {
		System.out.println("ActionDemo1已经执行了.............");
		return super.execute();
	}
}

五、Struts2标签库

1、通用标签

1、判断标签
	<s:if/><s:else if/><s:else/><s:iterator/>
	
<s:set var="i" value="2" scope="request"></s:set>
<s:if test="#request.i>3">
	i 大于3
</s:if>
<s:elseif test="#request.i=3">
	i 等于3
</s:elseif>
<s:else>
	i 小于3
</s:else>

<s:iterator var="i"	value="{'aa','bb','cc'}">
	<s:property value="#i"/>
</s:iterator>

<s:iterator var="entry" value="#{'aaa':'111','bbb':'222','ccc':'333' }">
	<s:property value="#entry.key"/>---<s:property value="#entry.value"/>
</s:iterator>

<s:iterator var="i" begin="1" end="10" step="1">
	<s:property value="#i"/>
</s:iterator>

<s:iterator var="i" begin="1" end="100" step="10" status="status">
	<s:if test="#status.count %2==0">
		<font color="red"><s:property value="i"/></font>
	</s:if>
	<s:else>
		<s:property value="i"/>
	</s:else>
</s:iterator>

2、其他常用的标签
	<s:property/>
	<s:debug/>
	<s:date/>--在页面上进行日期格式化

2、UI标签库(方便数据回显)

<h1>UI标签库</h1>
	<h3>传统的表单</h3>
	<form action="${pageContext.request.contextPath }/uiAction.action" method="post">
		<input type="hidden" name="id">
		用户名:<input type="text" name="name"> <br/>
		密码:<input type="password"	name="password"><br/>
		年龄:<input type="text"	name="age"><br/>
		性别:<input type="radio" name="sex" value="男"><input type="radio" name="sex" value="女"><br/>
		籍贯:<select name="city">
			<option value="">-请选择-</option>
			<option value="北京">-北京-</option>
			<option value="上海">-上海-</option>
			<option value="深圳">-深圳-</option>
			<option value="韩国">-韩国-</option>
		</select><br/>
		爱好:<input type="checkbox" name="hobby" value="basketball">篮球
		<input type="checkbox" name="hobby" value="football">足球 
		<input type="checkbox" name="hobby" value="volleyball">排球 
		<input type="checkbox" name="hobby" value="pingpang">乒乓球<br/>
		介绍:<textarea  name="info" rows="3" cols="10">我是xxx</textarea><br/>
		<input type="submit"  value="提交">
	</form>

	<h3>UI标签的表单</h3>
	<!--theme的属性可填xhtml(默认),或者simple,simple表示不使用UI标签库,和传统一样  -->
	<s:form action="uiAction"  namespace="/" method="post" theme="xhtml">
		<s:hidden name="id" value=""></s:hidden>
		<s:textfield name="name" label="用户名"></s:textfield>
		<!--密码默认不回显 showPassword默认为"false",改为true时密码回显  -->
		<s:password name="password" label="密码" showPassword="false"></s:password>
		<s:textfield name="age" label="年龄"></s:textfield>
		<s:radio list="{'男','女'}" name="sex" label="性别"></s:radio>
		<s:select list="{'北京','上海','深圳','韩国'}" name="city" label="籍贯" headerKey="" headerValue="-请选择-"></s:select>
		<s:checkboxlist list="#{'basketball':'篮球','football':'足球','volleyball':'排球','pingpang':'乒乓球' }" name="hobby" label="爱好"></s:checkboxlist>
		<s:textarea name="info" cols="10" rows="3" label="介绍" value="我是yyy"></s:textarea>
		<s:submit value="提交"></s:submit>
	</s:form>


public class UIAction extends ActionSupport {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private String password;
	private Integer age;
	private String sex;
	private String city;
	private String[] hobby;
	private String info;
	/**
	 * 这里本来只需要提供set方法就可以获得页面传来的数据值,提供get方法是为了方便数据回显
	 * 
	 * @return
	 */
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String[] getHobby() {
		return hobby;
	}
	public void setHobby(String[] hobby) {
		this.hobby = hobby;
	}
	public String getInfo() {
		return info;
	}
	public void setInfo(String info) {
		this.info = info;
	}
	public static long getSerialversionuid() {
		return serialVersionUID;
	}
	@Override
	public String execute() {
		System.out.println(hobby.toString());
		System.out.println(info);
		return NONE;
	}
}

<action name="uiAction" class="action.UIAction">
	<result name="input">/demo2/demo3.jsp</result>
</action>

六、数据校验

1、手动编码的方式

public class LoginAction extends ActionSupport {
	private String username;
	private String password;

	public void setUsername(String username) {
		this.username = username;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		return NONE;
	}
	@Override
	public void validate() {
		// 判断用户名不能为空
		if (username == null || username.trim().length() == 0) {
			// 阻止execute方法执行
			this.addFieldError("username", "用户名不能为空!");
		}
		// 判断密码不能为空
		if (password == null || password.trim().length() == 0) {
			// 阻止execute方法执行
			this.addFieldError("password", "密码不能为空!");
		}
	}
	// 针对一个方法检验
	public void validateExecute() {
		if (password.length() < 6 || password.length() > 12) {
			this.addFieldError("password", "密码需要在6-12位之间");
		}
	}
}

<style type="text/css">
.d1 {
	color: black;
	width: 150px;
	height: 50px;
	font-size: 20pt;
	padding: 15px;
	position: relative;
}
</style>
<body>
	<div class="d1">登录页面</div>
	<h3>
		<s:fielderror></s:fielderror>
	</h3>
	<s:form action="checkAction" namespace="/" method="post">
		<s:textfield name="username" label="用户名"></s:textfield>
		<s:password name="password" label="密码"></s:password>
		<s:submit value="提交"></s:submit>
	</s:form> 

	<form action="${pageContext.request.contextPath}/checkAction.action"
		method="post">
		用户名:<input type="text" name="username"> 密码:<input
			type="password" name="password"> <input type="submit"
			value="提交">
	</form>
</body>

<action name="checkAction" class="action.LoginAction">
		<result name="input">/demo3/demo1.jsp</result>
</action>


2、XML配置的方式

/**
 * 数据校验(需要提供属性的get方法)
 * 
 * @author Administrator
 *
 */
public class LoginAction2 extends ActionSupport {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	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;
	}
	@Override
	public String execute() throws Exception {
		System.out.println(username);
		System.out.println(password);
		return NONE;
	}
}

XML配置(校验一个字段)

<?xml version="1.0" encoding="UTF-8"?>
	  <!DOCTYPE validators PUBLIC
  		"-//Apache Struts//XWork Validator 1.0.3//EN"
  		"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
  		<!--该xml起名规则:Action类名-validation.xml  -->
<validators>
	<!-- name:要校验的字段名 -->
	<field name="username">
		<field-validator type="requiredstring">
			<message>用户名不能为空!(XML)</message>
		</field-validator>
	</field>

	<field name="password">
		<field-validator type="requiredstring">
			<message>密码不能为空!(XML)</message>
		</field-validator>
	</field>
</validators>

XML配置(校验方法)

<?xml version="1.0" encoding="UTF-8"?>
	  <!DOCTYPE validators PUBLIC
  		"-//Apache Struts//XWork Validator 1.0.3//EN"
  		"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
  		
  		<!--该xml起名规则:Action类名-action的name-validation.xml  -->
  		<!--针对一个方法校验  -->
 <validators>
 	<field name="password">
 		<field-validator type="stringlength">
 			<param name="minLength">6</param>
 			<param name="maxLength">12</param>
 			<message>密码需要在6~12位之间!</message>
 		</field-validator>
 	</field>
 </validators>

<action name="checkAction2" class="action.LoginAction2">
		<result name="input">/demo3/demo2.jsp</result>
</action>

<style type="text/css">
.d1 {
	color: black;
	width: 150px;
	height: 50px;
	font-size: 20pt;
	padding: 15px;
	position: relative;
}
</style>
<body>
	<div class="d1">登录页面</div>
	<h3>
		<s:fielderror></s:fielderror>
	</h3>
	<s:form action="checkAction" namespace="/" method="post">
		<s:textfield name="username" label="用户名"></s:textfield>
		<s:password name="password" label="密码"></s:password>
		<s:submit value="提交"></s:submit>
	</s:form> 

	<form action="${pageContext.request.contextPath}/checkAction2.action"
		method="post">
		用户名:<input type="text" name="username"> 密码:<input
			type="password" name="password"> <input type="submit"
			value="提交">
	</form>
</body>

七、Struts2中的国际化(了解)

总结:Struts2基础知识已经总结完,Struts2框架是一款用来快速构建灵活、稳定的wed应用程序的框架,相比于servlet,大大减少了程序的代码,而且Struts2框架还提供了资源文件和标签,用来解决硬编码和提示错误信息。

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.m或d论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 、1资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值