随笔-314  评论-209  文章-0  trackbacks-0
 
     摘要:  pattern比对    (grep)字符串 命令 /pattern/修饰词命令=~          表示比對符合pattern!~        &nbs...  阅读全文
posted @ 2012-03-10 15:44 xzc 阅读(8211) | 评论 (0)编辑 收藏

将ociuldr.exe复制到H:\oracle\product\10.2.0\db_1\BIN下, 或者path中的某个文件夹中

用法:
C:\Documents and Settings\tgm>ociuldr

Usage: ociuldr user=... query=... field=... record=... file=...
(@) Copyright Lou Fangxin 2004/2005, all rights reserved.
Notes:
       -si   = enable logon as SYSDBA
       user = username/password@tnsname
       sql   = SQL file name,one sql per file, do not include ";"
       query = select statement
       field = seperator string between fields
       record= seperator string between records
       file = output file name(default: uldrdata.txt)
       read = set DB_FILE_MULTIBLOCK_READ_COUNT at session level
       sort = set SORT_AREA_SIZE & SORT_AREA_RETAINED_SIZE at session level (UNIT:MB)
       hash = set HASH_AREA_SIZE at session level (UNIT:MB)
       serial= set _serial_direct_read to TRUE at session level
       trace = set event 10046 to given level at session level
       table = table name in the sqlldr control file
       mode = sqlldr option, INSERT or APPEND or REPLACE or TRUNCATE
       log   = log file name, prefix with + to append mode
       long = maximum long field size
       array = array fetch size
        head = 第一行是否为字段名(head=on), 默认为off

for field and record, you can use '0x' to specify hex character code,
\r=0x0d \n=0x0a |=0x7c ,=0x2c \t=0x09

一、导出数据

d:\>ociuldr user=test/test@acf query="select * from test" file=test.txt table=test

二、查看导出内容

1,a
2,b
3,c
4,d
5,e
6,f

三、查看自动生成的控制文件

--
-- Generated by OCIULDR
--
OPTIONS(BINDSIZE=8388608,READSIZE=8388608,ERRORS=-1,ROWS=50000)
LOAD DATA
INFILE 'test.txt' "STR X'0a'"
INTO TABLE test
FIELDS TERMINATED BY X'2c' TRAILING NULLCOLS 
(
ID CHAR(40),
NAME CHAR(10)
)

四、可以尝试使用这个控制文件将数据加载到数据库中

d:\>sqlldr test/test@acf control=test_sqlldr.ctl

这样数据就加载到数据库中。对于大数据库表的导出ociuldr工具还支持按照不同的批量导出数据,这通过一个参数batch来实现,默认一个batch是50万条记录,如果不指定batch为2就表示100万条记录换一个文件,默认这个选项值是0,就是指不生成多个文件。

在指定batch选项后,需要指定file选项来定义生成的文件名,文件名中间需要包含“%d”字样,在生成文件时,“%d”会打印成序号,请看以下一个测试:

D:\>ociuldr user=test/test@acf query="select * from test" batch=1 file=test_%d.txt table=test

刚才测试了一下,果然是强悍, 用spool按要求导出10万条记录要好几分钟, 用ociuldr导出来用了一秒,或许一秒都不到, NB!

posted @ 2012-03-03 15:39 xzc 阅读(5994) | 评论 (1)编辑 收藏
在ajax应用流行时,有时我们可能为了降低服务器的负担,把动态内容生成静态html页面或者是xml文件,供客户端访问!但是在我们的网站或系统中往住页面中某些部分是在后台没有进行修改时,其内容不会发生变化的。但是页面中也往往有部分内容是动态的更新的,比如一个新闻页面,新闻内容往往生成了之后就是静态的,但是新闻的最新评论往往是变化的,在这个时候有几种解决方案:

1、重新生成该静态页面,优点是用户访问时页面上的肉容可以实现全静态,不与服务器程序及数据库后端打交道!缺点是每次用户对页面任何部分更新都必须重新生成。

2、js调用请求动态内容,优点是静态页面只生成一次,动态部分才动态加载,却点是服务器端要用输出一段js代码并用js代码输出网页内容,也不利于搜索引擎收录。

3、ajax调用动态内容,和js基本相似,只是与服务器交互的方式不同!并且页面显示不会受到因动态调用速度慢而影响整个页面的加载速度!至于ajax不利于搜索收录,当然在《ajax in acation》等相关书籍中也介绍有变向的解决方案!

4、在服务器端ssl动态内容,用服务器端优化及缓存解决是时下最流行的方法!

对于第二种和第三种方法都是我最青睐的静态解决方法,适合以内容为主的中小型网站。那么在有时候可能会有js读取url参数的需求,事实证明的确也有很多时候有这种需求,特别是在胖客户端的情况下!以前也写过这样的代码,其实原理很简单就是利用javascript接口提供location对像得到url地址,然后通过分析url以取得参数,以下是我收录的一些优秀的url参数读取代码:

一、字符串分割分析法。
这里是一个获取URL+?带QUESTRING参数的JAVASCRIPT客户端解决方案,相当于asp的request.querystring,PHP的$_GET
函数:

