一、拦截器简介

1. Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现.

2. 拦截器栈(Interceptor Stack)。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。

二、Struts2拦截器原理

Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的    拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。

比如:应用要求用户登陆,且必须为指定用户名才可以查看系统中某个视图资源;否则,系统直接转入登陆页面。对于上面的需求,可以在每个Action的执行实际处理逻辑之前,先执行权限检查逻辑,但这种做法不利于代码复用。因为大部分Action里的权限检查代码都大同小异,故将这些权限检查的逻辑放在拦截器中进行将会更加优雅。

拦截器的工作原理如上图,每一个Action请求都包装在一系列的拦截器的内部。拦截器可以在Action执行直线做相似的操作也可以在Action执行直后做回收操作。

每一个Action既可以将操作转交给下面的拦截器,Action也可以直接退出操作返回客户既定的画面。

三、拦截器API

在Struts2的API中有一个com.opensymphony.xwork2.interceptor包,其中有一些Struts2的内置拦截器对象,他们有不同的功能。所有拦截器都直接或简介地实现Interceptor接口。

Interceptor接口声明了三个方法:

public interface Interceptor extends Serializable {void destroy();void init();String intercept(ActionInvocation invocation) throws Exception;
}

Init()方法在拦截器类被创建之后,在对Action镜像拦截之前调用,相当于一个post-constructor方法,使用这个方法可以给拦截器类做必要的初始话操作。

Destroy()方法在拦截器被垃圾回收之前调用,用来回收init方法初始化的资源。

Intercept()是拦截器的主要拦截方法,如果需要调用后续的Action或者拦截器,只需要在该方法中调用invocation.invoke()方法即可,在该方法调用的前后可以插入Action调用前后拦截器需要做的方法。如果不需要调用后续的方法,则返回一个String类型的对象即可,例如Action.SUCCESS。

另外AbstractInterceptor类提供了一个简单的Interceptor的实现,这个实现为:

public abstract class AbstractInterceptor implements Interceptor {public void init() {//Does nothing}public void destroy() {//Does nothing}public abstract String intercept(ActionInvocation invocation) throws Exception{// Override to handle interception}
}

在不需要编写init和destroy方法的时候,只需要从AbstractInterceptor继承而来,实现intercept方法即可。

四、自定义拦截器

自定义一个拦截器需要三步:

1 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。

2 在strutx.xml中注册上一步中定义的拦截器。

3 在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。

实例:使用拦截器过滤文字

(1)新建一个项目,将Struts2的环境搭建好

(2)在web.xml中配置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>

(3)新建com.xiaobai.action.MessageAction类,并继承ActionSupport类实现Action。主要代码:

import com.opensymphony.xwork2.ActionSupport;
public class MessageAction extends ActionSupport {private static final long serialVersionUID = 1L;private String title=null;private String content=null;//省略getter、setter方法public String execute(){return SUCCESS;}
}

(4)新建com.xiaobai.interceptor.MyInterceptor类,并继承AbstractInterceptor类,用于实现拦截器功能。主要代码:

import com.itzcn.action.MessageAction;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;public class MyInterceptor extends AbstractInterceptor {private static final long serialVersionUID = 1L;@Overridepublic String intercept(ActionInvocation ai) throws Exception {Object obj=ai.getAction();if(obj!=null){if(obj instanceof MessageAction){MessageAction action=(MessageAction) obj;String content=action.getContent();if(content.contains("administrator")){content=content.replaceAll("administrator","系统管理员");}if(content.contains("admin")){content=content.replaceAll("admin", "管理员");}action.setContent(content);return ai.invoke();}else{return Action.LOGIN;}}else{return Action.LOGIN;}}}

(5)在struts.xml中配置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><!--<constant name="struts.enable.DynamicMethodInvocation" value="true"/>--><package name="User" extends="struts-default"><interceptors><interceptor name="checkLogin" class="com.xiaobai.interceptor.MyInterceptor"></interceptor></interceptors><action name="check" method="execute" class="com.xiaobai.action.MessageAction"><result name="success">/success.jsp</result><result name="login">/success.jsp</result><interceptor-ref name="defaultStack"></interceptor-ref><interceptor-ref name="checkLogin"></interceptor-ref></action></package>  </struts>

(6)新建index.jsp页面,在其中使用Struts2标签,用于提交内容。主要代码:

