Hello Coder


  • 首页

  • 归档

  • 标签

  • 搜索
close

Spring Task定时任务

发表于 2017-11-17

Spring3.0以后引入的定时任务工具:spring task,可以将它比作一个轻量级的Quartz,而且使用起来很简单,除spring相关的包外不需要额外的包,而且支持注解和配置文件两种。spring通过接口TaskExecutor和TaskScheduler这两个接口的方式为异步定时任务提供了一种抽象。

XML配置

Spring提供了task命名空间,让配置定时任务非常简单。

1
2
3
4
5
6
7
<context:component-scan base-package="com.zsr.test.springTask"/>
<task:scheduler id="scheduler" pool-size="10" />
<task:scheduled-tasks scheduler="scheduler">
<task:scheduled ref="taskJob" method="execute" cron="0 * * * * ?"/>
</task:scheduled-tasks>

说明:ref参数指定的即任务类,method指定的即需要运行的方法,cron指定cronExpression表达式

注意:<task:schedule-tasks scheduler="scheduler"> 这里的scheduler必须显式指定,否则它只会使用默认的值,默认为单线程的。

任务Job

1
2
3
4
5
6
7
@Component
public class TaskJob {
public void execute() {
System.out.println("任务进行中.");
}
}

TaskExecutor

1
2
3
4
public interface TaskExecutor extends Executor {
@Override
void execute(Runnable task);
}

task:executor会注册一个ThreadPoolTaskExecutor执行器,可以使用它的相关属性来配置该执行器。

1
2
3
4
5
<task:executor
id="executor"
pool-size="10"
queue-capacity="100"
rejection-policy="CALLER_RUNS"/>
  • pool-size:线程池大小,如果只是设置了一个值。则corePoolsize和maxPoolSize都是这个值。
  • queue-capacity:队列大小
  • rejection-policy: 队列满的时候,使用的拒绝策略。
阅读全文 »

tomcat filter分析

发表于 2017-11-15

Filter

  • Filter是servlet规范中定义的java web组件, 在所有支持java web的容器中都可以使用
  • Filter和FilterChain密不可分, Filter可以实现依次调用正是因为有了FilterChain

img

Filter接口

1
2
3
4
5
6
7
8
9
10
11
12
13
// Filter.class
public interface Filter {
// 容器创建的时候调用, 即启动tomcat的时候调用
public void init(FilterConfig filterConfig) throws ServletException;
// 由FilterChain调用, 并且传入FilterChain本身, 最后回调FilterChain的doFilter()方法
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
// 容器销毁的时候调用, 即关闭tomcat的时候调用
public void destroy();
}

FilterChain接口

1
2
3
4
5
6
7
8
// FilterChain.class
public interface FilterChain {
// 由Filter.doFilter()中的chain.doFilter调用
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}

Tomcat filter源码

创建FilterChain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// StandardWrapperValve.class
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// .....省略......
MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC)
dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// Create the filter chain for this request
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
// .....省略......
// 执行过滤器链doFilter()
filterChain.doFilter(request.getRequest(), response.getResponse());
// .....省略......
}

每次请求过来都会创建一个过滤器链(filterChain),并把待执行的servlet对象存放到过滤器链中。对于每个url,对应的filter个数都是不固定的,filterchain需要保存每个请求所对应的一个filter数组,以及调用到的filter的position,以便继续向下调用filter

阅读全文 »

中文乱码

发表于 2017-10-26

乱码原因

字符在保存时的编码格式如果和要显示的编码格式不一样的话,就会出现乱码问题。Java在运行期一律以Unicode来存储字符,这样有利的支持了多语言环境。Java读文件的时候会用到系统默认的编码来解码文件。所以在用FileInputStream类读取文件可以指定编码读取。 

JAVA在网络传输中使用的编码是ISO-8859-1,故在输出时需要进行转化,如:

1
String str=new String(str.getBytes("开发环境编码"),"ISO-8859-1");

经过网络编码后的中文,要正确显示在页面上必须要用类似于:

1
Stirng str=new String(str.getBytes("ISO-8859-1"),"开发环境编码");

案例

浏览器请求服务,服务器返回响应

1
http request——————————>http response

request请求过程:

  • 浏览器http响应增加一个Content-Type:text/html; charset=utf-8,发送请求
  • Tomcat接受请求(不设置URIEncodng)
  • java服务端处理请求
1
2
3
4
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("name: "+newString(req.getParameter("name").getBytes("iso8859-1"),"utf-8"));
}

编码转换过程:

  • 浏览器对字符做百分号编码
  • Tomcat解百分号编码
  • ISO8859-1编码转Java内码
  • Java内码转ISO8859-1编码
  • 把字节数组当成utf-8编码转Java内码
  • Java内码转输出编码

    注:

1
2
String.getBytes("utf-8"): 把java内码转成utf-8编码
new String(bytes[],"utf-8"): 把字节数组当成utf-8编码转成java内码

浏览器对字符做百分号编码

对于[中国]这两个字符,他们的utf-8编码分别是0xE4B8AD、0xE59BBD,每个字符占用三个字节。经过百分号编码后变成了%E4%B8%AD、%E5%9B%BD,可以看到百分号编码对原始编码是无损的,它只是把原始字节变成了%+原始字节的16进制表示。比如字节0xE4,转成百分号编码为%E4,有一个字节变成了三个字节。

