Java线程死亡的几种情况

Java线程会议如下三种方式结束,结束后就处于死亡状态

1、run()或者call()方法执行完成,线程正常结束;

2、线程抛出一个未捕获的Exception或Error;

3、直接调用该线程的stop()方法来结束该线程;

 

注意:当主线程结束时,其他线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,不会受到主线程结束的影响。

为了测试某个线程是否已经死亡,可以调用线程对象的isAlive()方法,当线程处于就绪、运行、阻塞三种状态时,该方法将返回true;当线程处于新建、死亡两种状态时,该方法就返回false。

如下对线程死亡情况的1和2进行测试。主线程的代码如下:

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new RunTask());
        t.start();
        
        while (true) {
            Thread.sleep(1000);
            System.out.println("主线程:子线程状态为" + t.isAlive());
        }
    }
}

测试1:线程正常结束后,isAlive()返回False

编写线程正常结束的线程执行代码:

public class RunTask implements Runnable {
    
    @Override
    public void run() {
        for (int idx = 1; idx <= 10; idx++) {
            System.out.println("子线程:我还活着" + idx);
            
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

两个线程的输出结果如下所示,显示子线程正常执行结束后,使用Thread.isAlive()就返回False了。

主线程:子线程状态为true
主线程:子线程状态为true
子线程:我还活着8
主线程:子线程状态为true
主线程:子线程状态为true
主线程:子线程状态为true
子线程:我还活着9
主线程:子线程状态为true
主线程:子线程状态为true
主线程:子线程状态为true
子线程:我还活着10
主线程:子线程状态为true
主线程:子线程状态为true
主线程:子线程状态为true
主线程:子线程状态为false
主线程:子线程状态为false
主线程:子线程状态为false
主线程:子线程状态为false
主线程:子线程状态为false

测试2:子线程抛出异常之后,线程的isAlive()返回False

修改子线程的代码,加入异常抛出:

public class RunTask implements Runnable {
    
    @Override
    public void run() {
        for (int idx = 1; idx <= 10; idx++) {
            System.out.println("子线程:我还活着" + idx);
            
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            if (idx == 5) {
                throw new RuntimeException("i am die");
            }
        }
    }
}

再次执行,观察输出:

主线程:子线程状态为true
主线程:子线程状态为true
主线程:子线程状态为true
子线程:我还活着4
主线程:子线程状态为true
主线程:子线程状态为true
主线程:子线程状态为true
子线程:我还活着5
主线程:子线程状态为true
主线程:子线程状态为true
主线程:子线程状态为true
Exception in thread "Thread-0" java.lang.RuntimeException: i am die
	at RunTask.run(RunTask.java:15)
	at java.lang.Thread.run(Thread.java:662)
主线程:子线程状态为false
主线程:子线程状态为false
主线程:子线程状态为false

可以看到,抛出异常后,子线程直接终止,变成了Flase状态;

 

总结:线程正常结束后或者线程抛出了未捕获的异常,线程变成死亡状态,使用isAlive()函数返回False。

本文地址:http://crazyant.net/1861.html,转载请注明来源

通过JVM堆栈分析出现大量线程的原因

最近收到线上Tomcat线程数目超出的报警,于是想要分析下问题的原因:

首先进入线上,使用ps -aux命令,查看jvm进程,可以得到运行tomcat的jdk的地址:

/home/work/app/.jdk/bin/java

于是就知道了jdk的jstack、jps等命令的目录,然后找到jvm进程

/home/work/app/.jdk/bin/jps
29145 Jps
208 Bootstrap

得到了jvm的tomcat进程是208;

把堆栈导出,下载到本地:

jstack -l 208 > log.txt

下载后,发现线程堆栈中,有大量的这样的日志:

"pool-103-thread-1" prio=10 tid=0x00007f038001e000 nid=0x759d waiting on condition [0x00007f022e5e4000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000912fab28> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:399)
	at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
	at java.lang.Thread.run(Thread.java:662)

"pool-102-thread-1" prio=10 tid=0x00007f0380011000 nid=0x71ed waiting on condition [0x00007f022e6e5000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000912fa170> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:399)
	at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
	at java.lang.Thread.run(Thread.java:662)

可以看到,线程处于WAITING状态,阻塞在试图从任务队列中取任务(LinkedBlockingQueue.take),这个任务队列指的是ThreadPoolExecutor的线程池启动的线程任务队列;

也就是说,这些线程都是空闲状态,在等着任务的到来呢!

补充下LinkedBlockingQueue的知识:

并发库中的BlockingQueue是一个比较好玩的类,顾名思义,就是阻塞队列。该类主要提供了两个方法put()和take(),前者将一个对象放到队列尾部,如果队列已经满了,就等待直到有空闲节点;后者从head取一个对象,如果没有对象,就等待直到有可取的对象。 

定位到问题就简单了,查找代码,发现有个位置启动了线程池提交了任务,但是任务执行完返回后,线程池没有关闭导致的;

问题总结:

1、使用ExecutorService提交的线程任务,也要记得关闭;

2、启动新线程的时候,最好给线程起个名字,这样线程堆栈的问题排查更加容易;

 

文章地址:http://crazyant.net/1858.html,转载请注明来源

想要加悲观锁可是数据行还不存在怎么办?

两个并发事务想要对同一个KEY的数据进行更新,但是如果这个KEY的数据行还不存在的话,那么select .. for update当然不能锁住这行记录,想当然的想到,可不可以先insert一下,然后在悲观锁呢?

那么引入了一个新的问题,如果两个并发事务同时insert的话,就会插入重复的数据,如果insert的unique key重复的话,第二个线程会报错的,有没有更优雅的方法?

答案是MySQL innodb的INSERT … ON DUPLICATE KEY UPDATE语法;

用法见官网文档:https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html

If you specify ON DUPLICATE KEY UPDATE, and a row is inserted that would cause a duplicate value in a UNIQUEindex or PRIMARY KEY, MySQL performs an UPDATE of the old row. For example, if column a is declared as UNIQUEand contains the value 1, the following two statements have similar effect:

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=c+1;

UPDATE table SET c=c+1 WHERE a=1;

使用ON DUPLICATE KEY UPDATE语法,第二条insert语句,会自动变成Update语句,而不会导致重复插入数据的BUG;

做个简单的测试:

CREATE TABLE `tcc` (
  `idx` INT(11),
  `typeid` INT(11),
  `value1` INT(11),
  `value2` INT(11),
  UNIQUE KEY(idx, typeid)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

-- 第一条SQL的执行,会新增一条数据,本SQL多次执行,效果相同,因为后续的操作,变成了UPDATE
INSERT INTO tcc (idx, typeid, value1) 
VALUES (1,2,3) ON DUPLICATE KEY UPDATE value1=3;

-- 第二条SQL直接进行UPDATE,把新列的值update进去
INSERT INTO tcc (idx, typeid, value2) 
VALUES (1,2,4) ON DUPLICATE KEY UPDATE value2=4;

产出结果只有一条:

“idx” “typeid” “value1” “value2”
“1” “2” “3” “4”

有了这个利器,那么代码中就可以直接insert.. on duplicate key update,这个SQL的执行能保证数据库中会存在记录,然后加上悲观锁,来保证不同的事务不会出现更新冲突情况;

本文地址:http://crazyant.net/1835.html

Java堆溢出OutOfMemoryError之代码实例和原因分析

本文演示了编写代码使得出现”java.lang.OutOfMemoryError: Java heap space”异常,分析GC日志得出OOM的原因,同时对堆转储文件进行分析,以查看把Heap塞满的罪魁祸首;

实例代码

这段代码来自《深入理解Java虚拟机-JVM高级特性与最佳实践》一书:

package jvmtest;

import java.util.ArrayList;
import java.util.List;

/**
 * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 * 
 * @author zzm
 */
public class HeapOOM {
    
    static class OOMObject {
    }
    
    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<OOMObject>();
        
        while (true) {
            list.add(new OOMObject());
        }
    }
}

在eclipse运行该代码时,需要设置堆size的最小值和最大值,同时使用-XX:+PrintGCDetails参数开启GC日志打印,使用-XX:+HeapDumpOnOutOfMemoryError参数当OOM时转储堆数据

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError

在eclipse中设置vm arguments参数为了测试OOM

然后点运行,会出现以下的GC日志、异常、堆信息、转储信息

[GC[DefNew: 7640K->1024K(9216K), 0.0164901 secs] 7640K->4593K(19456K), 0.0166075 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
[GC[DefNew: 9216K->1024K(9216K), 0.0190825 secs] 12785K->10510K(19456K), 0.0191593 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
[GC[DefNew: 9216K->9216K(9216K), 0.0000369 secs][Tenured: 9486K->5704K(10240K), 0.0520898 secs] 18702K->14129K(19456K), [Perm : 148K->148K(12288K)], 0.0522306 secs] [Times: user=0.06 sys=0.00, real=0.05 secs] 
[Full GC[Tenured: 5704K->5704K(10240K), 0.0435436 secs] 14616K->14616K(19456K), [Perm : 148K->148K(12288K)], 0.0436766 secs] [Times: user=0.03 sys=0.00, real=0.04 secs] 
[Full GC[Tenured: 5704K->5695K(10240K), 0.0499650 secs] 14616K->14606K(19456K), [Perm : 148K->147K(12288K)], 0.0500832 secs] [Times: user=0.06 sys=0.00, real=0.05 secs] 
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid12428.hprof ...
Heap dump file created [29715028 bytes in 0.335 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:2245)
	at java.util.Arrays.copyOf(Arrays.java:2219)
	at java.util.ArrayList.grow(ArrayList.java:242)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
	at java.util.ArrayList.add(ArrayList.java:440)
	at jvmtest.HeapOOM.main(HeapOOM.java:20)
Heap
 def new generation   total 9216K, used 8921K [0x33640000, 0x34040000, 0x34040000)
  eden space 8192K, 100% used [0x33640000, 0x33e40000, 0x33e40000)
  from space 1024K,  71% used [0x33e40000, 0x33ef6600, 0x33f40000)
  to   space 1024K,   0% used [0x33f40000, 0x33f40000, 0x34040000)
 tenured generation   total 10240K, used 5695K [0x34040000, 0x34a40000, 0x34a40000)
   the space 10240K,  55% used [0x34040000, 0x345cfc28, 0x345cfe00, 0x34a40000)
 compacting perm gen  total 12288K, used 150K [0x34a40000, 0x35640000, 0x38a40000)
   the space 12288K,   1% used [0x34a40000, 0x34a658c8, 0x34a65a00, 0x35640000)
    ro space 10240K,  44% used [0x38a40000, 0x38eb73f0, 0x38eb7400, 0x39440000)
    rw space 12288K,  52% used [0x39440000, 0x39a8dd28, 0x39a8de00, 0x3a040000)

GC日志分析

从图中可以看出,发生了3次GC和2次FULL GC,当两次FULL GC完成后,仍然发现没有空间,于是抛出了OOM异常;

首先贴一下GC和FULL GC的日志格式:

young gc log format

full gc log format

还有jvm heap的内存结构

jvm-memory-model

在正常运行状况下:

  1. 新的对象都在eden上创建
  2. 如果eden的大小达到了阈值,就会触发GC,运行“标记-复制”算法,将Eden中有效的对象复制到Survivor中,并清除eden中无效的对象
  3. 如果eden的大小再次达到阈值,就会触发GC,将对象从eden对象复制到survivor,如果survivor中的对象达到了年龄限制,就会复制到old区;

如果young区向old区复制对象的时候,发现old区的空间无法满足,就会触发FULL GC,如果FULL GC之后,仍然无法承载Young区要晋升的对象大小,那么就会抛出OOM;

从Heap开头的日志可以看到eden区已经占用了100%,from survivor区也占用了71%无法承载来自eden的对象,eden+from survivor区对象之和为8192K+1024K*71%=8919.04K,但是old区只剩下10240*(1-55%)=的空间4608K的空间;新的对象无法在100%占用率的eden区创建,eden+survivor的对象又不能复制到old区,所以OOM;

注意:本文的代码很特殊,每个HeapOOM对象都没有被释放,正常情况下,eden很多无用对象是会被清除掉的,晋升到OLD的大小也不等于eden+survivor的大小之和;

堆转储文件分析

由于加上了-XX:+HeapDumpOnOutOfMemoryError参数,所以OOM时,自动生成了一个java_pid12464.hprof的堆转储文件,给eclipse安装Memory Analyzer插件后,直接可以用该插件打开;

打开后如下图所示

oom-memory-analyzer

可以看到main函数线程占用了13.9MB几乎所有的Heap空间,在饼图上右键 >  list objects > with outgoing references,可以查看该部分包含了那些对象:

oom-object-list

打开后发现,一个Obejct数组中包含了1215488个元素,每个元素都是HeapOOM对象,每个元素(引用类型)为8byte,这就是让堆OOM的罪魁祸首;

结论

  • 使用JVM参数-XX:+PrintGCDetails可以打印GC日志
  • 使用JVM参数-XX:+HeapDumpOnOutOfMemoryError可以在OOM时打印堆转储文件
  • 使用eclipse插件Memory Analyzer可以分析对转储文件
  • OOM的原因,是Old区在FULL GC之后的剩余空间,仍然无法承载Young区要晋升的对象大小

本文地址:http://crazyant.net/1810.html,转载请注明

使用javap命令或者eclipse的Bytecode visualizer插件阅读java字节码文件

阅读java的class文件,最常用的方法是jdk自带的javap命令,但是在eclipse有Bytecode visualizer插件,也可以很好地实现阅读;

直接阅读class文件,能够了解一下代码如何执行的内幕,以下面这个代码为例

package test;

import java.util.List;

public class Test {
	public void test() {
		String a = "x" + "y" + 1;
		String b = "xy1";
		System.out.println(a == b);
	}

	public static void main(String[] args) {
		Test t = new Test();

		t.test();
	}
}

test()方法很多人都知道返回true,但是为什么会这样呢?很多人可能说不清楚。我们分别用两种方法阅读一下它们的字节码;

该文件首先需要被编译,得到了Test.class文件

1、使用javap命令阅读

进入Test.class目录,然后使用javap -v Test.class > out将字节码文件读取后存入out文件,然后用notepad++打开out文件:

test()函数的样子:

bytecode-20150705102150

从字节码可以看到,编译器已经自动的将变量a的值合并在一起,成为了xy1,因此相当于xy1两个字符串的比较,两者都处于常量区,因此相等;

2、使用eclipse的Bytecode visualizer插件阅读

安装好插件之后,首先将其配置一下,以显示最大化的字节码信息

bytecode-visualizer-config

然后打开eclipse的Navigator窗口,找到编译好的Test.class,右键,用字节码方式打开,就会看到更易读的字节码

bytecode-visualizer-show

同时在编辑器的右侧,能够自动查看该段代码的流程图,非常方便;

 

使用这两种方式,就能够查看代码编译后的内幕,对一些奇怪的语法现象,就能够自己找出为什么了

但是在测试的过程中,自己写了个print(List<String> strs)的方法,使用Javap命令和Byte visualizer两者,输出的字节码中,前者带着泛型信息,后者进行了泛型擦除,两者不同,很是奇怪;

Java怎样单测void类型的方法?

Java的Sevice层会有很多void类型的方法,比如save*、update*,这类方法只是做一些更新,不会有返回值,其单测不能根据方法的返回值来编写,只能采用特殊方法;

本方法环境:Mockito、testng

被测试的方法:

@Override
    public void updateRuleName(Long ruleId, String newRuleName, Long ucId) {
        Assert.notNull(ruleId, "规则ID不能为Null");
        Assert.notNull(newRuleName, "规则名称不能为Null");
        Assert.notNull(ucId, "操作人的UCID不能为Null");
        
        String cleanNewRuleName = StringUtils.trim(newRuleName);
        if (StringUtils.isBlank(cleanNewRuleName)) {
            throw new IllegalArgumentException("新的规则名称不能为空");
        }
        
        // 查询规则对象
        Rule rule = queryRuleById(ruleId);
        if (null == rule) {
            throw new IllegalDataException("没有查到该规则");
        }
        
        rule.setRuleId(ruleId);
        rule.setRuleName(cleanNewRuleName);
        rule.setUpdateUcid(ucId);
        rule.setUpdateTime(new Date());
        
        ruleDao.updateSelective(rule);
    }

测试的方法:

 @Test
    public void testUpdateRuleName() {
        Long ruleId = 1L;
        String newRuleName = "newRuleName";
        Long ucId = 123L;
        
        List<Rule> rules = new ArrayList<Rule>();
        Rule rule = new Rule();
        rule.setRuleStatus((byte) DBValueSetting.RULE_STATUS_TAKE_EFFECT);
        rules.add(rule);
        
        // 查询规则对象
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("ruleId", ruleId);
        Mockito.when(ruleDao.queryRulesByCondition(params)).thenReturn(rules);
        
        Mockito.doAnswer(new Answer<Object>() {
            public Object answer(InvocationOnMock invocation) {
                // 断点2:这里随后执行
                Rule rule = (Rule) invocation.getArguments()[0];
                Assert.assertTrue(rule.getRuleName().equals("newRuleName"));
                return null;
            }
        }).when(ruleDao).updateSelective(Mockito.any(Rule.class));
        
        // 断点1:先执行到这里
        ruleService.updateRuleName(ruleId, newRuleName, ucId);
    }

如注释所示,如果加了两个断点的话,执行的过程中,会先执行最后的调用行,端点1执行的过程中,会执行到端点2的stub,这时候在断点2可以获取到方法执行的入参,对入参进行Assert校验,即可实现目的;

new Anwer是个接口,其中只有一个方法,用于设置方法调用的代理执行入口

public interface Answer<T> {
    /**
     * @param invocation the invocation on the mock.
     *
     * @return the value to be returned
     *
     * @throws Throwable the throwable to be thrown
     */
    T answer(InvocationOnMock invocation) throws Throwable;
}

当代码执行到“ruleDao.updateSelective(rule);”的时候,会触发针对mock对象调用的拦截器,在拦截器中,会创建一个动态代理,动态代理的invocation就是new Answer中覆盖的方法;

使用拦截、代理两种方法,实现了对mock对象方法的入参、出参的设定和获取,使用这种方式,就可以校验VOID方法内部的执行类调用的情况;

 

 

读书破万卷,代码如有神

IT技术领域的书籍普遍非常的厚,一本《JAVA编程思想》竟然多达800页,拿在手里很沉,只能在办公桌子上看。然而现在的工作普遍非常忙,白天忙着开发、开会,晚上回家累的想看电影、玩游戏,根本没有一整块的时间可以拿出来啃这种大部头书籍。能用来看书的时间只有地铁上、公交上、厕所里,碎片时间较多,可是书这么厚的话,地铁上、公交上、厕所里拿着太累了,不可行。有人说可以看电子书,我试了手机上看、Kindle上看,要么是字迹不清晰,要么排版错乱难以阅读,没有一个好办法。

其实解决办法很简单,把书撕开,每个章节单独阅读,有如下好处:

  1. 物尽其用,大部分大部头的书往往买了没办法看,就摆在那里垫桌子,浪费money;
  2. 克服恐惧心理,大部头的书看上去让人害怕,就像一座高山,如果把它撕开成小章节,首先心理上不会恐惧,就像一个个小目标一样,一章章看完后,惊讶的发现自己看完了大部头;
  3. 轻便易携带,拆开成小章节之后,往往只有几十页纸,折叠一下塞到小包里,任何时候都能拿出来,还不占地方;
  4. 按章节进行阅读,因为章节较小,我们可以按章节进行阅读,一边读不懂,可以读第二遍,每个章节彻底弄懂后,再去看下一章,容易管理自己的进度;

这么多好处,还等什么呢。

撕开的《JAVA编程思想第四版》

撕开的JAVA编程思想

撕开的《疯狂JAVA讲义》
撕开的疯狂JAVA讲义

 

这真是一种好办法,化整为零、逐个击破,大部头的书往往更加经典,终于有办法可以使用碎片时间学习这些知识了。

最近在看JAVA方面的书籍,找了一个书单列举如下,用这种阅读方法,再厚的书都能看完:

  1. 《Spring in Action》 3th
  2. 《Effective Java》2th
  3. 《JAVA编程思想》4th
  4. 《疯狂JAVA讲义》 3rd
  5. 《重构:改善既有代码的设计》
  6. 《代码整洁之道》
  7. 《Java并发编程实战》
  8. 《大型网站技术架构 核心原理与案例分析》
  9. 《大型网站系统与Java中间件实践》
  10. 《代码大全》(第二版)
  11. 《程序员修炼之道:从小工到专家》
  12. 《Java程序员修炼之道》
  13. 《设计模式:可复用面向对象软件的基础》
  14. 《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》
  15. 《数据结构与算法分析:Java语言描述(第2版)》
  16. 《Java常用算法手册(含盘)》

一起学习吧,i reading,i happy,let’s begin reading。

Java怎样创建两个KEY(key-pair)的MAP

就像在XY坐标系中,一个X刻度、一个Y刻度,会对应图上的一个点,即pair(x, y) – > point,那么就有创建一个点,或者根据(x, y)来寻求一个点的需求,如果用Python的语法表达,是这个样子:

dict((x, y) : point)

然而在JAVA中却变得不容易,骨钩了一下,有这么一些方法:

使用嵌套MAP

Map<Integer, Map<Integer, V>> map = //...
// ...

map.get(2).get(5);

这种方法将水平的KEY切换成了垂直KEY,我们要找寻XY坐标系中(x, y)对应的点,其实可以先找x点的那条垂直线,然后找出这条垂直线上y高度的那个点。

但是要取得这个双KEY的VALUE,得有2次GET,实例代码没有做第一次GET的NULL判断,有隐患!并且感觉不是很直观。 继续阅读Java怎样创建两个KEY(key-pair)的MAP

Java枚举类型代码的二逼写法和艺术写法

最近遇到一种需求场景:使用一个类封装Java服务端返回的(返回状态码,返回信息字符串)信息,比如(0,操作成功)、(1,新增失败)、(2,查询失败)等状态。作为一枚Java屌丝,我想到这种常量信息最好写到一个单独的类里面,并且用public static final的形式修饰,为了将返回码code和返回信息msg两者绑定起来可以提供一个公用方法。我做了以下两个努力,一是将两个返回码和返回信息的变量名写的相似,看代码的人一看就知道两个变量的含义和关系;第二个则提供一个根据返回码获得对应的返回信息的方法。写完后自我感觉良好,感觉满足了需求很是完备,于是出现了以下的2B代码:

枚举状态码和状态信息的2B写法

package net.crazyant;

/**
 * 
 * 返回给客户端的状态码和状态信息
 * 
 * @author crazyant.net
 *
 */
public class CommunicateCodeOne {
	/**
	 * 状态码:操作成功
	 */
	public static final int OPERATION_SUCCESS_CODE = 0;

	/**
	 * 状态信息:操作成功
	 */
	public static final String OPERATION_SUCCESS_MSG = "操作成功";

	/**
	 * 状态码:新增失败
	 */
	public static final int ADD_DATA_ERROR_CODE = 1;

	/**
	 * 状态信息:新增失败
	 */
	public static final String ADD_DATA_ERROR_MSG = "新增失败";

	/**
	 * 状态码:查询失败
	 */
	public static final int QUERY_DATA_ERROR_CODE = 2;

	/**
	 * 状态信息:查询失败
	 */
	public static final String QUERY_DATA_ERROR_MSG = "查询失败";

	/**
	 * 根据状态码获取状态信息
	 * 
	 * @param communicateCode
	 *            状态码
	 * @return 状态信息字符串
	 */
	public static String getMsg(int communicateCode) {
		String returnMsg = null;
		switch (communicateCode) {
		case OPERATION_SUCCESS_CODE:
			// 操作成功
			returnMsg = OPERATION_SUCCESS_MSG;
			break;
		case ADD_DATA_ERROR_CODE:
			// 新增失败
			returnMsg = ADD_DATA_ERROR_MSG;
			break;
		case QUERY_DATA_ERROR_CODE:
			// 查询失败
			returnMsg = QUERY_DATA_ERROR_MSG;
			break;
		}
		return returnMsg;
	}
}

继续阅读Java枚举类型代码的二逼写法和艺术写法

将普通Maven Spring项目转换成Web项目的方法

最近在按照李刚的《疯狂J2EE》一书学习Spring,其中第7章的一些代码是ant编译的,而公司用的是Maven,所以想要将其部署并转换成Maven Spring Web项目来执行一下。

本文用的是《疯狂j2ee》第7章的request_scope代码;

1、建立普通的maven project;

1

2

2、转换成web项目,这里选2.5版本,注意有的WEB项目的webcontent目录会有所不同,可以点击”further configuration available”按钮进行设定,设定的时候,路径从src目录开始复制即可。

3

配置WEB目录所在的位置
配置WEB目录所在的位置

3、把光盘的代码复制到相应的位置

这里看到test.jsp报错了,因为没有Spring的jar包导致,第4步会在Pom.xml中配置这个依赖;

4

4、在pom.xml中增加依赖的Spring的JAR包

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>pss</groupId>
	<artifactId>chapter75-requestScope</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.0.6.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.0.6.RELEASE</version>
		</dependency>
	</dependencies>
</project>

 

5、在web.xml中配置部署Spring配置文件

<?xml version="1.0" encoding="GBK"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
	http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>
</web-app>

 

6、设定将Maven的jar包也发布到Tomcat的web目录中,如果依赖了其他的子项目,也需要在这里进行添加,举个例子,如果你的项目用到了项目组的common的JAR包,那么在这里需要也加上才能正确的被部署。

5

7、新建一个server,把该web项目部署在其中

6

8、修改server中tomcat的路径并启动server

8

运行,看到结果;

7

总结:

1、需要将项目转成web项目;

2、需要在pom中引入spring的context和web两个依赖包;

3、需要在项目部署的设置中,将maven的包也部署一下;

3、需要在web.xml中指定applicationContext.xml的部署位置;

本文地址:http://crazyant.net/1607.html