<script>
function GetRequest()
{
var url = location.search; //获取url中"?"符后的字串
var theRequest = new Object();
if(url.indexOf("?") != -1)
{
  var str = url.substr(1);
    strs = str.split("&");
  for(var i = 0; i < strs.length; i ++)
    {
     theRequest[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
    }
}
return theRequest;
}
</script>

然后我们通过调用此函数获取对应参数值:

<script>
var Request=new Object();
Request=GetRequest();
var 参数1,参数2,参数3,参数N;
参数1=Request['参数1'];
参数2=Request['参数2'];
参数3=Request['参数3'];
参数N=Request['参数N'];
</script>


以此获取url串中所带的同名参数

二、正则分析法。

function     GetQueryString(name)   
{   
     var     reg     =   new   RegExp("(^|&)"+     name     +"=([^&]*)(&|$)");   
     var     r     =     window.location.search.substr(1).match(reg);   
     if     (r!=null)   return     unescape(r[2]);   return   null;   
}   
alert(GetQueryString("参数名1"));   
alert(GetQueryString("参数名2"));   
alert(GetQueryString("参数名3"));
posted @ 2011-12-12 17:15 xzc 阅读(5060) | 评论 (0)编辑 收藏

转自:http://blog.csdn.net/wh62592855/article/details/4988336
例如说吧,对DEPTNO 10中的每个员工,确定聘用他们的日期及聘用下一个员工(可能是其他部门的员工)的日期之间相差的天数。

SQL> select ename,hiredate,deptno from emp order by hiredate;

ENAME      HIREDATE            DEPTNO
---------- --------------- ----------
SMITH      17-DEC-80               20
ALLEN      20-FEB-81               30
WARD       22-FEB-81               30
JONES      02-APR-81               20
BLAKE      01-MAY-81               30
CLARK      09-JUN-81               10
TURNER     08-SEP-81               30
MARTIN     28-SEP-81               30
KING       17-NOV-81               10
JAMES      03-DEC-81               30
FORD       03-DEC-81               20

ENAME      HIREDATE            DEPTNO
---------- --------------- ----------
MILLER     23-JAN-82               10
SCOTT      19-APR-87               20
ADAMS      23-MAY-87               20

14 rows selected.

 

SQL> select ename,hiredate,next_hd,
  2  next_hd-hiredate diff
  3  from
  4  (
  5  select deptno,ename,hiredate,
  6  lead(hiredate) over(order by hiredate) next_hd
  7  from emp
  8  )
  9  where deptno=10;

ENAME      HIREDATE        NEXT_HD               DIFF
---------- --------------- --------------- ----------
CLARK      09-JUN-81       08-SEP-81               91
KING       17-NOV-81       03-DEC-81               16
MILLER     23-JAN-82       19-APR-87             1912

这里的LEAD OVER非常有用,它能够访问“未来的”行(“未来的”行相对于当前行,由ORDER BY子句决定)。这种无需添加联接就能够访问当前行附近行的功能,提高了代码的可读性和有效性。在采用窗口函数时,一定要记住,它在WHERE子句之后求值,因此在该解决方案中,需要使用内联视图。如果把对DEPTNO的筛选移到内联视图,则结果会发生改变(仅考虑了DETPNO 10中的HIREDATE)。

所以下面的结果是错误的:

SQL> select ename,hiredate,next_hd,
  2  next_hd-hiredate diff
  3  from
  4  (
  5  select deptno,ename,hiredate,
  6  lead(hiredate) over(order by hiredate) next_hd
  7  from emp
  8  where deptno=10
  9  );

ENAME      HIREDATE        NEXT_HD               DIFF
---------- --------------- --------------- ----------
CLARK      09-JUN-81       17-NOV-81              161
KING       17-NOV-81       23-JAN-82               67
MILLER     23-JAN-82

 

对于ORACLE的LEAD和LAG函数还需要特别注意,它们的结果中可能会有重复。在上面的例子中表EMP内不包含重复的HIREDATE,所以“看起来”似乎没有什么问题。下面我们向表中插入4个重复值来看看

SQL> insert into emp(empno,ename,deptno,hiredate)
  2  values(1,'a',10,to_date('17-NOV-1981'));

1 row created.

SQL> insert into emp(empno,ename,deptno,hiredate)
  2  values(2,'b',10,to_date('17-NOV-1981'));

1 row created.

SQL> insert into emp(empno,ename,deptno,hiredate)
  2  values(3,'c',10,to_date('17-NOV-1981'));

1 row created.

SQL> insert into emp(empno,ename,deptno,hiredate)
  2  values(4,'d',10,to_date('17-NOV-1981'));

1 row created.

SQL> select ename,hiredate
  2  from emp
  3  where deptno=10
  4  order by 2;

ENAME      HIREDATE
---------- ---------------
CLARK      09-JUN-81
b          17-NOV-81
c          17-NOV-81
a          17-NOV-81
d          17-NOV-81
KING       17-NOV-81
MILLER     23-JAN-82

7 rows selected.

现在还是用以前那个查询语句来试试

SQL> select ename,hiredate,next_hd,
  2  next_hd-hiredate diff
  3  from
  4  (
  5  select deptno,ename,hiredate,
  6  lead(hiredate) over(order by hiredate) next_hd
  7  from emp
  8  )
  9  where deptno=10;

ENAME      HIREDATE        NEXT_HD               DIFF
---------- --------------- --------------- ----------
CLARK      09-JUN-81       08-SEP-81               91
d          17-NOV-81       17-NOV-81                0
c          17-NOV-81       17-NOV-81                0
a          17-NOV-81       17-NOV-81                0
b          17-NOV-81       17-NOV-81                0
KING       17-NOV-81       03-DEC-81               16
MILLER     23-JAN-82       19-APR-87             1912

7 rows selected.

可以看到其中有4个员工的DIFF列值都是0,这是错误的,同一天聘用的所有员工都应该跟下一个聘用其他员工的HIREDATE进行计算。

幸运的是ORACLE针对这类情况提供了一个非常简单的措施:当调用LEAD函数时,可以给LEAD传递一个参数,以便准确的指定“未来的”行(是下一行?10行之后?等等)。

select ename,hiredate,next_hd,
next_hd-hiredate diff
from
(
select deptno,ename,hiredate,
lead(hiredate,cnt-rn+1) over(order by hiredate) next_hd
from
(
select deptno,ename,hiredate,
count(*) over(partition by hiredate) cnt,
row_number() over(partition by hiredate order by empno) rn
from emp
where deptno=10
)
)

 

posted @ 2011-12-09 10:40 xzc 阅读(4191) | 评论 (1)编辑 收藏
Java 定义的位运算(bitwise operators )直接对整数类型的位进行操作,这些整数类型包括long,int,short,char,and byte 。表4-2 列出了位运算: 
表4.2 位运算符及其结果

运算符 结果 
~ 按位非(NOT)(一元运算) 
& 按位与(AND) 
| 按位或(OR) 
^ 按位异或(XOR) 
>> 右移 
>>> 右移,左边空出的位以0填充 
运算符 结果 
<< 左移 
&= 按位与赋值 
|= 按位或赋值 
^= 按位异或赋值 
>>= 右移赋值 
>>>= 右移赋值,左边空出的位以0填充 
<<= 左移赋值 

续表

既然位运算符在整数范围内对位操作,因此理解这样的操作会对一个值产生什么效果是重要的。具体地说,知道Java 是如何存储整数值并且如何表示负数的是有用的。因此,在继续讨论之前,让我们简短概述一下这两个话题。

所有的整数类型以二进制数字位的变化及其宽度来表示。例如,byte 型值42的二进制代码是00101010 ,其中每个位置在此代表2的次方,在最右边的位以20开始。向左下一个位置将是21,或2,依次向左是22,或4,然后是8,16,32等等,依此类推。因此42在其位置1,3,5的值为1(从右边以0开始数);这样42是21+23+25的和,也即是2+8+32 。

所有的整数类型(除了char 类型之外)都是有符号的整数。这意味着他们既能表示正数,又能表示负数。Java 使用大家知道的2的补码(two’s complement )这种编码来表示负数,也就是通过将与其对应的正数的二进制代码取反(即将1变成0,将0变成1),然后对其结果加1。例如,-42就是通过将42的二进制代码的各个位取反,即对00101010 取反得到11010101 ,然后再加1,得到11010110 ,即-42 。要对一个负数解码,首先对其所有的位取反,然后加1。例如-42,或11010110 取反后为00101001 ,或41,然后加1,这样就得到了42。

如果考虑到零的交叉(zero crossing )问题,你就容易理解Java (以及其他绝大多数语言)这样用2的补码的原因。假定byte 类型的值零用00000000 代表。它的补码是仅仅将它的每一位取反,即生成11111111 ,它代表负零。但问题是负零在整数数学中是无效的。为了解决负零的问题,在使用2的补码代表负数的值时,对其值加1。即负零11111111 加1后为100000000 。但这样使1位太靠左而不适合返回到byte 类型的值,因此人们规定,-0和0的表示方法一样,-1的解码为11111111 。尽管我们在这个例子使用了byte 类型的值,但同样的基本的原则也适用于所有Java 的整数类型。

因为Java 使用2的补码来存储负数,并且因为Java 中的所有整数都是有符号的,这样应用位运算符可以容易地达到意想不到的结果。例如,不管你如何打算,Java 用高位来代表负数。为避免这个讨厌的意外,请记住不管高位的顺序如何,它决定一个整数的符号。

4.2.1 位逻辑运算符
位逻辑运算符有“与”(AND)、“或”(OR)、“异或(XOR )”、“非(NOT)”,分别用“&”、“|”、“^”、“~”表示,4-3 表显示了每个位逻辑运算的结果。在继续讨论之前,请记住位运算符应用于每个运算数内的每个单独的位。
表4-3 位逻辑运算符的结果 
A 0 1 0 1 B 0 0 1 1 A | B 0 1 1 1 A & B 0 0 0 1 A ^ B 0 1 1 0 ~A 1 0 1 0 

按位非(NOT)

按位非也叫做补,一元运算符NOT“~”是对其运算数的每一位取反。例如,数字42,它的二进制代码为:

00101010 

经过按位非运算成为

11010101 

按位与(AND)

按位与运算符“&”,如果两个运算数都是1,则结果为1。其他情况下,结果均为零。看下面的例子:

00101010 42 &00001111 15 

00001010 10 

按位或(OR)

按位或运算符“|”,任何一个运算数为1,则结果为1。如下面的例子所示:

00101010 42 | 00001111 15 

00101111 47 

按位异或(XOR)

按位异或运算符“^”,只有在两个比较的位不同时其结果是 1。否则,结果是零。下面的例子显示了“^”运算符的效果。这个例子也表明了XOR 运算符的一个有用的属性。注意第二个运算数有数字1的位,42对应二进制代码的对应位是如何被转换的。第二个运算数有数字0的位,第一个运算数对应位的数字不变。当对某些类型进行位运算时,你将会看到这个属性的用处。

00101010 42 ^ 00001111 15 

00100101 37
位逻辑运算符的应用

下面的例子说明了位逻辑运算符:

// Demonstrate the bitwise logical operators.
class BitLogic {
public static void main(String args[]) {


String binary[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" 

};
int a = 3; // 0 + 2 + 1 or 0011 in binary
int b = 6; // 4 + 2 + 0 or 0110 in binary
int c = a | b;
int d = a & b; 
int e = a ^ b; 
int f = (~a & b) | (a & ~b);
int g = ~a & 0x0f; 


System.out.println(" a = " + binary[a]);
System.out.println(" b = " + binary[b]);
System.out.println(" a|b = " + binary[c]);
System.out.println(" a&b = " + binary[d]);
System.out.println(" a^b = " + binary[e]);
System.out.println("~a&b|a&~b = " + binary[f]);
System.out.println(" ~a = " + binary[g]);


}
}


在本例中,变量a与b对应位的组合代表了二进制数所有的 4 种组合模式:0-0,0-1,1-0 ,和1-1 。“|”运算符和“&”运算符分别对变量a与b各个对应位的运算得到了变量c和变量d的值。对变量e和f的赋值说明了“^”运算符的功能。字符串数组binary 代表了0到15 对应的二进制的值。在本例中,数组各元素的排列顺序显示了变量对应值的二进制代码。数组之所以这样构造是因为变量的值n对应的二进制代码可以被正确的存储在数组对应元素binary[n] 中。例如变量a的值为3,则它的二进制代码对应地存储在数组元素binary[3] 中。~a的值与数字0x0f (对应二进制为0000 1111 )进行按位与运算的目的是减小~a的值,保证变量g的结果小于16。因此该程序的运行结果可以用数组binary 对应的元素来表示。该程序的输出如下:

a = 0011 b = 0110 a|b = 0111 a&b = 0010 a^b = 0101 ~a&b|a&~b = 0101 ~a = 1100 

4.2.2 左移运算符
左移运算符<<使指定值的所有位都左移规定的次数。它的通用格式如下所示:

value << num
这里,num 指定要移位值value 移动的位数。也就是,左移运算符<<使指定值的所有位都左移num位。每左移一个位,高阶位都被移出(并且丢弃),并用0填充右边。这意味着当左移的运算数是int 类型时,每移动1位它的第31位就要被移出并且丢弃;当左移的运算数是long 类型时,每移动1位它的第63位就要被移出并且丢弃。

在对byte 和short类型的值进行移位运算时,你必须小心。因为你知道Java 在对表达式求值时,将自动把这些类型扩大为 int 型,而且,表达式的值也是int 型。对byte 和short类型的值进行移位运算的结果是int 型,而且如果左移不超过31位,原来对应各位的值也不会丢弃。但是,如果你对一个负的byte 或者short类型的值进行移位运算,它被扩大为int 型后,它的符号也被扩展。这样,整数值结果的高位就会被1填充。因此,为了得到正确的结果,你就要舍弃得到结果的高位。这样做的最简单办法是将结果转换为byte 型。下面的程序说明了这一点:

// Left shifting a byte value.
class ByteShift {


public static void main(String args[]) {
byte a = 64, b;
int i; 


i = a << 2;
b = (byte) (a << 2); 


System.out.println("Original value of a: " + a);
System.out.println("i and b: " + i + " " + b);
}
}


该程序产生的输出下所示:

Original value of a: 64
i and b: 256 0 


因变量a在赋值表达式中,故被扩大为int 型,64(0100 0000 )被左移两次生成值256 (10000 0000 )被赋给变量i。然而,经过左移后,变量b中惟一的1被移出,低位全部成了0,因此b的值也变成了0。

既然每次左移都可以使原来的操作数翻倍,程序员们经常使用这个办法来进行快速的2 的乘法。但是你要小心,如果你将1移进高阶位(31或63位),那么该值将变为负值。下面的程序说明了这一点:

// Left shifting as a quick way to multiply by 2.
class MultByTwo {


public static void main(String args[]) {
int i;
int num = 0xFFFFFFE; 


for(i=0; i<4; i++) {
num = num << 1; 
System.out.println(num);


}
}
这里,num 指定要移位值value 移动的位数。也就是,左移运算符<<使指定值的所有位都左移num位。每左移一个位,高阶位都被移出(并且丢弃),并用0填充右边。这意味着当左移的运算数是int 类型时,每移动1位它的第31位就要被移出并且丢弃;当左移的运算数是long 类型时,每移动1位它的第63位就要被移出并且丢弃。

在对byte 和short类型的值进行移位运算时,你必须小心。因为你知道Java 在对表达式求值时,将自动把这些类型扩大为 int 型,而且,表达式的值也是int 型。对byte 和short类型的值进行移位运算的结果是int 型,而且如果左移不超过31位,原来对应各位的值也不会丢弃。但是,如果你对一个负的byte 或者short类型的值进行移位运算,它被扩大为int 型后,它的符号也被扩展。这样,整数值结果的高位就会被1填充。因此,为了得到正确的结果,你就要舍弃得到结果的高位。这样做的最简单办法是将结果转换为byte 型。下面的程序说明了这一点:

// Left shifting a byte value.
class ByteShift {


public static void main(String args[]) {
byte a = 64, b;
int i; 


i = a << 2;
b = (byte) (a << 2); 


System.out.println("Original value of a: " + a);
System.out.println("i and b: " + i + " " + b);
}
}


该程序产生的输出下所示:

Original value of a: 64
i and b: 256 0 


因变量a在赋值表达式中,故被扩大为int 型,64(0100 0000 )被左移两次生成值256 (10000 0000 )被赋给变量i。然而,经过左移后,变量b中惟一的1被移出,低位全部成了0,因此b的值也变成了0。

既然每次左移都可以使原来的操作数翻倍,程序员们经常使用这个办法来进行快速的2 的乘法。但是你要小心,如果你将1移进高阶位(31或63位),那么该值将变为负值。下面的程序说明了这一点:

// Left shifting as a quick way to multiply by 2.
class MultByTwo {


public static void main(String args[]) {
int i;
int num = 0xFFFFFFE; 


for(i=0; i<4; i++) {
num = num << 1; 
System.out.println(num);


}
}


该程序的输出如下所示:

536870908 
1073741816 
2147483632 
-32 


初值经过仔细选择,以便在左移 4 位后,它会产生-32。正如你看到的,当1被移进31 位时,数字被解释为负值。

4.2.3 右移运算符
右移运算符>>使指定值的所有位都右移规定的次数。它的通用格式如下所示:

value >> num 

这里,num 指定要移位值value 移动的位数。也就是,右移运算符>>使指定值的所有位都右移num位。下面的程序片段将值32右移2次,将结果8赋给变量a: 

int a = 32;
a = a >> 2; // a now contains 8 


当值中的某些位被“移出”时,这些位的值将丢弃。例如,下面的程序片段将35右移2 次,它的2个低位被移出丢弃,也将结果8赋给变量a: 

int a = 35; 
a = a >> 2; // a still contains 8 


用二进制表示该过程可以更清楚地看到程序的运行过程:

00100011 35 
>> 2 
00001000 8 


将值每右移一次,就相当于将该值除以2并且舍弃了余数。你可以利用这个特点将一个整数进行快速的2的除法。当然,你一定要确保你不会将该数原有的任何一位移出。

右移时,被移走的最高位(最左边的位)由原来最高位的数字补充。例如,如果要移走的值为负数,每一次右移都在左边补1,如果要移走的值为正数,每一次右移都在左边补0,这叫做符号位扩展(保留符号位)(sign extension ),在进行右移操作时用来保持负数的符号。例如,–8 >> 1 是–4,用二进制表示如下:

11111000 –8 >>1 11111100 –4 

一个要注意的有趣问题是,由于符号位扩展(保留符号位)每次都会在高位补1,因此-1右移的结果总是–1。有时你不希望在右移时保留符号。例如,下面的例子将一个byte 型的值转换为用十六
进制表示。注意右移后的值与0x0f进行按位与运算,这样可以舍弃任何的符号位扩展,以便得到的值可以作为定义数组的下标,从而得到对应数组元素代表的十六进制字符。

// Masking sign extension.
class HexByte {
static public void main(String args[]) {


char hex[] = {
’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’, 
’8’, ’9’, ’a’, ’b’, ’c’, ’d’, ’e’, ’f’’ 


};
byte b = (byte) 0xf1; 


System.out.println("b = 0x" + hex[(b >> 4) & 0x0f] + hex[b & 0x0f]);}} 

该程序的输出如下:

b = 0xf1 

4.2.4 无符号右移
正如上面刚刚看到的,每一次右移,>>运算符总是自动地用它的先前最高位的内容补它的最高位。这样做保留了原值的符号。但有时这并不是我们想要的。例如,如果你进行移位操作的运算数不是数字值,你就不希望进行符号位扩展(保留符号位)。当你处理像素值或图形时,这种情况是相当普遍的。在这种情况下,不管运算数的初值是什么,你希望移位后总是在高位(最左边)补0。这就是人们所说的无符号移动(unsigned shift )。这时你可以使用Java 的无符号右移运算符>>> ,它总是在左边补0。

下面的程序段说明了无符号右移运算符>>> 。在本例中,变量a被赋值为-1,用二进制表示就是32位全是1。这个值然后被无符号右移24位,当然它忽略了符号位扩展,在它的左边总是补0。这样得到的值255被赋给变量a。

int a = -1; a = a >>> 24; 

下面用二进制形式进一步说明该操作:

11111111 11111111 11111111 11111111 int型-1的二进制代码>>> 24 无符号右移24位00000000 00000000 00000000 11111111 int型255的二进制代码

由于无符号右移运算符>>> 只是对32位和64位的值有意义,所以它并不像你想象的那样有用。因为你要记住,在表达式中过小的值总是被自动扩大为int 型。这意味着符号位扩展和移动总是发生在32位而不是8位或16位。这样,对第7位以0开始的byte 型的值进行无符号移动是不可能的,因为在实际移动运算时,是对扩大后的32位值进行操作。下面的例子说明了这一点:

// Unsigned shifting a byte value.
class ByteUShift {
static public void main(String args[]) {
进制表示。注意右移后的值与0x0f进行按位与运算,这样可以舍弃任何的符号位扩展,以便得到的值可以作为定义数组的下标,从而得到对应数组元素代表的十六进制字符。

// Masking sign extension.
class HexByte {
static public void main(String args[]) {


char hex[] = {
’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’, 
’8’, ’9’, ’a’, ’b’, ’c’, ’d’, ’e’, ’f’’ 


};
byte b = (byte) 0xf1; 


System.out.println("b = 0x" + hex[(b >> 4) & 0x0f] + hex[b & 0x0f]);}} 

该程序的输出如下:

b = 0xf1 

4.2.4 无符号右移
正如上面刚刚看到的,每一次右移,>>运算符总是自动地用它的先前最高位的内容补它的最高位。这样做保留了原值的符号。但有时这并不是我们想要的。例如,如果你进行移位操作的运算数不是数字值,你就不希望进行符号位扩展(保留符号位)。当你处理像素值或图形时,这种情况是相当普遍的。在这种情况下,不管运算数的初值是什么,你希望移位后总是在高位(最左边)补0。这就是人们所说的无符号移动(unsigned shift )。这时你可以使用Java 的无符号右移运算符>>> ,它总是在左边补0。

下面的程序段说明了无符号右移运算符>>> 。在本例中,变量a被赋值为-1,用二进制表示就是32位全是1。这个值然后被无符号右移24位,当然它忽略了符号位扩展,在它的左边总是补0。这样得到的值255被赋给变量a。

int a = -1; a = a >>> 24; 

下面用二进制形式进一步说明该操作:

11111111 11111111 11111111 11111111 int型-1的二进制代码>>> 24 无符号右移24位00000000 00000000 00000000 11111111 int型255的二进制代码

由于无符号右移运算符>>> 只是对32位和64位的值有意义,所以它并不像你想象的那样有用。因为你要记住,在表达式中过小的值总是被自动扩大为int 型。这意味着符号位扩展和移动总是发生在32位而不是8位或16位。这样,对第7位以0开始的byte 型的值进行无符号移动是不可能的,因为在实际移动运算时,是对扩大后的32位值进行操作。下面的例子说明了这一点:

// Unsigned shifting a byte value.
class ByteUShift {
static public void main(String args[]) {
int b = 2;
int c = 3; 


a |= 4;
b >>= 1; 
c <<= 1; 
a ^= c;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);


}
}


该程序的输出如下所示:

a = 3 
b = 1 
c = 6
posted @ 2011-11-04 11:56 xzc 阅读(266) | 评论 (1)编辑 收藏
  1. import java.net.*;    
  2. String   key=URLEncoder.encode("中文key","GBK");   
  3. String   value=URLEncoder.encode("中文value","GBK");   
  4. Cookie   cook=new Cookie(key,value);        
  5. String   key=cook.getName(),value=cook.getValue();      
  6. key=URLDecoder.decode(key,"GBK");      
  7. value=URLDecoder.decode(value,"GBK");   



 

String value = java.net.URLEncoder.encode("中文","utf-8");

Cookie cookie = new Cookie("chinese_code",value);

cookie.setMaxAge(60*60*24*6);

response.addCookie(cookie);

 

 

 

encode() 只有一个参数的已经过时了,现在可以设置编码格式, 取cookie值的时候 也不用解码了。

 

posted @ 2011-10-03 11:29 xzc 阅读(6362) | 评论 (3)编辑 收藏
如下一段配置,熟悉DWR的再熟悉不过了:
<servlet>
   <servlet-name>dwr-invoker</servlet-name>
   <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
   <init-param>
    <param-name>debug</param-name>
    <param-value>true</param-value>
   </init-param>
   <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
   <servlet-name>dwr-invoker</servlet-name>
   <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

我们注意到它里面包含了这段配置:<load-on-startup>1</load-on-startup>,那么这个配置有什么作用呢?

贴一段英文原汁原味的解释如下:
Servlet specification:
The load-on-startup element indicates that this servlet should be loaded (instantiated and have its init() called) on the startup of the web application. The optional contents of these element must be an integer indicating the order in which the servlet should be loaded. If the value is a negative integer, or the element is not present, the container is free to load the servlet whenever it chooses.   If the value is a positive integer or 0, the container must load and initialize the servlet as the application is deployed. The container must guarantee that servlets marked with lower integers are loaded before servlets marked with higher integers. The container may choose the order of loading of servlets with the same load-on-start-up value.

翻译过来的意思大致如下:
1)load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。

2)它的值必须是一个整数,表示servlet应该被载入的顺序

2)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;

3)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。

