Java的单例模式希望始终获得的是一个实力,有以下几种情况可能破坏单例模式

一、反射
即使将构造器设为私有仍可以用反射机制访问构造器,因此应在构造器中判断是否instance == null,不为null时直接返回。
eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton{
private static final Singleton instance = new Singleton();

private Singleton(){
if(instance != null)
return instance;
}

public static Singleton getInstance(){
return instance;
}
//other method

}

二、序列化
序列化过程中静态变量都是transient的,不会被保存,在反序列化的过程中会重新生成实例,从而破坏单例,可以通过readResolve方法解决。
eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton implements Serializable{
private static final Singleton instance = new Singleton();

private Singleton(){
if(instance != null)
return instance;
}

public static Singleton getInstance(){
return instance;
}

private Object readResolve() {
return instance;
}
//other method
}

三、clone
所有类都继承自Object类,所以都有clone方法,为了防止clone方法破坏单例,可以override它的clone方法,让它抛出异常。

linux环境变量通常有
/etc/profile
/etc/bashrc
~/.bash_profile(或~/.bash_login、~/.profile中的一个,优先级从左到右)
~/.bashrc
四个文件

一、profile文件登录式shell打开时执行
/etc/profile 和 ~/.bash_profile 登录(即打开一个login shell)时执行一次。
/etc/profile先执行,对全部用户和所有类型的shell(如bash、csh、zch等)都有效,而~/.bash_profile只有当前用户的bash会去读,当没有时会依次尝试执行~/.bash_login、 ~/.profile,执行到一个则不再尝试。

二、bashrc文件非登录式shell打开时执行
/etc/bashrc 和 ~/.bashrc 打开一个非登录式shell(non-login shell)会被读取,同样/etc/bashrc是对所有用户有效,~/.bashrc只对当前用户有效。

三、执行顺序
/etc/profile先执行,再启动用户目录下的~/.bash_profile或~/.bash_login、 ~/.profile。
如果~/.bash_profile存在则一般还会执行~/.bashrc,~/.bashrc中还会执行/etc/bashrc。

四、登录式与非登录式shell
1、登录式(login shell)
某用户由/bin/login登陆进系统后启动的shell,跟这个用户绑定。这个shell是用户登陆后启动的第一个进程。当bash以login shell启动时,它会执行/etc/profile中的命令,然后/etc/profile调用/etc/profile.d目录下的所有脚本;然后执行~/.bash_profile,~/.bash_profile调用~/.bashrc,最后~/.bashrc又调用/etc/bashrc。
2、非登录式shell(non-login shell)
不需login而由某些程序启动的shell。还以Bash为例,当以非login方式启动时,它会调用~/.bashrc,随后~/.bashrc中调用/etc/bashrc,最后/etc/bashrc调用所有/etc/profile.d目录下的脚本。这个有兴趣的可以打开这些文件看一看。
非login的shell主要包括以”#su”,”#su USERNAME”启动的shell,和图形终端(例如Ubuntu的Terminal),执行的脚本等等。
3、识别登录式与非登录式
登录式shell在进程启动时传递的第0个参数是 “-shell name”,而非登录式shell时传递的是“shell name”。
以bash为例,执行#echo $0,如果是login shell会输出 “-bash”;如果是non-login shell则会输出 “bash”。

五、Mac OS
在Mac OS中,启动一个新的终端窗口就是打开了一个交互式shell,在我的电脑上测试,没有修改配置的情况下,/etc/profile中有:

. /etc/bashrc

执行了/etc/bashrc

六、export命令
export命令设置的环境变量只是保存在内存中,当shell关闭后就失效了,下次启动会重新读取配置文件中的信息,unset命令同理。

