其实这个本来没什么可写的,只是OneCoder恰巧用到又不会,在网上查了一下找到了答案,所以记录一下,也做分享。
很简单,一句话,执行:
mvn dependency:copy-dependencies -DoutputDirectory=lib -DincludeScope=compile
会将依赖的包拷贝到工程的lib目录下。
其实这个本来没什么可写的,只是OneCoder恰巧用到又不会,在网上查了一下找到了答案,所以记录一下,也做分享。
很简单,一句话,执行:
mvn dependency:copy-dependencies -DoutputDirectory=lib -DincludeScope=compile
会将依赖的包拷贝到工程的lib目录下。
OneCoder只是一个初学者,记录的只是自己的一个过程。不足之处还望指导。
看网上说导入大量数据,用bulk load的方式效率比较高。bulk load可以将固定格式的数据文件转换为HFile文件导入,当然也可以直接导入HFile文件。所以OneCoder最开始考虑的生成HFile文件供HBase导入,不过由于手太新,一直没有搞定。参考了很多网上的代码也没跑通。暂时搁浅。
后来OneCoder采用了,生成普通的数据格式文件,然后用过imporsttsv命令导入的方式成功。生成数据文件代码如下:
private static final String PATH = "F:/data.txt";
/**
* @param args
* @author lihzh
* @alia OneCoder
* @blog http://www.coderli.com
* @throws IOException
* @date 2012-11-14 下午4:51:22
*/
public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis();
File dataFile = getFile();
FileWriter writer = null;
try {
writer = new FileWriter(dataFile);
int timeCount = 1;
int resourceCount = 1;
for (int j = 0; j < timeCount; j++) {
long timeStamp = System.currentTimeMillis();
for (int i = 0; i < resourceCount; i++) {
UUID uuid = UUID.randomUUID();
String rowKey = uuid.toString() + "_" + timeStamp;
Random random = new Random();
String cpuLoad = String.valueOf(random.nextDouble())
.substring(0, 4);
String memory = String.valueOf(random.nextDouble())
.substring(0, 4);
StringBuilder builder = new StringBuilder();
builder.append(rowKey).append("\t").append(cpuLoad)
.append("\t").append(memory).append("\t").append(uuid.toString()).append("\t").append(timeStamp);
writer.append(builder.toString());
if ((i + 1) * (j + 1) < timeCount * resourceCount) {
writer.append("\r");
}
}
}
long endTime = System.currentTimeMillis();
System.out.println("Cost Time: " + (endTime - startTime));
} catch (IOException e) {
e.printStackTrace();
} finally {
writer.close();
}
}
/**
* 得到一个新文件
*
* @return
* @author lihzh
* @date 2012-11-14 下午4:53:31
*/
private static File getFile() {
File file = new File(PATH);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
文件格式大致如下:
29611690-69cb-4749-8bd5-be75793d6611_1352968490061 0.41 0.34 29611690-69cb-4749-8bd5-be75793d6611 1352968490061
然后将文件上传到HDFS中,
hadoop fs -put /home/admin/Desktop/data.txt /test<
转换成HFile格式存储
hadoop jar hbase-version.jar importtsv -Dimporttsv.columns=HBASE_ROW_KEY,c1,c2 -Dimporttsv.bulk.output=tmp hbase_table hdfs_file
生成HFile文件。其中c1,c2是列名,格式为:列族:列名
然后,导入到HBase中:
hadoop jar hbase-version.jar completebulkload /user/hadoop/tmp/cf hbase_table
这里的路径都是hdfs的路径。
在一个下雨的夜晚,我在思考Java中内存管理的问题,以及Java集合对内存使用的效率情况。我做了一个简单的实验,测试在16G内存条件下,Java的Map可以插入多少对象。
这个试验的目的是为了得出集合的内部上限。所以,我决定使用很小的key和value。所有的测试,都是在64w位linux环境下进行的,操作系统是ubuntu12.04。JVM版本为Oracle Java 1.7.0_09-bo5 (HotSpot 23.5-b02)。在这个JVM中,压缩指针(compressed pointers(-XX:+UseCompressedOops))的选项是默认打开的。
首先是简单的针对java.util.TreeMap的测试。不停向其中插入数字,直到其抛出内存溢出异常。JVM的设置是-xmx15G
import java.util.*;
Map m = new TreeMap();
for(long counter=0;;counter++){
m.put(counter,"");
if(counter%1000000==0) System.out.println(""+counter);
}
这个用例插入了1 7200 0000条数据。在接近结束的时候,由于高负荷的GC插入效率开始降低。第二次,我用HashMap代替TreeMap,这次插入了182 000 000条数据。
Java默认的集合并不是最高效利用内存的。所以,这回我们尝试最后化内存的测试。我选择了MapDB中的LongHashMap,其使用原始的long key并且对封装的内存占用进行了优化。JVM的设置仍然是-Xmx15G。
import org.mapdb.*
LongMap m = new LongHashMap();
for(long counter=0;;counter++){
m.put(counter,"");
if(counter%1000000==0) System.out.println(""+counter);
}
这次,计数器停止在了276 000 000。同样,在插入接近结束的时候,速度开始减慢。看起来这是基于堆的结合的限制所在。垃圾回收带来了瓶颈 。
现在是时候祭出杀手锏了:-)。我们可以采用非基于堆的方式存储,这样GC就不会发现我们的数据。我来介绍一下MapDB,它提供了基于数据库引擎的并发同步的TreeMap和HashMap。它提供了多样化的存储方式,其中一个就是非堆内存的方式。(声明:我是MapDB的作者)。
现在,让我们再跑一下之前的用例,这次采用的是非堆的Map。首先是配置并打开数据库,它打开的直接基于内存存储并且关闭事物的模式。接下来的代码是在这个db中创建一个新的map。
import org.mapdb.*
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.make();
Map m = db.getTreeMap("test");
for(long counter=0;;counter++){
m.put(counter,"");
if(counter%1000000==0) System.out.println(""+counter);
}
这是非堆的Map,所以我们需要不同的JVM配置: -XX:MaxDirectMemorySize=15G -Xmx128M。这次测试在达到980 000 000条记录的时候出现内存溢出。
但是,MapDB还可以优化。之前样例的问题在于记录的破碎分散,b-tree的节点每次插入都要调整它的容量。变通的方案是,将b-tree的节点在其插入前短暂的缓存起来。这使得记录的分散降到最低。所以,我们来改变一下DB的配置:
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.asyncFlushDelay(100)
.make();
Map m = db.getTreeMap("test");
这次记录数达到了 1 738 000 000。速度也是达到了惊人的31分钟完成了17亿数据的插入。
MapDB还能继续优化。我们把b-tree的节点容量从32提升到120并且打开透明(OneCoder理解为对用户不可见的)压缩:
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.asyncFlushDelay(100)
.compressionEnable()
.make();
Map m = db.createTreeMap("test",120, false, null, null, null);
这个用例在大约3 315 000 000条记录时出现内存溢出。由于压缩,他的速度 有所降低,不过还是在几个小时内完成。我还可以进行一些优化(自定义序列化等等) ,使得数据量达到大约40亿。
也许你好奇所有这些记录是怎么存储的。答案就是,delta-key压缩。(OneCoder注:不知如何翻译)。当然,向B-Tree插入已经排好序的递增key是最佳的使用场景,并且MapDB也对此进行了一些小小的 优化。最差的情形就是key是随机的.
后续更新:很多朋友对压缩有一些困惑。在这些用例中,Delta-key 压缩默认都是启用的。在下面的用例中,我又额外开启了zlib方式的压缩:
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.asyncFlushDelay(100)
.make();
Map m = db.getTreeMap("test");
Random r = new Random();
for(long counter=0;;counter++){
m.put(r.nextLong(),"");
if(counter%1000000==0) System.out.println(""+counter);
}
即使在随机序列情况下,MapDB也可以存储652 000 000条记录,大概4倍于基于堆的集合。
这个简单的试验没有太多的目的。这仅仅是我对MapDB的一种优化。也许,更多的惊喜在于插入效率确实不错并且MapDB可以抗衡基于内存的集合。
原文地址:http://kotek.net/blog/3G_map
OneCoder注:OneCoder仅做翻译,顺便了解一下知识,关于本文,下面的评论中也存在一定的争议,大家可以自行关注。此文,权当开阔视野,不是也很好么。
JUnit单元测试用例中使用Spring框架,之前我的使用方式很直接。
/**
* 用于需要用到Spring的测试用例基类
*
* @author lihzh
* @alia OneCoder
* @blog http://www.coderli.com
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/spring/applicationContext.xml" })
public class SpringTest {}
在测试的过程中,有人提到,想要获取ApplicationContext实例。于是,添加了对ApplicationContext的注入。
/**
* 用于需要用到Spring的测试用例基类
*
* @author lihzh
* @alia OneCoder
* @blog http://www.coderli.com
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/spring/applicationContext.xml" })
public class SpringTest {
@Autowired
protected ApplicationContext ctx;
其实,Spring中早已直接提供了更加方便使用的基类:AbstractJUnit4SpringContextTests。修改代码如下:
/**
* 用于需要用到Spring的测试用例基类
*
* @author lihzh
* @alia OneCoder
* @blog http://www.coderli.com
*/
@ContextConfiguration(locations = { "/spring/applicationContext.xml" })
public class SpringTest extends AbstractJUnit4SpringContextTests {
public <T> T getBean(Class<T> type) {
return applicationContext.getBean(type);
}
public Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
protected ApplicationContext getContext() {
return applicationContext;
}
}
代码也简洁多了。
现在想想,你想要的常用功能,一般人家都能想到了。做之前,不妨先查查有没有现成可用的工具吧:)
在上回介绍Spring3.1+Hibernate4.1.7基于注解配置的时候(《SpringMVC3.1+Hibernate4.1.7完全基于注解配置(零配置文件)》)说过,在修改配置方式的时候遇到过不少问题。这里介绍一下。
/**
* 数据源配置类
*
* @author lihzh
* @alia OneCoder
* @blog http://www.coderli.com
*/
@Configuration
@PropertySource("/conf/jdbc.properties")
public class DataSourceConfig {
@Value("${jdbc.driverClass}") String driverClass;
@Value("${jdbc.url}") String url;
@Value("${jdbc.user}") String user;
@Value("${jdbc.password}") String password;
@Bean(autowire=Autowire.BY_TYPE)
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driverClass);
dataSource.setJdbcUrl(url);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
}
/**
* Spring3.1基于注解的配置类, 用于代替原来的<b>applicationContext.xml</b>配置文件
*
* @author lihzh
* @date 2012-10-12 下午4:23:13
*/
@ComponentScan(basePackages = "com.coderli.shurnim.*.biz")
@Import(DataSourceConfig.class)
@Configuration
@EnableTransactionManagement
public class DefaultAppConfig {
@Autowired
DataSourceConfig config;
@Bean
public PropertySourcesPlaceholderConfigurer placehodlerConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public LocalSessionFactoryBean sessionFactory()
throws PropertyVetoException {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(config.dataSource());
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect",
"org.hibernate.dialect.MySQLDialect");
sessionFactoryBean.setHibernateProperties(hibernateProperties);
sessionFactoryBean.setPackagesToScan("com.coderli.shurnim.*.model");
return sessionFactoryBean;
}
@Bean
public HibernateTransactionManager txManager() throws PropertyVetoException {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory().getObject());
return txManager;
}
}
这里,这样配置的依据是参考Spring中@Configuration注解的注释说明:
With the @Import annotation
@Configuration classes may be composed using the @Import annotation, not unlike the way that works in Spring XML. Because @Configuration objects are managed as Spring beans within the container, imported configurations may be injected using @Autowired or @Inject:</p>
@Configuration public class DatabaseConfig { @Bean public DataSource dataSource() { // instantiate, configure and return DataSource } }
@Configuration @Import(DatabaseConfig.class) public class AppConfig { @Inject DatabaseConfig dataConfig;
@Bean public MyBean myBean() { // reference the dataSource() bean method return new MyBean(dataConfig.dataSource()); } }
Now both AppConfig and the imported DatabaseConfig can be bootstrapped by registering only AppConfig against the Spring context: new AnnotationConfigApplicationContext(AppConfig.class);
启动Tomcat,报错:
空指针异常,显然是DataSourceConfig没有注入进来,更换@Inject注解,问题依旧。
既然注入DataSourceCOnfig无效,OneCoder考虑,可否直接注入DataSource Bean。修改代码如下:
/**
* Spring3.1基于注解的配置类, 用于代替原来的<b>applicationContext.xml</b>配置文件
*
* @author lihzh
* @date 2012-10-12 下午4:23:13
*/
@ComponentScan(basePackages = "com.coderli.shurnim.config")
@Import(DataSourceConfig.class)
@Configuration
@EnableTransactionManagement
public class DefaultAppConfig {
@Autowired
DataSource dataSource;
@Bean
public PropertySourcesPlaceholderConfigurer placehodlerConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public LocalSessionFactoryBean sessionFactory()
throws PropertyVetoException {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect",
"org.hibernate.dialect.MySQLDialect");
sessionFactoryBean.setHibernateProperties(hibernateProperties);
sessionFactoryBean.setPackagesToScan("com.coderli.shurnim.*.model");
return sessionFactoryBean;
}
启动Tomcat,报错:
OneCoder在各个Bean的方法上加断点调试,发现dataSource也是null,看来还是没注入进来导致的。也许这两个场景是一个问题,先解决没注入的问题看看吧。
说实话,这个问题困扰了OneCoder近一天,搜索也没什么好的头绪,搜出来的东西差距都比较大。OneCoder只能一遍一遍阅读自己的代码分析可能的原因同时仔细查看Spring官方的注释文档。OneCoder在Debug中似乎意识到一个问题,就是OneCoder这里使用了
@Bean
public PropertySourcesPlaceholderConfigurer placehodlerConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
这个bean用来使用替换变量符”${}”。这个替换符正式在DataSourceConfig中配置数据源使用的,这样的花,是否会造成Spring初始化Bean的生命周期的混乱呢。因为初始化dataSource Bean的时候需要上述bean,而初始化上述bean的时候,已经错过了DataSource bean的注入时机。那么如何解决呢,OneCoder还是得求助官方文档。又是一遍又一遍的阅读,OneCoder在@Bean注解的注释中,发现这样的一段话。
A note on BeanFactoryPostProcessor-returning @Bean methods <p> Special consideration must be taken for @Bean methods that return Spring BeanFactoryPostProcessor (BFPP) types. Because BFPP objects must be instantiated very early in the container lifecycle, they can interfere with processing of annotations such as @Autowired, @Value, and @PostConstruct within @Configuration classes. To avoid these lifecycle issues, mark BFPP-returning @Bean methods as static. For example:</p> <p> @Bean
public static PropertyPlaceholderConfigurer ppc() {
// instantiate, configure and return ppc …
}
By marking this method as static, it can be invoked without causing instantiation of its declaring @Configuration class, thus avoiding the above-mentioned lifecycle conflicts. Note however that static @Bean methods will not be enhanced for scoping and AOP semantics as mentioned above. This works out in BFPP cases, as they are not typically referenced by other @Bean methods. As a reminder, a WARN-level log message will be issued for any non-static @Bean methods having a return type assignable to BeanFactoryPostProcessor.</p>
果然被我猜中了,这段意思大概是说,类似PropertyPlaceholderConfigurer这种的Bean是需要在其他Bean初始化之前完成的,这会影响到Spring Bean生命周期的控制,所以如果你用到了这样的Bean,需要把他们声明成Static的,这样就会不需要@Configuration的实例而调用,从而提前完成Bean的构造。并且,这里还提到,如果你没有把实现 BeanFactoryPostProcessor接口的Bean声明为static的,他会给出警告。
OneCoder赶紧去检查自己的控制台,果然发现了这样一句话:
WARNING: @Bean method DefaultAppConfig.placehodlerConfigurer is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean Javadoc for complete details
唉,本来OneCoder是很重视警告的,这次怎么就没注意到呢。赶紧改成static的。重新启动。终于,一切OK了!
其实这篇文章应该是上篇《Servlet3.0下基于注解的SpringMVC3.1配置-完全零配置文件》的续篇,因为上篇只介绍到web工程和Spring(包括MVC)的零配置,相对于传统的SSH来说,相当于SS零配置了。那么S和H的结合如果零配置文件呢。
Hibernate的注解配置大家应该不会陌生。主要就是对实体类的配置,注明对应的表和字段即可。
/**
* 用户模型
*
* @author lihzh
* @alia OneCoder
* @blog http://www.coderli.com
*/
@Entity
@Table(name="snm_user")
public class User implements Serializable {
private static final long serialVersionUID = -6925982814013703984L;
@Id
private int id;
@Column(length = 20, nullable = false)
private String name;
@Column(length = 32, nullable = false)
private String password;
@Column(length = 64, nullable = false)
private String email;
@Column(name = "ctime", length = 20, nullable = false)
private long createTime;
....
主要还是原有数据源和SessionFactory的注解配置方式的改变。这里利用Spring提供的注解方式,单独抽出一个数据源配置类
/**
* 数据源配置类
*
* @author lihzh
* @alia OneCoder
* @blog http://www.coderli.com
*/
@Configuration
@PropertySource("/conf/jdbc.properties")
public class DataSourceConfig {
@Value("${jdbc.driverClass}") String driverClass;
@Value("${jdbc.url}") String url;
@Value("${jdbc.user}") String user;
@Value("${jdbc.password}") String password;
@Bean
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driverClass);
dataSource.setJdbcUrl(url);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public LocalSessionFactoryBean sessionFactory() throws PropertyVetoException {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
sessionFactoryBean.setHibernateProperties(hibernateProperties);
sessionFactoryBean.setPackagesToScan("com.coderli.shurnim.*.model");
return sessionFactoryBean;
}
@Bean
public HibernateTransactionManager txManager() throws PropertyVetoException {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory().getObject());
return txManager;
}
}
配置了数据源,SessionFactory和事物管理类。在SessionFactory中声明了扫描实体注解的包路径。这里把数据源的配置单独提到了jdbc.properties这个配置文件中,主要是考虑在实际实施过程中,数据库链接是一个需要经常修改的项,所以提出在项目配置之外,以方便实施人员独立配置。
然后在原有的DefaultAppConfig中引用该配置。用@Import注解:
@Configuration
@ComponentScan(basePackages = "com.coderli.shurnim.*.biz")
@Import(DataSourceConfig.class)
@EnableTransactionManagement
public class DefaultAppConfig {
同时启用了事物注解。至此,Spring3.1+Hibernate4.1.7的配置基本完成了。启动项目。稍微修改一下原UserAction,使其读取数据库
@RequestMapping("/user.do")
@Transactional
public void test(HttpServletResponse response) throws IOException {
_log.info("Hi, u guy");
List<User> users = userDAO.getAll();
response.getWriter().write("name: " + users.get(0).getName() + "; email: " + users.get(0).getEmail());
}
启动服务,一切OK。
需要说明的是,OneCoder的学习习惯是不停的折腾,所以这里虽然是配置好了,正常使用。但是在配置过程中,还是遇到一些没有解决的问题。比如,根据@Configuration的注解说明,OneCoder曾响在DataSourceConfig中仅仅保留数据源配置,将SessionFactory和事物的配置都放到AppConfig中,然后通过@Autowire或者@Inject的注解引用进来,用了两种方式都还没有成功。待OneCoder研究清楚后,再一一说明。这里给出配置,仅仅给大家提供一个样例参考。更多的细节,还需要大家自己研究:)。OneCoder感觉,这种配置方式,还有挺有意思的。
Spring3.1.x系列的一个新特性就是支持了Servlet3.0规范,基于注解的方式配置SpringMVC框架。官方文档描述如下:
3.1.10 Support for Servlet 3 code-based configuration of Servlet Container
The new WebApplicationInitializer builds atop Servlet 3.0's ServletContainerInitializer support to provide a programmatic alternative to the traditional web.xml.
See org.springframework.web.WebApplicationInitializer Javadoc
Diff from Spring's Greenhouse reference application demonstrating migration from web.xml to WebApplicationInitializer
既然如此,我们就来实现一下。根据WebApplicationInitializer 的注释说明,实现该接口即可。
/**
* 基于Servlet3.0的,相当于以前<b>web.xml</b>配置文件的配置类
*
* @author OneCoder
* @Blog http://www.coderli.com
* @date 2012-9-30 下午1:16:59
*/
public class DefaultWebApplicationInitializer implements
WebApplicationInitializer {
@Override
public void onStartup(ServletContext appContext)
throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(DefaultAppConfig.class);
appContext.addListener(new ContextLoaderListener(rootContext));
ServletRegistration.Dynamic dispatcher = appContext.addServlet(
"dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
这里说明一下,根据WebApplicationInitializer 类的注释说明(OneCoder建议大家多阅读代码注释(Javadoc),其实也就是API文档。),其实这里有两种初始化rootContext的方式。一种是利用XmlWebApplicationContext,即Spring的配置是通过传统的Xml的方式配置的。另一种就是这里用到的AnnotationConfigWebApplicationContext ,即Spring的配置也是通过注解的方式。这里,我们既然说了是完全零配置文件,那么我就采用第二种方式。利用新建的配置类,配置注解的方式,完成配置。
/**
* Spring3.1基于注解的配置类, 用于代替原来的<b>applicationContext.xml</b>配置文件
*
* @author lihzh
* @date 2012-10-12 下午4:23:13
*/
@Configuration
@ComponentScan(basePackages = "com.coderli.shurnim.*.biz")
public class DefaultAppConfig {
}
可以看到,基本原有在配置文件里的配置,这里都提供的相应的注解与之对应。这里我们仅仅配置的包扫描的根路径,用于SpringMVC配置的扫描,以进一步达到零配置文件的效果。在业务包下,写一个MVC的controller类:
/**
* 用户操作Action类
*
* @author lihzh
* @date 2012-10-12 下午4:12:54
*/
@Controller
public class UserAction {
@RequestMapping("/user.do")
public void test(HttpServletResponse response) throws IOException {
response.getWriter().write("Hi, u guy.");
}
}
启动Tomcat,浏览器访问地址: http://localhost:8080/onecoder-shurnim/user.do
其实是一个不值得一提的小问题,不过既然验证了,就拿出来分享一下吧。
/**
* @author lihzh
* @alia OneCoder
* @blog http://www.coderli.com
*/
public class TimerMain {
/**
* JDK Timer类测试类。主要测试在一个Timer周期内,线程未结束时,timer的处理情况。
*
* @param args
* @author lihzh
* @alia OneCoder
*/
public static void main(String[] args) {
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Timer timer = new Timer();
timer.scheduleAtFixedRate(timerTask, 0, 1000);
}
}
结论也很简单,下一个任务会等待上一个任务执行完成再启动。也算合理。
十一匆匆的就过去了,OneCoder过的也是有点恍惚。不知道都做了什么,心却有点散。做一篇翻译,权当收收心,找找感觉吧。
String filePath = "/Users/mneedham/data/awesome-csv-file.csv";
CSVReader reader = new CSVReader(new FileReader(filePath), ',');
List<String[]> csvEntries = reader.readAll();
Iterator<String[]> iterator = csvEntries.iterator();
while (iterator.hasNext()) {
String[] row = iterator.next();
System.out.println("field 1: " + row[0]);
}
在它的项目主页中有更多使用场景的莉子。
OneCoder最近一直在使用Restful API,最近正好看到一篇自定义restful接口规范的“抛砖引玉”得的文章,索性翻译一下,与大家分享。 原文地址:http://java.dzone.com/articles/restful-standard-resolved
最近,我正在使用RESTfull的方式构建一个web服务。尽管现在有很多的一般的指导和提示告诉你如何定义restful接口,但是却没有一个明确的标准或大家都接受的schema定义去遵循。
在网上获取了一些信息后,我打算打破这一局面:)我打算分享一下我定义的规则和结构,并且很希望能得到一些反馈来帮助我完善这个规则,所以,不要犹豫,请毫无留情给我指出错误和毛病吧。</div>
这里,{version}代表api的版本信息。{domain}是一个你可以用来定义任何技术的区域(例如:安全-允许指定的用户可以访问这个区域。)或者业务上的原因。(例如:同样的功能在同一个前缀之下。)