4)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。

5)当值相同时,容器就会自己选择顺序来加载。

所以,<load-on-startup>x</load-on-startup>,中x的取值1,2,3,4,5代表的是优先级,而非启动延迟时间。

如下题目:

2.web.xml中不包括哪些定义(多选)

a.默认起始页

b.servlet启动延迟时间定义

c.error处理页面

d.jsp文件改动后重新载入时间

答案:b,d

通常大多数Servlet是在用户第一次请求的时候由应用服务器创建并初始化,但<load-on-startup>n</load-on-startup>   可以用来改变这种状况,根据自己需要改变加载的优先级!

posted @ 2011-09-29 15:22 xzc 阅读(149927) | 评论 (22)编辑 收藏

Keytool是一个Java数据证书的管理工具 ,Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中在keystore里,包含两种数据:
密钥实体(Key entity)——密钥(secret key)又或者是私钥和配对公钥(采用非对称加密)
可信任的证书实体(trusted certificate entries)——只包含公钥


ailas(别名)每个keystore都关联这一个独一无二的alias,这个alias通常不区分大小写

 

JDK中keytool常用命令:

-genkey 在用户主目录中创建一个默认文件".keystore",还会产生一个mykey的别名,mykey中包含用户的公钥、私钥和证书
(在没有指定生成位置的情况下,keystore会存在用户系统默认目录,如:对于window xp系统,会生成在系统的C:\Documents and Settings\UserName\文件名为“.keystore”)
-alias 产生别名
-keystore 指定密钥库的名称(产生的各类信息将不在.keystore文件中)
-keyalg 指定密钥的算法 (如 RSA DSA(如果不指定默认采用DSA))
-validity 指定创建的证书有效期多少天
-keysize 指定密钥长度
-storepass 指定密钥库的密码(获取keystore信息所需的密码)
-keypass 指定别名条目的密码(私钥的密码)
-dname 指定证书拥有者信息 例如: "CN=名字与姓氏,OU=组织单位名称,O=组织名称,L=城市或区域名称,ST=州或省份名称,C=单位的两字母国家代码"
-list 显示密钥库中的证书信息 keytool -list -v -keystore 指定keystore -storepass 密码
-v 显示密钥库中的证书详细信息
-export 将别名指定的证书导出到文件 keytool -export -alias 需要导出的别名 -keystore 指定keystore -file 指定导出的证书位置及证书名称 -storepass 密码
-file 参数指定导出到文件的文件名
-delete 删除密钥库中某条目 keytool -delete -alias 指定需删除的别 -keystore 指定keystore -storepass 密码
-printcert 查看导出的证书信息 keytool -printcert -file yushan.crt
-keypasswd 修改密钥库中指定条目口令 keytool -keypasswd -alias 需修改的别名 -keypass 旧密码 -new 新密码 -storepass keystore密码 -keystore sage
-storepasswd 修改keystore口令 keytool -storepasswd -keystore e:\yushan.keystore(需修改口令的keystore) -storepass 123456(原始密码) -new yushan(新密码)
-import 将已签名数字证书导入密钥库 keytool -import -alias 指定导入条目的别名 -keystore 指定keystore -file 需导入的证书

 

