在WebWork2.2中和Spring的结合变得简单了,WebWork的Action的也可以由Spring来管理。但是如何进行测试了,在google上搜了一下,其代码都是如下形式:
1 Map params = new HashMap();
2 params.put("a", "test");
3 Map paramCtx = new HashMap();
4 paramCtx.put(ActionContext.PARAMETERS, params);
5 ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy("/organiz", "new_depart", paramCtx, false, false);
6 proxy.setExecuteResult(false);
7 assertEquals(proxy.execute(), "success");
8
9 MyTestAction action = (MyTestAction) proxy.getAction();
10 assertEquals(action.getA(), "test");
该代码执行时会报错误,查看了一下源代码应该加入
1paramCtx.put(ActionContext.DEV_MODE, Boolean.FALSE);
其次需要加载spring的applicationContext,代码如下:
1SpringObjectFactory objectFactory = new SpringObjectFactory();
2 ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
3 objectFactory.setApplicationContext(appContext);
4 ObjectFactory.setObjectFactory(objectFactory);
posted @
2012-02-28 22:53 小小~咖啡豆 阅读(181) |
评论 (0) |
编辑 收藏
1.编译乱码,设置编译的字符集编码和环境编码
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.4</source>
<target>1.4</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
设置环境变量MAVEN_OPTS=-Xms64m -Xmx128m -Dfile.encoding=UTF-8
2.运行mvn test时乱码(IDE上运行TestCase时OK,但是运行maven test乱码,结果测试不通过)修改pom.xml增加如下内容即可
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7.2</version>
<configuration>
<forkMode>once</forkMode>
<argLine>-Dfile.encoding=UTF-8</argLine>
<systemProperties>
<property>
<name>net.sourceforge.cobertura.datafile</name>
<value>target/cobertura/cobertura.ser</value>
</property>
</systemProperties>
</configuration>
</plugin>
posted @
2011-06-30 02:15 小小~咖啡豆 阅读(1516) |
评论 (1) |
编辑 收藏
<title>屏蔽鼠标右键、Ctrl+n、shift+F10、F5刷新、退格键</title>
</head>
<body onkeydown="KeyDown()"
oncontextmenu="event.returnValue=false">
<script language="Javascript"><!--
//屏蔽鼠标右键、Ctrl+n、shift+F10、F5刷新、退格键
//Author: meizz(梅花雨) 2002-6-18
function KeyDown(){
if ((window.event.altKey)&&
((window.event.keyCode==37)|| //屏蔽 Alt+ 方向键 ←
(window.event.keyCode==39))){ //屏蔽 Alt+ 方向键 →
alert("不准你使用ALT+方向键前进或后退网页!");
event.returnValue=false;
}
/* 注:这还不是真正地屏蔽 Alt+ 方向键,
因为 Alt+ 方向键弹出警告框时,按住 Alt 键不放,
用鼠标点掉警告框,这种屏蔽方法就失效了。以后若
有哪位高手有真正屏蔽 Alt 键的方法,请告知。*/
if ((event.keyCode==8) || //屏蔽退格删除键
(event.keyCode==116)|| //屏蔽 F5 刷新键
(event.ctrlKey && event.keyCode==82)){ //Ctrl + R
event.keyCode=0;
event.returnValue=false;
}
if ((event.ctrlKey)&&(event.keyCode==78)) //屏蔽 Ctrl+n
event.returnValue=false;
if ((event.shiftKey)&&(event.keyCode==121)) //屏蔽 shift+F10
event.returnValue=false;
if (window.event.srcElement.tagName == "A" && window.event.shiftKey)
window.event.returnValue = false; //屏蔽 shift 加鼠标左键新开一网页
if ((window.event.altKey)&&(window.event.keyCode==115)){ //屏蔽Alt+F4
window.showModelessDialog("about:blank","","dialogWidth:1px;dialogheight:1px");
return false;}
}
/* 另外可以用 window.open 的方法屏蔽 IE 的所有菜单
第一种方法:
window.open("你的.htm", "","toolbar=no,location=no,directories=no,menubar=no,scrollbars=no,resizable=yes,status=no,top=0,left=0")
第二种方法是打开一个全屏的页面:
window.open("你的.asp", "", "fullscreen=yes")
*/
// --></script>
<h2 align=center>屏蔽鼠标右键、Ctrl+n、shift+F10、F5刷新、退格键</h2>
</body>
</html>
<div style="position: absolute; top: 10; right: 10; width: 148; height: 18;cursor:hand">
<input type="button" name="Button" value="查看源代码" onClick=
'window.location = "view-source:" +
window.location.href'></div>
posted @
2008-09-23 16:12 小小~咖啡豆 阅读(497) |
评论 (0) |
编辑 收藏
数据库测试
在创建企业级应用的时候,数据层的单元测试因为其复杂性往往被遗弃,Unitils大大降低了测试的复杂性,使得数据库的测试变得容易并且易维护。已下介绍databasemodule和dbunitmodule进行数据库的单元测试。
用dbUnit管理测试数据
数据库的测试应该在单元测试数据库上运行,单元测试数据库给我们提供了一个完整的并有着很好细粒度控制的测试数据,DbUnitModule是在dbunit的基础上进一步的为数据库的测试提供数据集的支持。
加载测试数据集
让我们以UserDAO中一个简单的方法findByName(检查姓氏和名字)为例子开始介绍。他的单元测试如下:
@DataSet
public class UserDAOTest extends UnitilsJUnit4 {
@Test
public void testFindByName() {
User result = userDao.findByName("doe", "john");
assertPropertyLenEquals("userName", "jdoe", result);
}
@Test
public void testFindByMinimalAge() {
List<User> result = userDao.findByMinimalAge(18);
assertPropertyLenEquals("firstName", Arrays.asList("jack"), result);
}
}
@DateSet 注解表示了测试需要寻找dbunit的数据集文件进行加载,如果没有指明数据集的文件名,则Unitils自动在class文件的同目录下加载文件名为 className.xml的数据集文件。(这种定义到class上面的数据集称为class级别的数据集)
数据集 文件必须是dbunit的FlatXMLDataSet文件格式,其中包含了所要测试的数据。测试数据库表中所有的内容将会被删除,然后再插入数据集中的 数据。如果表不属于数据集中的,哪么该表的数据将不会被删除。你也可以明确的加入一个空的表元素,例如<MY_TABLE/>(可以达到删除 测试数据库表中内容的作用),如果要明确指定一个空的值,那么使用值[null]。
为UserDAOTest我们创建一个数据集,并放在UserDAOTest.class文件同目录下。
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<usergroup name="admin" />
<user userName="jdoe" name="doe" firstname="john" userGroup="admin" />
<usergroup name="sales" />
<user userName="smith" name="smith" userGroup="sales" />
</dataset>
测试运行的时候,首先将删除掉usergroup表和user表中的所有内容,然后将插入数据集中的内容。其中name为smith的firstname的值将会是null。
假设testFindByMinimalAge()方法将使用一个特殊的数据集而不是使用class级别的数据集,你可以定义一个UserDAOTest.testFindByMinimalAge.xml 数据集文件并放在测试类的class文件同目录下。
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<user userName="jack" age="18" />
<user userName="jim" age="17" />
</dataset>
这时,你在testFindByMinimalAge()方法使用@DataSet注解,他将覆盖class级的数据集
public class UserDAOTest extends UnitilsJUnit4 {
@Test
@DataSet("UserDAOTest.testFindByMinimalAge.xml")
public void testFindByMinimalAge() {
List<User> result = userDao.findByMinimalAge(18);
assertPropertyLenEquals("firstName", Arrays.asList("jack"), result);
}
}
不要过多的使用method级的数据集,因为过多的数据集文件意味着你要花大量的时间去维护,你优先考虑的是使用class级的数据集。
配置数据集加载策略
缺省情况下数据集被写入数据库采用的是clean insert策略。这就意味着数据在被写入数据库的时候是会先删除数据集中有使用的表的数据,然后在将数据集中的数据写入数据库。加载策略是可配额制的,我们通过修改DbUnitModule.DataSet.loadStrategy.default 的属性值来改变加载策略。假设我们在unitils.properties属性文件中加入以下内容:
DbUnitModule.DataSet.loadStrategy.default=org.unitils.dbunit.datasetloadstrategy.InsertLoadStrategy
这时加载策略就由clean insert变成了insert,数据已经存在表中将不会被删除,测试数据只是进行插入操作。
加载策略也可以使用@DataSet的注解属性对单独的一些测试进行配置:
@DataSet(loadStrategy = InsertLoadStrategy.class)
对于那些树形DbUnit的人来说,配置加载策略实际上就是使用不同的DatabaseOperation,以下是默认支持的加载策略方式:
l CleanInsertLoadStrategy: 先删除dateSet中有关表的数据,然后再插入数据。
l InsertLoadStrategy: 只插入数据。
l RefreshLoadStrategy: 有同样key的数据更新,没有的插入。
l UpdateLoadStrategy: 有同样key的数据更新,没有的不做任何操作。
配置数据集工厂
在Unitils中数据集文件采用了multischema xml 格式,这是DbUnits的FlatXmlDataSet 格式的扩展。配置文件格式和文件的扩展可以采用DataSetFactory 。
虽然Unitils当前只支持一种数据格式,但是我们可以通过实现DataSetFactory来使用其他文件格式。当你想使用excel而不是xml格式的时候,可以通过unitils.property中的DbUnitModule.DataSet.factory.default 属性和@DataSet 注解来创建一个DbUnit's XlsDataSet 实例。
验证测试结果
有些时候我们想在测试时完毕后使用数据集来检查数据库中的内容,举个例子当执行完毕一个存储过程后你想检查一下啊数据是否更新了没有。
下面的例子表示的是禁用到一年内没有使用过的帐户
public class UserDAOTest extends UnitilsJUnit4 {
@Test @ExpectedDataSet
public void testInactivateOldAccounts() {
userDao.inactivateOldAccounts();
}
}
注意在test方法上增加了一个@ExpectedDataSet 注解。这将指明unitils将使用UserDAOTest.testInactivateOldAccounts-result.xml 这个数据集的内容和数据库的内容进行比较。
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<user userName="jack" active="true" />
<user userName="jim" active="false" />
</dataset>
根据这个数据集,将会检查是否有两条和记录集的值相同的记录在数据库中。而其他的记录和表将不理会。
使用的是@DataSet 注解的话,文件名可以明确指出,如果文件名没有明确指出来,那么文件名将匹配className .methodName -result.xml
使用少使用结果数据集,加入新的数据集意味着更多的维护。替代方式是在代码中执行相同的检查(如使用一个findactiveusers()方法)。
使用多模式的数据集
一个程序不单单只是连接一个数据库shema。Unitils采用了扩展的数据集xml来定义多schemas下的数据。以下就是一个读取数据到2个不同的schemas中的例子:
<?xml version='1.0' encoding='UTF-8'?>
<dataset xmlns="SCHEMA_A" xmlns:b="SCHEMA_B">
<user id="1" userName="jack" />
<b:role id="1" roleName="admin" />
</dataset>
在这个例子中我定义了两个schemas,SCHEMA_A 和 SCHEMA_B。第一个schema,SCHEMA_A 被连接到默认的xml命名空间中,第二个schema,SCHEMA_B 被连接到命名空间b。如果表xml元素的前缀使用了命名空间b,那么该表就是schema SCHEMA_B 中的,如果没有使用任何的命名空间那么该表将被认为是SCHEMA_A
中的。以上例子中测试数据定义了表SCHEMA_A.user 和SCHEMA_B.role。
如果在数据集中没有配置一个默认的命名空间,那么将会采用在unitils.properties中的属性database.schemaNames 的第一个值作为默认的
database.schemaNames=SCHEMA_A, SCHEMA_B
这个配置将SCHEMA_A 作为缺省的schema,这样你可以简化数据集的声明。
<?xml version='1.0' encoding='UTF-8'?>
<dataset xmlns:b="SCHEMA_B">
<user id="1" userName="jack" />
<b:role id="1" roleName="admin" />
</dataset>
连接测试数据库
在以上所有的例子中,我们都有一件重要的事情没有做:当我们进行测试的时候,怎样连接数据库并得到DataSource?
当测试套件的第一个测试数据库的案例运行的时候,Unitils将会通过属性文件创建一个DataSource 的实例来连接你单元测试时的数据库,以后的测试中都将使用这个DataSource 实例。连接配置的详细内容如下:
database.driverClassName=oracle.jdbc.driver.OracleDriver
database.url=jdbc:oracle:thin:@yourmachine:1521:YOUR_DB
database.userName=john
database.password=secret
database.schemaNames=test_john
按配置章节所说的那样,你可以将连接数据库的驱动类和url地址配置到unitils.properties 中去,而用户名,密码以及schema可以配置到unitils-local.properties 中去,这样可以让开发人员连接到自己的单元测试数据库中进行测试而不会干预到其他的人。
在属性或者setter方法前使用注解@TestDataSource ,将会将DataSource 实例注入到测试实例中去,如果你想加入一些代码或者配置一下你的datasource,你可以做一个抽象类来实现该功能,所有的测试类都继承该类。一个简单的例子如下:
public abstract class BaseDAOTest extends UnitilsJUnit4 {
@TestDataSource
private DataSource dataSource;
@Before
public void initializeDao() {
BaseDAO dao = getDaoUnderTest();
dao.setDataSource(dataSource);
}
protected abstract BaseDAO getDaoUnderTest();
}
上面的例子采用了注解来取得一个datasource的引用,另外一种方式就是使用DatabaseUnitils.getDataSource() 方法来取得datasource。
事务
出于不同的原因,我们的测试都是运行在一个事务中的,其中最重要的原因如下:
l 数据库的很多action都是在事务正常提交后才做,如SELECT FOR UPDATE 和触发器
l 许多项目在测试数据的时候都会填写一些测试数据,每个测试运行都会修改或者更新了数据,当下一个测试运行的时候,都需要将数据回复到原有的状态。
l 如果使用的是hibernate或者JPA的时候,都需要每个测试都运行在事务中,保证系统的正常工作。
缺省情况下,事务管理是disabled的,事务的默认行为我们可以通过属性文件的配置加以改变:
DatabaseModule.Transactional.value.default=commit
采用这个设置,每个的测试都将执行commit,其他的属性值还有rollback 和disabled
我们也可以通过在测试类上使用注解@Transactional 来改变默认的事务设置,如:
@Transactional(TransactionMode.ROLLBACK)
public class UserDaoTest extends UnitilsJUnit4 {
通过这种class上注解的事务管理,可以让每个测试都确保回滚,@Transactional 注解还可以继承的,因此我们可以将其放在父类中,而不必每个子类都进行声明。
.........
如果你使用Unitils的spring支持(见使用spring进行测试)你如果配置了PlatformTransactionManager 的bean,那么unitils将会使用这个事务管理。
posted @
2008-08-25 15:11 小小~咖啡豆 阅读(1929) |
评论 (2) |
编辑 收藏
1、使用Dir:
If Dir$(dirName, ATTR_DIRECTORY) = "" Then
'Directory does not exist
Else
'Directory does exist
End If
2、使用FileSystemObject:
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FolderExists(dirName) Then
'Directory does exist
Else
'Directory does not exist
End If
posted @
2008-07-04 10:06 小小~咖啡豆 阅读(663) |
评论 (0) |
编辑 收藏
Unitils模块
配置
和大多数的项目一样,unitils也需要一些配置,默认情况下有3个配置,每个配置都覆写前一个的配置
- unitils-default.properties 默认的配置,在unitils发行包中。
- unitils.properties 可包含项目的全部配置
- unitils-local.properties 可以包含用户特定配置
第一个配置文件unitils-default.properties,它包含了缺省值并被包含在unitils的发行包中。我们没有必要对这个文件进行修改,但它可以用来作参考。
第二个配置文件unitils.properties,它是我们需要进行配置的文件,并且能覆写缺省的配置。举个例子,如果你的项目使用的是oracle数据库,你可以创建一个unitils.properties文件并覆写相应的driver class和database url。
database.driverClassName=oracle.jdbc.driver.OracleDriver
database.url=jdbc:oracle:thin:@yourmachine:1521:YOUR_DB
这个文件并不是必须的,但是一旦你创建了一个,你就需要将该文件放置在项目的classpath下
最后一个文件,unitils-local.properties是可选的配置文件,它可以覆写项目的配置,用来定义开发者的具体设置,举个例子来说,如果每个开发者都使用自己的数据库schema,你就可以创建一个unitils-local.properties为每个用户配置自己的数据库账号、密码和schema。
database.userName=john
database.password=secret
database.schemaNames=test_john
每个unitils-local.properties文件应该放置在对应的用户文件夹中(System.getProperty("user.home"))。
本地文件名unitils-local.properties也可以通过配置文件定义,在unitils.properties覆写unitils.configuration.localFileName就可以。
unitils.configuration.localFileName=projectTwo-local.properties
启用你的unitils
unitils的功能是依赖于基础的测试框架,要使用unitils的功能,就必须先enable他们,这样做的目的也是为了容易扩展。目前支持的框架有:
- JUnit3 :org.unitils.UnitilsJUnit3
- JUnit4 :org.unitils.UnitilsJUnit4
- TestNG:org.unitils.UnitilsTestNG
举个例子,如果使用JUnit3,你要使用unitils
import org.unitils.UnitilsJUnit3;
public class MyTest extends UnitilsJUnit3 {
}
通常你将创建你自己的包含一些公共行为的测试基类,如dataSource的注入,你可以让这个基类继承unitils测试类。
当你使用的是JUnit4的话,你也可是使用@RunWith来代替继承unitils测试类
import org.junit.runner.RunWith;
import org.unitils.UnitilsJUnit4TestClassRunner;
@RunWith(UnitilsJUnit4TestClassRunner.class)
public class MyTest {
}
模块系统
在开始举例之前,让我们先了解一下unitils概念。
unitils的结构被设计成了十分容易扩展,每个模块提供了一种服务,当执行Test的时候通过TestListener调用相应的服务。
这种设计采用了一个统一的方式提供服务,如果你需要加入其他的服务,无需去改编测试基类(UnitilsJUnit4这些类)。要加入新的服务只需要添加一个新的模块并在unitls配置文件中登记这个模块。
目前unitils中所有有效的模块如下:
- DatabaseModule 数据库单元测试的维护和连接池。
- DbUnitModule 使用DBUnit来管理测试数据。
- hibernatemodule 支持Hibernate的配置和自动数据库映射检查。
- EasyMockModule 支持创建mock和宽松的反射参数匹配。
- InjectModule 支持在一个对象中注入另一个对象。
- SpringModule 支持加载spring的上下文配置,并检索和Spring Bean注入。
posted @
2008-04-07 00:45 小小~咖啡豆 阅读(2785) |
评论 (2) |
编辑 收藏
单元测试应该是简单和直观的,而现实中的项目大多都是采用多层方式的,如EJB和hibernate的数据驱动层的中间件技术。
unitils来源于一个尝试,就是希望能以更务实的方式来看待单元测试......
这个指南会告诉你,什么项目可以使用unitils。 并在这个指导方针页 中你可以了解到测试的准侧和它的特点。如果您想了解如何可以配置unitils ,并得以迅速地启动,请查看cookbook 。
- unitils的断言
- unitils的模块
- 数据库的测试
- 数据库的自动测试
- hibernate的测试
- jpa的测试
- spring的测试
- mock object的测试
- 今后的方向
unitils的断言
在开始这个指南之前我们先说明一下独立于unitils核心模块的断言。在下面的例子中,不需要进行配置,将unitils的jar包和依赖包放在你的classpath下,就可以进行测试了。
通过反射进行断言
一个典型的单元测试包含了结果值和期望值的比较,unitils提供了断言的方法以帮助你进行该操作,让我们看看实例2中对有着id、first name、last name属性的User类的2个实例的比较
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertEquals(user1, user2);
你期望这个断言是成功的,因为这两个实例含有相同的属性,但是运行的结果并非如此,应为User类并没有覆写
equals()方法,所以assertEquals是对两个实例是否相等进行判断(user1 == user2)结果导致了比较的失败。
假设你像如下代码一样实现了equals方法
public boolean equals(Object object) {
if (object instanceof User) {
return id == ((User) object).id;
}
return false;
}
这在你的程序逻辑中是一个合乎逻辑的实现,当两个User实例拥有相同的id的时候,那么这两个实例就是相等的。然而这种方式在你的单元测试中并不合适,并不能通过id的相同来认为两个user是相同的。
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "Jane", "Smith");
assertEquals(user1, user2);
这个断言将会成功,但这并不是你所期望的,因此不要使用assertEquals来对两个对象进行判定是否相等(外覆类和java.lang.String类除外)。要想断言他们相等,一种方法就是断言每个属性相等。
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertEquals(user1.getId(), user2.getId());
assertEquals(user1.getFirst(), user2.getFirst());
assertEquals(user1.getLast(), user2.getLast());
unitils提供了一些方法来帮助你执行断言更加的简单,通过反射,使用ReflectionAssert.assertRefEquals上面的代码重写如下:
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertRefEquals(user1, user2);
这个断言将通过反射对两个实例中的每个属性都进行比较,先是id、然后是first name、最后是last name。
如果一个属性本身也是一个对象,那么将会使用反射进行递归比较,这同样适合与集合、map、和数组之间的比较,他们
的每个元素会通过反射进行比较。如果值是一个基本类型或者是一个外覆类,那么将会使用==进行值的比较,因此下面的断
言会取得成功
assertRefEquals(1, 1L);
List<Double> myList = new ArrayList<Double>();
myList.add(1.0);
myList.add(2.0);
assertRefEquals(Arrays.asList(1, 2), myList);
宽松的断言
出于可维护性,这一点是十分重要的,举例说明:如果你要计算一个帐户的余额,那你就没比较检查这个帐户的名称。他只会增加复杂性,使之更难理解。如果你想让你的测试代码更容易生存,更容易重构的话,那请确保你断言的范围。
宽松的顺序
在比较集合和数组的时候你可能并不关心他们中元素的顺序,通过使用ReflectionAssert.assertRefEquals方法并配合ReflectionComparatorMode.LENIENT_ORDER参数将忽略元素的顺序。
List<Integer> myList = Arrays.asList(3, 2, 1);
assertRefEquals(Arrays.asList(1, 2, 3), myList, LENIENT_ORDER);
无视默认
第二种的从宽方式是使用ReflectionComparatorMode.IGNORE_DEFAULTS模式,当这种模式被设置的时候,java
的默认值,如null、0、false将会不参与断言(忽略)。
举个例子,如果你有一个User类,该类有着first name,last name,street等属性,但是你仅仅想对first name
和street进行检查而忽略其他的属性。
User actualUser = new User("John", "Doe", new Address("First street", "12", "Brussels"));
User expectedUser = new User("John", null, new Address("First street", null, null));
assertRefEquals(expectedUser, actualUser, IGNORE_DEFAULTS);
你所期望忽略的属性的对象必须放在断言左边,如果放在右边那么依然进行比较。
assertRefEquals(null, anyObject, IGNORE_DEFAULTS); // Succeeds
assertRefEquals(anyObject, null, IGNORE_DEFAULTS); // Fails
宽松的日期
第三种从宽处理是ReflectionComparatorMode.LENIENT_DATES,当两个日期都是值,或者都是null的时候,实际的日期将会被忽略(即断言为相等)。
Date actualDate = new Date(44444);
Date expectedDate = new Date();
assertRefEquals(expectedDate, actualDate, LENIENT_DATES);
assertLenEquals
ReflectionAssert还提供了一种断言,他提供宽松的顺序又提供无视的忽略。
List<Integer> myList = Arrays.asList(3, 2, 1);
assertLenEquals(Arrays.asList(1, 2, 3), myList);
assertLenEquals(null, "any"); // Succeeds
assertLenEquals("any", null); // Fails
属性断言
assertLenEquals和assertRefEquals都是比较对象,ReflectionAssert也提供方法对对象的属性进行比较。(依赖与ONGL)。
一些属性比较的例子
assertPropertyLenEquals("id", 1, user); //断言user的id属性的值是1
assertPropertyLenEquals("address.street", "First street", user); //断言user的address的street属性
在这个方式中你期望的值和判定的对象也可以使用集合
assertPropertyLenEquals("id", Arrays.asList(1, 2, 3), users);
assertPropertyLenEquals("address.street", Arrays.asList("First street",
"Second street", "Third street"), users);
posted @
2008-04-05 14:23 小小~咖啡豆 阅读(2313) |
评论 (5) |
编辑 收藏
- 写代码,就一定要写测试
- 不要受单元测试的教条所限
- 相信单元测试将会带来的成果
- 统一考虑编码和测试
- 测试比单元代码重要
- 测试的最佳时机是代码刚写完之时
- 测试不会白费
- 当天有瑕疵的测试也比后补的完美测试好
- 不好的测试也比没有测试强
- 测试有时可以验证意图
- 只有傻瓜不用工具
- 用好的去测试不好的
引至:Info中文网站http://www.infoq.com/cn/news/2007/04/savoia-tao-of-testing
posted @
2007-04-30 09:59 小小~咖啡豆 阅读(392) |
评论 (0) |
编辑 收藏
實際運用 Tomcat 5.0.19,我們了解在不修改 Tomcat 原始碼的狀況下,使用者透過 Form submit 的資料將一律以 ISO8859-1 處理,程式設計師必須自行將字串將轉換為 Big5(繁體中文) or GB2312/GBK(簡體中文),我們在應用程式中,對所有的 request.getParameter("xx"); 作了 toBig5String() 的處理,理論上,所有的中文問題應該不會出現才對,結果,還是發現某些狀況下,中文還是變成亂碼!
經過分析整理,我們發現問題出在 QueryString 的解析,以前在 Tomcat 4.x 時代,無論 SUBMIT 時採用 GET or POST,Tomcat server 對 parameters 的處理都採用相同的編碼,但在 Tomcat 5.x 版,不知何故,卻將 QueryString 的解析獨立出來,目前確認,Form 的 Method 採用 GET 及直接將參數寫在 URL 上的中文,上傳到 Tomcat 時,無論如何轉碼,都會變成亂碼,那怕你事先作過 URLEncode 也一樣。
網站上,有人針對這個問題,建議將所有中文改採用 base64 編碼,到了 server 上,程式將自行土 base64 decode 回來,確保中文不會發生問題。這樣作法當然可以解決這個問題,但是所有網頁變成限定要採用 POST,且程式設計師要隨時分清楚,那個參數是採用 GET 上傳,那個參數是採用 POST 上傳,然後再針對不同的方式採用不同的解析,這樣的程式一點兒移植性都沒有,更別提跨平台、跨國際語言了。
研究 Tomcat 的文件及原始碼,我們找到了問題所在及解決的方法,只有按著以下的作法,才能使 Form submit 的資料完全按著 ISO8859-1 的編碼,當然,若是全照著 Tomcat 的文件說明去作,肯定還是不行,你還是得加上這個參數到 server.xml 中才行。
解決方案
請先研究 $TOMCAT_HOME/webapps/tomcat-docs/config/http.html 這個說明檔,擷錄重點如下:
URIEncoding:This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
useBodyEncodingForURI:This specifies if the encoding specified in contentType should be used for URI query parameters, instead of using the URIEncoding. This setting is present for compatibility with Tomcat 4.1.x, where the encoding specified in the contentType, or explicitely set using Request.setCharacterEncoding method was also used for the parameters from the URL. The default value is false.
上述二個 Tomcat 參數,是設定在 server.xml 中的 http <Connector /> 區塊,要解決 QueryString 中文變成亂碼的問題,你必須至少設定這二個參數其中之一。
URIEncoding 請設定為 URIEncoding="ISO-8859-1" 指定為 "ISO-8859-1" 編碼,讓 QueryString 的字元編碼與 post body 相同。
useBodyEncodingForURI 這是用來相容 Tomcat 4.x 版的,設定的值是 "true" or "false",意思是指 "要不要讓 QueryString 與 POST BODY 採用相同的字元編碼 ?",若是設成 true,那也可達到 "ISO-8859-1" 編碼的需求。
建議,採用 URIEncoding 的設定,畢竟 useBodyEncodingForURI 的作法是為了相容 Tomcat 4.X。不過若照原文的說明,理論上這二個參數都不設,Tomcat 也該採用 "ISO-8859-1" 的編碼,那為什麼還是會有問題呢 ? 我們由 Tomcat Source Code 來看就清楚了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
// 這一段碼是 Tomcat 用來解 QueryString 的程式,
// 在 org.apache.tomcat.util.http.Parameters 這個 class 裡。
private String urlDecode(ByteChunk bc, String enc)
throws IOException {
if( urlDec==null ) {
urlDec=new UDecoder();
}
urlDec.convert(bc);
String result = null;
if (enc != null) {
bc.setEncoding(enc);
result = bc.toString();
}
else {
CharChunk cc = tmpNameC;
cc.allocate(bc.getLength(), -1);
// Default encoding: fast conversion
byte[] bbuf = bc.getBuffer();
char[] cbuf = cc.getBuffer();
int start = bc.getStart();
for (int i = 0; i < bc.getLength(); i++) {
cbuf[i] = (char) (bbuf[i + start] & 0xff);
}
cc.setChars(cbuf, 0, bc.getLength());
result = cc.toString();
cc.recycle();
}
return result;
}
|
請特別注意紅色區塊,當 Tomcat 發現 QueryString 並沒有設定 encode 時,並非像文件中所說預設採用 ISO-8859-1 的編碼,而是用一段 fast conversion 來處理,才會造成中文問題,所以,還是必須在 Server.xml 中,加上 URLEncoding 的參數設定才行哦。
Connector 的設定範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
<Connector
debug="0"
acceptCount="100"
connectionTimeout="20000"
disableUploadTimeout="true"
port="80"
redirectPort="8443"
enableLookups="false"
minSpareThreads="25"
maxSpareThreads="75"
maxThreads="150"
maxPostSize="0"
URIEncoding="ISO-8859-1"
>
</Connector>
|
文章来至:
http://www.javaworld.com.tw/jute/post/view?bid=9&id=44042&sty=1&tpg=1&age=0
posted @
2007-04-26 10:33 小小~咖啡豆 阅读(7018) |
评论 (0) |
编辑 收藏
绝对的最强的java ide的7.0M1已经出了,快去试试把,
http://www.jetbrains.com/idea/nextversion/
posted @
2007-04-12 09:07 小小~咖啡豆 阅读(283) |
评论 (0) |
编辑 收藏
http://www.jscud.com/srun/news/viewhtml/4_2005_1/26.htm
posted @
2007-03-28 15:36 小小~咖啡豆 阅读(1459) |
评论 (0) |
编辑 收藏
用StrutsTestCase能很好的对Struts来进行测试,可是如果时用Spring来管理Struts的action的时候,用StrutsTestCase的常规方式是不能进行测试的,以下的一个文章对此有很好的说明:
http://www.jetmaven.net/contents/documents/p_spring_junit_combination.php
posted @
2007-03-25 23:07 小小~咖啡豆 阅读(438) |
评论 (0) |
编辑 收藏
webwork的IOC(基于2.1.X版本)
webwork的ioc其实在webwork中使用起来挺方便的,虽然其功能不算强大,但是已经能很好的满足我们一般的需要了,就算我们使用spring 的ioc,如果不使用特别的功能,其一般我们也是基于接口,然后有个set方法,通过set来注入,没有太多的区别,不同的是webwork的ioc需要依赖xwork,而spring却是依赖spring这个容器。
webwork的ioc是怎么进行注入的了,我们从代码中进行分析:
首先看看拦截器的代码:
public class ComponentInterceptor extends AroundInterceptor {
//~ Static fields/initializers /////////////////////////////////////////////
public static final String COMPONENT_MANAGER = "com.opensymphony.xwork.interceptor.component.ComponentManager";
//~ Methods ////////////////////////////////////////////////////////////////
protected void after(ActionInvocation dispatcher, String result) throws Exception {
}
protected void before(ActionInvocation dispatcher) throws Exception {
ComponentManager container = (ComponentManager) ActionContext.getContext().get(COMPONENT_MANAGER);
if (container != null) {
container.initializeObject(dispatcher.getAction());
}
}
}
主要的代码用黑体标注出来了,container实际就是组件管理器,这里是一个ComponentManager接口的实现 DefaultComponentManager,然后调用了该类的方法initializeObject(dispatcher.getAction ());而dispatcher.getAction()实际就是所调用的action对象,我们再来看看 DefaultComponentManager做了什么。
public void initializeObject(Object obj) {
loadResource(obj, obj.getClass(), this);
}
private Class loadResource(Object resource, Class clazz, DefaultComponentManager dcm) {
// ~由此来判断是否要进行依赖注入
boolean resourceNotLoaded = !dcm.loadOrder.contains(resource);
if (resourceNotLoaded) {
Map resources = getResourceDependencies(clazz);
for (Iterator iterator = resources.entrySet().iterator();
iterator.hasNext();) {
Map.Entry mapEntry = (Map.Entry) iterator.next();
Class depResource = (Class) mapEntry.getKey();
DefaultComponentManager newDcm = (DefaultComponentManager) mapEntry.getValue();
try {
ResourceEnablerPair pair = setupAndOptionallyCreateResource(newDcm, depResource);
setupResource(resource, pair.enabler, pair.resource);
} catch (Exception e) {
e.printStackTrace();
if (log.isDebugEnabled()) {
log.debug("Error loading or setting up resource: " + resources.getClass().getName(), e);
}
}
}
dcm.alreadyLoaded.add(clazz);
if (resource instanceof Initializable) {
Initializable initializable = (Initializable) resource;
initializable.init();
}
dcm.resourceInstances.put(clazz, resource);
dcm.loadOrder.add(resource);
}
// now return this class's enabler
Class enabler = (Class) dcm.enablers2.get(clazz);
return enabler;
}
private Map getResourceDependencies(Class resourceClass) {
List interfaces = new ArrayList();
//~ 将所有的interface放入interfaces链表中
addAllInterfaces(resourceClass, interfaces);
Map dependencies = new HashMap();
for (Iterator iterator = interfaces.iterator(); iterator.hasNext();) {
Class anInterface = (Class) iterator.next();
DefaultComponentManager dcm = this;
while (dcm != null) {
Class possibleResource = (Class) dcm.enablers.get(anInterface);
if (possibleResource != null) {
dependencies.put(possibleResource, dcm);
break;
}
dcm = dcm.fallback;
}
}
return dependencies;
}
private void addAllInterfaces(Class clazz, List allInterfaces) {
if (clazz == null) {
return;
}
Class[] interfaces = clazz.getInterfaces();
allInterfaces.addAll(Arrays.asList(interfaces));
addAllInterfaces(clazz.getSuperclass(), allInterfaces);
}
重要的代码都用黑体进行了标注,方法initializeObject中所调用的loadResource(obj, obj.getClass(), this);就执行了查找接口,并注入接口实现类整个过程。
loadResource首先调用了getResourceDependencies(clazz);而getResourceDependencies又调用了addAllInterfaces(resourceClass, interfaces);addAllInterfaces作用就是取得这个类包括这个类的父类的所有实现的接口,而getResourceDependencies方法就是对这个接口进行过滤,返回只是在配置中有的接口。setupAndOptionallyCreateResource(newDcm, depResource);进行的就是创建这些接口的实现类的对象,这个代码的内容如下:
private ResourceEnablerPair setupAndOptionallyCreateResource(DefaultComponentManager newDcm, Class depResource) throws Exception {
ResourceEnablerPair pair = new ResourceEnablerPair();
Object newResource = newDcm.resourceInstances.get(depResource);
if (newResource == null) {
newResource = ObjectFactory.getObjectFactory().buildBean(depResource);
}
pair.resource = newResource;
Class enabler = loadResource(newResource, depResource, newDcm);
pair.enabler = enabler;
return pair;
}
因为准备创建出来的接口实现类对象的接口可能又实现了其他的接口,因此再调用了loadResource(newResource, depResource, newDcm)。对象创建了,然后就是注入这个对象,setupResource(resource, pair.enabler, pair.resource)就是起这个作用的。代码如下:
private void setupResource(Object resource, Class enabler, Object newResource) {
if (enabler == null) {
return;
}
try {
enabler.getMethods()[0].invoke(resource, new Object[] {newResource});
} catch (Exception e) {
e.printStackTrace();
if (log.isDebugEnabled()) {
log.debug("Error invoking method for resource: " + resource.getClass().getName(), e);
}
}
}
每个接口只有一个set方法,通过反射机制调用这个方法将创建出来的接口实现对象注入进去。整个IOC就完成了。
posted @
2007-03-22 10:30 小小~咖啡豆 阅读(330) |
评论 (0) |
编辑 收藏
http://www.scriptviewer.com/story.php?title=IntelliJ-IDEA-60-aeae
posted @
2007-03-11 00:43 小小~咖啡豆 阅读(338) |
评论 (0) |
编辑 收藏
Idea6.0默认是用的自带的jdk5启动的,如果想用jdk6可以按如下办法操作:
1.将idea目录下的jre目录改名
2.将安装jdk6目录中的jre目录拷贝到idea目录下,然后在将jdk6目录中的jdk\lib\tools.jar拷贝到jre\lib下
3.修改idea.exe.vmoptions文件中的启动参数(这个不是必须的,只是提高idea的响应速度)
posted @
2007-02-01 08:09 小小~咖啡豆 阅读(441) |
评论 (0) |
编辑 收藏
C/C++ Source Add Block Comment Ctrl+Shift+/ C/C++ Editor
C/C++ Source Add Include Ctrl+Shift+N C/C++ Editor
C/C++ Source Comment Ctrl+/ C/C++ Editor
C/C++ Source Find Declaration Ctrl+G C/C++ Editor
C/C++ Source Find References Ctrl+Shift+G C/C++ Editor
C/C++ Source Format Ctrl+Shift+F C/C++ Editor
C/C++ Source Go to Matching Bracket Ctrl+Shift+P C/C++ Editor
C/C++ Source Go to next C/C++ member Ctrl+Shift+向下键 C/C++ Editor
C/C++ Source Go to previous C/C++ member Ctrl+Shift+向上键 C/C++ Editor
C/C++ Source Open Declaration F3 C/C++ Editor
C/C++ Source Open Definition Ctrl+F3 C/C++ Editor
C/C++ Source Open Type Ctrl+Shift+T C/C++ Editor
C/C++ Source Remove Block Comment Ctrl+Shift+\ C/C++ Editor
C/C++ Source Show outline Ctrl+O C/C++ Editor
C/C++ Source Uncomment Ctrl+\ C/C++ Editor
Makefile Source Comment Ctrl+/ Makefile Editor
Makefile Source Open declaration F3 Makefile Editor
Makefile Source Uncomment Ctrl+\ Makefile Editor
Refactor - C/C++ Redo - Refactoring Alt+Shift+Y C/C++ Editor
Refactor - C/C++ Rename - Refactoring Alt+Shift+R C/C++ Editor
Refactor - C/C++ Undo - Refactoring Alt+Shift+Z C/C++ Editor
View Zoom In Ctrl+= 在窗口中
View Zoom Out Ctrl+- 在窗口中
搜索 工作空间中的声明 Ctrl+G 在窗口中
搜索 工作空间中的引用 Ctrl+Shift+G 在窗口中
搜索 打开"搜索"对话框 Ctrl+H 在窗口中
搜索 显示"文件中的出现位置"快速菜单 Ctrl+Shift+U 在窗口中
文件 "新建"菜单 Alt+Shift+N 在窗口中
文件 保存 Ctrl+S 在窗口中
文件 全部保存 Ctrl+Shift+S 在窗口中
文件 全部关闭 Ctrl+Shift+F4 在窗口中
文件 全部关闭 Ctrl+Shift+W 在窗口中
文件 关闭 Ctrl+F4 在窗口中
文件 关闭 Ctrl+W 在窗口中
文件 刷新 F5 在窗口中
文件 属性 Alt+Enter 在窗口中
文件 打印 Ctrl+P 在窗口中
文件 新建 Ctrl+N 在窗口中
文件 重命名 F2 在窗口中
文本编辑 上一个词语 Ctrl+左箭头 编辑文本
文本编辑 上滚行 Ctrl+向上键 编辑文本
文本编辑 下一个词语 Ctrl+右箭头 编辑文本
文本编辑 下滚行 Ctrl+向下键 编辑文本
文本编辑 全部展开 Ctrl+Numpad_Multiply 编辑文本
文本编辑 切换折叠 Ctrl+Numpad_Divide 编辑文本
文本编辑 删除上一个词语 Ctrl+Backspace 编辑文本
文本编辑 删除下一个词语 Ctrl+Delete 编辑文本
文本编辑 删除至行末 Ctrl+Shift+Delete 编辑文本
文本编辑 删除行 Ctrl+D 编辑文本
文本编辑 在当前行上面插入行 Ctrl+Shift+Enter 编辑文本
文本编辑 在当前行下面插入行 Shift+Enter 编辑文本
文本编辑 复制行 Ctrl+Alt+向下键 编辑文本
文本编辑 将行上移 Alt+向上键 编辑文本
文本编辑 将行下移 Alt+向下键 编辑文本
文本编辑 展开 Ctrl+Numpad_Add 编辑文本
文本编辑 折叠 Ctrl+Numpad_Subtract 编辑文本
文本编辑 改写切换 Insert 编辑文本
文本编辑 更改为大写 Ctrl+Shift+X 编辑文本
文本编辑 更改为小写 Ctrl+Shift+Y 编辑文本
文本编辑 选择上一个词语 Ctrl+Shift+左箭头 编辑文本
文本编辑 选择下一个词语 Ctrl+Shift+右箭头 编辑文本
文本编辑 重复行 Ctrl+Alt+向上键 编辑文本
查看 Java 包资源管理器 Alt+Shift+Q,P 在窗口中
查看 Java 声明 Alt+Shift+Q,D 在窗口中
查看 Java 类型层次结构 Alt+Shift+Q,T 在窗口中
查看 Javadoc Alt+Shift+Q,J 在窗口中
查看 变量 Alt+Shift+Q,V 在窗口中
查看 同步 Alt+Shift+Q,Y 在窗口中
查看 备忘单 Alt+Shift+Q,H 在窗口中
查看 控制台 Alt+Shift+Q,C 在窗口中
查看 搜索 Alt+Shift+Q,S 在窗口中
查看 断点 Alt+Shift+Q,B 在窗口中
查看 显示视图 (查看: 大纲) Alt+Shift+Q,O 在窗口中
查看 显示视图 (查看: 问题) Alt+Shift+Q,X 在窗口中
浏览 &Quick Cross References Alt+Shift+P 编辑 Java 源代码
浏览 Open AspectJ Type Alt+Shift+A 在窗口中
浏览 Open AspectJ Type in Hierarchy Alt+Shift+H 在窗口中
浏览 "显示位置"菜单 Alt+Shift+W 在窗口中
浏览 上一个编辑位置 Ctrl+Q 在窗口中
浏览 下一页 Ctrl+. 在窗口中
浏览 前一页 Ctrl+, 在窗口中
浏览 前移历史记录 Alt+右箭头 在窗口中
浏览 后退历史记录 Alt+左箭头 在窗口中
浏览 在层次结构中打开类型 Ctrl+Shift+H 在窗口中
浏览 快速大纲 Ctrl+O 编辑 Java 源代码
浏览 快速层次结构 Ctrl+T 编辑 Java 源代码
浏览 打开声明 F3 在窗口中
浏览 打开外部 Javadoc Shift+F2 在窗口中
浏览 打开类型 Ctrl+Shift+T 在窗口中
浏览 打开类型层次结构 F4 在窗口中
浏览 打开结构 Ctrl+F3 编辑 Java 源代码
浏览 打开调用层次结构 Ctrl+Alt+H 在窗口中
浏览 打开资源 Ctrl+Shift+R 在窗口中
浏览 转至上一个成员 Ctrl+Shift+向上键 编辑 Java 源代码
浏览 转至下一个成员 Ctrl+Shift+向下键 编辑 Java 源代码
浏览 转至匹配的方括号 Ctrl+Shift+P 编辑 Java 源代码
浏览 转至行 Ctrl+L 编辑文本
源代码 切换 Ant 标记出现 Alt+Shift+O 编辑 Ant 构建文件
源代码 切换标记出现 Alt+Shift+O 编辑 Java 源代码
源代码 切换注释 Ctrl+/ 编辑 Java 源代码
源代码 切换注释 Ctrl+7 编辑 Java 源代码
源代码 切换注释 Ctrl+Shift+C 编辑 Java 源代码
源代码 在文件中重命名 Alt+Shift+R 编辑 Ant 构建文件
源代码 快速辅助 - 在文件中重命名 Ctrl+2,R 编辑 Java 源代码
源代码 快速辅助 - 指定给字段 Ctrl+2,F 编辑 Java 源代码
源代码 快速辅助 - 指定给局部变量 Ctrl+2,L 编辑 Java 源代码
源代码 打开外部文档 Shift+F2 编辑 Ant 构建文件
源代码 显示工具提示描述 F2 编辑 Ant 构建文件
源代码 显示源代码快速菜单 Alt+Shift+S 在窗口中
源代码 格式 Ctrl+Shift+F 编辑 Ant 构建文件
源代码 格式化 Ctrl+Shift+F 编辑 Java 源代码
源代码 添加 Javadoc 注释 Alt+Shift+J 在窗口中
源代码 添加块注释 Ctrl+Shift+/ 编辑 Java 源代码
源代码 添加导入 Ctrl+Shift+M 编辑 Java 源代码
源代码 组织导入 Ctrl+Shift+O 在窗口中
源代码 缩进行 Ctrl+I 编辑 Java 源代码
源代码 除去出现注释 Alt+Shift+U 编辑 Java 源代码
源代码 除去块注释 Ctrl+Shift+\ 编辑 Java 源代码
源代码 添加 try catch 块 Alt+Shift+Z + Y 编辑 Java 源代码
窗口 上一个编辑器 Ctrl+Shift+F6 在窗口中
窗口 上一个视图 Ctrl+Shift+F7 在窗口中
窗口 上一个透视图 Ctrl+Shift+F8 在窗口中
窗口 下一个编辑器 Ctrl+F6 在窗口中
窗口 下一个视图 Ctrl+F7 在窗口中
窗口 下一个透视图 Ctrl+F8 在窗口中
窗口 切换至编辑器 Ctrl+Shift+E 在窗口中
窗口 将活动视图或编辑器最大化 Ctrl+M 在窗口中
窗口 打开编辑器下拉列表 Ctrl+E 在窗口中
窗口 显示标尺上下文菜单 Ctrl+F10 编辑文本
窗口 显示系统菜单 Alt+- 在窗口中
窗口 显示视图菜单 Ctrl+F10 在窗口中
窗口 显示键辅助 Ctrl+Shift+L 在对话框和窗口中
窗口 激活编辑器 F12 在窗口中
编辑 Add Block Comment Ctrl+Shift+/ Editing in Structured Text Editors
编辑 Format Active Elements Ctrl+I Editing in Structured Text Editors
编辑 Format Document Ctrl+Shift+F Editing in Structured Text Editors
编辑 Move Alt+Shift+V Editing JSP Source
编辑 Occurrences in File Ctrl+Shift+A Editing in Structured Text Editors
编辑 Open Selection F3 Editing in Structured Text Editors
编辑 Quick Fix Ctrl+1 Editing in Structured Text Editors
编辑 Remove Block Comment Ctrl+Shift+\ Editing in Structured Text Editors
编辑 Rename Alt+Shift+R Editing JSP Source
编辑 Rename XSD element Alt+Shift+R Editing XSD context
编辑 Restore Last Selection Alt+Shift+向下键 Editing in Structured Text Editors
编辑 Select Enclosing Element Alt+Shift+向上键 Editing in Structured Text Editors
编辑 Select Next Element Alt+Shift+右箭头 Editing in Structured Text Editors
编辑 Select Previous Element Alt+Shift+左箭头 Editing in Structured Text Editors
编辑 Show Tooltip Description F2 Editing in Structured Text Editors
编辑 Toggle Comment Ctrl+Shift+C Editing in Structured Text Editors
编辑 "快速差别"开关 Ctrl+Shift+Q 编辑文本
编辑 上下文信息 Alt+? 在窗口中
编辑 上下文信息 Alt+Shift+? 在窗口中
编辑 内容辅助 Alt+/ 在对话框和窗口中
编辑 切换插入方式 Ctrl+Shift+Insert 编辑文本
编辑 删除 Delete 在窗口中
编辑 剪切 Ctrl+X 在对话框和窗口中
编辑 剪切 Shift+Delete 在对话框和窗口中
编辑 增量查找 Ctrl+J 编辑文本
编辑 增量逆向查找 Ctrl+Shift+J 编辑文本
编辑 复制 Ctrl+C 在对话框和窗口中
编辑 复制 Ctrl+Insert 在对话框和窗口中
编辑 复原上一个选择 Alt+Shift+向下键 编辑 Java 源代码
编辑 快速修正 Ctrl+1 在窗口中
编辑 撤消 Ctrl+Z 在窗口中
编辑 文字补全 Ctrl+Alt+/ 编辑文本
编辑 显示工具提示描述 F2 编辑 Java 源代码
编辑 查找上一个 Ctrl+Shift+K 编辑文本
编辑 查找下一个 Ctrl+K 编辑文本
编辑 查找并替换 Ctrl+F 在窗口中
编辑 粘贴 Ctrl+V 在对话框和窗口中
编辑 粘贴 Shift+Insert 在对话框和窗口中
编辑 选择上一个元素 Alt+Shift+左箭头 编辑 Java 源代码
编辑 选择下一个元素 Alt+Shift+右箭头 编辑 Java 源代码
编辑 选择全部 Ctrl+A 在对话框和窗口中
编辑 选择外层元素 Alt+Shift+向上键 编辑 Java 源代码
编辑 重做 Ctrl+Y 在窗口中
运行/调试 Debug AspectJ/Java Application Alt+Shift+D,C 在窗口中
运行/调试 Debug on Server Alt+Shift+D,R 在窗口中
运行/调试 EOF Ctrl+Z 在控制台中
运行/调试 Profile on Server Alt+Shift+P,R 在窗口中
运行/调试 Run AspectJ/Java Application Alt+Shift+X,C 在窗口中
运行/调试 Run on Server Alt+Shift+X,R 在窗口中
运行/调试 切换单步执行过滤器 Shift+F5 在窗口中
运行/调试 切换行断点 Ctrl+Shift+B 在窗口中
运行/调试 单步跳入 F5 调试
运行/调试 单步跳入选择的内容 Ctrl+F5 调试
运行/调试 单步跳过 F6 调试
运行/调试 单步返回 F7 调试
运行/调试 执行 Ctrl+U 在窗口中
运行/调试 显示 Ctrl+Shift+D 在对话框和窗口中
运行/调试 检查 Ctrl+Shift+I 在对话框和窗口中
运行/调试 继续 F8 调试
运行/调试 调试 Ant 构建 Alt+Shift+D,Q 在窗口中
运行/调试 调试 Eclipse 应用程序 Alt+Shift+D,E 在窗口中
运行/调试 调试 JUnit 插件测试 Alt+Shift+D,P 在窗口中
运行/调试 调试 JUnit 测试 Alt+Shift+D,T 在窗口中
运行/调试 调试 Java Applet Alt+Shift+D,A 在窗口中
运行/调试 调试 Java 应用程序 Alt+Shift+D,J 在窗口中
运行/调试 调试 SWT 应用程序 Alt+Shift+D,S 在窗口中
运行/调试 调试上次启动 F11 在窗口中
运行/调试 运行 Ant 构建 Alt+Shift+X,Q 在窗口中
运行/调试 运行 Eclipse 应用程序 Alt+Shift+X,E 在窗口中
运行/调试 运行 JUnit 插件测试 Alt+Shift+X,P 在窗口中
运行/调试 运行 JUnit 测试 Alt+Shift+X,T 在窗口中
运行/调试 运行 Java Applet Alt+Shift+X,A 在窗口中
运行/调试 运行 Java 应用程序 Alt+Shift+X,J 在窗口中
运行/调试 运行 SWT 应用程序 Alt+Shift+X,S 在窗口中
运行/调试 运行上次启动 Ctrl+F11 在窗口中
运行/调试 运行至行 Ctrl+R 调试
重构 - Java 内联 Alt+Shift+I 在窗口中
重构 - Java 将局部变量转换为字段 Alt+Shift+F 编辑 Java 源代码
重构 - Java 抽取局部变量 Alt+Shift+L 在窗口中
重构 - Java 抽取方法 Alt+Shift+M 在窗口中
重构 - Java 撤销 - 重构 Alt+Shift+Z 在窗口中
重构 - Java 显示重构快速菜单 Alt+Shift+T 在窗口中
重构 - Java 更改方法特征符 Alt+Shift+C 在窗口中
重构 - Java 移动 - 重构 Alt+Shift+V 在窗口中
重构 - Java 重做 - 重构 Alt+Shift+Y 在窗口中
重构 - Java 重命名 - 重构 Alt+Shift+R 在窗口中
项目 全部构建 Ctrl+B 在窗口中
posted @
2006-12-31 11:17 小小~咖啡豆 阅读(224) |
评论 (0) |
编辑 收藏
JDK的开源已经发布了
https://openjdk.dev.java.net/
posted @
2006-11-14 08:08 小小~咖啡豆 阅读(470) |
评论 (0) |
编辑 收藏
common mail是一个小而方便的mail包,他实现了对Java Mail的封装,使用起来十分的方便,但是我在使用他的时候发现,使用纯文本的内容发送,结果是乱码,代码如下:
public class TestCommonMail {
public static void main(String[] args) throws EmailException, MessagingException {
SimpleEmail email = new SimpleEmail();
email.setCharset("GB2312");
email.setHostName("smtp.163.com");
email.setSubject("test");
email.addTo("test@163.com");
email.setFrom("test@163.com");
email.setMsg("我的测试");
email.setAuthentication("test", "test");
email.send();
}
}
分析了一下commons mail的源码找到了原因。源码如下:
public class SimpleEmail extends Email
{
public Email setMsg(String msg) throws EmailException, MessagingException
{
if (EmailUtils.isEmpty(msg))
{
throw new EmailException("Invalid message supplied");
}
setContent(msg, TEXT_PLAIN);
return this;
}
}
Email代码片段
public void setContent(Object aObject, String aContentType)
{
this.content = aObject;
if (EmailUtils.isEmpty(aContentType))
{
this.contentType = null;
}
else
{
// set the content type
this.contentType = aContentType;
// set the charset if the input was properly formed
String strMarker = "; charset=";
int charsetPos = aContentType.toLowerCase().indexOf(strMarker);
if (charsetPos != -1)
{
// find the next space (after the marker)
charsetPos += strMarker.length();
int intCharsetEnd =
aContentType.toLowerCase().indexOf(" ", charsetPos);
if (intCharsetEnd != -1)
{
this.charset =
aContentType.substring(charsetPos, intCharsetEnd);
}
else
{
this.charset = aContentType.substring(charsetPos);
}
}
}
}
email.send();的send方法将调用
public void buildMimeMessage() throws EmailException
{
try
{
this.getMailSession();
this.message = new MimeMessage(this.session);
if (EmailUtils.isNotEmpty(this.subject))
{
if (EmailUtils.isNotEmpty(this.charset))
{
this.message.setSubject(this.subject, this.charset);
}
else
{
this.message.setSubject(this.subject);
}
}
// ========================================================
// Start of replacement code
if (this.content != null)
{
this.message.setContent(this.content, this.contentType);
}
// end of replacement code
// ========================================================
else if (this.emailBody != null)
{
this.message.setContent(this.emailBody);
}
else
{
this.message.setContent("", Email.TEXT_PLAIN);
}
if (this.fromAddress != null)
{
this.message.setFrom(this.fromAddress);
}
else
{
throw new EmailException("Sender address required");
}
if (this.toList.size() + this.ccList.size() + this.bccList.size() == 0)
{
throw new EmailException(
"At least one receiver address required");
}
if (this.toList.size() > 0)
{
this.message.setRecipients(
Message.RecipientType.TO,
this.toInternetAddressArray(this.toList));
}
if (this.ccList.size() > 0)
{
this.message.setRecipients(
Message.RecipientType.CC,
this.toInternetAddressArray(this.ccList));
}
if (this.bccList.size() > 0)
{
this.message.setRecipients(
Message.RecipientType.BCC,
this.toInternetAddressArray(this.bccList));
}
if (this.replyList.size() > 0)
{
this.message.setReplyTo(
this.toInternetAddressArray(this.replyList));
}
if (this.headers.size() > 0)
{
Iterator iterHeaderKeys = this.headers.keySet().iterator();
while (iterHeaderKeys.hasNext())
{
String name = (String) iterHeaderKeys.next();
String value = (String) headers.get(name);
this.message.addHeader(name, value);
}
}
if (this.message.getSentDate() == null)
{
this.message.setSentDate(getSentDate());
}
if (this.popBeforeSmtp)
{
Store store = session.getStore("pop3");
store.connect(this.popHost, this.popUsername, this.popPassword);
}
}
catch (MessagingException me)
{
throw new EmailException(me);
}
}
由代码可以知道纯文本方式最终调用了Java Mail的
message.setContent(this.content, this.contentType);
content是内容
contentType是类型,如text/plain,
(我们可以试试直接用Java mail发邮件,设置文本内容不使用setText方法,也使用setContent("测试", "text/plain")方式,你可以看到内容也是乱码)
关键就在于text/plain,我们改成text/plain;charset=gb2312,ok乱码解决了。在commons mail我们看SimpleEmail 类中setMsg方法调用的就是 setContent(msg, TEXT_PLAIN);我们只需要将Email类中的常量TEXT_PLAIN修改一下加入 charset=你的字符集 ,重新打包jar,这样就可以了
posted @
2006-09-18 09:54 小小~咖啡豆 阅读(1366) |
评论 (0) |
编辑 收藏