??xml version="1.0" encoding="utf-8" standalone="yes"?> 1QDuplicated Code 4QDivergent Change
代码重复几乎是最常见的异味了。他也是Refactoring 的主要目标之一。代码重复往
往来自于copy-and-paste 的编E风根{与他相对应OAOO 是一个好pȝ的重要标?br />Q请参见我的duplicated code 一文:http://www.erptao.org/download.php?op=viewsdownload&sid=6Q?/p>
2QLong method
它是传统l构化的“遗毒“。一个方法应当具有自我独立的意图Q不要把几个意图
攑֜一P我的《大cd长方法》一文中有详l描q?/p>
3QLarge Class
大类是你把太多的责Ml了一个类。这里的规则是One Class One Responsibility?/p>
一个类里面的内容变化率不同。某些状态一个小时变一ơ,某些则几个月一q才?br />一ơ;某些状态因斚w的原因发生变化,而另一些则因ؓ其他斚w的原因变一ơ?br />面向对象的抽象就是把相对不变的和相对变化盔RR把问题变化的一斚w和另一
斚w盔RR这使得q些相对不变的可以重用。问题变化的每个斚w都可以单独重用?br />q种相异变化的共存得重用非常困难?/p>
5QShotgun Surgery
q正好和上面相反。对pȝ一个地方的改变涉及到其他许多地方的相关改变。这?br />变化率和变化内容怼的状态和行ؓ通常应当攑֜同一个类中?/p>
6QFeature Envy
对象的目的就是封装状态以及与q些状态紧密相关的行ؓ。如果一个类的方法频J?br />用get Ҏ存取其他cȝ状态进行计,那么你要考虑把行为移到涉及状态数目最多的
那个cR?/p>
7QData Clumps
某些数据通常像孩子一h玩耍:一起出现在很多cȝ成员变量中,一起出现在
许多Ҏ的参C…..Q这些数据或许应该自q立Ş成对象?/p>
8QPrimitive Obsession
面向对象的新手通常习惯使用几个原始cd的数据来表示一个概c譬如对于范_
他们会用两个数字。对于MoneyQ他们会用一个QҎ来表C。因Z没有使用对象
来表N题中存在的概念,q得代码变的难以理解,解决问题的难度大大增加?br />好的习惯是扩充语a所能提供原始类型,用小对象来表C围、金额、{化率、邮
政编码等{?/p>
9QSwitch Statement
Z帔R的开兌句是OO 的大敌,你应当把他变为子cRstate 或strategy?/p>
10Q?Parallel Inheritance Hierarchies
q行的承层ơ是shotgun surgery 的特D情c因为当你改变一个层ơ中的某一?br />cLQ你必须同时改变另外一个层ơ的q行子类?/p>
11Q?Lazy Class
一个干zM多的cR类的维护需要额外的开销Q如果一个类承担了太的责QQ应
当消除它?/p>
12Q?Speculative Generality
一个类实现了从未用到的功能和通用性。通常q样的类或方法唯一的用htest
case。不要犹豫,删除它?/p>
13Q?Temporary Field
一个对象的属性可能只在某些情况下才有意义。这L代码难以理解。专门徏?br />一个对象来持有q样的孤儿属性,把只和他相关的行为移到该cR最常见的是一个特?br />的算法需要某些只有该法才有用的变量?/p>
14Q?Message Chain
消息铑֏生于当一个客户向一个对象要求另一个对象,然后客户又向q另一对象?br />求另一个对象,再向q另一个对象要求另一个对象,如此如此。这Ӟ你需要隐藏分z?/p>
15Q?Middle Man
对象的基本特性之一是装Q而你l常会通过分派d现封装。但是这一步不能走得太q,如果你发C个类接口的一大半Ҏ都在做分z,你可能需要移去这个中?br />人?/p>
16Q?Inappropriate Intimacy
某些cȝ互之间太亲密Q它们花费了太多的时间去砖研别h的私有部分。对人类?br />aQ我们也怸应该太假正经Q但我们应当让自qcM格遵守禁Ʋ主义?/p>
17Q?Alternative Classes with Different Interfaces
做相同事情的Ҏ有不同的函数signatureQ一致把它们往cdơ上U,直至协议一
致?/p>
18Q?Incomplete Library Class
要徏立一个好的类库非常困难。我们大量的E序工作都基于类库实现。然而,如此
q泛而又相异的目标对库构提Z苛刻的要求。库构徏者也不是万能的。有时候我
们会发现库类无法实现我们需要的功能。而直接对库类的修Ҏ非常困难。这时候就需
要用各种手段q行Refactoring?/p>
19Q?Data Class
对象包括状态和行ؓ。如果一个类只有状态没有行为,那么肯定有什么地方出问题
了?/p>
20Q?Refused Bequest
类传下来很多行为和状态,而子cd是用了其中的很小一部分。这通常意味着?br />的类层次有问题?/p>
21Q?Comments
l常觉得要写很多注释表示你的代码难以理解。如果这U感觉太多,表示你需?br />Refactoring?/p> 卖艺|提?/a>
U程与进E相?是一D完成某个特定功能的代码,是程序中单个序的流控制;但与q程不同的是,同类的多个线E是׃n一块内存空间和一l系l资?而线E本w的数据通常只有微处理器的寄存器数据,以及一个供E序执行时用的堆栈.所以系l在产生一个线E?或者在各个U程之间切换?负担要比q程的?正因如此,U程也被UCؓ轻负药E?light-weight process).一个进E中可以包含多个U程.
多Q务是指在一个系l中可以同时q行多个E序,x多个独立q行的Q?每个d对应一个进E,同进E一?一个线E也有从创徏,q行到消亡的q程,UCؓU程的生命周?用线E的状?state)表明U程处在生命周期的哪个阶D?U程有创?可运?q行?d,M五中状?通过U程的控制与调度可ɾU程在这几种状态间转化每个E序臛_自动拥有一个线E?UCؓȝE?当程序加载到内存?启动ȝE?
[U程的运行机制以及调度模型]
java中多U程是一个类或一个程序执行或理多个U程执行d的能力,每个U程可以独立于其他线E而独立运行,当然也可以和其他U程协同q行Q一个类控制着它的所有线E,可以军_哪个U程得到优先U,哪个U程可以讉K其他cȝ资源Q哪个线E开始执行,哪个保持休眠状态?br />下面是线E的机制图:
U程的状态表C线E正在进行的zd以及在此旉D内所能完成的d.U程有创?可运?q行?d,M五中状?一个具有生命的U程,L处于q五U状态之一Q?br />1.创徏状?/strong>
使用newq算W创Z个线E后,该线E仅仅是一个空对象,pȝ没有分配资源,U该U程处于创徏状?new thread)
2.可运行状?/strong>
使用start()Ҏ启动一个线E后,pȝU程分配了除CPU外的所需资源,使该U程处于可运行状?Runnable)
3.q行中状?/strong>
Javaq行pȝ通过调度选中一个Runnable的线E?使其占有CPUq{行中状?Running).此时,pȝ真正执行U程的run()Ҏ.
4.d状?/strong>
一个正在运行的U程因某U原因不能l运行时,q入d状?Blocked)
5.M状?/strong>
U程l束后是M状?Dead)
同一时刻如果有多个线E处于可q行状?则他们需要排队等待CPU资源.此时每个U程自动获得一个线E的优先U?priority),优先U的高低反映U程的重要或紧急程?可运行状态的U程按优先排队,U程调度依据优先U基上的"先到先服?原则.
U程调度理器负责线E排队和CPU在线E间的分?q由U程调度法q行调度.当线E调度管理器选种某个U程?该线E获得CPU资源而进入运行状?
U程调度是先占式调度,卛_果在当前U程执行q程中一个更高优先的线E进入可q行状?则这个线E立卌调度执行.先占式调度分?独占式和分时方式.
独占方式?当前执行U程一直执行下??到执行完毕或׃某种原因d攑ּCPU,或CPU被一个更高优先的线E抢?br />分时方式?当前q行U程获得一个时间片,旉到时,即没有执行完也要让出CPU,q入可运行状?{待下一个时间片的调?pȝ选中其他可运行状态的U程执行
分时方式的系l每个U程工作若干?实现多线E同时运?br />
另外h意下面的U程调度规则Q如果有不理解,不急,往下看Q:
①如果两个或是两个以上的U程都修改一个对象,那么把执行修改的Ҏ定义同步的(SynchronizedQ?如果对象更新影响到只L法,那么只度Ҏ也应该定义ؓ同步?br />②如果一个线E必ȝ待一个对象状态发生变化,那么它应该在对象内部{待Q而不是在外部{待Q它可以调用一个被同步的方法,q让q个Ҏ调用wait()
③每当一个方法改变某个对象的状态的时候,它应该调用notifyAll()ҎQ这l等待队列的U程提供Z来看一看执行环境是否已发生改变
④记住wait(),notify(),notifyAll()Ҏ属于Objectc,而不是Threadc,仔细查看是否每次执行wait()Ҏ都有相应的notify()或notifyAll()ҎQ且它们作用与相同的对象 在java中每个类都有一个主U程Q要执行一个程序,那么q个cd中一定要有mainҎQ这个manҎ也就是java class中的ȝE。你可以自己创徏U程Q有两种ҎQ一是承Threadc,或是实现Runnable接口。一般情况下Q最好避免承,因ؓjava中是单根l承Q如果你选用l承Q那么你的类失MҎ,当然也不能全然否定承Thread,该方法编写简?可以直接操作U程,适用于单重承情c至于选用那一U,具体情况具体分析?br />
eg.l承Thread
public class MyThread_1 extends Thread
{
public void run()
{
//some code
}
}
eg.实现Runnable接口
public class MyThread_2 implements Runnable
{
public void run()
{
//some code
}
}
当用承创建线E,q样启动U程Q?br />
new MyThread_1().start()
当用实现接口创建线E,q样启动U程Q?br />
new Thread(new MyThread_2()).start()
注意Q其实是创徏一个线E实例,q以实现了Runnable接口的类为参C入这个实例,当执行这个线E的时候,MyThread_2中run里面的代码将被执行?br />下面是完成的例子Q?br />
public class MyThread implements Runnable
{
public void run()
{
System.out.println("My Name is "+Thread.currentThread().getName());
}
public static void main(String[] args)
{
new Thread(new MyThread()).start();
}
}
执行后将打印出:
My Name is Thread-0
你也可以创徏多个U程Q像下面q样
new Thread(new MyThread()).start();
new Thread(new MyThread()).start();
new Thread(new MyThread()).start();
那么会打印出Q?br />My Name is Thread-0
My Name is Thread-1
My Name is Thread-2
看了上面的结果,你可能会认ؓU程的执行顺序是依次执行的,但是那只是一般情况,千万不要用以为是U程的执行机Ӟ影响U程执行序的因素有几点Q首先看看前面提到的优先U别
public class MyThread implements Runnable
{
public void run()
{
System.out.println("My Name is "+Thread.currentThread().getName());
}
public static void main(String[] args)
{
Thread t1=new Thread(new MyThread());
Thread t2=new Thread(new MyThread());
Thread t3=new Thread(new MyThread());
t2.setPriority(Thread.MAX_PRIORITY);//赋予最高优先
t1.start();
t2.start();
t3.start();
}
}
再看看结果:
My Name is Thread-1
My Name is Thread-0
My Name is Thread-2
U程的优先分ؓ10U,分别??0的整C表,默认情况?。上面的t2.setPriority(Thread.MAX_PRIORITY){h与t2.setPriority(10Q?br />然后是线E程序本w的设计Q比如用sleep,yield,joinQwait{方法(详情LJDKDocument)
public class MyThread implements Runnable
{
public void run()
{
try
{
int sleepTime=(int)(Math.random()*100);//产生随机数字Q?br />Thread.currentThread().sleep(sleepTime);//让其休眠一定时_旉又上面sleepTime军_
//public static void sleep(long millis)throw InterruptedException QAPIQ?br />System.out.println(Thread.currentThread().getName()+" 睡了 "+sleepTime);
}catch(InterruptedException ie)//׃U程在休眠可能被中断Q所以调用sleepҎ的时候需要捕捉异?br />{
ie.printStackTrace();
}
}
public static void main(String[] args)
{
Thread t1=new Thread(new MyThread());
Thread t2=new Thread(new MyThread());
Thread t3=new Thread(new MyThread());
t1.start();
t2.start();
t3.start();
}
}
执行后观察其输出Q?br />
Thread-0 睡了 11
Thread-2 睡了 48
Thread-1 睡了 69
上面的执行结果是随机的,再执行很可能出现不同的结果。由于上面我在run中添加了休眠语句Q当U程休眠的时候就会让出cpuQcpu会选择执行处于runnable状态中的其他线E,当然也可能出现这U情况,休眠的Thread立即q入了runnable状态,cpu再次执行它?br />[U程l概念]
U程是可以被l织的,java中存在线E组的概念,每个U程都是一个线E组的成?U程l把多个U程集成Z个对?通过U程l可以同时对其中的多个线E进行操?如启动一个线E组的所有线E等.Java的线E组由java.lang包中的Thread——Groupcd?
ThreadGroupcȝ来管理一l线E?包括:U程的数?U程间的关系,U程正在执行的操?以及U程要启动或终止时间等.U程l还可以包含U程l?在Java的应用程序中,最高层的线E组是名位main的线E组,在main中还可以加入U程或线E组,在mian的子U程l中也可以加入线E和U程l?形成U程l和U程之间的树状承关pR像上面创徏的线E都是属于mainq个U程l的?br />借用上面的例子,main里面可以q样写:
public static void main(String[] args)
{
/***************************************
ThreadGroup(String name)
ThreadGroup(ThreadGroup parent, String name)
***********************************/
ThreadGroup group1=new ThreadGroup("group1");
ThreadGroup group2=new ThreadGroup(group1,"group2");
Thread t1=new Thread(group2,new MyThread());
Thread t2=new Thread(group2,new MyThread());
Thread t3=new Thread(group2,new MyThread());
t1.start();
t2.start();
t3.start();
}
U程l的嵌套Qt1,t2,t3被加入group2,group2加入group1?br />另外一个比较多是关于U程同步斚w的,试想q样一U情况,你有一W存Ƒ֜银行Q你在一安行ؓ你的账户存款Q而你的妻子在另一安行从q个账户提款Q现在你?000块在你的账户里面。你存入?000Q但是由于另一方也在对q笔存款q行操作Qh家开始执行的时候只看到账户里面原来?000元,当你的妻子提?000元后Q你d所在的银行pZ的̎户里面没有钱了,而你所在的银行却认Zq有2000元?br />看看下面的例子:
class BlankSaving //储蓄账户
{
private static int money=10000;
public void add(int i)
{
money=money+i;
System.out.println("Husband 向银行存入了 [K?+i+"]");
}
public void get(int i)
{
money=money-i;
System.out.println("Wife 向银行取C [K?+i+"]");
if(money<0)
System.out.println("余额不Q?);
}
public int showMoney()
{
return money;
}
}
class Operater implements Runnable
{
String name;
BlankSaving bs;
public Operater(BlankSaving b,String s)
{
name=s;
bs=b;
}
public static void oper(String name,BlankSaving bs)
{
if(name.equals("husband"))
{
try
{
for(int i=0;i<10;i++)
{
Thread.currentThread().sleep((int)(Math.random()*300));
bs.add(1000);
}
}catch(InterruptedException e){}
}else
{
try
{
for(int i=0;i<10;i++)
{
Thread.currentThread().sleep((int)(Math.random()*300));
bs.get(1000);
}
}catch(InterruptedException e){}
}
}
public void run()
{
oper(name,bs);
}
}
public class BankTest
{
public static void main(String[] args)throws InterruptedException
{
BlankSaving bs=new BlankSaving();
Operater o1=new Operater(bs,"husband");
Operater o2=new Operater(bs,"wife");
Thread t1=new Thread(o1);
Thread t2=new Thread(o2);
t1.start();
t2.start();
Thread.currentThread().sleep(500);
}
}
下面是其中一ơ的执行l果Q?br />
---------first--------------
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
看到了吗Q这可不是正的需求,在husbandq没有结束操作的时候,wife插了进来,q样很可能导致意外的l果。解军_法很单,是对数据q行操作Ҏ声明为synchronized,当方法被该关键字声明后,也就意味着Q如果这个数据被加锁Q只有一个对象得到这个数据的锁的时候该对象才能对这个数据进行操作。也是当你存款的时候,q笔账户在其他地Ҏ不能q行操作的,只有你存Ƒ֮毕,银行理人员̎戯锁,其他人才能对q个账户q行操作?br />修改public static void oper(String name,BlankSaving bs)为public static void oper(String name,BlankSaving bs)Q再看看l果:
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
当丈夫完成操作后Q妻子才开始执行操作,q样的话Q对׃n对象的操作就不会有问题了?br />[wait and notify]
你可以利用这两个Ҏ很好的控制线E的执行程Q当U程调用waitҎ后,U程被挂vQ直到被另一U程唤醒QnotifyQ或则是如果waitҎ指定有时间得话,在没有被唤醒的情况下Q指定时间时间过后也自动被唤醒。但是要注意一定,被唤醒ƈ不是指马上执行,而是从组塞状态变为可q行状态,其是否运行还要看cpu的调度?br />事例代码Q?br />
class MyThread_1 extends Thread
{
Object lock;
public MyThread_1(Object o)
{
lock=o;
}
public void run()
{
try
{
synchronized(lock)
{
System.out.println("Enter Thread_1 and wait");
lock.wait();
System.out.println("be notified");
}
}catch(InterruptedException e){}
}
}
class MyThread_2 extends Thread
{
Object lock;
public MyThread_2(Object o)
{
lock=o;
}
public void run()
{
synchronized(lock)
{
System.out.println("Enter Thread_2 and notify");
lock.notify();
}
}
}
public class MyThread
{
public static void main(String[] args)
{
int[] in=new int[0];//notice
MyThread_1 t1=new MyThread_1(in);
MyThread_2 t2=new MyThread_2(in);
t1.start();
t2.start();
}
}
执行l果如下Q?br />Enter Thread_1 and wait
Enter Thread_2 and notify
Thread_1 be notified
可能你注意到了在使用wait and notifyҎ得时候我使用了synchronized块来包装q两个方法,q是׃调用q两个方法的时候线E必获得锁Q也是上面代码中的lock[]Q如果你不用synchronized包装q两个方法的得话Q又或则锁不一是同一把,比如在MyThread_2中synchronized(lock)改ؓsynchronized(this),那么执行q个E序的时候将会抛出java.lang.IllegalMonitorStateException执行期异常。另外wait and notifyҎ是Object中的Qƈ不在Threadq个cM。最后你可能注意Cq点Qint[] in=new int[0];Z么不是创建new Object而是一?长度的数l,那是因ؓ在java中创Z?长度的数l来充当锁更加高效?br />
Thread作ؓjava中一重要l成部分Q当然还有很多地斚w要更深刻的认识,上面只是对Thread的一些常识和易错问题做了一个简要的ȝQ若要真正的掌握java的线E,q需要自己多做ȝ
Java是目前最行的面向对象的~程语言之一QJava支持UTF-8、ISO-8
影响Java中字体编码正显C的有几个因? 1)数据库的q接方式; 2)|页中用的字体~码; 3)数据库里存放数据的字体编? 4)Java的缺省字体编码。如果在~程中遇C能正显CZ文时Q要先弄清楚以上几项所使用的字体编码,再分析找出原因,卛_解决问题?
众所周知QJSP是Java的一U,和网|养I而网也有自q中文~码pȝQ所以JSP处理中文要比UJava的类文g更ؓȝ。本文的试数据库是MySQL3.2Q数据库q接驱动是用org.gjt.mm.mysql.DriverQ这里主要讨论UTF-8和GBK的显C? GB2312是GBK的一个子集,Java中可以用GBK来代替GBpd)。我们先来研IJSP中字体编码问题, 下面W一到第六点是针对JSP?因ؓ从数据库里读Z文数据与写入中文数据有所区别Q咱们分别说明,前三Ҏ从读取数据库到显C在|页Q后三点是从|页输入数据到存入数据库)Q第七到W九炚w对纯Java的类文g?以下rs表示ResultSet的一个实例,是执行Select语句之后产生的数据集?
一、数据库q接方式使用UTF-8
在连接数据库的驱动后面加上这句参数useUnicode=true&characterEncoding=
UTF-8Q例如jdbc:mysql://localhost/DBVF?autoReconnect=true&useUnicode=
true&characterEncoding=UTF-8Q从数据库里d中文昄在用GBK的JSP的网里Q如果数据库里存攄字体~码是UTF-8Q在JSP中?str=new String(rs.getBytes(1),"UTF-8")或者str=rs.getString(1)Q可以正显CZ文。如果数据库里存攄是GBK数据Q那么JSP中也要用str=new String(rs.getBytes(1),"GBK")来显C正的中文。值得注意的是如果面使用UTF-8Q数据库里存攄是UTF-8Q也可以用str=new String(rs.getBytes(1),"GBK")正确昄中文。如果网|UTF-8Q而数据库里存攄是GBKQ无法直接显CZ文,需?步{? str=new String(rs.getBytes(1),"GBK"); 再str=new String(str.getBytes("UTF-8"),"GBK")Q才可以正确昄中文?
二、数据库q接方式使用GBK
在连接数据库的驱动后面加上这句参数useUnicode=true&characterEncoding=
GBK,例如jdbc:mysql://localhost/DBVF?autoReconnect=true&UseUnicode=true&
characterEncoding=GBKQ从数据库里d中文Q显C在使用GBK的JSP的网里Q如果数据库里存攄字体~码是UTF-8Q在JSP中一定要使用 str=new String(rs.getBytes(1),"UTF-8")Q才正确昄中文。如果数据库里存攄是GBK数据Q那么JSP中也要用str=new String(rs.getBytes(1),"GBK") 或者直接用str=rs.getString(1)Q即可显C正的中文?如果|页是UTF-8Q而数据库里存攄是GBKQ只能用str=new String(rs.getString(1).getBytes("UTF-8"),"GBK")的方法来昄中文; 如果|页是UTF-8Q而数据库里存攄是UTF-8Q可用str=new String(rs.getBytes(1),"GBK") 或者rs.getString(1)Ҏ来显CZ文?
三、用缺省数据库q接方式
q接数据库的驱动后面没有q句参数useUnicode=&characterEncoding=Q例如jdbc:mysql://localhost/DBVF?autoReconnect=trueQ没有参数useUnicode=true&characterEncodingQ表CZ用默认的ISO-8895-1~码?
1. 从数据库里读Z文,昄在GBK的网里。如果数据库里存攄字体~码是UTF-8Q在JSP|页中一定要使用语句 str=new String(rs.getBytes(1),"UTF-8") 或者str= new String(rs.getString(1).getBytes("ISO-8859-1"),"UTF-8")Q才可正显CZ文。如果数据库里存攄是GBK数据Q那么JSP中也要用str=new String(rs.getBytes(1),"GBK")或str=new String(rs.getString(1).getBytes("ISO-8859-1"),"GBK") 昄正确的中文?
2. 如果|页是UTF-8,不能直接正确昄GBKQ需?步{换,str=new String(rs.getBytes(1),"GBK")Q再str=new String(str.getBytes("UTF-8"),"GBK") 才可以正显CZ文。如果数据库里存的是UTF-8Q直接用str=new String(rs.getBytes(1),"GBK")或者str=new String(rs.getString(1).getBytes("ISO-8859-1"),"GBK")可以显CZ文了?
以上是读取数据库里中文正显C在|页上,下面三点是如何正存入数据库?
四、数据库q接方式使用UTF-8~码
JSP中要把网输入的中文存入数据库,通常有一个提?Submit)的过E,是用str=request.getParameter("username")Q然后执行update或者insert语句来存入数据库。如何赋值给str很重要,而且q里中文输入与网|使用的字体编码有兟?
1?|页使用UTF-8Q用str= new String(request.getParameter("username").getBytes("ISO-8859-1"),"UTF-8")或者str= new String(request.getParameter("username").getBytes(),"UTF-8")Q都可以使得存到数据库里的数据是UTF-8~码?
2. |页使用GBKQ用str= new String(request.getParameter("username").getBytes(),"GBK")Q那么存入数据库的是UTF-8~码?
3. 值得注意的是使用UTF-8的数据库q接方式不能存得GBK?
五、数据库q接方式使用GBK~码
1. 输入使用GBK|页Q存到数据库里是GBK的方? str= new String(request.getParameter("username").getBytes("ISO-8859-1"),"GBK") 或者str= new String(request.getParameter("username").getBytes(),"GBK")?
2. |页使用GBKQ想存入UTF-8到数据库里,要分2? 先str=new String(request.getParameter("username").getBytes(),"GBK")Q再str=new String(str.getBytes("UTF-8"),"GBK")卛_?
3. |页使用UTF-8Q而且使用str= new String(request.getParameter("username").getBytes("ISO-8859-1"),"GBK") 或者str= new String(request.getParameter("username").getBytes(),"UTF-8")Q那么存到数据库里的数据是UTF-8~码?
4. |页使用UTF-8Q而且使用str= new String(request.getParameter("username").getBytes("ISO-8859-1"),"UTF-8")Q那么存到数据库里的数据是GBK~码?
六、数据库q接方式使用~省Q即不用参数useUnicode和characterEncoding
1. |页使用GBKQ如果用str= request.getParameter("username")或者str= new String(request.getParameter("username").getBytes())Q那么在数据库里的数据是GBK码。网用UTF-8 和用str= request.getParameter("username")Q则存入数据库是UTF-8~码?
2. 如果使用str= new String(request.getParameter("username").getBytes("ISO-8859-1"))Q那么根据网|供的字体~码而存到数据库里,比如是UTF-8的网,那么存到数据库中是UTF-8~码Q如果用GBK|页Q那么存到数据库里的字就是GBK~码?
3. 如果使用str= new String(request.getParameter("username").getBytes("UTF-8"),"UTF-8")q一U组合能存到正确的数据外Q其他存到数据库里的数据则都是ؕ码或者错误码。在q个UTF-8l合的特例中Q网用的是GBKQ则存放到数据库里就是GBKQ网用UTF-8Q那么存到数据库里的是UTF-8?
4. |页是GBK的要存得UTF-8Q一定需?? company=new String(request.getParameter("company").getBytes(),"GBK")和company=new String(company.getBytes("UTF-8"))?
5. |页是UTF-8的,不能存得GBK在数据库里,一句话Q改变数据库q接方式不能存得GBK码?
以上所有的都是ZJSP|页和数据库交换数据Q下面讨Z下纯JAVA~程下的字体~码转换?
七、数据库q接方式使用UTF-8~码
1. 数据库里的中文是UTF-8Q可以{换ؓGBK,但不能把GBK存入数据库?
2. 数据库是GBKQ如果{换ؓUTF-8Q用content=new String(rs.getBytes(2),"GBK")直接content存入数据库就可ؓUTF-8?
八、数据库q接方式使用GBK~码
1. 数据库里的中文是UTF-8Q如果{换ؓGBKQ用content= new String(rs.getString(2).getBytes(),"UTF-8"),再直接用update或者insert语句插入到数据库Q即存得GBK。如果用content= new String(rs.getString(2).getBytes(),"GBK")或者content= new String(rs.getString(2).getBytes())Q再存入数据库即存得q是UTF-8~码?
2. 数据库里的中文是GBKQ如果{换ؓUTF-8Q用content= new String(rs.getString(2).getBytes("UTF-8"))或者content= new String(rs.getString(2).getBytes("UTF-8"),"GBK")Q再直接使用update或者insert语句插入到数据库,卛_得UTF-8?
3. 如果某个String是GBKQ要转换为UTF-8Q也是用content= new String(GBKstr.getBytes("UTF-8"))或者content= new String(GBKstr.getBytes("UTF-8"),"GBK"); 如果某个String是UTF-8Q要转换为GBKQ应该用new String(UTFstr.getBytes("GBK"),"UTF-8")?
九、数据库q接方式使用~省,即不跟参?
1. str2=new String(GBKstr.getBytes("UTF-8"),"ISO-8859-1")Q可以将数据库里的GBK~码转换为UTF-8?
2. dUTF-8然后存入UTF-8Q则用str1=new String(UTFstr.getBytes(),"ISO-8859-1")或者str1=new String(UTFstr.getBytes("GBK"),"ISO-8859-1")?
3. 不能实现数据库里的UTF-8转换为GBK?
如果采用UTF-8的数据库q接方式或者缺省数据连接方式,那么无法UTF-8转ؓGBKQ而GBK的数据库q接方式可以实现UTF-8和GBK的相互{换。徏议大安用GBK的数据连接方式?/font>
WI部分 事务服务q?/font> 事务是一个非帔R要的~程概念Q用事务,可以很简单地构造出可靠E_的应用程序,其寚w些需要进行ƈ发数据访问的应用E序Q事务更是重要的多。事务的概念最初应用在那些用于商务操作的应用程序上Q在q些应用中,事务用来保护中央数据库中的数据。随后,随着分布式计的发展Q事务在分布式计领域中也得Cq泛的应用。现在,在分布式计算领域中,公认Z务是构造可靠性分布式应用E序的关键?/p> 1.1事务产生的动?/b> 1.1.1原子操作 考虑q样一个应用:用户把钱从一个银行̎可{账至另一个银行̎P需要将资金从一个银行̎号中取出Q然后再存入另一个银行̎号中。理x_q两ơ操作都应该成功。但是,如果有错误发生,则两ơ操作都应该p|Q否则的话,操作之后其中一个̎号中的金额将会是错误的,整个操作q程应该是原子性的Q两个操作都是一个原子事务操作的一部分?/p> 可以使用异常处理来处理上q问题,代码如下Q?/p>
上面q种解决Ҏ从存在着下面的问题:
因此Q需要一U事务处理机制来保证q种原子性的操作?/p> 1.1.2 |络故障或机器故?/p> Z在发生严重故障之后,能够保证pȝ的状态是一致的Q所以需要一U恢复性的机制来保证这一炏V?/p> 1.1.3 数据׃n 需要一U机制来保证多用户ƈ发访问数据的问题?/p> 以上q些问题的解x法,便是使用事务服务?/p> 1.2 使用事务服务带来的好?/b> 使用事务Q便可以利用事务的四个重要属性:ACID?/p>
事务服务支持的应用由下列实体l成Q?/p>
下图展示了一个简单的事务应用Q包含了基本的事务元素:
事务服务提供下列操作Q?/p>
3.1事务模型
3.2 事务的终?/b> 通过发出提交h或回滚请求来l止事务。通常Q事务是由发起事务的客户(事务发v?来终止的?/p> 3.3事务的完整?/b> 某些事务服务的实Cؓ了保证事务的完整性,在事务服务接口的使用上施加了一些限制。这UCؓchecked事务行ؓ。例如,在事务的所有Q务完成之前提交事务会D数据不完整。事务服务的Checked实现会阻止事务的q早提交。其他事务服务的实现则完全依赖应用程序来提供事务的完整性保证,q称为unchecked事务行ؓ?/p> 3.4事务的上下文 事务上下文惟一标识了一个事?保存了事务的当前状态。通过查询事务上下文,可以得知自n是否处于事务中,所在事务的状态以及其他一些有用的信息?/p> 事务上下文可以作Z务操作调用的一部分隐式C递给事务对象。事务服务也允许E序员将事务上下文作Z务请求的昄参数来传递?/p> 分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务理器分别位于不同的分布式系l的不同节点之上。ؓ了实现分布式事务Q需要用下面将介绍的两阶段提交协议?/p>
在分布式事务两阶D|交协议中Q有一个主事务理器负责充当分布式事务协调器的角色。事务协调器负责整个事务q之与|络中的其他事务理器协同工作?/p> Z实现分布式事务,必须使用一U协议在分布式事务的各个参与者之间传递事务上下文信息QIIOP便是q种协议。这p求不同开发商开发的事务参与者必L持一U标准协议,才能实现分布式的事务?/p> 以上从事务整体描q的角度要介l了事务服务产生的动机、事务服务的应用和事务服务的功能Q下面以J2EE中的事务服务Z对事务的实现作简要的介绍?/p> |
WII部分 J2EE中的事务服务 Java TM2 Platform, Enterprise Edition(J2EE)化了分布式事务管理应用程序的~写。J2EE包括了两套规范,用来支持分布式的事务Q一U是Java Transaction API(JTA)Q另一U是Java Transaction Service(JTS)。JTA是一U高层的Q与实现无关的,与协议无关的APIQ应用程序和应用服务器可以用JTA来访问事务。JTS则规定了支持JTA的事务管理器的实现规范,在高层API之下实现了OMG Object Transaction Service(OTS) 1.1规范的Java映射。JTS使用Internet Inter-ORB Protocol(IIOP)来传播事务。作为J2EEq_实现的一部分QSUN实现了一个支持JTS的事务管理器Q同时还实现了JTA? JTA和JTS让J2EE应用服务器完成事务管理,q样׃得组件开发h员摆׃事务理的负担。开发者只需在部|描q符中声明事务管理属性,便可以得EJBlg参与C务之中,由应用服务器来负责事务的理?/p>
企业UJava中间件的分布式事务服务包括五层:事务理?Transaction Manager)、应用服务器(Application Server)、资源管理器(Resource Manager)、应用程?Application Program)和通信资源理?Communication Resource Manager)。每一层都通过实现一l事务API和相x制参与到分布式事务处理系l中?/p>
从事务管理器的角度出发,事务服务的具体实现是不需要暴露出来的Q只需要定义高层接口,使得事务服务的用户可以驱动事务界限、资源获取、事务同步和事务恢复q程。JTA的目的是定义事务理器所要求的本地Java接口Q从而在企业U分布计环境中支持事务理。下图中的小半圆代表JTA规范?/p> 本节从事务管理器实现者的角度描述了实现方面的要求。如下图Q事务管理器必须实现JTA接口Q用于支持应用服务器和资源管理器。不要求实现对JDBC 1.0 Driver和非JTA资源理器支持。也不要求实现对各种CORBA应用实体的支持,如事务客L(Transactional Client)、事务服务器(Transactional Server)和恢复服务器(Recoverable Server)?/p> Java Transaction API׃部分l成Q高层的应用事务划分接口(供事务客户?、高层的事务理器接?供应用服务器使用)和X/Open XA协议的标准Java映射(供事务性资源管理器使用)?/p> 4.1 UserTransaction接口 Javax.transaction.UserTransaction接口使得应用E序能够~程控制事务边界。这个接口可以由Java客户端程序或者EJB来用?/p> 4.1.1在EJB Server中的UserTransaction支持 EJB中对事务的管理有两种cdQ?/p>
4.1.2 事务客户端中的UserTransaction支持 Java客户端程序需要首先通过JNDI来获得UserTransaction对象的引用,然后使用该对象的Ҏ完成事务的开始、提交或者回滚?/p>
4.2 TransactionManager接口 应用服务器用javax.transaction.TransactionManager接口来代表受控的应用E序控制事务的边界。例如,EJB容器Z务性EJBlg理事务状态?/p> 4.3 Transaction接口 使用Transaction接口可以执行与目标对象相兌的事务操作?/p> 4.4 XAResource接口 Javax.transaction.xa.XAResource接口是基于X/Open CAE规范(分布式事务处理:XA规范)工业标准XA接口的Java映射?XAResource接口定义了分布式事务处理环境(DTP)中资源管理器和事务管理器之间是如何交互的。资源管理器的资源适配器实CXAResource接口Q将事务同事务资源联pv来,cM关系数据库的一个连接?/p> 4.5 Xid接口 Javax.transaction.xa.Xid接口是X/Open事务标识WXIDl构的Java映射。这个接口由事务理器和资源理器来使用Q对于应用程序和应用服务器而言q个接口是不可见的?/p> |
Dale Green?/p>
JSP WU ?/p>
一个典型的企业应用E序在一个或多个数据库中讉K和存储信息。因些信息对于商业操作非帔R要,它必ȝ、实时、可靠。如果允许多个程序同时更新相同的数据Q就会破坏数据的完整性。如果在一个商业交易处理过E中Q部分数据被更新后系l崩溃也破坏数据完整性。事务通过预防以上情况的发生确保数据的完整性。事务控制多个应用程序对数据库的q发操作。如果发生系l崩溃,事务保恢复的数据崩溃前保持一致?/p>
本章内容Q?/strong>
什么是事务
容器理事务
事务的属?/p>
回滚容器管理事?/p>
同步会话bean实例变量
容器管理事务中不允怋用的Ҏ
Bean 理事务
JDBC事务
JTA 事务
非提交q回事务
在Bean理事务中不允许使用的方?/p>
企业Bean事务摘要
事务时
隔离U别
更新多个数据?/p>
Web lg事务
模拟一个商业交易,应用E序需要完成几个步骤。例如,一个胦物应用程序,可能会将资金从经常性帐Pchecking accountQ{到储蓄性̎Psaving accountQ,该交易的伪码表示如下Q?/p>
begin transaction
debit checking account
credit savings account
update history log
commit transaction
三个步骤要么全部完成Q要么一个都不做。否则数据完整性将被破坏。因Z务中的所有步骤被看作一个统一的整体,所以事务一般被定义Z个不可分割的工作单元?
l束事务有两U方法:提交或者回滚。当一个事务提交,数据修改被保存。如果事务中有一个步骤失败,事务回滚,q个事务中的已经执行的动作被撤销。例如在上面的伪码中Q如果在处理W二步的时候硬盘驱动器崩溃Q事务的W一步将被撤销。尽事务失败,数据的完整性不会被破坏Q因为帐目仍然保持^衡?/p>
前面伪码中,begin和commit标明了事务的界限。当设计一个企业Bean的时候,你要军_怎样通过容器理或bean理事务来指定事务界限?/p>
在容器管理事务的企业Bean中,EJB容器来设定事务界Uѝ你能够在Q何企业Bean中用容器管理事务:会话Bean、实体Bean或?Message-driven Bean。容器管理事务简化了开发,因ؓ企业Bean不用~码来显式制定事务界限。代码不包括开始结束事务的语句。典型的Q容器会在一个企业Bean的方法被调用前立卛_始一个事务,在这个方法退Z前提交这个事务。每个方法都兌一个事务。在一个方法中不允许嵌套或多个的事务存在。容器管理事务不需要所有的Ҏ都关联事务。当部v一个BeanӞ通过讑֮部v描述W中的事务属性来军_Ҏ是否兌事务和如何关联事务?/p>
事务的属?/b>
一个事务的属性控制了事务的用范围。图 14-1说明了ؓ什么控制事务的范围很重要。图中,method-A开始一个事务然后调用Bean-2中的method-B.它运行在method-A开始的事务中还是重新执行一个新的事务?l果要看method-B中的事务属性?/p>
?14-1 Transaction Scope
一个事务属性可能有下面的属性之一Q?/p>
?Required
?RequiresNew
?Mandatory
?NotSupported
?Supports
?Never
如果客户端正在一个运行的事务中调用一个企业Bean的方法,q个Ҏ在q个客户端的事务中执行。如果客L不关联一个事务,q个容器在运行该Ҏ前开始一个新的事务?/p>
Required属性在许多事务环境中可以很好的工作Q因此你可以把它作ؓ一个默认|臛_可以在早期开发中使用。因Z务的属性是在部|描q符中声明的Q在以后的Q何时候修改它们都很容易?/p>
如果客户端在一个运行的事务中调用企业Bean的方法,容器的步骤是Q?/p>
1Q挂起客L的事?/p>
2Q开始一个新的事?/p>
3Q代理方法的调用
4Q方法完成后重新开始客L的事?/p>
如果客户端不兌一个事务,容器q行q个Ҏ以前同样开始一个新的事务。如果你想保证该Ҏ在Q何时候都在一个新事物中运行,使用RequiresNew属性?/p>
如果客户端在一个运行的事务中调用企业Bean的方法,q个Ҏ在客户端的事务中执行。如果客L不关联事务,容器抛出TransactionRequiredException 异常?/p>
如果企业Bean的方法必M用客L的事务,那么׃用Mandatory属性?/p>
如果客户端在一个运行的事务中调用企业Bean的方法,q个容器在调用该Ҏ以前挂v客户端事务。方法执行完后,容器重新开始客L的事务?/p>
如果客户端不兌事务Q容器在Ҏq行以前不会开始一个新的事务。ؓ不需要事务的Ҏ使用NotSupported属性。因Z务包括整个过E,q个属性可以提高性能?/p>
Supports
如果客户端在一个运行的事务中调用企业Bean的方法,q个Ҏ在客L的事务中执行Q如果这个客L不关联一个事务,容器q行该方法前也不会开始一个新的事务。因属性Ҏ的事务行Z定Q你应该谨慎使用Supports属性?/p>
Never
如果客户端在一个运行的事务中调用企业Bean的方法,容器抛出RemoteException异常。如果这个客L不关联一个事务,容器q行该方法以前不会开始一个新的事务?/p>
Summary of Transaction AttributesQ事务属性概要)
?14-1 列出了事务属性的影响。事务T1和T2都被容器控制。T1是调用企业BeanҎ的客L的事务环境。在大多数情况下Q客L是其它的企业Bean。T2是在Ҏ执行以前容器启动的事务。在?14-1中,“None”的意思是q个商业Ҏ不在容器控制事务中执行。然而,该商业方法中的数据库操作可能在DBMS理控制的事务中执行?/p>
Setting Transaction Attributes Q设定事务属性)
因ؓ事务属性被保存在配|描q符中,他们会在J2EE应用E序开发的几个阶段被改变:创徏企业BeanQ应用程序装配和部v。然而, 当创个Bean的时候指定它的属性是企业Bean开发者的责Q。只有将该组件装配到一个更大的应用E序时可以由开发者修改该属性,而不要期待J2EE应用E序部v者来指定该事务属性?/p>
?14-1 事物属性和范围 |
||
事务属?/b> |
客户端事?/b> |
商业Ҏ事务 |
Required |
None |
T2 |
T1 |
T1 |
|
RequiresNew |
None |
T2 |
T1 |
T2 |
|
Mandatory |
None |
error |
T1 |
T1 |
|
NotSupported |
None |
None |
T1 |
None |
|
Supports |
None |
None |
T1 |
T1 |
|
Never |
None |
None |
T1 |
Error |
你可以ؓ整个企业Bean或者单个方法指定事务属性。如果你为整个企业Bean和它某个Ҏ各指定一个事务属性,Ҏ指定的事务属性优先。当为单个方法指定事务属性时Q不同类型企业Bean的要求也不同。会话Bean需要ؓ商业Ҏ定义属性属性,但createҎ不需要定义事务属性。实体Bean需要ؓ商业Ҏ、createҎ、removeҎ和查找(finderQ方法定义事务属性。Message-driven Bean需要ؓonMessageҎ指定属性事务,而且只能是Required和NotSupported其中之一?/p>
容器理事务的回?/b>
在以下两中情况下Q事务将回滚。第一Q如果生一个系l异常,容器自动回滚该事务。第二,通过调用EJBContext接口SetRollbackOnlyҎQBeanҎ通知容器回滚该事务。如果Bean抛出一个应用异常,事务不会自动回滚,但可以调用SetRollbackOnly回滚。对于一个系l和应用的异常,参考第5章的处理异常一节?/p>
下面q个例子的代码在j2eetorial/examples/src/bank目录下。在命o行窗口下q入j2eetutorial/examples目录执行ant bank命o~译q些代码Q执?ant create-bank-table命o创徏要用到的表。一个BankApp.earh文g在j2eetutorial/examples/ears目录下。通过BnankEJB 实例的transferToSavingҎ来说明setRollbackOnlyҎ的用法。如果余额检查出现负敎ͼ那么transferToSaving调用setRollBackOnly回滚事务q抛Z个应用程序异常(InsufficientBalanceExceptionQ。updateChecking和updateSaving Ҏ更新数据表。如果更新失败,q两个方法抛出SQLException异常而transgerToSavingҎ抛出EJBException异常。因为EJBException是一个系l异常,它容器事务自动回滚事务。TransferTuSaving Ҏ的代码如下:
public void transferToSaving(double amount) throws
InsufficientBalanceException {
checkingBalance -= amount;
savingBalance += amount;
try {
updateChecking(checkingBalance);
if (checkingBalance < 0.00) {
context.setRollbackOnly();
throw new InsufficientBalanceException();
}
updateSaving(savingBalance);
} catch (SQLException ex) {
throw new EJBException
("Transaction failed due to SQLException: "
+ ex.getMessage());
}
}
当一个容器回滚一个事务,它L会撤消事务中已执行的SQL语句造成的数据改动。然而,仅仅在实体Bean中容器回滚才会改变Bean的实例变量(与数据库状态有关的字段Q。(q是因ؓ容器会自动调用实体Bean的ejbLoadҎQ该Ҏ从数据库中读入实例变量的倹{)当发生回滚,会话Bean必须昑ּ重新讄所有被事务改动q的实例变量。重设会话Bean的实例变量最单的Ҏ是实现SessionSynchronization接口?/p>
同步会话Bean的实例变?/b>
SessionSynchronization接口是可选的Q它允许你在Bean的实例变量和它们在数据库中的相应g间保持同步。容器会在事务的几个主要阶段调用SessionSynchronization接口的对应方法—afterBegin、beforeCompletion和afterCompletion?/p>
AfterBeginҎ通知Bean实例一个新的事务已l开始。容器在调用商业Ҏ以前立即调用afterBeginҎ。afterBeginҎ是从数据库中d实例变量值的最佳位|。例如,在BanBeancMQ在afterBeginҎ中从d了CheckingBalance和savingBalance变量的|
public void afterBegin() {
System.out.println("afterBegin()");
try {
checkingBalance = selectChecking();
savingBalance = selectSaving();
} catch (SQLException ex) {
throw new EJBException("afterBegin Exception: " +
ex.getMessage());
}
}
商业ҎҎ完成以后Q容器调用beforeCompletionҎQ不q仅仅是在事务提交以前。BeforeCompletionҎ是会话Bean回滚事务的最后时机(通过调用setRollbackOnlyҎQ?如果会话Beanq没有实例变量的值更新数据库Q就在beforCompletionҎ里实现?/p>
afterCompletionҎ指出事务已经完成。它只有一个布型的参敎ͼtrue表示事务被正提交false表示事务回滚。如果事务回滚,会话Bean可以在该Ҏ中从数据库中重新d它的实例变量|
public void afterCompletion(boolean committed) {
System.out.println("afterCompletion: " + committed);
if (committed == false) {
try {
checkingBalance = selectChecking();
savingBalance = selectSaving();
} catch (SQLException ex) {
throw new EJBException("afterCompletion SQLException:
" + ex.getMessage());
}
}
}
容器理事务中不允许使用的方?/b>
你不应该调用可能q扰容器讄的事务界U的ҎQ下面列Z所有禁止的ҎQ?/p>
?java.sql.Connection接口的commit、setAutoCommit和rollbackҎ
?javax.ejb.EJBContext 接口的getUserTransactionҎ
?javax.transaction.UserTransaction接口的所有方?
然而你可以在Bean理事务中用这些方法设|事务界限?/p>
在一个Bean理事务中,会话Bean或者Message-driven Bean是用代码昑ּ讄事务界线的。实体Bean不能使用Bean理事务Q只能用容器管理的事务。虽然容器管理事务Bean需要较的代码Q但它也有一个局限:Ҏ执行Ӟ它只能关联一个事务或不关联Q何事务。如果这个局限你Bean~码困难Q你应该考虑使用Bean理事务。(译者:实际上J2EE服务器不支持嵌套事物Q那么Bean理事务唯一的优点就是可以在一个方法中一ơ启动多个事务)
下面的伪码很好说明了Bean理事对商业逻辑的紧密控制。通过查各U条Ӟ伪码军_是否在商业方法中启动或停止不同的事务?/p>
begin transaction
...
update table-a
...
if (condition-x)
commit transaction
else if (condition-y)
update table-b
commit transaction
else
rollback transaction
begin transaction
update table-c
commit transaction
当ؓ会话Bean或Message-driver Bean的Bean理事务~码Ӟ你必d定是使用jdbc或者JTA事务。下面的内容了两U事务类型?/p>
JDBC 事务
JDBC事务通过DBMS事务理器来控制。你可能会ؓ了用会话Bean中的原有代码而采用JDBC事务这些代码封装到一个事务中。用JDBC事务Q要调用java.sql.Connection接口的commit和rollbackҎ。事务启动是隐式的。一个事务的从最q的提交、回滚或q接操作后的W一个SQL的语句开始。(q个规则通常是正的Q但可能DBMS厂商的不同而不同)
代码资源
下面的例子在j2eetutorial/examples/src/ejb/warehouse目录下。在命o行窗口中q入j2eetutorial/examples目录执行ant bank命o~译q些源文Ӟ执行ant create-warehouse-table命o创徏要用到的表,一个样本WarehouseApp.ear文g在j2eetutorial/example/ears 目录下?/p>
下面的代码来自WarehouseEJB例子Q一个会话Bean通过使用Connection接口的方法来划定Bean理事务界限。shipҎ以调用名为con的连接对象的setAutoCommitҎ开始,该方法通知DBMS不要自动提交每个SQL语句。接下来ship Ҏ更新order_item和inventory数据表。如果更新成功,q个事务׃被提交。如果出现异常,事务回滚?/p>
public void ship (String productId, String orderId, int
quantity) {
try {
con.setAutoCommit(false);
updateOrderItem(productId, orderId);
updateInventory(productId, quantity);
con.commit();
} catch (Exception ex) {
try {
con.rollback();
throw new EJBException("Transaction failed: " +
ex.getMessage());
} catch (SQLException sqx) {
throw new EJBException("Rollback failed: " +
sqx.getMessage());
}
}
}
JTA 事务
JTA是Java Transaction API 的羃写。这些API 允许你用独立于具体的事务理器实现的Ҏ定事务界限。J2EE SDK 事务理器通过Java事务服务QJava Transaction Service, JTSQ实现。但是你的代码ƈ不直接调用JTS中的ҎQ而是调用JTAҎ来替代,JTAҎ会调用底层的JTS实现?/p>
JTA事务被J2EE 事务理器管理。你可能需要用一个JTA事务Q因为它能够l一操作不同厂商的数据库。一个特定DBMS的事务管理器不能工作在不同种cȝ数据库上。然而J2EE事务理器仍然有一个限制——它不支持嵌套事务。就是说Q它不能在前一个事务结束前启动另一个事务?/p>
下面例子的源代码在j2eetutorial/examples/src/ejb/teller目录下,在命令行H口q入j2eetutorial/examples目录Q执行ant teller命o~译q些源文Ӟ执行ant create-bank-teller命o创徏要用到的表。一个样本TellerApp.ear文g在j2eetutorial/examples/ears目录下?/p>
要自q定事务界限,可以调用javax.transaction.UserTransaction接口的begin、commit和rollbackҎ来确定事务界限(该接口只能在SessionBean中用,实体Bean不允怋用用戯定义的)。下面选自TellerBeancȝ代码C了UserTransaction的用法。begin和commitҎ定了数据库操作的事务界限,如果操作p|则调用rollback回滚事务q抛出EJBException异常?/p>
public void withdrawCash(double amount) {
UserTransaction ut = context.getUserTransaction();
try {
ut.begin();
updateChecking(amount);
machineBalance -= amount;
insertMachine(machineBalance);
ut.commit();
} catch (Exception ex) {
try {
ut.rollback();
} catch (SystemException syex) {
throw new EJBException
("Rollback failed: " + syex.getMessage());
}
throw new EJBException
("Transaction failed: " + ex.getMessage());
}
}
非提交返回事?/b>
使用Bean理事务的无状态会话Bean在事务返回前必须提交或者返回事务,而有状态的会话Bean没有q个限制?/p>
对于使用JTA事务的有状态会话BeanQBean实例和事务的兌过大量用户调用被保持,甚至被调用的每个商业Ҏ都打开和关闭数据库q接Q该市无兌也不断开Q直C务完成(或回滚)?/p>
对于使用JDBC事务的有状态会话BeanQJDBCq接过用户调用保持Bean和事务之间的兌。连接关闭,事务兌被释放?/p>
在Bean理事务中不允许使用的方?/b>
在Bean理的事务中不能调用EJBContext接口的getRollbackOnly和setRollbackOnlyҎQ这两个Ҏ只能在容器管理事务中被调用。在Bean理事务中,应调用UserTransaction接口的getStatus和rollbackҎ?/p>
如果你不能确定怎么在企业Bean中用事务,可以用这个小技巧:在Bean的部|描q符中,制定事务cd为容器管理,把整个BeanQ所有方法)的事务属性设|ؓRequired。大多数情况下,q个配置可以满你的事务需求?/p>
?4-2列出了不同类型的企业Bean所允许使用的事务类型。实体Bean只能使用容器理事务Q但可以在部|描q符中配|事务属性,q可以调用EJBContext接口的setRollbackOnlyҎ来回滚事务?/p>
?14-2 企业Bean 允许的事务类?/b> |
||||
企业Bean cd |
容器理事务 |
Bean理事务 |
||
JTA |
JDBC |
|||
实体Bean |
Y |
N |
N |
|
会话Bean |
Y |
Y |
Y |
|
Message-driven |
Y |
Y |
Y |
会话Bean既可以用容器管理事务也可以使用Bean理事务。Bean理事务又有两种cdQJDBC事务和JTA事务。JDBC事务使用Connection接口的commit和rollbackҎ来划分事务界限。JTA事务使用UserTransaction接口的begin、commit和rollbackҎ来划分事务界限?/p>
在Bean理事务的会话Bean中,混合使用JTA事务和JDBC事务是可能的。但是我不推荐这样用,因ؓq样会造成代码的调试和l护都很困难?/p>
Message-driver Bean和会话Bean一h可以使用容器理事务也可以用Bean理事务?/p>
对于容器理事务Q事务超旉隔是通过讄default.properties文g中ransaction.timeout属性的值来定的,该文件在J2EE SDK安装目录的config子目录下。如下例事务超旉隔设|ؓ5U钟Q?/p>
transaction.timeout=5
q样Q当事务?U钟内还没有完成Q容器将回滚该事务?/p>
J2EE SDK安装后,时间隔的缺省gؓ0Q表CZ计算时Q无Z务执行多长时_除非异常出错回滚Q一直等待事务完成?/p>
只有使用容器理事务的企业Bean才会受到transaction.timeout属性值的影响。Bean理的JTA事务使用UserTransaction接口的setTransactionTimeoutҎ来设|事务超旉隔?/p>
事务不仅保证事务界限内的数据库操作全部完成(或回滚)同时q隔L据库更新语句。隔ȝ别描q被修改的数据对其他事物的可见度?/p>
假如一个应用程序在事务中修改一个顾客的电话LQ在事务l束前另一个应用程序要d该条记录的电话号码。那么第二个应用E序是读取修改过但还没提交的数据Q还是读取未修改前的老数据呢Q答案就取决于事务的隔离U别。如果事务允许其他程序读取未提交的数据,会因Z用等待事务结束而提高性能Q同时也有一个缺点,如果事务回滚Q其他应用程序读取的是错误的数据?/p>
容器理持久性(CMPQ的实体Bean的事务别无法修改,它们使用DBMS的默认个理解别,通常是READ_COMMITTED?/p>
Bean理持久性(BMPQ的实体Bean和两U会话Bean都可以通过在程序中调用底层DBMS提供的API来设|事务别。例如,一个DBMS可能允许你如下调用setTransactionIsolationҎ隔ȝ别设|成可读取未提交数据Q?/p>
Connection con;
...
con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);
不要在事务执行期间更攚wȝ别,通常隔离U别的更改会引vDBMS产生一ơ隐式提交。因为隔ȝ别的控制会跟具体的DBMS厂商不同而不同,具体的信息请参考DBMS的文档。J2EEq_规范不包括隔ȝ别标准?/p>
J2EE事务理器控制着除了Bean理的JDBC事务以外的所有企业Bean事务Q它允许企业Bean在同一个事务中更新多个数据库。下面示范在单个事务中更新多个数据库的两个应用?/p>
?4-2中,客户端调用Bean-A的商业方法,商业Ҏ启动一个事务,更新数据库X和YQBean-A的商业方法有调用Bean-B的商业方法,Bean-B的商业方法更新数据库Z然后q回事务的控制权lBean-A的商业方法,由Bean-A提交该事务。三个数据库的更新都在同一个事务中发生?/p>
?14-2 更新多个数据?/strong>
?4-3中,客户端调用Bean-A的商业方法,该商业方法启动一个事务ƈ更新数据库XQ然后调用另一个J2EE服务器中的Bean-B的方法,该方法更新数据库Y。J2EE服务器保证两个数据库的更新都在同一个事务中q行Q笔者认为应该是W一个J2EE服务器的事务理器管理整个事物)?/p>
?14-3 跨越J2EE服务器更新多个数据库
Weblg中划分事务界限可以用java.sql.Connection接口和javax.transaction.UserTransaction接口中的L一个。跟Bean理事务的会话Bean使用一L两个接口。这两个接口的用方法参考前面几节的内容。Weblg事务的例子在W?0章Servlet技术第四节׃n信息的访问数据库节讲述q?/p>
优质理的四大要素:
选择正确的h?br /> Z们分配正的工作?br /> 保持他们的积极性?br /> 帮助团队凝聚hq保持团队的凝聚力?br /> (其他一切都只是"文案"?
安全和变?br /> 除非感到安全Q否则h们就不能去迎接变化。
在所有成功的工程?以及在绝大多数其他有价值的工作?Q变化都是基本的要素之一。
安全感的~Z会让Z反对变化。
逃避风险是致命的Q因会让你也得不C风险同在的利益。
Z可能会因为来自客观世界的直接的恐吓而觉得没有安全感Q但是如果察觉到理者可能滥用权力来惩罚自己Q他们也会觉得没有安全感?/p>
负面效应
威胁不是提高业W最好的Ҏ?br /> 如果分配的时间一开始就不够Q不威胁有多么吓hQ工作也无法按时完成?br /> 更糟p的是,如果目标没有实现Q你必dC的威胁?/p>
理者必需的n体部?br /> 理涉及到心、肠胃、灵和d?br /> 因此……用心来领导Q相信你的肠?怿你的预感)Q构{团队的灵魂Q训l一个能嗅出谎言的E子?/p>
用指挥战争来作ؓ理的一个比?br /> 在战役开始的时候,理者真正的工作已经完成了?/p>
面试和招?br /> 招聘涉及到所有与理相关的n体部位:心、灵、E子和肠胃(但是主要是肠??br /> 不要试图单独L聘——两副肠胃远比一副肠胃的两倍要好?br /> 对于新的雇员Q让他们承担与以前曾l成功过的同样难度的目Q把有挑战性的目标推迟C一ơ?br /> 征求提示Q你最希望雇的那个人可能还知道其他很好的h选?br /> 多听Q少说?br /> 如果先把材料整理好,那么所有的事情都会q行得更好?/p>
生力的提高
没有"短期生力提?q样的东ѝ?br /> 生力的提高是来自长期投资的?br /> M承诺立刻见效的东襉K很可能是江湖游医所卖的万灵沏V?/p>
风险控制
通过控制风险来管理项目?br /> 为每个项目创建ƈl护风险l计表?br /> 跟踪Ҏ性的风险Q而不只是最后那讨厌的结果?br /> 评估每种风险具体化的概率和可能造成的开销?br /> 对于每种风险Q预标志其具体化的早期征兆?br /> d一个风险控制官Q这个h不应该维护组l内?我能?的态度?br /> 建立单的(可能是匿名的)通道Q让坏消息能传递到高层?/p>
防止p|
壮士断腕?br /> 控制住失败比优化成功更能提高你全面的成W?br /> 要有闯劲Q尽早取消失败的工作?br /> 除非必要Q否则就不要自己d聚一个团队:出去找一个已l成型的团队来用?br /> 保持好的团队在一?只要他们自己愿意)Q以帮助你的lQ者避免团队凝聚得慢或者不能凝聚的问题?br /> 把凝聚在一L团队--准备好、ƈ且也愿意接受新的工作--作ؓ目的收获之一?br /> 目开始时费的一天和最后阶D|费的一天对目造成的伤x同等的?br /> 有无数种Ҏ可以费一天的旉……但是没有Q何一U方法可以拿回一天的旉?/p>
开发过E的建模和模?br /> 你关于完成工作q程的直觉徏模?br /> 在同事的交流中用这些模型,以便交流、提炼关于项目运转的思想?br /> 用模型来模拟目的结果?br /> Ҏ实际的结果来调整模型?/p>
"病态的政治"
每一天,你都必须准确度拿自已的工作去打赌......
......但是q也不能保证"病态的政治"不会影响你?br /> "病态的政治"可能在Q何地方出玎ͼ哪怕是在最健康的组l里面?br /> "病态的政治"的特征:对个人权势的渴望过了组l本w的目标?br /> 即ɘq种不合理的目标与组l的目标背道而驰Q它也可能出现?br /> "病态的政治"的副作用Q它使精的项目变得危险?/p>
度量
度量每个产品的规模?br /> 不要执着于单位——在{待客观度量的时候,先用你自q主观单位?br /> 从所有能得到的原始数?可计的软g特征)自己构造度量单位?br /> 不断完善你的度量方程式,直到它的计算l果与原始数据库中的目工作量有最好的对应关系?br /> 借助数据库画一条趋势线Q把预期的工作量作ؓ人造度量单位值的函数昄出来?br /> 现在Q针Ҏ个要度量的项目,计算Zh造度量单位|q根据这个值在势U上扑ֈ预期工作量倹{?br /> 用生产力势周围的干扰水q作为映的公差指示?/p>
q程和过E改q?br /> 好的q程和持l的q程是绝好的目标?br /> 它们也是非常自然的目标:优秀的技术工作者一定会x它们Q而不你是否告诉他们?br /> 正式的过E改q程序需要花钱、花旉Q特定的q程改进工作q会延缓目q度。尽最l体现出生力上的收P它们也不可能抉|花在q程改进上的旉?br /> 但是目有希望从单个的、正选择的方法改q中得到_的收益,q赢回ؓq次改变付出的时间和金钱?br /> 在项目进行过E中Q不要希望在过一个地方的范围内实施必改进。多U技术的改进E序Q比如说提高整整一个CMM{)很可能让目比不实施q些E序完成得更晚?br /> 标准q程的危险就在于Z可能失去重要的走捷径的机会?br /> 特别是对于h员超~的目Q标准的q程看去会很严}Q因为它们制造出了够的工作Q有用的和无用的Q,让所有h都忙于不停?/p>
改变完成工作的方?br /> 如果不大q减调试时_没有办法让目大幅度提前完成?br /> 高速完成的目用在调试上的旉成比例地得多?br /> 高速完成的目用在设计上的旉也成比例地多得多?br /> 如果你不兛_别hQ?不照ֈ人,别惌他们做一些不同寻常的事情。如果要让他们改变,必d了解Qƈ赞赏Q他们的q去?/p>
压力的效?br /> 压力之下的h无法更快的思考?br /> 增加加班旉只会降低生力?br /> 短期的压力乃至于加班可能是有用的{略Q因为它们能使员工集中精力,q且让他们感到工作的重要性。但是长期的压力肯定是错误的?br /> l理们之所以会施加那么多压力,也许是因Z们不知道该做什么,或者因为其他办法的困难而感到气馁?br /> 最坏的猜想Q用压力和加班的真正原因是Z在项目失败的时候让所有h看上去能好一炏V?br />
愤怒的l理
理中的愤怒和i是会传染的。如果高U管理者喜Ƣ骂人,低理者也会有样学Ppl常被骂的小孩很Ҏ变成爱骂人的父母Q?br /> 理中的辱骂常被认ؓ是一U刺ȀQ可以让员工提高效率。在" 胡萝卜加大棒"的管理策略中Qi骂是最常见?大棒"。但是,哪有辱骂之后q能做得更好的?
如果l理使用辱骂的方法来刺激员工Q这p现经理的无能Q而不是员工的无能?/p>
含糊的规格文?br /> 规格文档中的含糊标志着不同的系l参与者之间存在未解决的冲H?br /> 如果一份规格文档不包含完整的输入输出列表,那么它就是毫无希望的Q它Ҏ没有开始说明Q何东ѝ?br /> 没有Z告诉你一份规格文档是不是p糕。h们往往們于责备自己,而不责备文档?/p>
冲突
只要在开发过E中有多个参与者,׃定会有冲H存在?br /> 创徏、安装系l的业务中特别容易出现冲H?br /> l大多数pȝ开发团队都~Z解决冲突的能力?br /> 冲突应当引v重视。冲Hƈ不是~Z职业道d的行为?br /> 应当提前声明Q所有h??都是受重视的。确保每个别的人都能赢?br /> 谈判困难Q调解容易?br /> 如果两个人的利益是完全或者部分相斥的Q预先做好安排,准备好请双方通过调解来解军_H?br /> CQ我们都站在同一边;跟我们对立的Q是我们要解决的问题?br />
催化剂的角色
有这样一U催化剂式的人格。这LZ帮助团队成型q凝聚,保持团队的健康和生力,从而对目作出贡献。就?催化?别的什么都不干Q其实,通常他们q会q很多别的事Q,q种催化剂的角色也是重要而有价值的?br /> 调解?催化?的一特D工作。调解是可以学的Q而且只需要很的投资p学会?br /> 调解应该从一个小的仪式开始?我能帮你们调解一下吗Q?在解军_H的时候,q是必要的第一个步骤?/p>
人类的错?br /> 你|于ȝQ不是你不知道的东西……而正是你"知道"l不会置你于d的东ѝ?/p>
人员安排
在早期,人员编会迫佉K目跨q关键的设计阶段Q这是ؓ了让所有的人都有事可做Q?br /> 如果在设计完成之前, 工作先被分给了许多hQ那么h与h之间、工作与工作之间的接口就会很复杂?br /> q会使团队内部耦合度提高,会议旉、重复劳动和无效的工作都会增加?br /> 理想的h员安排是q样的:在项目的大部分时间内由小型核心团队来做设计工作,在开发的最后阶D(旉安排的最?/6Q加入大量的人手?br /> 可怕的猜想Q时间安排紧q的目Q与旉安排比较合理的项目比hQ完成的旉反而会更长?/p>
目C会?br /> 让不必与会的人可以放心离开Q从而保持会议的_。有一份公开的议E,q严格执行,q是最单的办法?br /> 目需要A式?br /> 用小的仪式来Z注意目的目标的理想状态:规模会议、零~陷工作{等?br /> 采取行动Q防止h们随便发怒?br /> CQ愤?恐惧。随便对下发怒的l理一定是因ؓ恐惧才会q样做的?br /> 意见Q如果所有h都懂?愤?恐惧"q个道理Q就能明昄出发怒的人是在害怕。由于无法隐瞒自q恐惧Q他也就不会再生气了。(q不能解册些生气的人的问题Q但是肯定可以让其他人好受一些。)
"病态的政治"(旧话重提)
别想Ҏ一个病态的人?br /> 不要费旉Q也不要因ؓ试ȝ上司的病态而自己受到威胁?br /> 有时候,你唯一的的选择是{待Q等问题自己解决Q或者等一个让你l前q的Z?br /> 奇迹是有可能发生?但是千万别去指望??/p>
_օ?br /> _օ政是p|公司使用的办法,它员工负但p|的责仅R?br /> 公司的目标应该正好相反:兴旺而h性化?br /> 当你听到"_օ?q个词的时候,误住它的u外之韻Ip|和恐吓?/p>
基本常识
目既需要目标,也需要计划?br /> 而且q两者应该不同?/p>
?/strong>jsp规范里,标签h比javabean更丰富的q行时协议。它可以非常机密的和jsp的表C逻辑联系在一P同时Q又hjavabean相同业务处理能力。所以,标签的学习成切的需要,但ؓ了满_际项目的开发,自定义标{学习是不定wq的?/font>
?/strong>q实现接口或者承现有的c,我们可以开发自定义的标{?/font>
?/strong>用的接口有: JspTag
Tag SimpleTag<----SimpleTagSupport
IterationTag <----TagSupport
BodyTag<----BodyTagSupport
?/strong>定义标签的开发包括两个部分的开发:
Q?Q、开发标{处理E序Qjavac)
Q?Q、标{描q文Ӟ.tld文gQ?/font>
?/strong>定义标签的种cL许多Q可以根据实际项目的需要进行编写。但Z不重复的开发轮子,jsp标准推出JSTLQ标准标{ֺQ?/font>
开始我们的旅行吧,只要是快乐的Q谁都想qԌ做的好与不好另当别Z?/font>
Q?Q从Hello World开始吧Q自定义一个类它实现Tag接口QTag接口主要定义的是标签声明周期的方法,比如QdoStartTag()QdoEndTag(){,通过PageContext对象来访问Jsp面的上下文Q?/font>
package com.xmddl.tag.demo;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
public class HelloTag implements Tag {
private PageContext context;
private Tag parent;
public void setPageContext(PageContext ctx) {
this.context=ctx;
}
public void setParent(Tag tag) {
this.parent=tag;
}
public Tag getParent() {
return this.parent;
}
public int doStartTag() throws JspException {
System.out.println("doStartTag");
return Tag.SKIP_BODY;
}
public int doEndTag() throws JspException {
System.out.println("doStartTag");
try {
this.context.getOut().write("Hello World");
} catch (Exception e) {
e.printStackTrace();
}
return Tag.EVAL_PAGE;
}
public void release() {
// TODO Auto-generated method stub
}
}
Tag接口中静态常量:
Tag.SKIP_BODY
Tag.EVAL_PAGE
Tag.EVAL_BODY_INCLUDE
Tag.SKIP_PAGE
Q?Q编写标{描q文Ӟ.tldQ:
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="
http://java.sun.com/xml/ns/j2ee
"
xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance
"
xsi:schemaLocation="
http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd
"
version="2.0">
<description>xmddl 1.1 hello library</description>
<display-name>xmddl hello</display-name>
<tlib-version>1.1</tlib-version>
<short-name>hello</short-name>
<uri>http://www.xmddl.com/tag</uri>
<tag>
<description>我的Hello World</description>
<name>hello</name>
<tag-class>com.xmddl.tag.demo.HelloTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
Q?Q、在web.xml中对标签的引用:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "
http://java.sun.com/dtd/web-app_2_3.dtd
">
<web-app>
<taglib>
<taglib-uri>http://www.xmddl.com/tag</taglib-uri>
<taglib-location>/WEB-INF/mytag.tld</taglib-location>
</taglib>
</web-app>
Q?Q、编写测试页面:
<%@ page language="java" pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib uri="http://www.xmddl.com/tag" prefix="lu"%>
<html>
<head>
<title>MyJsp.jsp</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
<h1>This is a test tag page for person.</h1> <br>
<lu:hello/>
</body>
</html>
下面看看标签中的属性是如何使用的,我们直接l承TagSupportcL~写Q?/font>
package com.xmddl.tag.demo;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
public class HelloSupport extends TagSupport {
private String value;
private static final long serialVersionUID = 1L;
public HelloSupport(){
super();
}
public int doStartTag() throws JspException {
System.out.println("doStartTag");
return TagSupport.EVAL_BODY_INCLUDE;
}
public int doEndTag() throws JspException {
System.out.println("doEndTag");
try {
this.pageContext.getOut().write("标签的属性|"+this.value);
} catch (Exception e) {
e.printStackTrace();
}
return Tag.EVAL_PAGE;
}
public int doAfterBody() throws JspException{
System.out.println("doAfterBody");
try {
this.pageContext.getOut().write("<br>以上为标{内容<br>");
} catch (Exception e) {
e.printStackTrace();
}
return TagSupport.EVAL_BODY_INCLUDE;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
======================
...
<tag>
<name>hellosupport</name>
<tag-class>com.xmddl.tag.demo.HelloSupport</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
...
=======================
<%@ page language="java" pageEncoding="gb2312" import="java.util.*"%>
<%@ taglib prefix="lu" uri="
http://www.xmddl.com/tag
" %>
<html>
<head>
</head>
<body>
<h1>This is a test tag page for person.</h1> <br>
<lu:hellosupport value="red">xmddl</lu:hellosupport>
</body>
</html>
利用TagSupport带来很大的方便,只要重写其中的部分方法即可实C个标{֤理程序,以上开发的标签E序和JSTL1.1标签库一起用时出现一些疑问,原来标签的uri=" http://java.sun.com/jsp/jstl/core "Q但是,在如下的试中会发现c:out中的D不出来,所以,只能改ؓuri=" http://java.sun.com/jstl/core "Q?/font>
<%@ page language="java" pageEncoding="gb2312" import="java.util.*"%>
<%@ taglib prefix="lu" uri="
http://www.xmddl.com/tag
" %>
<%@ taglib prefix="c" uri="
http://java.sun.com/jstl/core
" %>
<html>
<head>
</head>
<body>
<h1>This is a test tag page for person.</h1> <br>
<%request.setAttribute("name","xmddl"); %>
<%session.setAttribute("names","xmddls"); %>
<lu:hellosupport value="red">xmddl</lu:hellosupport><hr>
<c:forEach begin="1" end="5" step="1">
name=<c:out value="${name}"></c:out>
names=<c:out value="${names}"></c:out><br>
</c:forEach>
</body>
</html>
输出如下Q?/font>
name=xmddl names=xmddls
name=xmddl names=xmddls
name=xmddl names=xmddls
name=xmddl names=xmddls
name=xmddl names=xmddls
接着看看q代标签的开发,首先Q我们关注的是接口IterationTagQ还有它的实现类TagSupportQ接口BodyTag对IterationTag的扩展,q有BodyTag的实现类BodyTagSupport?/font>
package com.xmddl.tag.demo;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class MyIteratorSupport extends BodyTagSupport {
private static final long serialVersionUID = 1L;
public String count;
public String strColor;
private int cou;
public void setCount(String count) {
this.count = count;
cou=Integer.parseInt(count);
}
public int doAfterBody() throws JspException {
try {
this.pageContext.getOut().write("<br>");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(cou>1){
cou--;
return MyIteratorTag.EVAL_BODY_AGAIN;
}else{
return MyIteratorTag.SKIP_BODY;
}
}
public int doStartTag() throws JspException {
try {
this.pageContext.getOut().write("<font color='"+this.strColor+"'>");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return MyIteratorTag.EVAL_BODY_INCLUDE;
}
public int doEndTag() throws JspException {
try {
this.pageContext.getOut().write("</font>");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return MyIteratorTag.EVAL_PAGE;
}
public int getCou() {
return cou;
}
public void setCou(int cou) {
this.cou = cou;
}
public String getCount() {
return count;
}
public String getStrColor() {
return strColor;
}
public void setStrColor(String strColor) {
this.strColor = strColor;
}
}
Q=Q=Q=Q=Q=Q=Q=
。。?/p>
<tag>
<description>寚w色的讄</description>
<name>myColor</name>
<tag-class>com.xmddl.tag.demo.MyIteratorSupport</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>count</name>
<required>true</required>
</attribute>
<attribute>
<name>strColor</name>
<required>false</required>
</attribute>
</tag>
。。?/p>
Q=Q=Q=Q=Q=Q=Q=
<%@ page language="java" pageEncoding="gb2312" import="java.util.*"%>
<%@ taglib prefix="lu" uri="<html>
<head>
</head>
<body>
<h1>This is a test tag page for person.</h1> <br>
<lu:myColor count="3" strColor="blue">xmddl</lu:myColor>
</body>
</html>
输出如下
xmddl
xmddl
xmddl