之前启动jetty时突然开始报下面错误:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
2015-11-24 13:15:04.125:WARN:oeja.AnnotationParser:Problem processing jar entry com/ibm/icu/impl/data/LocaleElements_zh__PINYIN.class
java.lang.ArrayIndexOutOfBoundsException: 48188
at org.objectweb.asm.ClassReader.readClass(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.eclipse.jetty.annotations.AnnotationParser.scanClass(AnnotationParser.java:658)
at org.eclipse.jetty.annotations.AnnotationParser$2.processEntry(AnnotationParser.java:630)
at org.eclipse.jetty.webapp.JarScanner.matched(JarScanner.java:155)
at org.eclipse.jetty.util.PatternMatcher.matchPatterns(PatternMatcher.java:82)
at org.eclipse.jetty.util.PatternMatcher.match(PatternMatcher.java:64)
at org.eclipse.jetty.webapp.JarScanner.scan(JarScanner.java:78)
at org.eclipse.jetty.annotations.AnnotationParser.parse(AnnotationParser.java:642)
at org.eclipse.jetty.annotations.AnnotationParser.parse(AnnotationParser.java:651)
at org.eclipse.jetty.annotations.AnnotationConfiguration.parseWebInfLib(AnnotationConfiguration.java:341)
at org.eclipse.jetty.annotations.AnnotationConfiguration.configure(AnnotationConfiguration.java:99)
at org.eclipse.jetty.webapp.WebAppContext.configure(WebAppContext.java:441)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1229)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:699)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:467)
at org.mortbay.jetty.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:256)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:167)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:90)
at org.eclipse.jetty.server.Server.doStart(Server.java:262)
at org.mortbay.jetty.plugin.JettyServer.doStart(JettyServer.java:65)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
at org.mortbay.jetty.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:511)
at org.mortbay.jetty.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:364)
at org.mortbay.jetty.plugin.JettyRunMojo.execute(JettyRunMojo.java:516)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:355)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:155)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:216)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:160)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)

从错误信息中基本可以确认是maven里配置的jetty插件的问题,但单单一个数组越界错误很难确认具体是什么问题,百度48188也没什么有用的答案。

虽然一直报错但是对程序运行没影响,再加上项目在赶进度就一直没处理,今天闲下来便好好解决下这个问题。

确定是jetty问题了,就先换个jetty版本试试,从原来的

<version>8.1.4.v20120524</version>

换成

<version>9.3.6.v20151106</version>