下面是各选项的缺省值。
-alias "mykey"

-keyalg "DSA"

-keysize 1024

-validity 90

-keystore 用户宿主目录中名为 .keystore 的文件

-file 读时为标准输入,写时为标准输出




1、keystore的生成:

分阶段生成:
keytool -genkey -alias yushan(别名) -keypass yushan(别名密码) -keyalg RSA(算法) -keysize 1024(密钥长度) -validity 365(有效期,天单位) -keystore

e:\yushan.keystore(指定生成证书的位置和证书名称) -storepass 123456(获取keystore信息的密码);回车输入相关信息即可;

一次性生成:
keytool -genkey -alias yushan -keypass yushan -keyalg RSA -keysize 1024 -validity 365 -keystore e:\yushan.keystore -storepass 123456 -dname "CN=(名字与

姓氏), OU=(组织单位名称), O=(组织名称), L=(城市或区域名称), ST=(州或省份名称), C=(单位的两字母国家代码)";(中英文即可)

2、keystore信息的查看:
keytool -list -v -keystore e:\keytool\yushan.keystore -storepass 123456
显示内容:
---------------------------------------------------------------------
Keystore 类型: JKS
Keystore 提供者: SUN

您的 keystore 包含 1 输入

别名名称: yushan
创建日期: 2009-7-29
项类型: PrivateKeyEntry
认证链长度: 1
认证 [1]:
所有者:CN=yushan, OU=xx公司, O=xx协会, L=湘潭, ST=湖南, C=中国
签发人:CN=yushan, OU=xx公司, O=xx协会, L=湘潭, ST=湖南, C=中国
序列号:4a6f29ed
有效期: Wed Jul 29 00:40:13 CST 2009 至Thu Jul 29 00:40:13 CST 2010
证书指纹:
MD5:A3:D7:D9:74:C3:50:7D:10:C9:C2:47:B0:33:90:45:C3
SHA1:2B:FC:9E:3A:DF:C6:C4:FB:87:B8:A0:C6:99:43:E9:4C:4A:E1:18:E8
签名算法名称:SHA1withRSA
版本: 3
--------------------------------------------------------------------

 