 <s:form action="check" method="post"><s:textfield name="title" label="标题" ></s:textfield><s:textarea name="content" label="内容" rows="5" cols="18"></s:textarea><s:submit value="发表"></s:submit></s:form>

(7)新建success.jsp页面,在其中使用Struts2标签,用于显示提交后的内容。主要代码:

  标题:<s:property value="title"/><br>内容:<s:property value="content"/>

另一个小例子:编写一个Session过滤用的拦截器,该拦截器查看用户Session中是否存在特定的属性(LOGIN属性)如果不存在,中止后续操作定位到LOGIN,否则执行原定操作,代码为:

public class CheckLoginInterceptor extends AbstractInterceptor {public static final String LOGIN_KEY = "LOGIN";public static final String LOGIN_PAGE = "global.login";public String intercept(ActionInvocation actionInvocation) throws Exception {System.out.println("begin check login interceptor!");// 对LoginAction不做该项拦截Object action = actionInvocation.getAction();if (action instanceof LoginAction) {System.out.println("exit check login, because this is login action.");return actionInvocation.invoke();}// 确认Session中是否存在LOGINMap session = actionInvocation.getInvocationContext().getSession();String login = (String) session.get(LOGIN_KEY);if (login != null && login.length() > 0) {// 存在的情况下进行后续操作。System.out.println("already login!");return actionInvocation.invoke();} else {// 否则终止后续操作,返回LOGINSystem.out.println("no login, forward login page!");return LOGIN_PAGE;}}
}
<!--注册拦截器-->
<interceptors><interceptor  name="login" class="com.jpleasure.teamware.util.CheckLoginInterceptor"/><interceptor-stack name="teamwareStack"><interceptor-ref name="login"/><interceptor-ref name="defaultStack"/></interceptor-stack>
</interceptors><!--将上述拦截器设定为默认拦截器:-->
<default-interceptor-ref name="teamwareStack"/>
<!--这样在后续同一个package内部的所有Action执行之前都会被login拦截。-->

五、Struts2(XWork)提供的拦截器的功能说明:

拦截器

名字

说明

Alias Interceptor

alias

在不同请求之间将请求参数在不同名字件转换,请求内容不变

Chaining Interceptor

chain

让前一个Action的属性可以被后一个Action访问,现在和chain类型的result(<result type=”chain”>)结合使用。

Checkbox Interceptor

checkbox

添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox。

Cookies Interceptor

cookies

使用配置的name,value来是指cookies

Conversion Error Interceptor

conversionError

将错误从ActionContext中添加到Action的属性字段中。

Create Session Interceptor

createSession

自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。

Debugging Interceptor

debugging

提供不同的调试用的页面来展现内部的数据状况。

Execute and Wait Interceptor

execAndWait

在后台执行Action,同时将用户带到一个中间的等待页面。

Exception Interceptor

exception

将异常定位到一个画面

File Upload Interceptor

fileUpload

提供文件上传功能

I18n Interceptor

i18n

记录用户选择的locale

Logger Interceptor

logger

输出Action的名字

Message Store Interceptor

store

存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。

Model Driven Interceptor

model-driven

如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。

Scoped Model Driven

scoped-model-driven

如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用Action的setModel方法将其放入Action内部。

Parameters Interceptor

params

将请求中的参数设置到Action中去。

Prepare Interceptor

prepare

如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法。

Scope Interceptor

scope

将Action状态存入session和application的简单方法。

Servlet Config Interceptor

servletConfig

提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问。

Static Parameters Interceptor

staticParams

从struts.xml文件中将<action>中的<param>中的内容设置到对应的Action中。

Roles Interceptor

roles

确定用户是否具有JAAS指定的Role,否则不予执行。

Timer Interceptor

timer

输出Action执行的时间

Token Interceptor

token

通过Token来避免双击

Token Session Interceptor

tokenSession

和Token Interceptor一样,不过双击的时候把请求的数据存储在Session中

Validation Interceptor

validation

使用action-validation.xml文件中定义的内容校验提交的数据。

Workflow Interceptor

workflow

调用Action的validate方法,一旦有错误返回,重新定位到INPUT画面

Parameter Filter Interceptor

N/A

从参数列表中删除不必要的参数

Profiling Interceptor

profiling

通过参数激活profile

<!--注册并引用Interceptor-->
<package name="default" extends="struts-default"><interceptors><interceptor name="timer" class=".."/><interceptor name="logger" class=".."/></interceptors><action name="login" class="tutorial.Login"><interceptor-ref name="timer"/><interceptor-ref name="logger"/><result name="input">login.jsp</result><result name="success"type="redirect-action">/secure/home</result></action>
</package><!--可以将多个拦截器合并在一起作为一个堆栈调用,当一个拦截器堆栈被附加到一个Action的时候,
要想Action执行,必须执行拦截器堆栈中的每一个拦截器。-->
<package name="default" extends="struts-default"><interceptors><interceptor name="timer" class=".."/><interceptor name="logger" class=".."/><interceptor-stack name="myStack"><interceptor-ref name="timer"/><interceptor-ref name="logger"/></interceptor-stack></interceptors><action name="login" class="tutuorial.Login"><interceptor-ref name="myStack"/><result name="input">login.jsp</result><result name="success"type="redirect-action">/secure/home</result></action>
</package><!--上述说明的拦截器在默认的Struts2应用中,根据惯例配置了若干个拦截器堆栈,详细情参看struts-default.xml-->
<!--其中有一个拦截器堆栈比较特殊,他会应用在默认的每一个Action上。-->
<interceptor-stack name="defaultStack"><interceptor-ref name="exception"/><interceptor-ref name="alias"/><interceptor-ref name="servletConfig"/><interceptor-ref name="prepare"/><interceptor-ref name="i18n"/><interceptor-ref name="chain"/><interceptor-ref name="debugging"/><interceptor-ref name="profiling"/><interceptor-ref name="scopedModelDriven"/><interceptor-ref name="modelDriven"/><interceptor-ref name="fileUpload"/><interceptor-ref name="checkbox"/><interceptor-ref name="staticParams"/><interceptor-ref name="params"><param name="excludeParams">dojo"..*</param></interceptor-ref><interceptor-ref name="conversionError"/><interceptor-ref name="validation"><param name="excludeMethods">input,back,cancel,browse</param></interceptor-ref><interceptor-ref name="workflow"><param name="excludeMethods">input,back,cancel,browse</param></interceptor-ref>
</interceptor-stack><!--每一个拦截器都可以配置参数,有两种方式配置参数,一是针对每一个拦截器定义参数,二是针对一个拦截器堆栈统一定义所有的参数,例如:-->
<interceptor-ref name="validation"><param name="excludeMethods">myValidationExcudeMethod</param>
</interceptor-ref>
<interceptor-ref name="workflow"><param name="excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref>
<!--或者-->
<interceptor-ref name="defaultStack"><param name="validation.excludeMethods">myValidationExcludeMethod</param><param name="workflow.excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref><!--
每一个拦截器都有两个默认的参数:
excludeMethods - 过滤掉不使用拦截器的方法和
includeMethods – 使用拦截器的方法。需要说明的几点:
拦截器执行的顺序按照定义的顺序执行,例如:-->
<interceptor-stack name="xaStack"><interceptor-ref name="thisWillRunFirstInterceptor"/><interceptor-ref name="thisWillRunNextInterceptor"/><interceptor-ref name="followedByThisInterceptor"/><interceptor-ref name="thisWillRunLastInterceptor"/>
</interceptor-stack>
<!--的执行顺序为:
thisWillRunFirstInterceptorthisWillRunNextInterceptorfollowedByThisInterceptorthisWillRunLastInterceptorMyAction1MyAction2 (chain)MyPreResultListenerMyResult (result)thisWillRunLastInterceptorfollowedByThisInterceptorthisWillRunNextInterceptor
thisWillRunFirstInterceptor
-->
<!--使用默认拦截器配置每个Action都需要的拦截器堆栈,例如:-->
<action name="login" class="tutorial.Login"><interceptor-ref name="timer"/><interceptor-ref name="logger"/><interceptor-ref name="default-stack"/><result name="input">login.jsp</result><result type="redirect-action">/secure/home</result>
</action>
<!--可以按照如下的方式定义:-->
<interceptors><interceptor-stack name="myStack"><interceptor-ref name="timer"/><interceptor-ref name="logger"/><interceptor-ref name="default-stack"/></interceptor-stack>
</interceptors><default-interceptor-ref name="myStack"/><action name="login" class="tutorial.Login"><result name="input">login.jsp</result><result type="redirect-action">/secure/home</result>
</action>
<!--如何访问HttpServletRequest,HttpServletResponse或者HttpSession
有两种方法可以达到效果,使用ActionContext:
Map attibutes = ActionContext.getContext().getSession();
或者实现相应的接口:
HttpSession            SessionAware
HttpServletRequest     ServletRequestAware
HttpServletResponse    ServletResponseAware
-->

Struts2自定义拦截器实例—只允许从登录页面进入系统
【1】struts.xml:

 <!-- 定义一个拦截器 -->  <interceptors>  <interceptor name="authority"  class="org.interceptot.LoginInterceptor">  </interceptor>  <!-- 拦截器栈 -->  <interceptor-stack name="mydefault">  <interceptor-ref name="defaultStack" />  <interceptor-ref name="authority" />  </interceptor-stack>  </interceptors><!-- 定义全局Result -->  <global-results>  <!-- 当返回login视图名时,转入/login.jsp页面 -->  <result name="login">/login.jsp</result>  </global-results><action name="show" class="org.action.showAction">  <result name="success">/main.jsp</result>  <!-- 使用此拦截器 -->  <interceptor-ref name="mydefault" />  </action>  <!--验证登录用户信息  --><action name="login" class="org.action.loginAction" method="execute"><result name="error">/login.jsp</result>  <result name="input">/login.jsp</result> </action>

【2】自定义拦截器org.interceptot.LoginInterceptor:

package org.interceptot;
import java.util.Map;  import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class LoginInterceptor extends AbstractInterceptor {  @Override  public String intercept(ActionInvocation invocation) throws Exception {  // 取得请求相关的ActionContext实例  ActionContext ctx = invocation.getInvocationContext();  Map session = ctx.getSession();  String user = (String) session.get("username");  // 如果没有登陆,即用户名不存在,都返回重新登陆  System.out.println("user:"+user);if (user != null) {  System.out.println("test");  return invocation.invoke();  }  System.out.println("你还没有登录"); ctx.put("tip", "你还没有登录");  return Action.LOGIN;    //返回一个叫login的result结果}  }  

【3】进入主页面的Action:org.action.showAction

package org.action;import com.opensymphony.xwork2.ActionSupport;  public class showAction extends ActionSupport {  public String execute() {  return "success";  }
}  

【4】LoginAction:

private boolean isInvalid(String value) {
return (value == null || value.length() == 0);
}
if (isInvalid(user.getUsername()))   return INPUT;     if (isInvalid(user.getPassword()))     return INPUT;    //登录成功将User放入session中
HttpServletRequest request = ServletActionContext.getRequest();
Map  map=ActionContext.getContext().getSession();
map.put("username", user.getUsername());

【5】如果我们通过show.action访问main.jsp那么就会被自定义拦截器拦住,拦截器检查session中
是否有值,有证明用户已经登录,没有则为没有登录,那么就会被跳转到登陆页面。

六、拦截器与过滤器的区别

过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符

拦截器,是在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。

拦截器与过滤器的区别 :

  1. 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
  2. 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
  3. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  4. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
  5. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次

执行顺序 :过滤前 - 拦截前 - Action处理 - 拦截后 - 过滤后。个人认为过滤是一个横向的过程,首先把客户端提交的内容进行过滤(例如未登录用户不能访问内部页面的处理);过滤通过后,拦截器将检查用户提交数据的验证,做一些前期的数据处理,接着把处理后的数据发给对应的Action;Action处理完成返回后,拦截器还可以做其他过程(还没想到要做啥),再向上返回到过滤器的后续操作。

面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。
      但是人们也发现,在分散代码的同时,也增加了代码的重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。
    也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。 
      一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。
这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。 
     AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

Struts2的拦截器相关推荐

  1. struts2自定义拦截器并配置拦截器使其生效

    首先编写一个struts2的拦截器,要继承 MethodFilterInterceptor 并获取其中的方法 package star.july.d_interceptor;import com.op ...

  2. (转)Struts2的拦截器

    http://blog.csdn.net/yerenyuan_pku/article/details/68648101 Struts2的拦截器 拦截器的概述 拦截器,在AOP(Aspect-Orien ...

  3. Struts2之拦截器篇

    拦截器是Struts2框架的核心和基础,Struts2绝大多数功能都是通过拦截器来完成的,当StrutsPrepareAndExecuteFilter拦截到用户请求后,大量拦截器会对该请求进行处理,然 ...

  4. Struts2【拦截器】就是这么简单

    2019独角兽企业重金招聘Python工程师标准>>> 什么是拦截器 拦截器Interceptor.....拦截器是Struts的概念,它与过滤器是类似的...可以近似于看作是过滤器 ...

  5. struts2 18拦截器详解(五)

    I18nInterceptor 该拦截器处理defaultStack第四的位置,是用来方便国际化的,如果说我们的一个Web项目要支持国际化的话,通常的做法是给定一个下拉框列出所支持的语言,当用户选择了 ...

  6. Struts2自定义拦截器实例—登陆权限验证

    版本:struts2.1.6 此实例实现功能:用户需要指定用户名登陆,登陆成功进入相应页面执行操作,否则返回到登陆页面进行登陆,当直接访问操作页面(登陆后才能访问的页面)时则不允许,须返回登陆页面. ...

  7. dwz ajax session超时跳转登录页(struts2自定义拦截器)

    1.定义struts2拦截器(网上例子很多) 代码如下: package rt.intercepter;import java.util.Map;import javax.servlet.http.H ...

  8. Struts2 自定义拦截器(方法拦截器)

    转自:http://05061107cm.iteye.com/blog/365504 struts2系统自带了很多拦截器,有时需要我们自己定义,一般有两种方式: 一.实现Interceptor接口 J ...

  9. struts2中拦截器的使用

    拦截器的使用 实现AOP 转自http://www.cnblogs.com/fmricky/archive/2010/05/24/1742514.html 1.什么是拦截器(Interceptor) ...

  10. Struts2 自定义拦截器(easy example)

    要自定义拦截器需要实现com.opensymphony.xwork2.interceptor.Interceptor接口: 新建一个MyIntercept package com.action;imp ...

最新文章

  1. 新时代的网络工程师需要掌握哪些技能
  2. Hibernate 中配置属性详解(hibernate.properties)
  3. 京东面试题:二叉树直径
  4. Shell脚本实现简单分割字符串
  5. 前端预览word文件_[装机必备] QuickLook —— 敲击空格即可快速预览文件
  6. 优化体系结构 - 解决多样性数据源
  7. mysql - 内存表使用总结
  8. 堪称奇迹!8 天诞生一个产品,这家创业公司做到了
  9. 倒数58天 -- 分治法 -- 使用循环求方程的一个解
  10. 微波接力通信、卫星通信、无线移动通信
  11. 开源GIS软件初探(转载)
  12. java fastjson 格式化_json的格式化展示(基于 fastjson)
  13. DVWA--SQL Injection(SQL注入-非盲注)(全难度)
  14. EXCEL根据两点经纬度计算距离
  15. [zz] 基于sinc的音频重采样(一):原理
  16. OpenMV与Arduino通信—串口
  17. UE4学习笔记#三、蓝图混合空间
  18. 江科大stm32-概述
  19. TCP/UDP/Socket 通俗讲解
  20. matlab使用load函数读取txt数据时,出现锘? xxxxx 。的解决办法

热门文章

  1. 干货十足:一大波好用的Windows软件帮你开路!
  2. Appstore抓包获取APP历史版本
  3. 私域运营如何做到高转化高复购?快鲸scrm必不可少
  4. 说课稿模板计算机,计算机系统的组成说课稿1模板.doc
  5. 走进中关村软件园-光环敏捷PMI-ACP落地分享会
  6. XML学习之做过的实验——实验二
  7. android手机屏幕投影,安卓手机屏幕投影到电脑(笔记本)教程分享
  8. 在proteus软件80C51芯片隐藏电源接口VCC
  9. python画玫瑰曲线_「风向玫瑰图」python绘制风向玫瑰图和污染物玫瑰图 - seo实验室...
  10. DIADEM_metric不能运行及解决办法