再启动jetty,还是报错,可是错误信息变了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java.lang.RuntimeException: Error scanning entry com/ibm/icu/impl/data/LocaleElements_zh__PINYIN.class from jar file:///Users/ywq/.m2/com/ibm/icu/icu4j/2.6.1/icu4j-2.6.1.jar
at org.eclipse.jetty.annotations.AnnotationParser.parseJar(AnnotationParser.java:937)
at org.eclipse.jetty.annotations.AnnotationParser.parse(AnnotationParser.java:851)
at org.eclipse.jetty.annotations.AnnotationConfiguration$ParserTask.call(AnnotationConfiguration.java:163)
at org.eclipse.jetty.annotations.AnnotationConfiguration$1.run(AnnotationConfiguration.java:548)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
at java.lang.Thread.run(Thread.java:745)
Caused by:
java.lang.ArrayIndexOutOfBoundsException: 48188
at org.objectweb.asm.ClassReader.readClass(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.eclipse.jetty.annotations.AnnotationParser.scanClass(AnnotationParser.java:1004)
at org.eclipse.jetty.annotations.AnnotationParser.parseJarEntry(AnnotationParser.java:984)
at org.eclipse.jetty.annotations.AnnotationParser.parseJar(AnnotationParser.java:933)
at org.eclipse.jetty.annotations.AnnotationParser.parse(AnnotationParser.java:851)
at org.eclipse.jetty.annotations.AnnotationConfiguration$ParserTask.call(AnnotationConfiguration.java:163)
at org.eclipse.jetty.annotations.AnnotationConfiguration$1.run(AnnotationConfiguration.java:548)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
at java.lang.Thread.run(Thread.java:745)

原来之前一直没注意到的的WARN信息才是重点,查看Maven Dependencies下果然有个icu4j-2.6.1.jar,在Dependency Hierarchy下发现是jaxen 1.1.1中依赖了icu4j这个jar包,于是把jaxen换成最新的1.1.6版本,重启jetty,问题解决了。

百度时发现不少类似问题但没有答案,都可以参考这样的思路解决。

在终端中输入如下命令:

1
defaults write ~/Library/Preferences/com.apple.finder AppleShowAllFiles -bool true

true 改成 false 就可以不再显示隐藏文件

先看下面代码,是通过并发来计算文件大小:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.climbran.totalFileSize;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
* Created by ywq on 15/11/7.
*/

public class NaivelyConcurrentTotalFileSIze {
private long getTotalSizeOfFileInDir(ExecutorService service,final File file)
throws Exception{

if(file.isFile()) return file.length();

final File[] children = file.listFiles();
long total = 0;
if(children!=null) {
final List<Future<Long>> partialTotalFutures = new ArrayList<Future<Long>>();
for (final File child : children)
partialTotalFutures.add(service.submit(new Callable<Long>() {
@Override
public Long call() throws Exception {
return getTotalSizeOfFileInDir(service, child);
}
}));

for(final Future<Long> partoalTotalFuture : partialTotalFutures)
total+=partoalTotalFuture.get(100, TimeUnit.SECONDS);
}
return total;
}

public long getTotalSizeOfFile(final String fileName)
throws Exception {

final ExecutorService service = Executors.newFixedThreadPool(200);
try {
return getTotalSizeOfFileInDir(service,new File(fileName));
}finally {
service.shutdown();
}
}

public static void main(final String[] args)
throws Exception{

final long start = System.nanoTime();
final long totalSize = new NaivelyConcurrentTotalFileSIze().getTotalSizeOfFile("/Users");
final long end = System.nanoTime();
System.out.println("Total Size:" + totalSize);
System.out.println("Cost Time(Second):"+(end-start)/1.0e9);

}


}

看起来似乎没什么问题,但当扫描目录下文件层次较深时可能出来

Exception in thread "main" java.util.concurrent.TimeoutException

这是由于getTotalSiizeOfFilesInDir()中有阻塞线程的操作,当线程扫描其子目录时,改线程就会阻塞起来,当子目录数量不是很多时不会有什么问题,但是当目录层次很深且目录数大于线程数时,可能较外层的线程都被阻塞了,而子目录分配不到新的线程,从而进入死锁状态。

解决这个问题的一个方式是使深度优先遍历变为广度优先遍历,使每个任务返回当前目录下的文件(file)大小和该目录下的子目录(dir)集合,另一个方式是使用Fork-join API来管理线程,在Fork-join中任务被阻塞时,执行该任务的线程可以将任务挂起执行其他任务。

这里说的并发策略是指并发时的线程数和任务数。
我们将需要并发的任务分为io密集型和计算密集型,对于两种情况要分别对待

一、线程数
IO密集型
当一个线程执行io操作时,处理器核心会进行上下文切换,将该线程阻塞,执行其他就绪的线程,所以需要比处理器核心数更多的线程数,否则当线程阻塞时没有其他线程可以调用,从而浪费cpu时间。

计算密集型
当多个线程就绪时,处理器核心会频繁切换上下文,这种切换对性能损耗较大,所以将任务拆分后所有任务都是计算密集型的,则创建处理器数相同的线程数就可以了。

如果任务的阻塞时间大于执行时间(即阻塞时间大于50%)可认为是io密集型,否则是计算密集型,对于线程数有以下计算公式:

线程数=CPU核心数/(1-阻塞系数)

其中,阻塞系数=阻塞时间/执行时间

二、任务数
理想的状态是子任务数等于线程数就好了,并且我们希望任务拆分后各个子任务工作量是相等的。
但在实际开发中,将工作量均等分配到各个任务往往十分困难,所以一种简单粗暴的方式是使任务数比线程数更多。当任务足够多时,一个任务完成后立即切换下一个任务使所以线程一直繁忙,同时可以使用线程池来减少任务切换时线程创建和销毁的开销。

三、Demo

ConcurrentDemo.java
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.climbran.base;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
* Created by swe on 15/11/6.
*/

public class IOConcurrentDemo {

public void test(final int partitionsNum)
throws InterruptedException,ExecutionException{
//获取处理器核心数
final int numberOfCores = Runtime.getRuntime().availableProcessors();

final double blockingCoefficient = 0.99; //阻塞率
final int poolSize = (int)(numberOfCores/(1-blockingCoefficient));

System.out.println("Number of Cores avaliable is " + numberOfCores);
System.out.println("Pool size is " + poolSize);

final long start = System.nanoTime();
final List<Callable<Integer>> partitions = new ArrayList<Callable<Integer>>();
for(int i = 1; i<= partitionsNum;i++){
final int tmp = i;
partitions.add(new Callable<Integer>() {
public Integer call() throws Exception {
//并发任务,这里用Thread.sleep(2000)代替
Thread.sleep(2000);
return Integer.valueOf(tmp);
}
});
}
final ExecutorService executorPool = Executors.newFixedThreadPool(poolSize);
final List<Future<Integer>> valueOfStocks = executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);
final long end = System.nanoTime();
int sumValue = 0;
for(final Future<Integer> valueOfStock : valueOfStocks)
sumValue +=valueOfStock.get();
executorPool.shutdown();
System.out.println("value:"+sumValue);
System.out.println("Time (seconds) taken is " + (end - start)/1.0e9);
}

public static void main(final String[] args)
throws ExecutionException, InterruptedException, IOException {
new IOConcurrentDemo().test(1000);
}
}