缺省情况下,-list 命令打印证书的 MD5 指纹。而如果指定了 -v 选项,将以可读格式打印证书,如果指定了 -rfc 选项,将以可打印的编码格式输出证书。


keytool -list -rfc -keystore e:\yushan.keystore -storepass 123456

显示:

-------------------------------------------------------------------------------------------------------

Keystore 类型: JKS
Keystore 提供者: SUN

您的 keystore 包含 1 输入

别名名称: yushan
创建日期: 2009-7-29
项类型: PrivateKeyEntry
认证链长度: 1
认证 [1]:
-----BEGIN CERTIFICATE-----
MIICSzCCAbSgAwIBAgIESm8p7TANBgkqhkiG9w0BAQUFADBqMQ8wDQYDVQQGDAbkuK3lm70xDzAN
BgNVBAgMBua5luWNlzEPMA0GA1UEBwwG5rmY5r2tMREwDwYDVQQKDAh4eOWNj+S8mjERMA8GA1UE
CwwIeHjlhazlj7gxDzANBgNVBAMTBnl1c2hhbjAeFw0wOTA3MjgxNjQwMTNaFw0xMDA3MjgxNjQw
MTNaMGoxDzANBgNVBAYMBuS4reWbvTEPMA0GA1UECAwG5rmW5Y2XMQ8wDQYDVQQHDAbmuZjmva0x
ETAPBgNVBAoMCHh45Y2P5LyaMREwDwYDVQQLDAh4eOWFrOWPuDEPMA0GA1UEAxMGeXVzaGFuMIGf
MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJoru1RQczRzTnBWxefVNspQBykS220rS8Y/oX3mZa
hjL4wLfOURzUuxxuVQR2jx7QI+XKME+DHQj9r6aAcLBCi/T1jwF8mVYxtpRuTzE/6KEZdhowEe70
liWLVE+hytLBHZ03Zhwcd6q5HUMu27du3MPQvqiwzTY7MrwIvQQ8iQIDAQABMA0GCSqGSIb3DQEB
BQUAA4GBAGoQQ1/FnTfkpQh+Ni6h3fZdn3sR8ZzDMbOAIYVLAhBb85XDJ8QZTarHbZMJcIdHxAl1
i08ct3E8U87V9t8GZFWVC4BFg/+zeFEv76SFpVE56iX7P1jpsu78z0m69hHlds77VJTdyfMSvtXv
sYHP3fxfzx9WyhipBwd8VPK/NgEP
-----END CERTIFICATE-----

