??xml version="1.0" encoding="utf-8" standalone="yes"?> 我们通常在开发web应用q程中,展现层Action的单元测试经常被我们忽视了,主要原因是: 1、Action层的业务逻辑比较单。大家潜意识认ؓq一部分的代码不重要? 2、Action层难以模拟httph传递参敎ͼ需要依赖web容器Q因此给单元试~写带来一定的隑ֺ? 我写了一个简单的Action单元试用例Q供大家参考。基于struts的mock和webwork的ActionProxyFactory都可以进行Action的单元测试。我个h比较們与ActionProxyFactory做单元测试。其实写action单元试非常单,大致分ؓ三步可以完成单元测试: 一、设|ActionContext上下文参? 表单传递的h参数d到map? 二、创建Action动态代理对? 通过public abstract ActionProxy createActionProxy(String namespace, String actionName, Map extraContext) throws Exception 创徏action代理对象? 三、junit断言执行l果 assertEquals(testAction.login(),”success”) 详细用例参? public class TestActionTest extends BaseCaseTest{ private ActionProxy proxy = null; private IVoucherService voucherService; @Before public void setUp() throws Exception { IMocksControl control = EasyMock.createControl(); voucherService = control.createMock(IVoucherService.class); Map<String, Object> params = new HashMap<String, Object>(); params.put(”loginId”,”test”); params.put(”password”,”111111″); params.put(”website”,” http://www.bt285.cn ″); params.put(”name”,”说″); params.put(”voucherService”, voucherService); Map extraContext = new HashMap(); extraContext.put(ActionContext.PARAMETERS,params); try { proxy = ActionProxyFactory.getFactory().createActionProxy(”/ http://www.5a520.cn user”, “testAction”, extraContext); proxy.setExecuteResult(false); assertEquals(proxy.execute(),”success”); } catch (Exception e) { e.printStackTrace(); } } @Test public void testLogin() { TestAction testAction = (TestAction) proxy.getAction(); assertEquals(testAction.login(),”success”); } } 注:创徏代理action一定要执行proxy.execute()ҎQ否则参C能够增加到actionContext上下文中。因为proxy.execute()中会执行 invocation.invoke()核心ҎQ遍历执行action中所有的拦截器,包括其中的参数拦截器 某天在服务器上的|页打不开了,频繁报以下错误?/p>
2007-3-18 1:08:26 org.apache.tomcat.util.threads.ThreadPool logFull 在网上找了些回答Q以下是我觉得正的回答Q?br />
1.我想你的部分资源没有释放,U压卡死?br />
2.q接池问?br />
3.应该是服务器端响应request的线E的处理旉q长D?/p>
分析Q?br />
当时使用|站的h数只?个hQ不可能{到Cq发U程150的上Uѝ所以应该不是数据库的问题?br />
通过对出错的提示判断Q应该是q接池用不合理造成的,或者根本没讄q接池。和数据库连接的部分是用Spring的数据源JDBCq的Q如下: 问题应该出现在Spring的DriverManagerDataSource上,它负责管理这些连接的?br />
下边是对DriverManagerDataSource 的解?br />
DriverManagerDataSource in Spring Framework javax.sql Interface DataSource Implementation of SmartDataSource that configures a plain old JDBC Driver via Useful for test or standalone environments outside of a J2EE container, either In a J2EE container, it is recommended to use a JNDI DataSource provided by the If you need a "real" connection pool outside of a J2EE container, consider ----------------------------------------------- There are several Database Connection Pools already available, both within The commons-dbcp package relies on code in the commons-pool package to provide Applications can use the commons-dbcp component directly or through the existing 看完了解释,事实上是因ؓDriverManagerDataSource建立q接是只要有q接新Z个connection,Ҏ没有q接池的作用。改Z下开源的q接池会好点?br />
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 试通过Q问题消除,如果没有搜烦引擎扄案不会这么快解决问题?/p>
Nexus 是Maven仓库理器,如果你用MavenQ你可以?a >Maven中央仓库 下蝲所需要的构gQartifactQ,但这通常不是一个好的做法,你应该在本地架设一个Maven仓库服务器,在代理远E仓库的同时l护本地仓库Q以节省带宽和时_Nexus可以满L需要。此外,他还提供了强大的仓库理功能Q构件搜索功能,它基于RESTQ友好的UI是一个extjs的REST客户端,它占用较的内存Q基于简单文件系l而非数据库。这些优点其日成为最行的Maven仓库理器?/p>
你可以从http://nexus.sonatype.org/downloads/ 或是http://www.5a520.cn 下蝲最新版本的NexusQ笔者用的?.3.0版本?/p>
Nexus提供了两U安装方式,一U是内嵌Jetty的bundleQ只要你有JREp直接q行。第二种方式是WARQ你只须单的其发布到web容器中即可用?/p>
解压nexus-webapp-1.3.0-bundle.zip 至Q意目录,?em>D:\dev_tools Q然后打开CMDQcd至目?em>D:\dev_tools\nexus-webapp-1.3.0\bin\jsw\windows-x86-32 Q运?strong>Nexus.bat 。你会看到Nexus的启动日志,当你看到“Started SelectChannelConnector@0.0.0.0:8081”之后Q说明Nexus启动成功了,然后打开览器,讉Khttp://127.0.0.1:8081/nexusQ你会看到如下的面Q?/p>
要停止NexusQCtrl+C卛_Q此外InstallNexus.bat可以用来Nexus安装成一个windows服务Q其余的脚本则对应了启动Q停止,暂停Q恢复,卸蝲Nexus服务?/p>
你需要有一个能q行的web容器Q这里以TomcatZQ加入Tomcat的安装目录位?em>D:\dev_tools\apache-tomcat-6.0.18 Q首先我们将下蝲?em>nexus-webapp-1.3.0.war 重命名ؓnexus.war Q然后复制到D:\dev_tools\apache-tomcat-6.0.18\webapps\nexus.war Q然后启动CMDQcd?em>D:\dev_tools\apache-tomcat-6.0.18\bin\ 目录Q运?strong>startup.bat 。一切OKQ现在可以打开览器访?a >http://www.bt285.cn :8080/nexusQ你会得到和上图一L界面?/p>
要管?a >NexusQ你首先需要以理员n份登陆,点击界面右上角的loginQ输入默认的d名和密码Qadmin/admin123Q登陆成功后Q你会看到左边的D栏增加了很多内容Q?/p>
q里Q可以管理仓库,配置NexuspȝQ管理Q务,理用户Q角Ԍ权限Q查看系l的RSS源,理及查看系l日志,{等。你会看到Nexus的功能十分丰富和强大Q本文,W者只介绍一些最基本的管理和操作?/p>
点击左边D栏的RepositoriesQ界面的主面板会昄所有一个所有仓库及仓库l的列表Q你会看到它们的Type字段的值有groupQhostedQproxyQvirtual。这里我们不兛_virtualQ只介绍下另外三U类型: 由此我们知道Q我们需要配|一个Maven中央仓库的proxyQ其实Nexus已经内置了Maven CentralQ但我们需要做一些配|。点M库列表中的Maven CentralQ你会注意到它的Policy是releaseQ这说明它不会代理远E仓库的snapshot构gQ这是有原因的,q程仓库的snapshot版本构g不稳定且不受你控Ӟ使用q样的构件含有潜在的风险。然后我们发C面板下方有三个TabQ分别ؓBrowseQConfiguration和MirrorsQ我们点击Configurationq行配置Q你现在需要关心的是两个配|项Q?#8220;Remote Storage Location”E仓库的地址Q对于Maven Central来说是http://repo1.maven.org/maven2/Q?#8220;Download Remote Indexes”思义是指是否下蝲q程索引文gQMaven Central的该字段默认为FalseQ这是ؓ了防止大量Nexus无意识的L耗中央仓库的带宽Q中央仓库有大量的构Ӟ其烦引文件也很大Q。这里我们需要将其设|ؓTrueQ然后点击Save。在Nexus下蝲的中央仓库烦引文件之后,我们可以在本地搜烦中央仓库的所有构件。下囑ֱCZ我们刚才所涉及的配|: q里我们再D一个例子,我们惌代理Sonatype的公׃库,其地址为:http://repository.sonatype.org/content/groups/public/。步骤如下,在Repositories面板的上方,点击AddQ然后选择Proxy RepositoryQ在下方的配|部分,我们填写如下的信息:Repository ID - sonatypeQRepository Name - Sonatype RepositoryQRemote Storage Location - http://repository.sonatype.org/content/groups/public/。其余的保持默认|需要注意的是Repository PolicyQ我们不想代理snapshot构gQ原因前面已l描q。然后点击Save。配|页面如下: Nexus预定义了3个本C库,分别为ReleasesQSnapshotsQ和3rd Party。这三个仓库都有各自明确的目的。Releases用于部v我们自己的release构gQSnapshots用于部v我们自己的snapshot构gQ?rd Party用于部vW三ҎӞ有些构g如Oracle的JDBC驱动Q我们不能从公共仓库下蝲刎ͼ我们需要将光|到自己的仓库中?/p>
当然你也可以创徏自己的本C库,步骤和创Z理仓库类|点击Repository面板上方的Add按钮Q然后选择Hosted RepositoryQ然后在下方的配|面板中输入id和nameQ注意这里我们不再需要填写远E仓库地址QRepository Type则ؓ不可修改的hostedQ而关于Repository PolicyQ你可以Ҏ自己的需要选择Release或者SnapshotQ如图: Nexus中仓库组的概忉|Maven没有的,在Maven看来Q不你是hosted也好Qproxy也好Q或者group也好Q对我都是一LQ我只管ҎgroupIdQartifactIdQversion{信息向你要构g。ؓ了方便Maven的配|,Nexus能够多个仓库,hosted或者proxy合ƈ成一个groupQ这PMaven只需要依赖于一个groupQ便能用所有该group包含的仓库的内容?/p>
Nexus预定义了“Public Repositories”?#8220;Public Snapshot Repositories”两个仓库l,前者默认合q所有预定义的Release仓库Q后者默认合q所有预定义的Snapshot仓库。我们在本文前面的部分创Z一个名?#8220;Sonatype Repository”的仓库,现在其合ƈ?#8220;Public Repositories”中?/p>
点击仓库列表中的“Public Repositories”Q然后选择下方?Configuration" TabQ在配置面板中,右?#8220;Avaiable Repositories”中的“Sonatype Repository”拖拽到左边的“Ordered Group Repository”中,如图Q?/p>
创徏仓库l和创徏proxy及hosted仓库cMQ这里不再赘q。需要注意的是format字段需要填?#8220;maven2”Q添加你感兴的仓库卛_?/p>
在浩大的Maven仓库中一下下点击链接Q浏览\径以L感兴的构g是一件很郁闷的事情。NexusZnexus-indexer提供构g搜烦功能Q要惛_仓库q行搜烦Q无论是hostedQproxyQ或者groupQ你都必ȝ认烦引文件存在。这一点对于代理仓库尤光要,有些q程仓库可能Ҏ没有烦引,所以你无法搜烦q些代理仓库。有些远E仓库的q程索引非常大,如中央仓库达C70M左右Q那么第一ơ下载烦引需要花很多旉Q所以要期望得到搜烦l果Q确保看到如下的文gQ?/p>
一旦你的Nexus拥有了本地或者远E仓库的索引文gQ你可以n受Nexus的构件搜索功能了。不论登陆与否,你都可以使用关键字进行模p搜索,比如我在左边D栏上部的搜烦框内输入junitQ然后点L索按钮,双立刻会分|C?00多条的junit相关构g信息。如果你了解更多的信息,你也可以通过限定groupIdQartifactIdQversionq行搜烦Q点d航栏中的“Advanced Search”Q点dҎ有页面左上角的下拉框Q选择“GAV Search”。笔者这里输入junit:junit:4.4Q然后回车: 选择一Ҏ索结果,在页面下方会昄“Artifact Information”的面板,你可以点?artifact"或?pom"下蝲对应文gQ而该面板双更显CZ一个Maven依赖配置Q你可以直接复制该配|到Maven POM中,q是个十分方便的Ҏ?/p>
此外Q值得一提的是,Nexusq支持基于classname的搜索,你只需点击搜烦面右上角的下拉框,选择“Classname Search”Q然后输入类名即可,q里我不再赘q?/p>
默认情况下,Maven依赖于中央仓库,q是Z能让Maven开即用,但仅仅这么做明显是错误的Q这会造成大量的时间及带宽的浪贏V既然文章的前面已经介绍了如何安装和配置NexusQ现在我们就要配|Maven来用本地的NexusQ以节省旉和带宽资源?/p>
我们可以Repository配置到POM中,但一般来说这不是很好的做法,原因很简单,你需要ؓ所有的Maven目重复该配|。因此,q里我将Repository的配|放?user_home/.m2/settings.xml中: ׃我们不能直接在settings.xml中插?lt;repositories>元素Q这里我们编写了一个profileQƈd了一个profileq?lt;activeProfile>元素自动这个profileȀzR这里的local-nexus仓库指向了刚才我们配|的Nexus?#8220;Public Repositories”仓库l,也就是说Q所有该仓库l包含的仓库都能供我们用。此外,我们通过<releases>?lt;snapshots>元素ȀzMMaven对于仓库所有类型构件下载的支持Q当然你也可以调节该配置Q比如说止Maven从Nexus下蝲snapshot构g?/p>
使用该配|,Maven׃从你的Nexus服务器下载构件了Q速度和从Central下蝲可不是一个数量的?/p>
Nexus提供了两U方式来部v构gQ你可以从UI直接上传Q也可以配置Maven部v构g?/p>
有时候有个jar文g你无法从公共Maven仓库扑ֈQ但是你能从其它得到q个jar文gQ甚xPOMQ,那么你完全可以将q个文g部v到Nexus中,使其成ؓ标准程的一部分。步骤如下: 点击左边D栏的"Repository"Q在双的仓库列表中选择一个仓库,?#8220;3rd Party”Q然后会看到面下方有四个tabQ选择最后一?#8220;Upload”Q你会看到构件上传界面。选择你要上传的构Ӟq指定POMQ(或者手工编写GAV{信息)Q最后点击UploadQ该构gq接被部vCNexus?3rd Party"仓库中。如图: 更常见的用例是:团队在开发一个项目的各个模块Qؓ了让自己开发的模块能够快速让其他Z用,你会惌snapshot版本的构仉|到Maven仓库中,其他人只需要在POMd一个对于你开发模块的依赖Q就能随时拿到最新的snapshot?/p>
以下的pom.xml配置和settings.xml能让你通过Maven自动化部|构Ӟ pom.xml settings.xml q里我们配置所有的snapshot版本构g部v到Nexus的Snapshots仓库中, 所有的release构g部v到Nexus的Releases仓库中。由于部|需要登陆,因ؓ我们在settings.xml中配|对应Repository id的用户名和密码?/p>
然后Q在目目录中执?strong>mvn deploy Q你会看到maven项目构仉|到Nexus中,览Nexus对应的仓库,可以看到刚才部|的构g。当其他人构建其目ӞMaven׃从NexusL依赖q下载?/p>
本文介绍强大的仓库管理器——NexusQ包括如何下载安装NexusQ配|Nexus代理中央仓库Q管理Nexus的代理仓库,本地仓库Q以及仓库组。ƈ帮助你了解如何通过Nexus搜烦构g。最后,如何在Maven中配|Nexus仓库Q以及如何部|构件到Nexus仓库中。这些都是Nexus中最基本也是最常用的功能。随着使用的深入,你会发现Nexusq有很多其它的特性,如用L理,角色权限理{等?/p>
Nexus的OSS版本是完全开源的Q如果你有兴,你可以学习其源码Q甚臌己实C个REST客户端?/p>
马上拥抱Nexus吧,它是免费的!
2、一定要定自己的发展方向,qؓ此目的制定可行的计划。不要说什么,“我刚毕业Q还不知道将来可能做什么?”Q?#8220;跟着感觉赎ͼ先做做看”。因为,q样的观点会通过你的潜意识去暗示你的行ؓ无所事事、碌无为。一直做技术,来成ؓ专家Uh物?向管理方向走Q成业经理hQ先熟悉行业和领域,来自立门户Q还是先在行业里面Pq几q{行做点别的?q很重要Q它决定你q几q、十q内“做什么事情才是在做正的事情Q?#8221;?8R(X1}1g R/D$Z*K$y5U
3、Y件开发团队中Q技术不是万能的Q但没有技术是万万不能的!在技术型团队中,技术与人品同等重要Q当焉怹比较重要哈,其在MM比较多的团队中。在软g目团队中,技术水qx受h重视和尊重的重要砝码。无Z是做理、系l分析、设计、编码,q是产品理、测试、文档、实施、维护,多少你都要有技术基。算我孤陋寡闻,我还真没有亲眼看到过一个外行带领一个Y件开发团队成功地完成qY件开发项目,哪怕就一个,也没有看到。倒是曄看到q一?#8220;高学历的牛h”(非技术型)带一堆h做完q一个项目,目交付的第二天Q项目组成员扔下一?#8220;再也受不了啦Q?#8221;四分五裂、各奔东ѝ那个项目的“成功?#8221;大家可想而知了?tech.techweb.com.cn&F,q9R;K/u.@
4、详l制定自pY件开发专业知识学习计划,q注意及时修正和调整(软g开发技术变化实在太?。请牢记Q?#8220;如果一个Y件开发h员在1?q内都没有更新过自己的知识,那么Q其实他已经不再属于q个行业了?#8221;不要告诉自己没有旉。来自时间管理领域的著名?#8220;三八原则”告诫我们Q另外的?时如何使用决定你的h生成败!本h自毕业以来,q_每天实际学习旉过2时?+j&G1B p7U/? g
5、书c是人类q步的阶梯,对Y件开发h员尤其如此。书c是学习知识的最有效途径Q不要过多地指望在工作中能遇?#8220;世外高h”Qƈ不厌其烦地教你。对于花׃书,我个人经验是Q千万别买国内那帮h出的书!我买的那些家伙出的书Q?00%全部后悔了,无一本例外。更气愤的是Q这些书在二手市场的地摊上都很难卖掉?#8220;拥有书籍q不表示拥有知识Q拥有知识ƈ不表C拥有技能;拥有技能ƈ不表C拥有文化;拥有文化q不表示拥有智慧?#8221;只有书本变成的自己智慧Q才是真正拥有了它?
]]>
严重: All threads (150) are currently busy, waiting. Increase maxThreads (150) or check the servlet status
<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- driver for MySQL-->
<property name="driverClassName"><value>org.gjt.mm.mysql.Driver</value></property>
<property name="url"><value>jdbc:mysql:// www.bt285.cn :3306/test?useUnicode=true&characterEncoding=UTF8</value></property>
<property name="username"><value>test</value></property>
<property name="password"><value>test</value></property>
</beans>
bean properties, and returns a new Connection every time.
as a DataSource bean in a respective ApplicationContext, or in conjunction with
a simple JNDI environment. Pool-assuming Connection.close() calls will simply
close the connection, so any DataSource-aware persistence code should work.
container. Such a DataSource can be exported as a DataSource bean in an
ApplicationContext via JndiObjectFactoryBean, for seamless switching to and from
a local DataSource bean like this class.
Apache's Jakarta Commons DBCP. Its BasicDataSource is a full connection pool
bean, supporting the same basic properties as this class plus specific settings.
It can be used as a replacement for an instance of this class just by changing
the class name of the bean definition to
"org.apache.commons.dbcp.BasicDataSource".
Many Jakarta projects support interaction with a relational database. Creating a
new connection for each user can be time consuming (often requiring multiple
seconds of clock time), in order to perform a database transaction that might
take milliseconds. Opening a connection per user can be unfeasible in a
publicly-hosted Internet application where the number of simultaneous users can
be very large. Accordingly, developers often wish to share a "pool" of open
connections between all of the application's current users. The number of users
actually performing a request at any given time is usually a very small
percentage of the total number of active users, and during request processing is
the only time that a database connection is required. The application itself
logs into the DBMS, and handles any user account issues internally.
Jakarta products and elsewhere. This Commons package provides an opportunity to
coordinate the efforts required to create and maintain an efficient,
feature-rich package under the ASF license.
the underlying object pool mechanisms that it utilizes.
interface of their container / supporting framework. For example the Tomcat
servlet container presents a DBCP DataSource as a JNDI Datasource. James (Java
Apache Mail Enterprise Server) has integrated DBCP into the Avalon framework. A
Avalon-style datasource is created by wrapping the DBCP implementation. The
pooling logic of DBCP and the configuration found in Avalon's excalibur code is
what was needed to create an integrated reliable DataSource.
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:hsql:// www.5a520.cn :9001</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>下蝲和安?/h2>
Bundle方式安装
WAR方式安装
代理外部Maven仓库
登陆
代理Maven中央仓库
d一个代理仓?/em>
理本地Maven仓库
理Maven仓库l?/h2>
搜烦构g
配置Maven使用Nexus
部v构g至Nexus
通过Nexus UI部v
通过Maven部v
ȝ
]]>
Z填补q个I缺Q我们已l发明了一些简单的替代品,例如日志文gQ典型情况下用于服务器处理)和状态条Q用于GUI应用E序Q。但是,׃q些工具只能捕捉和报告可用信息的一个很的子集Qƈ且通常必须把这些信息用Ҏ理解的方式表现出来,所以程序员向于把它们明确地编写到应用E序中。而这些代码会~绕着应用E序的业务逻辑Q当开发者试图调试或了解核心功能的时候,他们必须"围绕q些代码工作"Q而且q要记得功能发生改变后更新这些代码。我们希望实现的真正功能是把状态报告集中在某个位置Q把单个状态消息作为元数据QmetadataQ来理?br />
在本文中我将考虑使用嵌入GUI应用E序中的状态条lg的情形。我介l多U实现这U状态报告的不同ҎQ从传统的硬~码习惯开始。随后我会介lJava 1.5的大量新Ҏ,包括注解QannotationQ和q行时字节码重构QinstrumentationQ?
状态管理器QStatusManagerQ?br />
我的主要目标是徏立一个可以嵌入GUI应用E序的JStatusBar Swinglg。图1昄了一个简单的Jframe中状态条的样式?br />
![]() ?.我们动态生成的状态条 |
׃我不希望直接在业务逻辑中引用Q何GUIlgQ我徏立一个StatusManagerQ状态管理器Q来充当状态更新的入口炏V实际的通知会被委托lStatusState对象Q因此以后可以扩展它以支持多个ƈ发的U程。图2昄了这U安排?/p>
![]() ?. StatusManager和JstatusBar |
现在我必ȝ写代码调用StatusManager的方法来报告应用E序的进E。典型情况下Q这些方法调用都分散地诏I于try-finally代码块中Q通常每个Ҏ一个调用?/p>
/*http://www.bt285.cn */ public void connectToDB (String url) { |
q些代码实现了我们所需要功能,但是在代码库中数十次、甚至于数百ơ地复制q些代码之后Q它看v来就有些混ؕ了。此外,如果我们希望用一些其它的方式讉Kq些消息该怎么办呢Q在本文的后面部分中Q我定义一个用户友好的异常处理E序Q它׃n了相同的消息。问题是我把状态消息隐藏在Ҏ的实C中了Q而没有把消息攑֜消息所属的接口中?br />
面向属性编E?/strong>
我真正想实现的操作是把对StatusManager的引用都攑ֈ代码外面的某个地方,q简单地用我们的消息标记q个Ҏ。接着我可以用代码生成(code-generationQ或q行时反省(introspectionQ来执行真正的工作。XDoclet目把这U方法归Uؓ面向属性编E(Attribute-Oriented ProgrammingQ,它还提供了一个框架组Ӟ可以把自定义的类似Javadoc的标记{换到源代码之中?br />
但是QJSR-175包含了这L内容QJava 1.5Z包含真实代码中的q些属性提供了一U结构化E度更高的格式。这些属性被UCؓ"注解QannotationsQ?Q我们可以用它们ؓcR方法、字D|变量定义提供元数据。它们必被昑ּ声明Qƈ提供一l可以包含Q意常量|包括原语、字W串、枚丑֒c)的名U?值对Qname-value pairQ?br />
注解QAnnotationsQ?br />
Z处理状态消息,我希望定义一个包含字W串值的新注解。注解的定义非常cM接口的定义,但是它用@interface关键字代替了interfaceQƈ且只支持ҎQ尽它们的功能更像字段Q:
public @interface Status { String value(); } |
与接口类|我把@interface攑օ一个叫做Status.java的文件中Qƈ把它导入CQ何需要引用它的文件中?br />
Ҏ们的字段来说Qvalue可能是个奇怪的名称。类似message的名U可能更适合Q但是,value对于Java来说hҎ的意义。它允许我们使用@Status("...")代替@Status(value="...")来定义注解,q明显更加简捗?br />
我现在可以用下面的代码定义自己的方法:
@Status("Connecting to database") public void connectToDB (String url) { ... } |
h意,我们在编译这D代码的时候必M?source 1.5选项。如果你使用Ant而不是直接用javac命o行徏立应用程序,那么你需要用Ant 1.6.1以上版本?br />
作ؓcR方法、字D和变量的补充,注解也可以用于ؓ其它的注解提供元数据。特别地QJava引入了少量注解,你可以用这些注解来定制你自q注解的工作方式。我们用下面的代码重新定义自q注解Q?/p>
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Status { String value(); } |
@Target注解定义了@Status注解可以引用什么内宏V理x况下Q我希望标记大块的代码,但是它的选项只有Ҏ、字Dc类、本地变量、参数和其它注解。我只对代码感兴,因此我选择了METHODQ方法)?br />
@Retention注解允许我们指定Java什么时候可以自d抛弃消息。它可能是SOURCEQ在~译时抛弃)、CLASSQ在c蝲入时抛弃Q或RUNTIMEQ不抛弃Q。我们先选择SOURCEQ但是在本文后部我们会更新它?/p>
重构源代?br />
现在我的消息都被~码攑օ元数据中了,我必ȝ写一些代码来通知状态监听程序。假讑֜某个时候,我l把connectToDBҎ保存源代码控件中Q但是却没有对StatusManager的Q何引用。但是,在编译这个类之前Q我希望加入一些必要的调用。也是_我希望自动地插入try-finally语句和push/pop调用?br />
XDoclet框架lg是一UJava源代码生成引擎,它用了cM上述的注解,但是把它们存储在Java源代码的注释QcommentQ中。XDoclet生成整个JavacR配|文件或其它建立的部分的时候非常完,但是它不支持对已有Javacȝ修改Q而这限制了重构的有效性。作Z替,我可以用分析工P例如JavaCC或ANTLRQ它提供了分析Java源代码的语法基础Q,但是q需要花费大量精力?br />
看v来没有什么可以用于Java代码的源代码重构的很好的工具。这cd具可能有市场Q但是你在本文的后面部分可以看到Q字节码重构可能是一U更强大的技术?重构字节?br />
不是重构源代码然后编译它Q而是~译原始的源代码Q然后重构它所产生的字节码。这L操作可能比源代码重构更容易,也可能更加复杂,而这依赖于需要的准确转换。字节码重构的主要优Ҏ代码可以在运行时被修改,不需要用编译器?br />
管Java的字节码格式相对单,我还是希望用一个Javacd来执行字节码的分析和生成Q这可以把我们与未来JavacL件格式的改变隔离开来)。我选择了用Jakarta的Byte Code Engineering LibraryQ字节码引擎cdQBCELQ,但是我还可以选用CGLIB、ASM或SERP?br />
׃我将使用多种不同的方式重构字节码Q我从声明重构的通用接口开始。它cM于执行基于注解重构的单框架组件。这个框架组件基于注解,支持类和方法的转换Q因此该接口有类g面的定义Q?br />
public interface Instrumentor { public void instrumentClass (ClassGen classGen,Annotation a); public void instrumentMethod (ClassGen classGen,MethodGen methodGen,Annotation a); } |
ClassGen和MethodGen都是BCELc,它们使用了Builder模式QpatternQ。也是_它们为改变其它不可变的(immutableQ对象、以及可变的和不可变的表玎ͼrepresentationQ之间的转换提供了方法?br />
现在我需要ؓ接口~写实现Q它必须用恰当的StatusManager调用更换@Status注解。前面提刎ͼ我希望把q些调用包含在try-finally代码块中。请注意Q要辑ֈq个目标Q我们所使用的注解必ȝ@Retention(RetentionPolicy.CLASS)q行标记Q它指示Java~译器在~译q程中不要抛弃注解。由于在前面我把@Status声明为@Retention(RetentionPolicy.SOURCE)的,我必L新它?br />
在这U情况下Q重构字节码明显比重构源代码更复杂。其原因在于try-finally是一U仅仅存在于源代码中的概cJava~译器把try-finally代码块{换ؓ一pd的try-catch代码块,q在每一个返回之前插入对finally代码块的调用。因此,Z把try-finally代码块添加到已有的字节码中,我也必须执行cM的事务?br />
下面是表C个普通方法调用的字节码,它被StatusManager更新环绕着Q?/p>
0: ldc #2; //字符串消?br />
2: invokestatic #3; //ҎStatusManager.push:(LString;)V 5: invokestatic #4; //Ҏ doSomething:()V 8: invokestatic #5; //Ҏ StatusManager.pop:()V 11: return |
下面是相同的Ҏ调用Q但是位于try-finally代码块中Q因此,如果它生了异常会调用StatusManager.pop()Q?/p>
0: ldc #2; //字符串消?br />
2: invokestatic #3; //Ҏ StatusManager.push:(LString;)V 5: invokestatic #4; //Ҏ doSomething:()V 8: invokestatic #5; //Ҏ StatusManager.pop:()V 11: goto 20 14: astore_0 15: invokestatic #5; //Ҏ StatusManager.pop:()V 18: aload_0 19: athrow 20: return Exception table: from to target type 5 8 14 any 14 15 14 any |
你可以发玎ͼZ实现一个try-finallyQ我必须复制一些指令,q添加了几个跌{和异常表记录。幸q的是,BCEL的InstructionListcMɘq种工作相当单?br />
在运行时重构字节?/strong>
现在我拥有了一个基于注解修改类的接口和该接口的具体实现了,下一步是~写调用它的实际框架lg。实际上我将~写量的框架组Ӟ先从q行旉构所有类的框架组件开始。由于这U操作会在buildq程中发生,我决定ؓ它定义一个Ant事务。build.xml文g中的重构目标的声明应该如下:
Qinstrument class="com.pkg.OurInstrumentor"Q?br /> Qfileset dir="$(classes.dir)"Q?br /> Qinclude name="**/*.class"/Q?br /> Q?filesetQ?br /> Q?instrumentQ?/td> |
Z实现q种事务Q我必须定义一个实现org.apache.tools.ant.Task接口的类。我们的事务的属性和子元素(sub-elementsQ都是通过set和addҎ调用传递进来的。我们调用执行(executeQ方法来实现事务所要执行的工作--在示例中Q就是重构<filesetQ中指定的类文g?/p>
public class InstrumentTask extends Task { ... public void setClass (String className) { ... } public void addFileSet (FileSet fileSet) { ... } public void execute () throws BuildException { Instrumentor inst = getInstrumentor(); try { DirectoryScanner ds =fileSet.getDirectoryScanner(project); // Java 1.5 ?for" 语法 for (String file : ds.getIncludedFiles()) { instrumentFile(inst, file); } } catch (Exception ex) { throw new BuildException(ex); } } ... } |
用于该项操作的BCEL 5.1版本有一个问?-它不支持分析注解。我可以载入正在重构的类q用反(reflectionQ查看注解。但是,如果q样Q我׃得不使用RetentionPolicy.RUNTIME来代替RetentionPolicy.CLASS。我q必dq些cM执行一些静态的初始化,而这些操作可能蝲入本地类库或引入其它的依赖关pR幸q的是,BCEL提供了一U插ӞpluginQ机Ӟ它允许客L分析字节码属性。我~写了自qAttributeReader的实玎ͼimplementationQ,在出现注解的时候,它知道如何分析插入字节码中的RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations属性。BCEL未来的版本应该会包含q种功能而不是作为插件提供?br />
~译时刻的字节码重构Ҏ昄在示例代码的code/02_compiletime目录中?br />
但是q种Ҏ有很多缺陗首先,我必ȝ建立q程增加额外的步骤。我不能Z命o行设|或其它~译时没有提供的信息来决定打开或关闭重构操作。如果重构的或没有重构的代码需要同时在产品环境中运行,那么必d立两个单独的.jars文gQ而且q必d定用哪一个?/p>
在类载入旉构字节码
更好的方法可能是延迟字节码重构操作,直到字节码被载入的时候才q行重构。用这U方法的时候,重构的字节码不用保存h。我们的应用E序启动时刻的性能可能会受到媄响,但是你却可以Z自己的系l属性或q行旉|数据来控制q行什么操作?br />
Java 1.5之前Q我们用定制的c蝲入程序可能实现这U类文gl护操作。但是Java 1.5中新增加的java.lang.instrumentE序包提供了数附加的工兗特别地Q它定义了ClassFileTransformer的概念,在标准的载入q程中我们可以用它来重构一个类?br />
Z在适当的时候(在蝲入Q何类之前Q注册ClassFileTransformerQ我需要定义一个premainҎ。Java在蝲入主c(main classQ之前将调用q个ҎQƈ且它传递进来对Instrumentation对象的引用。我q必ȝ命o行增?javaagent参数选项Q告诉Java我们的premainҎ的信息。这个参数选项把我们的agent classQ代理类Q它包含了premainҎQ的全名和Q意字W串作ؓ参数。在例子中我们把Instrumentorcȝ全名作ؓ参数Q它必须在同一行之中)Q?br />
-javaagent:boxpeeking.instrument.InstrumentorAdaptor= boxpeeking.status.instrument.StatusInstrumentor |
现在我已l安排了一个回调(callbackQ,它在载入M含有注解的类之前都会发生Qƈ且我拥有Instrumentation对象的引用,可以注册我们的ClassFileTransformer了:
public static void premain (String className, Instrumentation i) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class instClass = Class.forName(className); Instrumentor inst = (Instrumentor)instClass.newInstance(); i.addTransformer(new InstrumentorAdaptor(inst)); } |
我们在此处注册的适配器将充当上面l出的Instrumentor接口和Java的ClassFileTransformer接口之间的桥梁?br />
public class InstrumentorAdaptor implements ClassFileTransformer { public byte[] transform (ClassLoader cl,String className,Class classBeingRedefined, ProtectionDomain protectionDomain,byte[] classfileBuffer) { try { ClassParser cp =new ClassParser(new ByteArrayInputStream(classfileBuffer),className + ".java"); JavaClass jc = cp.parse(); ClassGen cg = new ClassGen(jc); for (Annotation an : getAnnotations(jc.getAttributes())) { instrumentor.instrumentClass(cg, an); } for (org.apache.bcel.classfile.Method m : cg.getMethods()) { for (Annotation an : getAnnotations(m.getAttributes())) { ConstantPoolGen cpg =cg.getConstantPool(); MethodGen mg =new MethodGen(m, className, cpg); instrumentor.instrumentMethod(cg, mg, an); mg.setMaxStack(); mg.setMaxLocals(); cg.replaceMethod(m, mg.getMethod()); } } JavaClass jcNew = cg.getJavaClass(); return jcNew.getBytes(); } catch (Exception ex) { throw new RuntimeException("instrumenting " + className, ex); } } ... } |
q种在启动时重构字节码的Ҏ位于在示例的/code/03_startup目录中?br />
异常的处?/strong>
文章前面提到Q我希望~写附加的代码用不同目的的@Status注解。我们来考虑一下一些额外的需求:我们的应用程序必L捉所有的未处理异常ƈ把它们显C给用户。但是,我们不是提供Java堆栈跟踪Q而是昄拥有@Status注解的方法,而且q不应该昄M代码Q类或方法的名称或行L{)?br />
例如Q考虑下面的堆栈跟t信息:
java.lang.RuntimeException: Could not load data for symbol IBM at boxpeeking.code.YourCode.loadData(Unknown Source) at boxpeeking.code.YourCode.go(Unknown Source) at boxpeeking.yourcode.ui.Main+2.run(Unknown Source) at java.lang.Thread.run(Thread.java:566) Caused by: java.lang.RuntimeException: Timed out at boxpeeking.code.YourCode.connectToDB(Unknown Source) ... 更多信息 |
q将D?中所C的GUI弹出框,上面的例子假设你的YourCode.loadData()、YourCode.go()和YourCode.connectToDB()都含有@Status注解。请注意Q异常的ơ序是相反的Q因此用h先得到的是最详细的信息?br />
?.昄在错误对话框中的堆栈跟踪信息
Z实现q些功能Q我必须对已有的代码q行E微的修攏V首先,Z保在运行时@Status注解是可以看到的Q我必dơ更新@RetentionQ把它设|ؓ@Retention(RetentionPolicy.RUNTIME)。请CQ@Retention控制着JVM什么时候抛弃注解信息。这L讄意味着注解不仅可以被编译器插入字节码中Q还能够使用新的Method.getAnnotation(Class)Ҏ通过反射来进行访问?br />
现在我需要安排接收代码中没有明确处理的Q何异常的通知了。在Java 1.4中,处理M特定U程上未处理异常的最好方法是使用ThreadGroup子类q给该类型的ThreadGroupd自己的新U程。但是Java 1.5提供了额外的功能。我可以定义UncaughtExceptionHandler接口的一个实例,qؓM特定的线E(或所有线E)注册它?br />
h意,在例子中为特定异常注册可能更好,但是在Java 1.5.0beta1Q?4986764Q中有一个bugQ它使这h作无法进行。但是ؓ所有线E设|一个处理程序是可以工作的,因此我就q样操作了?
现在我们拥有了一U截取未处理异常的方法了Qƈ且这些异常必被报告l用戗在GUI应用E序中,典型情况下这L操作是通过弹出一个包含整个堆栈跟t信息或单消息的模式对话框来实现的。在例子中,我希望在产生异常的时候显CZ个消息,但是我希望提供堆栈的@Status描述而不是类和方法的名称。ؓ了实现这个目的,我简单地在Thread的StackTraceElement数组中查询,扑ֈ与每个框架相关的java.lang.reflect.Method对象Qƈ查询它的堆栈注解列表。不q的是,它只提供了方法的名称Q没有提供方法的特征量(signatureQ,因此q种技术不支持名称相同的(但@Status注解不同的)重蝲Ҏ?br />
实现q种Ҏ的示例代码可以在peekinginside-pt2.tar.gz文g?code/04_exceptions目录中找到?/p>
取样QSamplingQ?br />
我现在有办法把StackTraceElement数组转换为@Status注解堆栈。这U操作比表明看到的更加有用。Java 1.5中的另一个新Ҏ?-U程反省QintrospectionQ?-使我们能够从当前正在q行的线E中得到准确的StackTraceElement数组。有了这两部分信息之后,我们可以构造JstatusBar的另一U实现。StatusManager不会在发生Ҏ调用的时候接攉知Q而是单地启动一个附加的U程Q让它负责在正常的间隔期间抓取堆栈跟t信息和每个步骤的状态。只要这个间隔期间够短Q用户就不会感觉到更新的延迟?
下面?sampler"U程背后的代码,它跟t另一个线E的l过Q?br />
class StatusSampler implements Runnable { private Thread watchThread; public StatusSampler (Thread watchThread) { this.watchThread = watchThread; } public void run () { while (watchThread.isAlive()) { // 从线E中得到堆栈跟踪信息 StackTraceElement[] stackTrace =watchThread.getStackTrace(); // 从堆栈跟t信息中提取状态消?br /> ListQStatusQ?statusList =StatusFinder.getStatus(stackTrace); Collections.reverse(statusList); // 用状态消息徏立某U状?br /> StatusState state = new StatusState(); for (Status s : statusList) { String message = s.value(); state.push(message); } // 更新当前的状?br /> StatusManager.setState(watchThread,state); //休眠C一个周?br /> try { Thread .sleep(SAMPLING_DELAY); } catch (InterruptedException ex) {} } //状态复?br /> StatusManager.setState(watchThread,new StatusState()); } } |
![]() ?.重构Ҏ的分?/div> |
/* http://www.5a520.cn */ public void connectToDB (String url) { |
环境要求Qwindows xppȝQjdk 1.4Q不要太高或太低Q不然会发生与dll不匹配)Q运行javaE序的机器需要有猫及驱动Q一般的猫都会支持fax功能Q,jacob版本1.9Q最好用这个版本,其他版本会报错)Qxp本n能够通过猫发送传真(认一下环境可以发送传真即可)
实现功能Qjava jni调用本地jacob.dllQjacob.dll中封装好的接口调用本地服务(如excel、outlook、vbscript{)faxserver.faxserver.1Q实现服务器端发送传真?/p>
步骤Q?/p>
1 jacob.dll文g拯到windows/system32?/p>
2 创徏java目Q将jacob.jarQjacob.dll攑ֈ目lib路径下,同时他们也攑ֈjdk的lib路径?/p>
3 ~写E序Q可参考jacob官方的api文 http://www.5a520.cn Q,如下Q?/p>
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.DispatchEvents;
import com.jacob.com.Variant;
public class faxtest {
public void sendFax(String filenameQSring faxnumber) {
ActiveXComponent objFax = new ActiveXComponent("FaxServer.FaxServer.1");//q个名字一般要与注册表里fax服务名匹配对?/p>
Dispatch faxObject = objFax.getObject();
Dispatch.call(faxObject, "Connect", "");
Dispatch doc = Dispatch.call(faxObject, "CreateDocument", filename)
.toDispatch();
Dispatch.put(doc, "RecipientName", "someone");
Dispatch.put(doc, "FaxNumber", faxnumber); //注意电话L的格?br />
Dispatch.put(doc, "DisplayName", "zhupan");
Dispatch.call(doc, "Send");
Dispatch.call(faxObject, "DisConnect");
}
public static void main(String[] args) {
try {
faxtest faxDocumentProperties = new faxtest();
faxDocumentProperties.sendFax(" http://www.bt285.cn /WW.doc","028886666");
System.out.print("ok fax transfer successfully !");
} catch (Exception e) {
System.out.println(e);
}
}
}
4 调试Q如果报?no progid"异常Q一般问题都是jdk与dll不匹配,或者传真服务名U?FaxServer.FaxServer.1)不匹配?/p>
没有服务器端脚本的帮助,JavaScript׃能在服务器上写文?/strong>
使用AjaxQJavaScript可以向服务器发送请求。这个请求可以用XML或纯文本的方式读取文Ӟ但是它不能写文gQ除非被服务器调用的文g以脚本方式运行才能写文g。比?http://www.bt285.cn/content.php?id=1196863 q张甜性ӆ׃载页面是用json获取的,但是在此面里不能直接写入数据?br /> JavaScript不能讉K数据?/strong>
除非你用AjaxQƈ且服务器端脚本ؓ你执行数据库讉K
JavaScript不能从用户处d或写文g
管JavaScript在用L计算Zq行Q而该用户端也正在览|页Q但仍不允许对Q何网|w以外的数据q行讉K。这样做是出于安全的考虑Q因为其他网|可能更新您的计算机ƈ且非法安装上我们都不清楚的东ѝ唯一例外的是所谓的cookies文gQ它是小文本文gQ可以由JavaScript写入和读取。该览器限制对Cookie的访问,所以一个给定的|页只能讉K该网|创造的cookie?/p>
如果没有打开H口QJavaScript不能选择H口
该项限制同样Z安全性的考虑
JavaScript不能讉K|页
管不同的网可以在同一旉打开Q可以在单独的浏览器中或者同一个浏览器的不同窗体中打开。在|页上运行的JavaScript从属于一个网,因此不能讉K来自不同域名中不同网늚信息。这一限制有助于确保你的隐U信息不会被其他同时打开|页的h׃n。而唯一能访问来自另一域名的文件的Ҏ是对你的服务器进行Ajax调用Qƈ却具备一个可以访问其他域名的服务器端脚本?/p>
JavaScript不能保护你的面资源和图?/strong>
面上的M囑փ都是分开下蝲到电脑上的,所以我们在看网늚时候,已l拥有了所有图像的备䆾。而对于网上真正的HTML资源Q也同样如此。网需要解密所有加密的|页Q以昄该网c而一个加密的我那个也可能要求按顺序依ơ激zJavaScriptQ以辑ֈ依次解密再显C出来的目的。一旦网被解密QQ何知道该Ҏ的h都能L保存解密的网资源备份,比如 http://www.5a520.cn/s_c1vvs30vvf5a6Y6Lev6aOO5rWB q张官\风流最新章?20面Q一但下载到客户端,那些q张面所相当的js,css,jpg{httpq接也下载到你本C?/p>