这是一个简单的io密集型任务,运行后输出:

Number of Cores avaliable is 4
Pool size is 399
value:500500
Time (seconds) taken is 6.057576742

输出的500500说明所有线程都有正确执行到,可以将其中的延时函数替换为网页访问、文件读取等操作,有兴趣的朋友也可以测试以下顺序访问结果…
对于计算密集型可能还需要一个拆分任务数作为参数,在实现拆分策略时使用,具体就不在这展示了。

内存栅栏是指本地或者工作内存到主存间的拷贝动作。在程序运行过程中。所有变更会先在线程的寄存器或本地cache中完成,然后才会拷贝到主存以跨越内存栅栏。理解内存栅栏先看下面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class RaceCondition{
private static boolean done;

public static void main(final String[] args) throws InterruptedException{
new Thread(
public void run(){
int i = 0;
while(!done){ i++ ; }
System.out.println("Done!");
}
).start();
System.out.println("OS:" + System.getProperty("os.name"));
Thread.sleep(2000);
done = true;
System.out.println("flag done set to true");
}
}

如果在win7上运行

java RaceCondition

可能输出以下结果:

OS: Windows 7

flag done set to true

Done!

而在Mac上运行同样命令,则可能是:

OS: Mac OS X

flag done set to true

这是由于windows平台执行java 命令默认是以server模式,而Mac下默认是以client模式运行。
在server模式下,JIT编译器会对代码新线程里的while循环进行优化,新线程会从寄存器或本地cache中读取done的拷贝值,而不会去速度相对较慢的内存里读取。由于以上原因,主线程中对于done的变更在新线程中不可见。

为解决这个问题,可以使用volatile关键字,其作用是告知JIT编译器改变量可能被某个线程更改,不要对其进行优化。volatile可使对变量的每次读写都忽略本地cache直接对内存操作,但正是这样会使得每次访问变量都要跨越内存栅栏导致程序性能下降。

由于每个线程对volatile字段的访问都是独立处理的,所以volatile关键字无法保证读写操作的原子性,为解决这个问题,可以使用synchronized关键字标注的setter和getter方法,synchronized可使同步区块内跨越内存栅栏。

一、Sitemesh介绍
Sitemesh是一个网页布局框架,基于servlet中的Filter拦截器,在servlet(如spring)的拦截器外层拦截,对html文件进行装饰后再进入servlet,本文使用最新版本sitemesh3

二、Sitemesh配置
maven配置:

pom.xml
1
2
3
4
5
<dependency>
<groupId>org.sitemesh</groupId>
<artifactId>sitemesh</artifactId>
<version>3.0.0</version>
</dependency>

web.xml配置,注意sitemesh的filter必须配置在servlet的filter之前:
web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<web-app>
...

<filter>
<filter-name>sitemesh</filter-name>
<filter-class>org.sitemesh.config.ConfigurableSiteMeshFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

...
</web-app>

三、sitemsh3.xml配置文件详解
使用sitemesh3需要新建/WEB-INF/sitemesh3.xml
sitemesh3.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<sitemesh>
<!--默认情况下,sitemesh 只对 HTTP 响应头中 Content-Type 为 text/html 的类型进行拦截和装饰,我们可以添加更多的 mime 类型-->
<mime-type>text/html</mime-type>
<mime-type>application/xhtml+xml</mime-type>

<!-- 默认装饰器,当下面的路径都不匹配时,启用该装饰器进行装饰 -->
<mapping decorator="/default-decorator.html"/>

<!-- 指明满足path的页面,将被decorator所装饰,可配置多个 -->
<mapping path="/*" decorator="/WEB-INF/views/decorators/decorator.html" />