-------------------------------------------------------------------------------------------------------

3、证书的导出:

keytool -export -alias yushan -keystore e:\yushan.keystore -file e:\yushan.crt(指定导出的证书位置及证书名称) -storepass 123456

4、查看导出的证书信息
keytool -printcert -file yushan.crt
显示:(在windows下可以双击yushan.crt查看)
-----------------------------------------------------------------------
所有者:CN=yushan, OU=xx公司, O=xx协会, L=湘潭, ST=湖南, C=中国
签发人:CN=yushan, OU=xx公司, O=xx协会, L=湘潭, ST=湖南, C=中国
序列号:4a6f29ed
有效期: Wed Jul 29 00:40:13 CST 2009 至Thu Jul 29 00:40:13 CST 2010
证书指纹:
MD5:A3:D7:D9:74:C3:50:7D:10:C9:C2:47:B0:33:90:45:C3
SHA1:2B:FC:9E:3A:DF:C6:C4:FB:87:B8:A0:C6:99:43:E9:4C:4A:E1:18:E8
签名算法名称:SHA1withRSA
版本: 3
-----------------------------------------------------------------------

5、证书的导入:
准备一个导入的证书:
keytool -genkey -alias shuany -keypass shuany -keyalg RSA -keysize 1024 -validity 365 -keystore e:\shuany.keystore -storepass 123456 -dname "CN=shuany,

OU=xx, O=xx, L=xx, ST=xx, C=xx";
keytool -export -alias shuany -keystore e:\shuany.keystore -file e:\shuany.crt -storepass 123456

现在将shuany.crt 加入到yushan.keystore中:
keytool -import -alias shuany(指定导入证书的别名,如果不指定默认为mykey,别名唯一,否则导入出错) -file e:\shuany.crt -keystore e:\yushan.keystore -storepass

123456

keytool -list -v -keystore e:\keytool\yushan.keystore -storepass 123456
显示:
------------------------------------------------------------------------------
Keystore 类型: JKS
Keystore 提供者: SUN

您的 keystore 包含 2 输入

别名名称: yushan
创建日期: 2009-7-29
项类型: PrivateKeyEntry
认证链长度: 1
认证 [1]:
所有者:CN=yushan, OU=xx公司, O=xx协会, L=湘潭, ST=湖南, C=中国
签发人:CN=yushan, OU=xx公司, O=xx协会, L=湘潭, ST=湖南, C=中国
序列号:4a6f29ed
有效期: Wed Jul 29 00:40:13 CST 2009 至Thu Jul 29 00:40:13 CST 2010
证书指纹:
MD5:A3:D7:D9:74:C3:50:7D:10:C9:C2:47:B0:33:90:45:C3
SHA1:2B:FC:9E:3A:DF:C6:C4:FB:87:B8:A0:C6:99:43:E9:4C:4A:E1:18:E8
签名算法名称:SHA1withRSA
版本: 3


*******************************************
*******************************************


别名名称: shuany
创建日期: 2009-7-29
输入类型: trustedCertEntry

所有者:CN=shuany, OU=xx, O=xx, L=xx, ST=xx, C=xx
签发人:CN=shuany, OU=xx, O=xx, L=xx, ST=xx, C=xx
序列号:4a6f2cd9
有效期: Wed Jul 29 00:52:41 CST 2009 至Thu Jul 29 00:52:41 CST 2010
证书指纹:
MD5:15:03:57:9B:14:BD:C5:50:21:15:47:1E:29:87:A4:E6
SHA1:C1:4F:8B:CD:5E:C2:94:77:B7:42:29:35:5C:BB:BB:2E:9E:F0:89:F5
签名算法名称:SHA1withRSA
版本: 3


*******************************************
*******************************************
------------------------------------------------------------------------------

6、证书条目的删除:
keytool -delete -alias shuany(指定需删除的别名) -keystore yushan.keystore -storepass 123456

7、证书条目口令的修改:
keytool -keypasswd -alias yushan(需要修改密码的别名) -keypass yushan(原始密码) -new 123456(别名的新密码) -keystore e:\yushan.keystore -storepass 123456

8、keystore口令的修改:
keytool -storepasswd -keystore e:\yushan.keystore(需修改口令的keystore) -storepass 123456(原始密码) -new yushan(新密码)

 

9、修改keystore中别名为yushan的信息

 

keytool -selfcert -alias yushan -keypass yushan -keystore e:\yushan.keystore -storepass 123456 -dname "cn=yushan,ou=yushan,o=yushan,c=us

posted @ 2011-09-15 08:30 xzc 阅读(412) | 评论 (0)编辑 收藏
转自:http://www.iteye.com/topic/255397

1.那即将离我远去的

用buffalo作为我的ajax类库也有些历史了,几乎是和Spring同时开始接触的.

按照官方的方式,Buffalo与Spring的集成是很简单:

    在Spring中配置一个BuffaloServiceConfigure bean,把spring托管的服务在其中声明即可,Buffalo可以通过ServletContext得到Spring的WebApplicationContext,进而得到所需的服务:

Java代码 复制代码 收藏代码
  1. <bean name="buffaloConfigBean"    
  2.                 class="net.buffalo.service.BuffaloServiceConfigurer">    
  3.                 <property name="services">    
  4.                         <map>    
  5.                                 <entry key="springSimpleService">    
  6.                                         <ref bean="systemService" />    
  7.                                 </entry>    
  8.                                 <entry key="springSimpleService2">    
  9.                                         <ref bean="systemService2" />    
  10.                                 </entry>    
  11.                         </map>    
  12.                 </property>    
  13.  </bean>   

 

     似乎很简单,但,有没有觉得似乎很傻?只是把Spring里已经配置好的bean再引用一次而已,

一旦面临协作开发,和所有的全局配置文件一样,BuffaloServiceConfigure bean下面就会囊括几十上百个service ref,一大堆人围着这个配置文件转,CVS冲突就成了家常便饭了,苦恼不已.当然,按我们这么多年的开发经验是不会出现这种低级错误的,早早的在项目设计阶段就会按模块划分出多个配置文件,一人独用,无需和别人共享配置,轻松面对冲突问题,带来的局面就是每个包里都塞着一个buffalo.xml,一个项目里配置文件到处有,不断得copy/paste,层层套套,那可不是硕果累累的满足感.

     当然,Spring本身在2.5之前也因XML配置繁琐而让人诟病,Guice才能异军突起,那时Spring比Buffalo的配置更多,所以Buffalo的问题也就不是问题了.但有一天,我终于要正式升级到Spring2.5.

     世界清静了!使用annotation,看到怎么多配置文件消失,看到简洁的Bean/MVC配置,呵呵,还真是令人心情愉悦的.

     诶,等等,怎么还有大堆XML?哦?原来是Buffalo...

     Buffalo像个刺头,傻愣愣地杵在XML里.

 

