第一篇:词法分析设计实验报告(附代码)
实验一
词法分析设计
实验学时:4 实验类型:综合 实验要求:必修
一、实验目的
通过本实验的编程实践,使学生了解词法分析的任务,掌握词法分析程序设计的原理和构造方法,使学生对编译的基本概念、原理和方法有完整的和清楚的理解,并能正确地、熟练地运用。
二、实验内容
用VC++/VB/JAVA语言实现对C语言子集的源程序进行词法分析。通过输入源程序从左到右对字符串进行扫描和分解,依次输出各个单词的内部编码及单词符号自身值;若遇到错误则显示“Error”,然后跳过错误部分继续显示 ;同时进行标识符登记符号表的管理。以下是实现词法分析设计的主要工作:(1)从源程序文件中读入字符。
(2)统计行数和列数用于错误单词的定位。(3)删除空格类字符,包括回车、制表符空格。
(4)按拼写单词,并用(内码,属性)二元式表示。(属性值——token的机内表示)(5)如果发现错误则报告出错
(6)根据需要是否填写标识符表供以后各阶段使用。单词的基本分类: 关键字:由程序语言定义的具有固定意义的标识符。也称为保留字例如 标识符:用以表示各种名字,如变量名、数组名、函数名; 常数: 任何数值常数。如 125, 1,0.5,3.1416; 运算符:+、-、*、/;
关系运算符: <、<=、=、>、>=、<>; 分界符: ;、,、(、)、[、]; if、for、while、printf ;
单词种别码为1。
三、实验要求
1、编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。
2、将标识符填写的相应符号表须提供给编译程序的以后各阶段使用。
3、根据测试数据进行测试。测试实例应包括以下三个部分: 全部合法的输入。 各种组合的非法输入。 由记号组成的句子。
4、词法分析程序设计要求输出形式: 例:输入VC++语言的实例程序:
If i=0 then
n++;a﹤= 3b %);输出形式为:
单词
二元序列
类 型
(单词种别,单词属性)
for
(1,for)
关键字
i
(6,i)
标识符
=
(4,=)
关系运算符0
(5,0)
常数
then
(1,then)
关键字 n
(6,n)
标识符
++
Error
Error
;
(2,;)
分界符
位置(行,列)
(1,1)
(1,2)
(1,3)
(1,4)
(1,5)
(1,6)
(1,7)
(1,8)
a
(6,a)
标识符
(2,1)﹤=
(4,<=)
关系运算符
(2,2)3b
Error
Error
(2,4)%
Error
Error
(2,4))
(2,))
分界符
(2,5);
(2,;)
分界符
(2,6)
实验报告正文:
功能描述:该程序具有词法分析功能,即面对一段程序源代码,通过该程序,能检查出源代码是否由词法错误。
三、词法分析实验设计思想及算法: 首先构造六个表,key[]={“auto”,“break”,“case”,“catch”,“char”,“class”,“const”,“continue”,“default”,“delete”,“do”,“double”,“else”,“enum”,“float”,“for”,“if”,“int”,“long”,“new”,“private”,“protected”,“public”,“register”,“return”,“short”,“static”,“struct”,“switch”,“this”,“void”,“while”,“then”};关键字表, 单词种别码1;
Delimiter[]={“;”,“(”,“)”,“[”,“]”,“,”,“.”,“{”,“}”};分界符表 单词种别码2 Operator[]={“+”,“-”,“*”,“/”};算术运算符表
单词种别码3 R_operators[]={“<”,“<=”,“==”,“>”,“>=”},关键字表 , 单词种别码1;string Number[100];常数表
单词种别码5;string Identifier[100];标示符表
单词种别码6;构造关键字判断函数Iskey(),字母判断函数Isletter(),数字判断函数Isnumber();构造标示符判别函数InsertId(),若输入的标示符在标示符数组Identifier []中,返回其下标,若不在,将该标示符插到数组末尾。构造标示符判别函数InsertNumber(),若输入的数字在数字数组Number[]中,返回其下标,若不在,将该数字插到数组末尾。具体分析函数analyse()具体实现输入源代码的识别。anaiyse()构造思路,程序设计图:
综合以上分析,画出整个程序的运行分析程序图,如下: 开始 输入源文件路径否 路径是否有效是打开源文件初始化文件指针识别指针内容文件结束?否是空格,空白或换行吗是跳过该字符是结束否是字母吗是将字符加入字符数组Word[]否是数字吗否是界符吗否将字符加入字符数组Word[]是将字符加入字符数组Word[]是指向下一字符识别指针内容是输出word为界符输出Word内容为不可识别将字符加入字符数组Word[]将字符加入字符数组Word[]指向下一字符指向下一字符是字母惑数字吗回退否将word与关键字表key进行匹配输出word为普通标示符是数字吗否输出word为常数指向下一字符否匹配?是输出word为关键字整个程序的运行分析程序图
软件的测试方法和测试结果:
首先,将要分析的源代码写入一个文本,存于磁盘中,然后运行程序,输入源代码文件存放的路径,若输入路径正确,程序将自动分析源代码,若输入路径不正确,程序将显示,路径错误,请重新输入的提示。下面为具体的运行实例: 源代码为:If i=0 then
n++;a﹤= 3b %)
输出满足要求。
实验总结(设计的特点、不足、收获与体会):
通过此次实验,让我了解到如何设计、编制并调试词法分析程序,熟悉了构造词法分析程序的手工方式的相关原理,加深了对编译原理词法分析的理解,本次使用C++语言直接编写此法分析程序,也让我重新熟悉了C++语言的相关内容,加深了对C++语言的用途的理解。本程序的数据输入采取直接从文件中读取,而不是由键盘输入,因此在测试过程中,输入得到大大简化,但是本程序的关键字表只初始化了一部分关键字,还可继续扩充(只需扩大数组,向其中补充要添加的关键字),而且程序的测试数据存在不足,程序可能存在未发现的漏洞,以上两点有待改善。附录该程序的源代码: #include 一.实验序号:《编译原理》第一次实验 二.实验题目:词法分析 三.实验日期:2010.10 四.实验环境(操作系统,开发语言) 操作系统:Windows 开发语言:C 五.实验内容(实验要求) a)将标识符的词法改为“以大写字母或小写字母开头,后面可以跟大写字 母或小写字母或数字或下划线”。 b)将<条件>中的表示相等关系的单词“=”改为“= =” c)将原来的无小数的数改为可以有小数的数 六.实验步骤 a)打开VC++,找到getsym()项目。 int getsym() {...} 在getsym()函数中设置断点,F10逐过程调试 根据要求a修改为 if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z') 往后面看,修改while(ch>='a' && ch<='z' || ch>='0' && ch<='9' ||ch>='A' && ch<='Z' || ch=='_'); b)在检测赋值符号的程序段中找到等号 = 的判断代码,改变成为 = = 即 可。 c)在获取整数数字的值的程序段后面添加判断小数点的代码。如果是,即 读取符号并且按照小数的要求将其缩小并且累加到总的符号串中。 七.实验体会(包括收获、心得体会、存在的问题及解决问题的方法、建议等) 通过实验,如果要修改代码的话,首先应该读懂源码,在修改之前了解到程序段的功能是什么,然后再在相应的行进行修改添加,再进行合理的调试。如果问题太过困难可以查询资料或与同学进行讨论 八.实验结果(关键源程序) a) int getsym() { int i,j,k; while(ch==' '||ch==10||ch==9)/*忽略空格,换行和TAB*/{ getchdo; } if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')/*名字或保留字以a..zA。Z开头*/{ k=0; do{ if(k { a[k]=ch; k++; } getchdo; }while(ch>='a' && ch<='z' || ch>='0' && ch<='9' || ch>='A' && ch<='Z' || ch=='_');// if(ch>='0'&&ch<='9')/*检测是否为数字,以0..9开头*/{ k=0; num=0; sym=number; do{ //435 num=10*num+ch-'0'; k++; getchdo; }while(ch>='0' && ch<='9'); if(ch=='.') { k++; sym=period; getchdo; sym=number; int div=10; do{num=num+(ch-'0')/div; k++; getchdo; div=div*10; }while(ch>='0'&&ch<='9'); } b) else { if(ch=='>')//检测大于或大于等于符号{ getchdo; if(ch=='=')//add by M { sym=geq;//构成>= getchdo; } else { sym=gtr;//否则就是一个单独的>号} } else { if(ch=='=') { getchdo; if(ch=='=') { sym=deq; getchdo; } else { sym=eql; } } c)else {if(ch == ’.’) {k=10; getchdo; do{num=num+(ch-'0')/k; k=k*10; getchdo; }while(ch>='0'&&ch<='9');} } 词法分析是编译器工作的第一阶段,它的工作就是从输入(源代码)中取得token,以作为parser(语法分析)的输入,一般在词法分析阶段都会把一些无用的空白字符(white space,即空格、tab和换行)以及注释剔除,以降低下一步分析的复杂度,词法分析器一般会提供一个gettoken()这样的方法,parser可以在做语法分析时调用词法分析器的这个方法来得到下一个token,所以词法分析器并不是一次性遍历所有源代码,而是采取这种on-demand的方式:只在parser需要时才工作,并且每次只取一个token。 token和lexeme 首先,token不等于lexeme。token和lexeme的关系就类似于面向对象语言中“类”和“实例”(或“对象”)之间的关系,这个用中文不知该如何解释才好,比如语言中的变量a和b,它们都属于同一种token:identifier,而a的lexeme是”a”,b则是”b”,而每个关键字都是一种token。token可以附带有一个值属性,例如变量a,当调用词法分析器的gettoken()时,会返回一个identifier类型的token,这个token带有一个属性“a”,属性可以是多样的,例如表示数字的token可以带有一个表示数字值的属性,它是整型的。 如下代码: int age = 23; int count = 50; 可以依次提取出8个token:int(值为”int”),id(值为”age”),assign(值为”=”),number(值为整型数值23),int(值为”int”),id(值为”count”),assign(值为”=”),number(值为50) 正则表达式 正则表达式可以用来描述字符串模式,例如我们可以用digit+来表示number的token,其中digit表示单个数字(这里说正则表达式并不完全和实现的正则引擎所识别的正则表达式等价,这里只是为了描述问题而已)。 然而像c语言的的多行注释,用正则表达式来描述就比较麻烦,此时更倾向于直接用有穷自动机(finite automaton)来描述,因为用它来描述非常直观且很容易。 有穷自动机(finite automata) 有穷自动机也称为有限状态机,状态在输入字符的作用下发生迁移,因此,它可以用来识别token,也因此,我们只要画得出fa,之后再用代码实现这个fa,那词法分析器也就差不多弄好了。 有穷自动机分确定性(dfa)和非确定性(nfa)两种,如果对于同一个输入,只会有一个确定的状态迁移路线,也就是只有一个确定的“下一状态”,那就是dfa,否则就是nfa。 因为dfa对于同一个输入只有一个确定的下一状态,所以词法分析器当然优先采用它,那nfa拿来干嘛用呢?nfa用来做描述用时更方便,我们可以非常迅速地画出一个识别token的nfa图,但要想直接画出个dfa那要动不少脑筋。 根据正则表达式构建nfa 如上所述,nfa更容易画出,那我们就先研究nfa,在定义token时,我们可以用正则表达式来描述它,因为正则表达式干这行很合适,例如一个digit+就可以描述数字,多方便。因此,我们需要根据正则表达式画出与之等价的nfa。而这个算法非常简单,就是tompson’s construction,这个书上写得很清楚了。 将nfa转化成dfa(nfa的确定化) 对于计算机来说,面对同一个输入,如果有多个下一状态,那计算机就不清楚要转到哪个状态,所以我们期望能从正则表达式得到dfa,而不是nfa,因为这样将来编程实现时比较自然(同一输入有确定的一个下一状态),而幸运的是,每个nfa都可以转化成dfa。为什么nfa可以转化成dfa?因为fa(finite automata)中的状态都是我们自己画的,只要fa能正确的识别token,那就ok了,也就是,如果nfa和dfa都可以达到一样的效果:识别token,那其它的我们就不管了。 实现词法分析器 对于一个token,比如用来表示数字的token:num,我们可以用正则表达式描述它,然后画出nfa,再将nfa转化成dfa,再最小化dfa的状态,但是我们的词法分析器是不是分析一个token,所以我们要把所有类型的token的dfa合并成一个dfa,这样,这个dfa也就可以识别语言的所有token了,如果在某一连串的输入下,dfa达不到终结状态,那就说明源代码有错误了。 我用c#实现了一个用于《compiler construction: principles and practice》中tiny语言的词法分析器,tiny语言有关键字:if, then, else, end, repeat, until, read, write,有操作符+,-,*,/,=,<,(,),;,:=(全角逗号不算,是文章的分隔符)这10个,然后其余的token有number(一或多个数字)和identifier(一或多个字母),其dfa如下图: 上面这张图和《编译原理及实践》中的一样,其中的带中括号的输入说明这个输入是lookahead的,在匹配成功后是要重新放回输入流中的,比如识别num时,如果发现个非digit的,那就说明识别到了一个number,但是最后识别的那个非digit字符是要放回输入流的,因为它要留着下一次识别。 其中从start到done的那个other,指所有非white space,非{,非letter,非digit,也非:的字符,它有可能是合法的+, *, /这些,也可能是不合法的其它输入,如#号。因此,done这个状态只是说本次gettoken已经结束,状态机是有可能因为不合法的输入而进入done状态的。究竟从start到done是因为合法的,如+号导致的,还是由不合法的如#号导致的,将在代码中实现判断,但可以肯定的是,不管是+号还是#号作用于start状态,都会进入done状态。 1.Struts2架构图和请求处理流程 请求首先通过Filter chain,Filter主要包括ActionContextCleanUp,它主要清理当前线程的ActionContext和Dispatcher;FilterDispatcher主要通过AcionMapper来决定需要调用哪个Action。 ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里创建ActionProxy,ActionProxy创建ActionInvocation,然后ActionInvocation调用Interceptors,执行Action本身,创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。 2.Struts2部分类介绍 这部分从Struts2参考文档中翻译就可以了。 ActionMapper ActionMapper其实是HttpServletRequest和Action调用请求的一个映射,它屏蔽了Action对于Request等java Servlet类的依赖。Struts2中它的默认实现类是DefaultActionMapper,ActionMapper很大的用处可以根据自己的需要来设计url格式,它自己也有Restful的实现,具体可以参考文档的docs¥actionmapper.html。 ActionProxy&ActionInvocation Action的一个代理,由ActionProxyFactory创建,它本身不包括Action实例,默认实现DefaultActionProxy是由ActionInvocation持有Action实例。ActionProxy作用是如何取得Action,无论是本地还是远程。而ActionInvocation的作用是如何执行Action,拦截器的功能就是在ActionInvocation中实现的。 ConfigurationProvider&Configuration ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其实现类XmlConfigurationProvider及其子类StrutsXmlConfigurationProvider来解析。 3.Struts2请求流程 1、客户端发送请求 2、请求先通过ActionContextCleanUp-->FilterDispatcher 3、FilterDispatcher通过ActionMapper来决定这个Request需要调用哪个Action 4、如果ActionMapper决定调用某个Action,FilterDispatcher把请求的处理交给ActionProxy,这儿已经转到它的Delegate--Dispatcher来执行 5、ActionProxy根据ActionMapping和ConfigurationManager找到需要调用的Action类 6、ActionProxy创建一个ActionInvocation的实例 7、ActionInvocation调用真正的Action,当然这涉及到相关拦截器的调用 8、Action执行完毕,ActionInvocation创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现。 首先强调一下struts2的线程程安全,在Struts2中大量采用ThreadLocal线程局部变量的方法来保证线程的安全,像Dispatcher等都是通过ThreadLocal来保存变量值,使得每个线程都有自己独立的实例变量,互不相干.接下来就从Dispatcher开始看起,先看其构造函数: //创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param参数 this.initParams = initParams; } //创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param参数 this.initParams = initParams; } 我们再看在FilterDispatcher创建Dispatcher的: protected Dispatcher createDispatcher(FilterConfig filterConfig){ Map for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){ String name =(String)e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } 都可以从FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); } protected Dispatcher createDispatcher(FilterConfig filterConfig){ Map for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){ String name =(String)e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } 都可以从FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); } 分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.下面将分析这七层功夫是怎样一步步练成的.首先是init_DefaultProperties() 创建Dispatcher之后,来看init()方法 init()方法是用来Load用户配置文件,资源文件以及默认的配置文件.主要分七步走,看下面注释 public void init(){ if(configurationManager == null){ //设置ConfigurationManager的defaultFrameworkBeanName.//这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等 configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //读取properties信息,默认的default.properties,init_DefaultProperties();// [1] //读取xml配置文件 init_TraditionalXmlConfigurations();// [2] //读取用户自定义的struts.properties init_LegacyStrutsProperties();// [3] //自定义的configProviders init_CustomConfigurationProviders();// [5] //载入FilterDispatcher传进来的initParams init_FilterInitParameters();// [6] //将配置文件中的bean与具体的类映射 init_AliasStandardObjects();// [7] //构建一个用于依赖注射的Container对象 //在这里面会循环调用上面七个ConfigurationProvider的register方法 //其中的重点就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if(!dispatcherListeners.isEmpty()){ for(DispatcherListener l : dispatcherListeners){ l.dispatcherInitialized(this); } } } public void init(){ if(configurationManager == null){ //设置ConfigurationManager的defaultFrameworkBeanName.//这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等 configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //读取properties信息,默认的default.properties,init_DefaultProperties();// [1] //读取xml配置文件 init_TraditionalXmlConfigurations();// [2] //读取用户自定义的struts.properties init_LegacyStrutsProperties();// [3] //自定义的configProviders init_CustomConfigurationProviders();// [5] //载入FilterDispatcher传进来的initParams init_FilterInitParameters();// [6] //将配置文件中的bean与具体的类映射 init_AliasStandardObjects();// [7] //构建一个用于依赖注射的Container对象 //在这里面会循环调用上面七个ConfigurationProvider的register方法 //其中的重点就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if(!dispatcherListeners.isEmpty()){ for(DispatcherListener l : dispatcherListeners){ l.dispatcherInitialized(this); } } } 分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.下面将分析这七层功夫是怎样一步步练成的.首先是init_DefaultProperties() private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings(“org/apache/struts2/default”); } catch(Exception e){ throw } loadSettings(props, defaultSettings); } private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings(“org/apache/struts2/default”); } catch(Exception e){ new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e); throw } new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e); loadSettings(props, defaultSettings); } //PropertiesSettings构造方法 //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写 public PropertiesSettings(String name){ URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass()); if(settingsUrl == null){ LOG.debug(name + “.properties missing”); settings = new LocatableProperties(); return; } settings // Load settings InputStream in = null; try { in = settingsUrl.openStream(); settings.load(in); } catch(IOException e){ throw new StrutsException(“Could not load ” + name + “.properties:” + e, e); } finally { if(in!= null){ try { = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); in.close(); } catch(IOException io){ LOG.warn(“Unable to close input stream”, io); } } } } //loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props //这个props是register的一个入参.protected void loadSettings(LocatableProperties props, final Settings settings){ // We are calling the impl methods to get around the single instance of Settings that is expected for(Iterator i = settings.listImpl();i.hasNext();){ String name =(String)i.next(); props.setProperty(name, settings.getLocationImpl(name)); } } //PropertiesSettings构造方法 //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写 public PropertiesSettings(String name){ URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass()); if(settingsUrl == null){ LOG.debug(name + “.properties missing”); settings = new LocatableProperties(); return; } settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); settings.getImpl(name),// Load settings InputStream in = null; try { in = settingsUrl.openStream(); settings.load(in); } catch(IOException e){ throw new StrutsException(“Could not load ” + name + “.properties:” + e, e); } finally { if(in!= null){ try { in.close(); } catch(IOException io){ LOG.warn(“Unable to close input stream”, io); } } } } //loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props //这个props是register的一个入参.protected void loadSettings(LocatableProperties props, final Settings settings){ // We are calling the impl methods to get around the single instance of Settings that is expected for(Iterator i = settings.listImpl();i.hasNext();){ String name =(String)i.next(); props.setProperty(name, settings.getLocationImpl(name)); } } 再来看第二步:init_TraditionalXmlConfigurations() private void init_TraditionalXmlConfigurations(){ settings.getImpl(name), //首先读取web.xml中的config初始参数值 //如果 没 有 配 置 就 使 用 默 认的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//这儿就可以看出为什么默认的配置文件必须取名为这三个名称了 //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可 String configPaths = initParams.get(“config”); if(configPaths == null){ configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split(“¥¥s*[,]¥¥s*”); for(String file : files){ if(file.endsWith(“.xml”)){ if(“xwork.xml”.equals(file)){ //XmlConfigurationProvider负责解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider来解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException(“Invalid configuration file name”); } } } private void init_TraditionalXmlConfigurations(){ //首先读取web.xml中的config初始参数值 //如果 没 有 配 置 就 使 用 默 认的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//这儿就可以看出为什么默认的配置文件必须取名为这三个名称了 //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可 String configPaths = initParams.get(“config”); if(configPaths == null){ configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split(“¥¥s*[,]¥¥s*”); for(String file : files){ if(file.endsWith(“.xml”)){ if(“xwork.xml”.equals(file)){ //XmlConfigurationProvider负责解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider来解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException(“Invalid configuration file name”); } } } 对于其它配置文件只用接口。 类XmlConfigurationProvider负责配置文件的读取和解析,首先通过init()中的loadDocuments(configFileName);利用DomHelper中的 public static Document parse(InputSource inputSource, Map addAction()方法负责读取 loadInterceptorStack()方法负责将 StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProviderloadInterceptorStacks()方法负责将 而上面的方法最终会被addPackage()方法调用,addPackage又会被Provider的loadPackages()调用,将所读取到的数据汇集到PackageConfig对象中。 protected PackageConfig addPackage(Element packageElement) throws ConfigurationException { PackageConfig.Builder newPackage = buildPackageContext(packageElement); if(newPackage.isNeedsRefresh()){ return newPackage.build(); } // add result types(and default result)to this package addResultTypes(newPackage, packageElement); // load the interceptors and interceptor stacks for this package loadInterceptors(newPackage, packageElement); // load the default interceptor reference for this package loadDefaultInterceptorRef(newPackage, packageElement); // load the default class ref for this package loadDefaultClassRef(newPackage, packageElement); // load the global result list for this package loadGlobalResults(newPackage, packageElement); // load the global exception handler list for this package loadGobalExceptionMappings(newPackage, packageElement); // get actions NodeList actionList = packageElement.getElementsByTagName(“action”); for(int i = 0;i < actionList.getLength();i++){ Element actionElement =(Element)actionList.item(i); addAction(actionElement, newPackage); } // load the default action reference for this package loadDefaultActionRef(newPackage, packageElement); PackageConfig cfg = newPackage.build(); configuration.addPackageConfig(cfg.getName(), cfg); return cfg; } loadConfigurationFiles解析读取xml中的内容 private List loadConfigurationFiles(String fileName,Element includeElement){ ...//通过DomHelper调用SAX进行解析xml doc = DomHelper.parse(in, dtdMappings); ...Element rootElement = doc.getDocumentElement(); NodeList children = rootElement.getChildNodes(); int childSize = children.getLength(); for(int i = 0;i < childSize;i++){ Node childNode = children.item(i); if(childNode instanceof Element){ Element child =(Element)childNode; final String nodeName = child.getNodeName(); if(“include”.equals(nodeName)){ String includeFileName = child.getAttribute(“file”); //解析每个action配置是,对于include文件可以使用通配符*来进行配置 //如Struts.xml中可配置成 if(includeFileName.indexOf('*')!=-1){ ClassPathFinder wildcardFinder = new ClassPathFinder(); wildcardFinder.setPattern(includeFileName); Vector for(String match : wildcardMatches){ //递归Load子file中的 docs.addAll(loadConfigurationFiles(match, child)); } } else { docs.addAll(loadConfigurationFiles(includeFileName, child)); } } } } docs.add(doc); loadedFileUrls.add(url.toString()); ...return docs; } 首先强调一下struts2的线程程安全,在Struts2中大量采用ThreadLocal线程局部变量的方法来保证线程的安全,像Dispatcher等都是通过ThreadLocal来保存变量值,使得每个线程都有自己独立的实例变量,互不相干.接下来就从Dispatcher开始看起,先看其构造函数: //创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param参数 this.initParams = initParams; } //创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方 public Dispatcher(ServletContext servletContext, Map this.servletContext = servletContext; //配置在web.xml中的param参数 this.initParams = initParams; } 我们再看在FilterDispatcher创建Dispatcher的: protected Dispatcher createDispatcher(FilterConfig filterConfig){ Map for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){ String name =(String)e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } 都可以从FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); } protected Dispatcher createDispatcher(FilterConfig filterConfig){ Map for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){ String name =(String)e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } 都可以从FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); } 分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.下面将分析这七层功夫是怎样一步步练成的.首先是init_DefaultProperties() 创建Dispatcher之后,来看init()方法 init()方法是用来Load用户配置文件,资源文件以及默认的配置文件.主要分七步走,看下面注释 public void init(){ if(configurationManager == null){ //设置ConfigurationManager的defaultFrameworkBeanName.//这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等 configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //读取properties信息,默认的default.properties,init_DefaultProperties();// [1] //读取xml配置文件 init_TraditionalXmlConfigurations();// [2] //读取用户自定义的struts.properties init_LegacyStrutsProperties();// [3] //自定义的configProviders init_CustomConfigurationProviders();// [5] //载入FilterDispatcher传进来的initParams init_FilterInitParameters();// [6] //将配置文件中的bean与具体的类映射 init_AliasStandardObjects();// [7] //构建一个用于依赖注射的Container对象 //在这里面会循环调用上面七个ConfigurationProvider的register方法 //其中的重点就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if(!dispatcherListeners.isEmpty()){ for(DispatcherListener l : dispatcherListeners){ l.dispatcherInitialized(this); } } new } public void init(){ if(configurationManager == null){ //设置ConfigurationManager的defaultFrameworkBeanName.//这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等 configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //读取properties信息,默认的default.properties,init_DefaultProperties();// [1] //读取xml配置文件 init_TraditionalXmlConfigurations();// [2] //读取用户自定义的struts.properties init_LegacyStrutsProperties();// [3] //自定义的configProviders init_CustomConfigurationProviders();// [5] //载入FilterDispatcher传进来的initParams init_FilterInitParameters();// [6] //将配置文件中的bean与具体的类映射 init_AliasStandardObjects();// [7] //构建一个用于依赖注射的Container对象 //在这里面会循环调用上面七个ConfigurationProvider的register方法 //其中的重点就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if(!dispatcherListeners.isEmpty()){ for(DispatcherListener l : dispatcherListeners){ l.dispatcherInitialized(this); } } new } 分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.下面将分析这七层功夫是怎样一步步练成的.首先是init_DefaultProperties() private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings(“org/apache/struts2/default”); } catch(Exception e){ throw } loadSettings(props, defaultSettings); } private void init_DefaultProperties(){ configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e); register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings(“org/apache/struts2/default”); } catch(Exception e){ throw } loadSettings(props, defaultSettings); } //PropertiesSettings构造方法 //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写 public PropertiesSettings(String name){ URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass()); if(settingsUrl == null){ LOG.debug(name + “.properties missing”); settings = new LocatableProperties(); return; } settings // Load settings InputStream in = null; try { = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e); in = settingsUrl.openStream(); settings.load(in); } catch(IOException e){ throw new StrutsException(“Could not load ” + name + “.properties:” + e, e); } finally { if(in!= null){ try { in.close(); } catch(IOException io){ LOG.warn(“Unable to close input stream”, io); } } } } //loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props //这个props是register的一个入参.protected void loadSettings(LocatableProperties props, final Settings settings){ // We are calling the impl methods to get around the single instance of Settings that is expected for(Iterator i = settings.listImpl();i.hasNext();){ String name =(String)i.next(); props.setProperty(name, settings.getLocationImpl(name)); } } //PropertiesSettings构造方法 //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写 public PropertiesSettings(String name){ URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass()); settings.getImpl(name),if(settingsUrl == null){ LOG.debug(name + “.properties missing”); settings = new LocatableProperties(); return; } settings // Load settings InputStream in = null; try { in = settingsUrl.openStream(); settings.load(in); } catch(IOException e){ throw new StrutsException(“Could not load ” + name + “.properties:” + e, e); } finally { if(in!= null){ try { in.close(); } catch(IOException io){ LOG.warn(“Unable to close input stream”, io); } } } } //loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props //这个props是register的一个入参.protected void loadSettings(LocatableProperties props, final Settings settings){ // We are calling the impl methods to get around the single instance of Settings that is expected for(Iterator i = settings.listImpl();i.hasNext();){ String name =(String)i.next(); = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); props.setProperty(name, settings.getLocationImpl(name)); } } 再来看第二步:init_TraditionalXmlConfigurations() private void init_TraditionalXmlConfigurations(){ //首先读取web.xml中的config初始参数值 //如果 没 有 配 置 就 使 settings.getImpl(name),用默认的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//这儿就可以看出为什么默认的配置文件必须取名为这三个名称了 //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可 String configPaths = initParams.get(“config”); if(configPaths == null){ configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split(“¥¥s*[,]¥¥s*”); for(String file : files){ if(file.endsWith(“.xml”)){ if(“xwork.xml”.equals(file)){ //XmlConfigurationProvider负责解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider来解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException(“Invalid configuration file name”); } } } private void init_TraditionalXmlConfigurations(){ //首先读取web.xml中的config初始参数值 //如果 没 有 配 置 就 使 用 默 认的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//这儿就可以看出为什么默认的配置文件必须取名为这三个名称了 //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可 String configPaths = initParams.get(“config”); if(configPaths == null){ configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split(“¥¥s*[,]¥¥s*”); for(String file : files){ if(file.endsWith(“.xml”)){ if(“xwork.xml”.equals(file)){ //XmlConfigurationProvider负责解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider来解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException(“Invalid configuration file name”); } } } 对于其它配置文件只用接口。 类XmlConfigurationProvider负责配置文件的读取和解析,首先通过init()中的loadDocuments(configFileName);利用DomHelper中的 public static Document parse(InputSource inputSource, Map addAction()方法负责读取 loadInterceptorStack()方法负责将 loadInterceptorStacks()方法负责将 而上面的方法最终会被addPackage()方法调用,addPackage又会被Provider的loadPackages()调用,将所读取到的数据汇集到PackageConfig对象中。 protected PackageConfig addPackage(Element packageElement) throws ConfigurationException { PackageConfig.Builder newPackage = buildPackageContext(packageElement); if(newPackage.isNeedsRefresh()){ return newPackage.build(); } // add result types(and default result)to this package addResultTypes(newPackage, packageElement); // load the interceptors and interceptor stacks for this package loadInterceptors(newPackage, packageElement); // load the default interceptor reference for this package loadDefaultInterceptorRef(newPackage, packageElement); // load the default class ref for this package loadDefaultClassRef(newPackage, packageElement); // load the global result list for this package loadGlobalResults(newPackage, packageElement); // load the global exception handler list for this package loadGobalExceptionMappings(newPackage, packageElement); // get actions NodeList actionList = packageElement.getElementsByTagName(“action”); for(int i = 0;i < actionList.getLength();i++){ Element actionElement =(Element)actionList.item(i); addAction(actionElement, newPackage); } // load the default action reference for this package loadDefaultActionRef(newPackage, packageElement); PackageConfig cfg = newPackage.build(); configuration.addPackageConfig(cfg.getName(), cfg); return cfg; } loadConfigurationFiles解析读取xml中的内容 private List loadConfigurationFiles(String fileName, includeElement){ ...//通过DomHelper调用SAX进行解析xml doc = DomHelper.parse(in, dtdMappings); ...Element rootElement = doc.getDocumentElement(); NodeList children = rootElement.getChildNodes(); int childSize = children.getLength(); for(int i = 0;i < childSize;i++){ Node childNode = children.item(i); if(childNode instanceof Element){ Element child =(Element)childNode; final String nodeName = child.getNodeName(); if(“include”.equals(nodeName)){ String includeFileName = child.getAttribute(“file”); //解析每个action配置是,对于include文件可以使用通配符*来进行配置 //如Struts.xml中可配置成 if(includeFileName.indexOf('*')!=-1){ ClassPathFinder wildcardFinder = new ClassPathFinder(); wildcardFinder.setPattern(includeFileName); Element Vector for(String match : wildcardMatches){ //递归Load子file中的 docs.addAll(loadConfigurationFiles(match, child)); } } else { docs.addAll(loadConfigurationFiles(includeFileName, child)); } } } } docs.add(doc); loadedFileUrls.add(url.toString()); ...return docs; } 接下来第三步:init_LegacyStrutsProperties()调用的是调用的是LegacyPropertiesConfigurationProvider 通过比较前 面 DefaultPropertiesProvider 与 调 用的是LegacyPropertiesConfigurationProvider.发现DefaultPropertiesProvider继承自后者,但重写了register()方法,主要是生成PropertiesSetting的不同,前者是根据org/apache/struts2/default.properties 后者是根据struts.properties 我们展开register()中的Settings.getInstance(),最后是调用getDefaultInstance() private static Settings getDefaultInstance(){ if(defaultImpl == null){ // Create bootstrap implementation //不带参数的DefaultSettings(),区别与DefaultPropertiesProvider中直接带default.properties参数 //不带参数就是默认为struts.propertes,并且加载struts.custom.properties所定义的properties文件 defaultImpl = new DefaultSettings(); // Create default implementation try { //STRUTS_CONFIGURATION为:struts.configuration //在struts.proterties中查找struts.configuration的值,这个值必须是org.apache.struts2.config.Configuration接口的实现类 //所以我有个困惑就是在下面的转换当中怎么将Configuration转换成Setting类型的...//这一点先放下了,有时间再研究 String className = get(StrutsConstants.STRUTS_CONFIGURATION); if(!className.equals(defaultImpl.getClass().getName())){ try { // singleton instances shouldn't be built accessing request or session-specific context data defaultImpl oader().loadClass(className), null); } catch(Exception e){ LOG.error(“Settings: } } } catch(IllegalArgumentException ex){ // ignore } private static Settings getDefaultInstance(){ if(defaultImpl == null){ // Create bootstrap implementation //不带参数的DefaultSettings(),区别与DefaultPropertiesProvider中直接带default.properties参数 //不带参数就是默认为struts.propertes,并且加载struts.custom.properties所定义的properties文件 defaultImpl = new DefaultSettings(); // Create default implementation try { //STRUTS_CONFIGURATION为:struts.configuration //在struts.proterties中查找struts.configuration的值,这个值必须是 Could not instantiate the struts.configuration object, substituting the default implementation.”, e); = (Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLorg.apache.struts2.config.Configuration接口的实现类 //所以我有个困惑就是在下面的转换当中怎么将Configuration转换成Setting类型的...//这一点先放下了,有时间再研究 String className = get(StrutsConstants.STRUTS_CONFIGURATION); if(!className.equals(defaultImpl.getClass().getName())){ try { // singleton instances shouldn't be built accessing request or session-specific context data defaultImpl oader().loadClass(className), null); } catch(Exception e){ LOG.error(“Settings: } } } catch(IllegalArgumentException ex){ // ignore } 在2.1.6中去掉了第四步:init_ZeroConfiguration();第五步是自定义的configProviders private void init_CustomConfigurationProviders(){ //从这里可以看到可以将自定义的Provider定义在web.xml中FilterDispatcher的param中:configProviders String configProvs = initParams.get(”configProviders“); if(configProvs!= null){ String[] classes = configProvs.split(”¥¥s*[,]¥¥s*“); for(String cname : classes){ try { Class cls = ClassLoaderUtils.loadClass(cname, this.getClass()); ConfigurationProvider(ConfigurationProvider)cls.newInstance(); configurationManager.addConfigurationProvider(prov); prov = Could not instantiate the struts.configuration object, substituting the default implementation.”, e); = (Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassL } ...} } } private void init_CustomConfigurationProviders(){ //从这里可以看到可以将自定义的Provider定义在web.xml中FilterDispatcher的param中:configProviders String configProvs = initParams.get(“configProviders”); if(configProvs!= null){ String[] classes = configProvs.split(“¥¥s*[,]¥¥s*”); for(String cname : classes){ try { Class cls = ClassLoaderUtils.loadClass(cname, this.getClass()); ConfigurationProvider(ConfigurationProvider)cls.newInstance(); configurationManager.addConfigurationProvider(prov); } ...} } } 第六步:init_FilterInitParameters //从这里可以看出struts.properties中的属性不仅可以在struts.xml中以constant形式定义,而且可以在FilterDispatcher的param中定义 private void init_FilterInitParameters(){ configurationManager.addConfigurationProvider(new ConfigurationProvider(){ public void destroy(){} public void init(Configuration configuration) throws ConfigurationException {} public void loadPackages()throws ConfigurationException {} public boolean needsReload(){ return false;} prov = public void register(ContainerBuilder builder, LocatableProperties props)throws ConfigurationException { props.putAll(initParams);//在这里实现滴~ } }); } //从这里可以看出struts.properties中的属性不仅可以在struts.xml中以constant形式定义,而且可以在FilterDispatcher的param中定义 private void init_FilterInitParameters(){ configurationManager.addConfigurationProvider(new ConfigurationProvider(){ public void destroy(){} public void init(Configuration configuration) throws ConfigurationException {} public void loadPackages()throws ConfigurationException {} public boolean needsReload(){ return false;} public void register(ContainerBuilder builder, LocatableProperties props)throws ConfigurationException { props.putAll(initParams);//在这里实现滴~ } }); } 第七步:init_AliasStandardObjects,使用BeanSelectionProvider 这是将配置文件中定义的 接下来是看怎样调用这些ConfigurationProviders 展开init_PreloadConfiguration() private Container init_PreloadConfiguration(){ Configuration config = configurationManager.getConfiguration(); Container container = config.getContainer(); boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD)); LocalizedTextUtil.setReloadBundles(reloadi18n); return container; } //再看getConfiguration() public synchronized Configuration getConfiguration(){ if(configuration == null){ setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName)); try { //重点就是这个reloadContainer configuration.reloadContainer(getContainerProviders()); } catch(ConfigurationException e){ setConfiguration(null); throw new ConfigurationException(“Unable to load configuration.”, e); } } else { conditionalReload(); } return configuration; } private Container init_PreloadConfiguration(){ Configuration config = configurationManager.getConfiguration(); Container container = config.getContainer(); boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD)); LocalizedTextUtil.setReloadBundles(reloadi18n); return container; } //再看getConfiguration() public synchronized Configuration getConfiguration(){ if(configuration == null){ setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName)); try { //重点就是这个reloadContainer configuration.reloadContainer(getContainerProviders()); } catch(ConfigurationException e){ setConfiguration(null); throw new ConfigurationException(“Unable to load configuration.”, e); } } else { conditionalReload(); } return configuration; } 展开DefaultConfiguration中的reloadContainer public synchronized List reloadContainer(List packageContexts.clear(); loadedFileNames.clear(); List packageProviders = new ArrayList (); //Struts2(xwork2)用Container来完成依赖注入的功能 //首先初始化一个ContainerBuilder,再由builder来保存接口与实现类或工厂类的对应关系 //然后通过builder.create(boolean)方法产生container //由container.getInstance(Class);就可以得到接口的实现实例了 //这一部分比较复杂,后面研究完成了,会单独拿出来讲,这里先弄清楚Xwork依赖注入的实现步骤就可以了 ContainerProperties props = new ContainerProperties(); ContainerBuilder builder = new ContainerBuilder(); for(final ContainerProvider containerProvider : providers) { //循环调用ConfigurationProvider的init和register方法,明白了吧,在这里统一循环调用 containerProvider.init(this); containerProvider.register(builder, props); } props.setConstants(builder); //注入依赖关系,在这里并不产生实例 builder.factory(Configuration.class, new Factory public Configuration create(Context context)throws Exception { return DefaultConfiguration.this; } }); ActionContext oldContext = ActionContext.getContext(); try { // Set the bootstrap container for the purposes of factory creation Container bootstrap = createBootstrapContainer(); setContext(bootstrap); //create已经注入依赖关系的Container container = builder.create(false); setContext(container); objectFactory = container.getInstance(ObjectFactory.class); // Process the configuration providers first for(final ContainerProvider containerProvider : providers) { if(containerProvider instanceof PackageProvider){ container.inject(containerProvider); //调用PackageProvider的loadPackages()方法,这里主要是针对XmlConfigurationProvider和StrutsXmlConfigurationProvider ((PackageProvider)containerProvider).loadPackages(); packageProviders.add((PackageProvider)containerProvider); } } // Then process any package providers from the plugins Set packageProviderNames = container.getInstanceNames(PackageProvider.class); if(packageProviderNames!= null){ for(String name : packageProviderNames){ PackageProvider provider.init(this); provider.loadPackages(); packageProviders.add(provider); } } rebuildRuntimeConfiguration(); } finally { if(oldContext == null){ ActionContext.setContext(null); } } return packageProviders; } Dispatcher已经在之前讲过,这就好办了。FilterDispatcher是Struts2的核心控制器,首先看一下init()方法。 public void init(FilterConfig filterConfig)throws ServletException { try { this.filterConfig = filterConfig; initLogging(); //创建dispatcher,前面都已经讲过啰 dispatcher = createDispatcher(filterConfig); dispatcher.init(); //注入将FilterDispatcher中的变量通过container注入,如下面的staticResourceLoader dispatcher.getContainer().inject(this); //StaticContentLoader在BeanSelectionProvider中已经被注入了依赖关系:DefaultStaticContentLoader //可以在struts-default.xml中的 staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig)); } finally { provider = container.getInstance(PackageProvider.class, name); ActionContext.setContext(null); } } public void init(FilterConfig filterConfig)throws ServletException { try { this.filterConfig = filterConfig; initLogging(); //创建dispatcher,前面都已经讲过啰 dispatcher = createDispatcher(filterConfig); dispatcher.init(); //注入将FilterDispatcher中的变量通过container注入,如下面的staticResourceLoader dispatcher.getContainer().inject(this); //StaticContentLoader在BeanSelectionProvider中已经被注入了依赖关系:DefaultStaticContentLoader //可以在struts-default.xml中的 staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig)); } finally { ActionContext.setContext(null); } } //下面来看DefaultStaticContentLoader的setHostConfig public void setHostConfig(HostConfig filterConfig){ //读取初始参数 pakages,调用 parse(),解析成类似/org/apache/struts2/static,/template的数组 String param = filterConfig.getInitParameter(“packages”); //“org.apache.struts2.static org.apache.struts2.interceptor.debugging static” String packages = getAdditionalPackages(); if(param!= null){ packages = param + “ ” + packages; } this.pathPrefixes = parse(packages); initLogging(filterConfig); } template //下面来看DefaultStaticContentLoader的setHostConfig public void setHostConfig(HostConfig filterConfig){ //读取初始参数 pakages,调用 parse(),解析成类似/org/apache/struts2/static,/template的数组 String param = filterConfig.getInitParameter(“packages”); //“org.apache.struts2.static org.apache.struts2.interceptor.debugging static” String packages = getAdditionalPackages(); if(param!= null){ packages = param + “ ” + packages; } this.pathPrefixes = parse(packages); initLogging(filterConfig); } 现在回去doFilter的方法,每当有一个Request,都会调用这些Filters的doFilter方法 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException { HttpServletRequest request =(HttpServletRequest)req; HttpServletResponse response =(HttpServletResponse)res; ServletContext servletContext = getServletContext(); String timerKey = “FilterDispatcher_doFilter: ”; try { // FIXME: this should be refactored better to not duplicate work with the action invocation //先看看ValueStackFactory所注入的实现类OgnlValueStackFactory //new OgnlValueStack ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); ActionContext ctx = new ActionContext(stack.getContext()); ActionContext.setContext(ctx); template UtilTimerStack.push(timerKey); //如果是multipart/form-data就用MultiPartRequestWrapper进行包装 //MultiPartRequestWrapper 是 StrutsRequestWrapper的子类,两者都是HttpServletRequest实现 //此时在MultiPartRequestWrapper中就会把Files给解析出来,用于文件上传 //所有request都会StrutsRequestWrapper进行包装,StrutsRequestWrapper是可以访问ValueStack //下面是参见Dispatcher的wrapRequest // String content_type = request.getContentType(); //if(content_type!= null&&content_type.indexOf(“multipart/form-data”)!=-1){ //MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class); //request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext)); //} else { // request = new StrutsRequestWrapper(request); // } request = prepareDispatcherAndWrapRequest(request, response); ActionMapping mapping; try { //根据url取得对应的Action的配置信息 //看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存储在 ActionMapping对象中 mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager()); } catch(Exception ex){ log.error(“error getting ActionMapping”, ex); dispatcher.sendError(request,return; } //如果找不到对应的action配置,则直接返回。比如你输入***.jsp等等 //这儿有个例外,就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,当然.class文件除外。如果再没有则跳转到 response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); =new 404 if(mapping == null){ // there is no action in this request, should we look for a static resource? String resourcePath = RequestUtils.getServletPath(request); if(“".equals(resourcePath)&& null!= request.getPathInfo()){ resourcePath = request.getPathInfo(); } if(staticResourceLoader.canHandle(resourcePath)){ // 在DefaultStaticContentLoader 中 :return serveStatic &&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“)); staticResourceLoader.findStaticResource(resourcePath, response); } else { // this is a normal request, let it pass through chain.doFilter(request, response); } // The framework did its job here return; } //正式开始Action的方法 dispatcher.serviceAction(request, response, servletContext, mapping); } finally { try { ActionContextCleanUp.cleanUp(req); } finally { UtilTimerStack.pop(timerKey); } } } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException { HttpServletRequest request =(HttpServletRequest)req; request,HttpServletResponse response =(HttpServletResponse)res; ServletContext servletContext = getServletContext(); String timerKey = ”FilterDispatcher_doFilter: “; try { // FIXME: this should be refactored better to not duplicate work with the action invocation //先看看ValueStackFactory所注入的实现类OgnlValueStackFactory //new OgnlValueStack ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); ActionContext ctx = new ActionContext(stack.getContext()); ActionContext.setContext(ctx); UtilTimerStack.push(timerKey); //如果是multipart/form-data就用MultiPartRequestWrapper进行包装 //MultiPartRequestWrapperHttpServletRequest实现 //此时在MultiPartRequestWrapper中就会把Files给解析出来,用于文件上传 //所有request都会StrutsRequestWrapper进行包装,StrutsRequestWrapper是可以访问ValueStack //下面是参见Dispatcher的wrapRequest // String content_type = request.getContentType(); //if(content_type!= null&&content_type.indexOf(”multipart/form-data“)!=-1){ //MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class); //request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext)); //} else { // request = new StrutsRequestWrapper(request); // } request = prepareDispatcherAndWrapRequest(request, response); ActionMapping mapping; try { =new 是 StrutsRequestWrapper的子类,两者都是 //根据url取得对应的Action的配置信息 //看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存储在 ActionMapping对象中 mapping } catch(Exception ex){ log.error(”error getting ActionMapping“, ex); dispatcher.sendError(request,return; } //如果找不到对应的action配置,则直接返回。比如你输入***.jsp等等 //这儿有个例外,就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,当然.class文件除外。如果再没有则跳转到404 if(mapping == null){ // there is no action in this request, should we look for a static resource? String resourcePath = RequestUtils.getServletPath(request); if(”“.equals(resourcePath)&& null!= request.getPathInfo()){ resourcePath = request.getPathInfo(); } if(staticResourceLoader.canHandle(resourcePath)){ // 在DefaultStaticContentLoader 中 :return serveStatic &&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“)); staticResourceLoader.findStaticResource(resourcePath, response); } else { // this is a normal request, let it pass through chain.doFilter(request, response); } // The framework did its job here return; } request,response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); = actionMapper.getMapping(request, dispatcher.getConfigurationManager()); //正式开始Action的方法 dispatcher.serviceAction(request, response, servletContext, mapping); } finally { try { ActionContextCleanUp.cleanUp(req); } finally { UtilTimerStack.pop(timerKey); } } } //下面是ActionMapper接口的实现类 DefaultActionMapper的getMapping()方法的源代码: public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){ ActionMapping mapping = new ActionMapping(); String uri = getUri(request);//得到请求路径的URI,如:testAtcion.action或testAction.do int indexOfSemicolon = uri.indexOf(”;“);//修正url的带;jsessionid 时找不到而且的bug uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri; uri = dropExtension(uri, mapping);//删除扩展名,默认扩展名为action if(uri == null){ return null; } parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace handleSpecialParameters(request, mapping);//去掉重复参数 //如果Action的name没有解析出来,直接返回 if(mapping.getName()== null){ returnnull; } //下面处理形如testAction!method格式的请求路径 if(allowDynamicMethodCalls){ // handle ”name!method“ convention.String name = mapping.getName(); int exclamation = name.lastIndexOf(”!“);//!是Action名称和方法名的分隔符 if(exclamation!=-1){ mapping.setName(name.substring(0, exclamation));//提取左边为name mapping.setMethod(name.substring(exclamation + 1));//提取右边的method } } return mapping; } //下面是ActionMapper接口的实现类 DefaultActionMapper的getMapping()方法的源代码: public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){ ActionMapping mapping = new ActionMapping(); String uri = getUri(request);//得到请求路径的URI,如:testAtcion.action或testAction.do int indexOfSemicolon = uri.indexOf(”;“);//修正url的带;jsessionid 时找不到而且的bug uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri; uri = dropExtension(uri, mapping);//删除扩展名,默认扩展名为action if(uri == null){ return null; } parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace handleSpecialParameters(request, mapping);//去掉重复参数 //如果Action的name没有解析出来,直接返回 if(mapping.getName()== null){ returnnull; } //下面处理形如testAction!method格式的请求路径 if(allowDynamicMethodCalls){ // handle ”name!method“ convention.String name = mapping.getName(); int exclamation = name.lastIndexOf(”!“);//!是Action名称和方法名的分隔符 if(exclamation!=-1){ mapping.setName(name.substring(0, exclamation));//提取左边为name mapping.setMethod(name.substring(exclamation + 1));//提取右边的method } } return mapping; } 从代码中看出,getMapping()方法返回ActionMapping类型的对象,该对象包含三个参数:Action的name、namespace和要调用的方法method。 如果getMapping()方法返回ActionMapping对象为null,则FilterDispatcher认为用户请求不是Action,自然另当别论,FilterDispatcher会做一件非常有意思的事:如果请求以/struts开头,会自动查找在web.xml文件中配置的 packages初始化参数,就像下面这样: org.apache.struts2.dispatcher.FilterDispatcher packages com.lizanhong.action org.apache.struts2.dispatcher.FilterDispatcher packages com.lizanhong.action FilterDispatcher会将com.lizanhong.action包下的文件当作静态资源处理,即直接在页面上显示文件内容,不过会忽略扩展名为class的文件。比如在com.lizanhong.action包下有一个aaa.txt的文本文件,其内容为“中华人民共和国”,访问 http://localhost:8081/Struts2Demo/struts/aaa.txt时会输出txt中的内容 FilterDispatcher.findStaticResource()方法 protectedvoid findStaticResource(String name,HttpServletRequest request, HttpServletResponse response)throws IOException { if(!name.endsWith(”.class“)){//忽略class文件 //遍历packages参数 for(String pathPrefix : pathPrefixes){ InputStream is = findInputStream(name, pathPrefix);//读取请求文件流 if(is!= null){ ...// set the content-type header String contentType = getContentType(name);//读取内容类型 if(contentType!= null){ response.setContentType(contentType);//重新设置内容类型 } ...try { //将读取到的文件流以每次复制4096个字节的方式循环输出 copy(is, response.getOutputStream()); } finally { is.close(); } return; } } } } protectedvoid findStaticResource(String name,HttpServletRequest request, HttpServletResponse response)throws IOException { if(!name.endsWith(”.class“)){//忽略class文件 //遍历packages参数 for(String pathPrefix : pathPrefixes){ InputStream is = findInputStream(name, pathPrefix);//读取请求文件流 if(is!= null){ ...// set the content-type header String contentType = getContentType(name);//读取内容类型 if(contentType!= null){ response.setContentType(contentType);//重新设置内容类型 } ...try { //将读取到的文件流以每次复制4096个字节的方式循环输出 copy(is, response.getOutputStream()); } finally { is.close(); } return; } } } } 如果用户请求的资源不是以/struts开头——可能是.jsp文件,也可能是.html文件,则通过过滤器链继续往下传送,直到到达请求的资源为止。 如果getMapping()方法返回有效的ActionMapping对象,则被认为正在请求某个Action,将调用 Dispatcher.serviceAction(request, response, servletContext, mapping)方法,该方法是处理Action的关键所在。 下面就来看serviceAction,这又回到全局变量dispatcher中了 //Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping)throws ServletException { //createContextMap方法主要把Application、Session、Request的key value值拷贝到Map中 Map // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action ValueStack stack = (ValueStack)request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); boolean nullStack = stack == null; if(nullStack){ ActionContext ctx = ActionContext.getContext(); if(ctx!= null){ stack = ctx.getValueStack(); } } if(stack!= null){ extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = ”Handling request from Dispatcher“; try { UtilTimerStack.push(timerKey); String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration(); //创建一个Action的代理对象,ActionProxyFactory是创建ActionProxy的工厂 //参考实现类:DefaultActionProxy和DefaultActionProxyFactory ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); // if the ActionMapping says to go straight to a result, do it! //如果是Result,则直接转向,关于Result,ActionProxy,ActionInvocation下一讲中再分析 if(mapping.getResult()!= null){ Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { //执行Action proxy.execute(); } // If there was a previous value stack then set it back onto the request if(!nullStack){ request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch(ConfigurationException e){ // WW-2874 Only log error if in devMode if(devMode){ LOG.error(”Could not find action or result“, e); } else { LOG.warn(”Could not find action or result“, e); } sendError(request, HttpServletResponse.SC_NOT_FOUND, e); } catch(Exception e){ sendError(request,} finally { UtilTimerStack.pop(timerKey); } } 下面开始讲一下主菜ActionProxy了.在这之前最好先去了解一下动态Proxy的基本知识.ActionProxy是Action的一个代理类,也就是说Action的调用是通过ActionProxy实现的,其实就是调用了ActionProxy.execute()方法,而该方法又调用了ActionInvocation.invoke()方法。归根到底,最后调用的是DefaultActionInvocation.invokeAction()方法。DefaultActionInvocation()->init()->createAction()。 最后 通 过 调 用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()这里的步骤是先由ActionProxyFactory创建ActionInvocation和ActionProxy.public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map ActionInvocation inv = new DefaultActionInvocation(extraContext, true); container.inject(inv); return } public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext); response,context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); response,context,ActionInvocation inv = new DefaultActionInvocation(extraContext, true); container.inject(inv); return } 下面先看DefaultActionInvocation的init方法 public void init(ActionProxy proxy){ this.proxy = proxy; Map // Setting this so that other classes, like object factories, can use the ActionProxy and other // contextual information to operate ActionContext actionContext = ActionContext.getContext(); if(actionContext!= null){ actionContext.setActionInvocation(this); } //创建Action,struts2中每一个Request都会创建一个新的Action createAction(contextMap); if(pushAction){ stack.push(action); contextMap.put(”action“, action); } invocationContext = new ActionContext(contextMap); invocationContext.setName(proxy.getActionName()); // get a new List so we don't get problems with the iterator if someone changes the list List interceptorList = new ArrayList interceptors = interceptorList.iterator(); createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext); } protected void createAction(Map // load action String timerKey = ”actionCreate: “ + proxy.getActionName(); try { UtilTimerStack.push(timerKey); //默认为SpringObjectFactory:struts.objectFactory=spring.这里非常巧妙,在struts.properties中可以重写这个属性 //在前面BeanSelectionProvider中通过配置文件为ObjectFactory设置实现类 //这里以Spring为例,这里会调到SpringObjectFactory的buildBean方法,可以通过ApplicationContext的getBean()方法得到Spring的Bean action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap); } catch(InstantiationException e){ throw new XWorkException(”Unable to intantiate Action!“,e, proxy.getConfig()); } catch(IllegalAccessException e){ throw new XWorkException(”Illegal access to constructor, is it public?", e, proxy.getConfig()); } catch(Exception e){ ...} finally { UtilTimerStack.pop(timerKey); } if(actionEventListener!= null){ action = actionEventListener.prepare(action, stack); } } //SpringObjectFactory public Object buildBean(String beanName, Map Object o = null; try { //SpringObjectFactory 会 通 过 web.xml 中的 一.一个简单的“photoshop”软件 二.设计目的: 数字图像处理,就是用数字计算机及其他有关数字技术,对图像进行处理,以达到预期的目的。随着计算机的发展,图像处理技术在许多领域得到了广泛应用,数字图像处理已成为电子信息、通信、计算机、自动化、信号处理等专业的重要课程。 数字图像处理课程设计是在完成数字图像处理的相关理论的学习后,进行的综合性训练课程,其目的主要包括: 1、使学生进一步巩固数字图像处理的基本概念、理论、分析方法和实现方法; 2、增强学生应用VC++编写数字图像处理的应用程序及分析、解决实际问题的能力; 3、尝试将所学的内容解决实际工程问题,培养学生的工程实践能力,提高工科学生的就业能力。 三.设计内容: 1.打开图像: 主要代码: static char szFilter[]=“BMP文件(*.bmp)|*.bmp||”;//定义过滤文件的类型 象 CString filename; int ret=dlg.DoModal();//运行打开文件对方框 if(ret==IDOK) { filename=dlg.GetFileName(); //获取所选择图像的路径 m_dib.LoadFromFile(filename); //加载图像 if(!m_dib.m_bLoaded) //判断是否加载图像成功 { AfxMessageBox(“图像打不开”); } return; CFileDialog dlg(TRUE,“bmp”,NULL, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,szFilter);//定义文件对话框对 效果图: 2.水平镜像:把图像的第一列和最后一列调转,第二列和倒数第二列换过来,以此类推下去,直到第nw/2为止。代码: int temp,i,j; for(j=0;j for(i=0;i { temp=m_dib.m_pdata[j*nw+i]; m_dib.m_pdata[j*nw+i]=m_dib.m_pdata[nw-i-1+j*nw]; m_dib.m_pdata[nw-i-1+j*nw]=temp; } 效果图: 3.素描风格:先把灰度值与右下的作对比,如果差值大于一个值则说明这是轮廓,先把非轮廓的位置像素置为黑色,最后对所有像素进行底片化处理 代码: int temp,i,j; for(j=0;j for(i=0;i { temp=m_dib.m_pdata[j*nw+i]-m_dib.m_pdata[(j+1)*nw+i+1]; } if(temp<10)m_dib.m_pdata[j*nw+i]=0; //黑色为0 for(j=0;j for(i=0;i 效 { int gray=m_dib.m_pdata[j*nw+i]; m_dib.m_pdata[j*nw+i]=255-gray; } 果 图: 4图像雾化:在图像中引入一定的随机值,打乱图像中的像素值 代码: int i,j,k,dat; //i表示列,j表行 byte *ptemp=(byte *)new byte[nw*nh]; memset(ptemp,0,nw*nh); for(j=0;j for(i=0;i { k=rand()%8;//取任意的随机值 dat=j*nw+i+k;if(dat>=nw*nh)dat=nw*nh-1;ptemp[j*nw+i]=m_dib.m_pdata[dat]; } memcpy(m_dib.m_pdata,ptemp,nw*nh);效果图: 5.浮雕处理:通过勾画图象轮廓和降低周围像素色值,从而生成具有凹凸感的浮雕效果。其方法是生成一缓冲区,计算当前像素的左上角与右下角的像素值之差,再加上一个补值。将其存储到缓冲区。再将缓冲区的数据逐点替换到图像中并显示出来。代码: int w=3,i,j; //w为模板宽度 BYTE *p=new BYTE[nw*nh]; memcpy(p,m_dib.m_pdata,nw*nh); for(j=w/2;j for(i= w/2;i { p[j*nw+i]=m_dib.m_pdata[(j-1)*nw+i-1]*(1)+m_dib.m_pdata[(j+1)*nw+i+1]*(-1)+120; } memcpy(m_dib.m_pdata,p,nw*nh); delete []p;效果图: 6.直方图均衡化 代码: int n[256]={0},g[256]={0};//定义频数数组n,均衡化每个像素的灰度级的数组g double f[256],t[256];//定义频率数组f,累加的频率数组t int g_max=0,g_mim=255; int i,j,k,z; for(j=0;j //统计灰度级的频数n for(i=0;i z=m_dib.m_pdata[j*nw+i]; n[z]++; } for(k=0;k<=255;k++) //统计每个灰度级出现的频率 f[k]=n[k]/(nw*nh*1.0); //累计灰度级的频率 t[0]=f[0];for(k=1;k<=255;k++) t[k]=t[k-1]+f[k]; for(j=0;j for(i=0;i g_max=w>g_max?w:g_max;//得到最大值 g_mim=w } for(k=0;k<=255;k++) //利用公式求每个像素均衡化后的灰度级 g[k]=(int)((g_max-g_mim)*t[k]+g_mim+0.5); for(j=0;j //逐个替换 for(i=0;i k=m_dib.m_pdata[j*nw+i]; m_dib.m_pdata[j*nw+i]=g[k]; } for(j=0;j //计算均衡化的直方图 { //绘制原图像的直方图 for(i=0;i { BYTE temp=m_dib.m_pdata[j*nw+i]; m_hist[temp]++;} m_bHist=true; CString str;int nh=m_dib.GetDIBHeight();int i;// 画坐标轴 // 绘制坐标轴 pDC->MoveTo(410,nh+20);//(410,nh+20)是直方图的左上角坐标 // 垂直轴 pDC->LineTo(410,nh+200);//(410,nh+200)是直方图的左下角坐标 // 水平轴 pDC->LineTo(710,nh+200);//(710,nh+200)是直方图的右下角坐标 // 写X轴刻度值 str.Format(“0”);pDC->TextOut(410, nh+200+10, str);str.Format(“50”);pDC->TextOut(460, nh+200+10, str);str.Format(“100”);pDC->TextOut(510, nh+200+10, str);str.Format(“150”);pDC->TextOut(560, nh+200+10, str);str.Format(“200”);pDC->TextOut(610, nh+200+10, str);str.Format(“255”);pDC->TextOut(665, nh+200+10, str);// 绘制X轴刻度 for(i = 0;i < 256;i += 25){ if((i & 1)== 0){ } // 10的倍数 pDC->MoveTo(i + 10, nh+200-2);pDC->LineTo(i + 10, nh+200+2); } else { } // 10的倍数 pDC->MoveTo(i + 10, nh+200-2);pDC->LineTo(i + 10, nh+200+2);} // 绘制X轴箭头 pDC->MoveTo(705,nh+200-5);pDC->LineTo(710,nh+200);pDC->LineTo(705,nh+200+5);// 绘制y轴箭头 pDC->MoveTo(410,nh+20);pDC->LineTo(405,nh+20+5);pDC->MoveTo(410,nh+20);pDC->LineTo(415,nh+20+5);int max=0;for(i=0;i<256;i++)if(m_yuan[i]>max){ } max=m_yuan[i]; for(i=0;i<256;i++)pDC->MoveTo(410+i,nh+200);pDC->LineTo(410+i,nh+200-(m_yuan[i]*160/max));} if(m_bHist==true)//绘画新的直方图 { CString str;int nh=m_dib.GetDIBHeight();int i;// 画坐标轴 // 绘制坐标轴 pDC->MoveTo(10,nh+20);//(10,nh+20)是直方图的左上角坐标 // 垂直轴 pDC->LineTo(10,nh+200);//(10,nh+200)是直方图的左下角坐标 // 水平轴 pDC->LineTo(310,nh+200);//(310,nh+200)是直方图的右下角坐标 // 写X轴刻度值 str.Format(“0”); pDC->TextOut(10, nh+200+10, str);str.Format(“50”);pDC->TextOut(60, nh+200+10, str);str.Format(“100”);pDC->TextOut(110, nh+200+10, str);str.Format(“150”);pDC->TextOut(160, nh+200+10, str);str.Format(“200”);pDC->TextOut(210, nh+200+10, str);str.Format(“255”);pDC->TextOut(265, nh+200+10, str);// 绘制X轴刻度 for(i = 0;i < 256;i += 25){ if((i & 1)== 0){ // 10的倍数 } else { // 10的倍数 pDC->MoveTo(i + 10, nh+200-2);pDC->LineTo(i + 10, nh+200+2);pDC->MoveTo(i + 10, nh+200-2);pDC->LineTo(i + 10, nh+200+2);} } // 绘制X轴箭头 pDC->MoveTo(305,nh+200-5);pDC->LineTo(310,nh+200);pDC->LineTo(305,nh+200+5);// 绘制y轴箭头 pDC->MoveTo(10,nh+20);pDC->LineTo(5,nh+20+5);pDC->MoveTo(10,nh+20);pDC->LineTo(15,nh+20+5);int max=0;for(i=0;i<256;i++)if(m_hist[i]>max){ max=m_hist[i]; for(i=0;i<256;i++)pDC->MoveTo(10+i,nh+200);pDC->LineTo(10+i,nh+200-(m_hist[i]*160/max)); } } 效果图: 四.心得体会: 通过这次数字图像处理的课程设计,对图片有了更深一层的认识,理解了对图像处理的一些原理,在这个课程设计过程中,需要自己去查阅资料,找资料,还需要理解所找到的资料,遇到问题独立去思考,或者去请教同学,给了我一个很好的锻炼机会,做事情一定要坚持,最后一定会有收获的。 五.参考文献: 《数字图像处理》 ——电子工业出版社 《vc++数字图像处理实验指导书》 曹老师、何家峰主编第二篇:《编译原理》课程(词法分析)实验报告
第三篇:词法分析小结
第四篇:struts2代码分析
第五篇:简易photoshop代码数字图像处理实验报告