<!-- 指明满足path的页面,将被排除,不被装饰,可配置多个 -->
<mapping path="/exclude.jsp*" exclue="true" />
</sitemesh>

四、使用Sitemesh
根据上面的配置,首先新建在/WEB-INF/views下新建decorator.html
/WEB-INF/views/decorator.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>
<sitemesh:write property='title' /> - hello world
</title>
<sitemesh:write property='head' />
</head>
<body>
<header>header</header>
<hr />
<sitemesh:write property='title' /><br />
<sitemesh:write property='body' />
<hr />
<footer>footer</footer>
</body>
</html>

再新建被装饰的html页面demo.html:

demo.html
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>demo title</title>
demo head
</head>
<body>
demo content
</body>
</html>

sitemesh会通过sitemesh:write标签将property在被装饰页面demo.html中相应的标签抽取出来插入sitemesh:write所在位置,上面两个html页面最终通过sitemesh输出的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>demo title - hello world</title>
demo head
</head>
<body>
<header>header</header>
<hr />
demo title<br />
demo content
<hr />
<footer>footer</footer>
</body>
</html>

五、自定义标签的property属性
Sitemesh3标签默认只提供了body,title,head等property属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.sitemesh.SiteMeshContext;
import org.sitemesh.content.ContentProperty;
import org.sitemesh.content.tagrules.TagRuleBundle;
import org.sitemesh.content.tagrules.html.ExportTagToContentRule;
import org.sitemesh.tagprocessor.State;

public class MyTagRuleBundle implements TagRuleBundle {
@Override
public void install(State defaultState, ContentProperty contentProperty,
SiteMeshContext siteMeshContext)
{

defaultState.addRule("sidebar", new ExportTagToContentRule(siteMeshContext,contentProperty.getChild("sidebar"), false));

}

@Override
public void cleanUp(State defaultState, ContentProperty contentProperty,
SiteMeshContext siteMeshContext)
{

}
}

然后在sitemesh3.xml中增加以下代码:
1
2
3
<content-processor>
<tag-rule-bundle class="com.lt.common.ext.sitemesh3.MyTagRuleBundle" />
</content-processor>

这样就可以在装饰器文件中使用下面代码:

<sitemesh:write property='sidebar' />

window.setTimeout(code,millisec)
window.setTimeout(code,millisec,lang)

code:传入函数或代码
millisec:时间,单位毫秒,1秒=1000毫秒
lang:脚本语言类型(JScript | VBScript | JavaScript)

window.setTimeout(“alert(1);”,2000)

window.setTimeout(function(){alert(“hello”)},2000)

由于javascript是单线程,所以下面函数会进入死循环:

1
2
3
4
5
flag = true;
window.setTimeout(function(){flag = false;},3000);
while(flag){
}
alert(ok);

在一个新搭建的spring测试框架中,执行ajax时没有返回,发现是jquery的js文件加载时返回404错误,猜测是spring配置文件中缺少静态文件路径的相关配置,查看后果然是如此。查找后了解到静态文件有3种配置方式:

####方法一:在web.xml中增加静态资源的url
该方法是通过web容器来访问静态资源,需要写再DispatcherServlet前面,让web容器的servlet在spring之前先拦截,这种那个方式不能访问WEB-INF目录,只能访问web根目录下的路径(webapp、webContent等):

1
2
3
4
5
6
7
8
9
10
11
12
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>

各种常见web容器的servlet名称如下:

Tomcat、Jetty、JBoss、GlassFish : “default”

Google App Engine : “_ah_default”

Resin : “resin-file”

WebLogic : “FileServlet”

WebSphere : “SimpleFileServlet”

####方法二:Spring MVC中配置
spring3.0.4后提供的方式

1
<mvc:resources mapping="/images/**" location="/images/" />

将mapping的url映射到ResourceHttpRequestHandler进行处理,location指定静态资源位置,可以使webapp根目录下,也可以使WEB-INF目录和jar包里面,这样可以把静态资源压缩到jar包种

####方法三:Spring MVC中配置使用

1
<mvc:default-servlet-handler/>

这种方式只需要一行代码,但也不能访问WEB-INF目录下

方法二和方法三还需要同时配置

<mvc:annotation-driven/>