2.于是我开始把Buffalo也Annotation化.

 

话说Spring的扩展能力还是ganggang的,一天时间,就有成果了.

先写个注解:

Java代码 复制代码 收藏代码
  1. package cn.tohot.common.annotation;   
  2.   
  3. import java.lang.annotation.ElementType;   
  4. import java.lang.annotation.Retention;   
  5. import java.lang.annotation.RetentionPolicy;   
  6. import java.lang.annotation.Target;   
  7.   
  8. /**  
  9.  * buffalo扩展接口,用于表明该类是一个buffalo服务.  
  10.  * @author tedeyang  
  11.  *  
  12.  */  
  13. @Retention(RetentionPolicy.RUNTIME)   
  14. @Target(ElementType.TYPE)   
  15. public @interface Buffalo {   
  16.     /**  
  17.      * @return 远程调用时的服务名.  
  18.      */    
  19.     String value();   
  20. }  

 

接着再写Spring的扩展

 

Java代码 复制代码 收藏代码
  1. /**  
  2.  *   
  3.  */  
  4. package cn.tohot.common.annotation;   
  5.   
  6. import java.util.HashMap;   
  7.   
  8. import net.buffalo.service.BuffaloServiceConfigurer;   
  9.   
  10. import org.apache.log4j.Logger;   
  11. import org.springframework.beans.BeansException;   
  12. import org.springframework.beans.factory.DisposableBean;   
  13. import org.springframework.beans.factory.FactoryBean;   
  14. import org.springframework.beans.factory.InitializingBean;   
  15. import org.springframework.beans.factory.config.BeanPostProcessor;   
  16.   
  17. /**  
  18.  * 该类作为FactoryBean可以无缝替换buffalo 2.0自带的配置类,并使用annotation进行配置.  
  19.  * @author tedeyang  
  20.  *  
  21.  */  
  22. @SuppressWarnings("unchecked")   
  23. public class BuffaloAnnotationServiceFactoryBean implements FactoryBean, InitializingBean, DisposableBean, BeanPostProcessor {   
  24.     private static final Logger log = Logger.getLogger(BuffaloAnnotationServiceFactoryBean.class);   
  25.   
  26.     private BuffaloServiceConfigurer buffaloConfigurer = null;   
  27.   
  28.     public BuffaloAnnotationServiceFactoryBean() {   
  29.         buffaloConfigurer = new BuffaloServiceConfigurer();   
  30.         buffaloConfigurer.setServices(new HashMap());   
  31.     }   
  32.   
  33.     private void addBuffaloBean(String buffaloServiceName,Object bean) {   
  34.         buffaloConfigurer.getServices().put(buffaloServiceName, bean);   
  35.         log.info("Add a buffalo service :"+buffaloServiceName);   
  36.     }   
  37.   
  38.     public Object getObject() throws Exception {   
  39.         return this.buffaloConfigurer;   
  40.     }   
  41.   
  42.     public Class getObjectType() {   
  43.         return BuffaloServiceConfigurer.class;   
  44.     }   
  45.   
  46.     public boolean isSingleton() {   
  47.         return true;   
  48.     }   
  49.   
  50.     public void afterPropertiesSet() throws Exception {   
  51.     }   
  52.   
  53.     public void destroy() throws Exception {   
  54.         if (buffaloConfigurer != null)   
  55.             buffaloConfigurer.setServices(null);   
  56.         buffaloConfigurer = null;   
  57.     }   
  58.     
  59.     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {   
  60.         return bean;   
  61.     }   
  62.   
  63.     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {   
  64.         log.debug("find a bean:"+beanName);   
  65.         if (bean.getClass().isAnnotationPresent(Buffalo.class)) {   
  66.             Buffalo buffalo = bean.getClass().getAnnotation(Buffalo.class);   
  67.             addBuffaloBean(buffalo.value(), bean);    
  68.         }   
  69.         return bean;   
  70.     }   
  71.   
  72. }  

 

 主要思路是用FactoryBean替换原BuffaloServiceConfigurer,并挂上BeanPostProcessor的钩子,检测一下annotation,发现buffalo服务就添加到原BuffaloServiceConfigurer中去.

 

3.今天我这样配置Buffalo:

Java代码 复制代码 收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
  4.     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"  
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  6.      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd   
  7.      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd   
  8.      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">   
  9.   
  10.     <!-- Spring Annotation配置, 自动搜索组件 -->   
  11.     <context:component-scan base-package="cn.tohot.demo"/>    
  12.     <bean id="buffalo"   class="cn.tohot.common.annotation.BuffaloAnnotationServiceFactoryBean" />    
  13. </beans>  
 

 服务端的Buffalo bean 类:

Java代码 复制代码 收藏代码
  1. package cn.tohot.demo;   
  2.   
  3. import org.springframework.stereotype.Service;   
  4.   
  5. import cn.tohot.common.annotation.Buffalo;   
  6.   
  7. @Service     //声明Spring bean,   
  8. @Buffalo("testbean"//声明一个名为"testbean"的Buffalo service   
  9. public class BuffaloBeanTestService {   
  10.     public String run() {   
  11.         System.out.println("run");   
  12.         return "run";   
  13.     }   
  14. }  

 很简洁,不是吗?

posted @ 2011-08-26 09:21 xzc 阅读(229) | 评论 (0)编辑 收藏
转自:http://www.iteye.com/topic/11738
前几天解释了Spring的抽象事务机制。这次讲讲Spring中的DataSource 事务。
DataSource事务相关的类比较多,我们一步步来拨开其中的密团。

1 如何获得连接
看DataSourceUtils代码
Java代码 复制代码 收藏代码
  1. protected static Connection doGetConnection(DataSource dataSource, boolean allowSynchronization);   
  2.             throws SQLException {   
  3.            
  4.         ConnectionHolder conHolder = (ConnectionHolder); TransactionSynchronizationManager.getResource(dataSource);;   
  5.         if (conHolder != null); {   
  6.             conHolder.requested();;   
  7.             return conHolder.getConnection();;   
  8.         }   
  9.   
  10.            
  11.         Connection con = dataSource.getConnection();;   
  12.         if (allowSynchronization && TransactionSynchronizationManager.isSynchronizationActive();); {   
  13.                         conHolder = new ConnectionHolder(con);;   
  14.             TransactionSynchronizationManager.bindResource(dataSource, conHolder);;   
  15.             TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, dataSource););;   
  16.             conHolder.requested();;   
  17.         }   
  18.   
  19.         return con;   
  20.     }  

原来连接是从TransactionSynchronizationManager中获取,如果TransactionSynchronizationManager中已经有了,那么拿过来然后调用conHolder.requested()。否则从原始的DataSource这创建一个连接,放到一个ConnectionHolder,然后再调用TransactionSynchronizationManager.bindResource绑定。
好,我们又遇到两个新的类TransactionSynchronizationManager和ConnectionHolder和。继续跟踪