Tomcat解码百分号编码

解码百分号编码也很简单,其实就是去掉百分号,然后将百分号后的两个字节合并成一个字节,如百分号编码%E4,解码后变为字节0xE4。到这一步“中国”这两个字符就变成了0xE4B8AD、0xE59BBD。

ISO8859-1转java内码

ISO8859-1与ascii一样都是单字节字符集,不同的是它把最高位利用起来了,增加了一些西方字符(如±、÷等字符)

这里说的java内码是java程序运行时,在内存中存储字符的编码,用的是unicode标准中定义的utf-16编码

utf-16是把unicode字符编码成2字节或4字节。ISO8859-1是8位长的单字节字符编码,所以utf-16编码和ISO8859-1编码是不兼容的,但是utf-16包含ISO8859-1中的所有字符.

Tomcat解码百分号编码后,[中国]这两个字符在内存中是这样的0xE4B8ADE59BBD,正好六个字节。这其实是这两个字符的utf-8编码序列,但是由于并没有告诉tomcat这是什么字符编码序列,所以tomcat就认为这是一个ISO8859-1编码序列,并把它告诉了java程序,java程序要做的就是把这个字节序列按照ISO8859-1转换成utf-16,转换成功后的对应关系是这样的:

ISO8859-1 0xE4 0xB8 0xAD 0xE5 0x9B 0xBD
UTF-16 0x00E4 0x00B8 0x00AD 0x00E5 0x009B 0x00BD

可以看到原本的两个字符,在java中变成了六个字符;原本的六个字节,在java中变成了12个字节。

Java内码转换成ISO8859-1编码

1
req.getParameter("name").getBytes("iso8859-1")

getBytes(“iso8859-1”)这个方法,也就是把utf-16转换成ISO8859-1。有第三步(ISO8859-1转java内码)中的对应表格可以看到,utf-16转ISO8859-1只需要把每个字符前面的8位0去掉就可以了,转换成功后俩个字符就又变成了0xE4B8ADE59BBD。虽然两次转换过程中,对字节的解释是错误的,但是并没有丢失原始字节信息。

把字节数组当成utf-8编码转java内码

1
new String(0xE4B8ADE59BBD,"utf-8")

因为字节数组本来就是utf-8编码,所以按照utf-8来转码肯定是没问题的,转换成功后的对应关系是这一样的:

UTF-8 0xE4B8AD 0xE59BBD
UTF-16 0x4E2D 0x56FD

到这里[中国]这两个字符在java内部才得到了正确的表示。

Java内码转输出编码

1
System.out.println("中国")

现在[中国]这两个字符在java内部用utf-16得到了正确的表示,剩下的最后一步就是对外输出,也就是对外翻译的过程,我们这里用的java自带的println方法,这个方法会根据当前平台的自身编码进行输出,比如你的平台环境是中文,那输出的可能就是GBK编码。

Unicode和UTF-8字符编码

发表于 2017-10-25

概念

  • Unicode: (Universal Code 统一码)是一个囊括了世界上所有字符的字符集,其中每一个字符都对应有唯一的编码值(code point),然而它并不是一种什么编码格式,仅仅是字符集而已

    Unicode 字符不管具体怎么编码和存储,可以用 UTF-8、UTF-16来编码

    UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因

  • UTF-8: (Unicode Transformation Format)就是在互联网上使用最广的一种Unicode的实现方式(怎么编码,怎么存储)。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32 (字符用四个字节表示)

    UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度

1
UTF-8 and Unicode cannot be compared. UTF-8 is an encoding used to translate numbers into binary data. Unicode is a character set used to translate characters into numbers.

UTF-8编码

字符 Unicode UTF-8 GBK
中 01001110 00101101(4e2d) 01001110 00101101(e4b8ad) 11010110 11010000(d6d0)

UTF-8常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4个字节.如果要传输的文本包含大量英文字符,用UTF-8编码就能节省空间:

1
2
3
4
5
6
7
8
9
10
>>> a = u"中"
>>> a
u'\u4e2d'
>>> b = a.encode("utf-8")
>>> b
'\xe4\xb8\xad'
>>> c = a.encode("gbk")
>>> c
'\xd6\xd0'
>>>

「中」用 GBK 编码转后就是用2个字节表示,用 UTF-8 编码就是 3 个字节,同一个字符用不同的编码方式占用的字节长度可能不一。

阅读全文 »

SimpleDateFormat线程安全

发表于 2017-10-24

SimpleDateFormat 是 Java 中非常常用的一个类,用于解析和格式化日期字符串,但是 SimpleDateFormat 在多线程环境中并不是线程安全的。

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.text.SimpleDateFormat;
public class Test {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
private static class Task implements Runnable {
public void run() {
try {
System.out.println(sdf.parse("2016-03-21 12:00:00").getTime());
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Task task = new Task();
Thread t1 = new Thread(task);
t1.start();
Thread t2 = new Thread(task);
t2.start();
}
}

运行结果报错,如下:

1
2
3
4
5
6
7
8
java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1110)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1793)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
阅读全文 »
1…789…31
David

David

Develop Notes

155 日志
37 标签
GitHub Weibo
© 2016 - 2020 David
由 Hexo 强力驱动
主题 - NexT.Pisces