2 TransactionSynchronizationManager
看其中的一些代码
Java代码 复制代码 收藏代码
  1. private static ThreadLocal resources = new ThreadLocal();;   
  2. public static Object getResource(Object key); {   
  3.         Map map = (Map); resources.get();;   
  4.         if (map == null); {   
  5.             return null;   
  6.         }   
  7.         Object value = map.get(key);;   
  8.                 return value;   
  9.     }   
  10. public static void bindResource(Object key, Object value); throws IllegalStateException {   
  11.         Map map = (Map); resources.get();;   
  12.                 if (map == null); {   
  13.             map = new HashMap();;   
  14.             resources.set(map);;   
  15.         }   
  16.         map.put(key, value);;   
  17.             }  
原来TransactionSynchronizationManager内部建立了一个ThreadLocal的resources,这个resources又是和一个map联系在一起的,这个map在某个线程第一次调用bindResource时生成。
联系前面的DataSourceUtils代码,我们可以总结出来。
某个线程使用DataSourceUtils,当第一次要求创建连接将在TransactionSynchronizationManager中创建出一个ThreadLocal的map。然后以DataSource作为键,ConnectionHolder为值放到map中。等这个线程下一次再请求的这个DataSource的时候,就从这个map中获取对应的ConnectionHolder。用map是为了解决同一个线程上多个DataSource。
然后我们来看看ConnectionHolder又是什么?



3 对连接进行引用计数
看ConnectionHolder代码,这个类很简单,看不出个所以然,只好再去看父类代码ResourceHolderSupport,我们感兴趣的是这两个方法
Java代码 复制代码 收藏代码
  1. public void requested(); {   
  2.         this.referenceCount++;   
  3.     }   
  4.   
  5.     public void released(); {   
  6.         this.referenceCount--;   
  7.     }  

看得出这是一个引用计数的技巧。原来Spring中对Connection是竟量使用已创建的对象,而不是每次都创建一个新对象。这就是DataSourceUtils中
Java代码 复制代码 收藏代码
  1. if (conHolder != null); {   
  2.             conHolder.requested();;   
  3.             return conHolder.getConnection();;   
  4.         }  
的原因


4 释放连接
完成事物后DataSourceTransactionManager有这样的代码
Java代码 复制代码 收藏代码
  1. protected void doCleanupAfterCompletion(Object transaction); {   
  2.         DataSourceTransactionObject txObject = (DataSourceTransactionObject); transaction;   
  3.   
  4.         // Remove the connection holder from the thread.   
  5.         TransactionSynchronizationManager.unbindResource(this.dataSource);;   
  6.         txObject.getConnectionHolder();.clear();;   
  7.   
  8.         //...       DataSourceUtils.closeConnectionIfNecessary(con, this.dataSource);;   
  9.     }  

DataSourceUtils
Java代码 复制代码 收藏代码
  1. protected static void doCloseConnectionIfNecessary(Connection con, DataSource dataSource); throws SQLException {   
  2.         if (con == null); {   
  3.             return;   
  4.         }   
  5.   
  6.         ConnectionHolder conHolder = (ConnectionHolder); TransactionSynchronizationManager.getResource(dataSource);;   
  7.         if (conHolder != null && con == conHolder.getConnection();); {   
  8.             // It's the transactional Connection: Don't close it.   
  9.             conHolder.released();;   
  10.             return;   
  11.         }   
  12.            
  13.         // Leave the Connection open only if the DataSource is our   
  14.         // special data source, and it wants the Connection left open.   
  15.         if (!(dataSource instanceof SmartDataSource); || ((SmartDataSource); dataSource);.shouldClose(con);); {   
  16.             logger.debug("Closing JDBC connection");;   
  17.             con.close();;   
  18.         }   
  19.     }  

恍然大悟。如果事物完成,那么就
TransactionSynchronizationManager.unbindResource(this.dataSource);将当前的ConnectionHolder
从TransactionSynchronizationManager上脱离,然后doCloseConnectionIfNecessary。最后会把连接关闭掉。

5 两个辅助类JdbcTemplate和TransactionAwareDataSourceProxy
JdbcTemplate中的execute方法的第一句和最后一句
Java代码 复制代码 收藏代码
  1. public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action);   
  2.             throws DataAccessException {   
  3.   
  4.         Connection con = DataSourceUtils.getConnection(getDataSource(););;   
  5.         //其他代码   
  6.     DataSourceUtils.closeConnectionIfNecessary(con, getDataSource(););;   
  7.         }   
  8.     }  

作用不言自明了吧

从TransactionAwareDataSourceProxy中获取的连接是这个样子的
Java代码 复制代码 收藏代码
  1. public Connection getConnection(); throws SQLException {   
  2.         Connection con = DataSourceUtils.doGetConnection(getTargetDataSource();, true);;   
  3.         return getTransactionAwareConnectionProxy(con, getTargetDataSource(););;   
  4.     }  

万变不离其宗,不过我们还是看看getTransactionAwareConnectionProxy
Java代码 复制代码 收藏代码
  1. protected Connection getTransactionAwareConnectionProxy(Connection target, DataSource dataSource); {   
  2.         return (Connection); Proxy.newProxyInstance(   
  3.                 ConnectionProxy.class.getClassLoader();,   
  4.                 new Class[] {ConnectionProxy.class},   
  5.                 new TransactionAwareInvocationHandler(target, dataSource););;   
  6.     }  

原来返回的是jdk的动态代理。继续看TransactionAwareInvocationHandler
Java代码 复制代码 收藏代码
  1. public Object invoke(Object proxy, Method method, Object[] args); throws Throwable {   
  2.         //...           if (method.getName();.equals(CONNECTION_CLOSE_METHOD_NAME);); {   
  3.                 if (this.dataSource != null); {   
  4.                     DataSourceUtils.doCloseConnectionIfNecessary(this.target, this.dataSource);;   
  5.                 }   
  6.                 return null;   
  7.             }   
  8.   
  9.                     }  

TransactionAwareDataSourceProxy会先从DataSourceUtils获取连接。然后将这个连接用jdk的动态代理包一下返回。外部代码如果调用的这个冒牌的Connection,就会先调用TransactionAwareInvocationHandler的invoke,在这个invoke 中,完成原来调用DataSourceUtils的功能。

总结上面的流程
Spring 对DataSource进行事务管理的关键在于ConnectionHolder和TransactionSynchronizationManager。
  0.先从TransactionSynchronizationManager中尝试获取连接
  1.如果前一步失败则在每个线程上,对每个DataSouce只创建一个Connection
   2.这个Connection用ConnectionHolder包装起来,由TransactionSynchronizationManager管理
  3.再次请求同一个连接的时候,从TransactionSynchronizationManager返回已经创建的ConnectionHolder,然后调用ConnectionHolder的request将引用计数+1
  4.释放连接时要调用ConnectionHolder的released,将引用计数-1
  5.当事物完成后,将ConnectionHolder从TransactionSynchronizationManager中解除。当谁都不用,这个连接被close

以上所有都是可以调用DataSourceUtils化简代码,而JdbcTemplate又是调用DataSourceUtils的。所以在Spring文档中要求尽量首先使用JdbcTemplate,其次是用DataSourceUtils来获取和释放连接。至于TransactionAwareDataSourceProxy,那是下策的下策。不过可以将Spring事务管理和遗留代码无缝集成。

所以如某位朋友说要使用Spring的事务管理,但是又不想用JdbcTemplate,那么可以考虑TransactionAwareDataSourceProxy。这个类是原来DataSource的代理。
其次,想使用Spring事物,又不想对Spring进行依赖是不可能的。与其试图自己模拟DataSourceUtils,不如直接使用现成的。
posted @ 2011-08-09 14:59 xzc 阅读(4004) | 评论 (1)编辑 收藏
仅列出标题
共32页: First 上一页 6 7 8 9 10 11 12 13 14 下一页 Last