??xml version="1.0" encoding="utf-8" standalone="yes"?>
首先推荐<Painting in AWT and Swing>by Amy Fowler?
Sun在JDK 1.0最初发布了囑ŞAPI?代号AWT (abstract windowing toolkit)Q里面除对GUI基本支持Q如l合各OS的事件分发机制等Q外Q自有一套重量开发GUI的思\Qƈ提供了一l常规用的重量U组件。所谓重量lg是每个lg都引用一个本地对{体peer成员对象Q这个对{体对象利用本地pȝGUI APIl制lg。后来在JDK1.1QAWT包中引进了一套轻量开发GUI的新思\Qƈ提供了一l轻量lg。所谓轻量lg是自n没有本地对等体,而借助重量U组件作为容器来l制lg。JDK 1.1之后Qsun在开发GUI思\上,在效率,扩展性等斚wl出了很多创斎ͼq基于这U新思\推出一套丰富的新组Ӟ轻量U组ӞQsun为此打出一个新的响亮的代号---SwingQƈ推荐以后的GUI开发都应该ZSWING的GUI开发思\开展,应该使用或扩展这套SWING的组件?
不论是AWT模式q是SWING模式QSun的GUI开发思\都是UOO的。开发h员L构徏多个lg对象实例来组合徏立GUIQ这些对象是因不同的输入输出表现被封装ؓ多种lgcȝ实例Q而这些组件类是有合理的承关pd而容易扩展的“套g”。而且两种模式最基本的统一的程序运行思\都是Q?
1Q通过建立各种lg的实例来负责GUI的工作?
2Q?U定出GUI变化时机—java应用E序随需发出h调用或对操作pȝU某U操作的监听Q如暴露被遮挡的H口内容Q?
3. 在时机到来时?#8220;框架E序”来判断ƈ调用应该调用的目标组件实例所提供的各UŞ式的paintҎQ各lg在此Ҏ里通过java 2d API包来实现自己的具体绘刉辑Q来完成各组件绘制?
4. 在GUI的整个生命周期里Q通过以上?23模式来完成整个应用界面的随需而变?
下文主要分析SWING模式?
Swing?开发GUI的基本约定包括:SWING提供4个顶层容器JFrame,JDialog,JApplet,JWindowQ如果是桌面应用Q则GUI必须要有一个JFrameQ如果是览器应用,则GUI必须要有一个JApplet。其他swinglgQ或自定义开发的Swinglg都扩展自JComponentQƈ且其实例要存在于层容器的层ơ树中。下面是一个符合约定的GUI的运行分析?
import javax.swing.JFrame;
import javax.swing.JLabel;
public class BasicSwing {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
private void createAndShowGUI() {
JFrame frame = new JFrame("BasicSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label=new JLabel("hello world");
frame.getContentPane().add(label);
frame.setSize(100,200);
frame.setVisible(true);
}
});
}
}
invokeLaterҎ在执行时首先会gq创建getToolkit---pȝ属性awt.toolkitl出了要加蝲的类Q在windowsq_下即为WToolkit。WToolkit在初始化时会启动AWT-WINDOWSU程(setDaemon-true)Q该U程一直负责从win32pȝ中获取底层事件ƈ接挂到EventQueue事g队列中;同时ȀzAWT-ShutdownU程(setDaemon-false)Q该U程一直监是否满_闭GUI的条ӞpeerMap is nullQAWT-WINDOWS is busyQEDT is busyQ,若是则主动要求关闭EDT也就是GUI最l退出(因ؓGUI环境下只有EDT是非daemonU程Q;WToolkitq有是加蝲sun.java2d.Disposerc,其将在类加蝲初始化时启动Java2D DisposerU程(setDaemon-true, MAX_PRIORITY)Q该U程一直跟t监被废弃的注册记录(WToolkit一个,q有各种peerQ,监测到后执行对应的DisposecL完成相应的资源回收?
invokeLaterҎ同时会创建EventQueue挂在AppContext里,q上向EventQueue提交InvocationEvent以执行上面例子中的RunableQ这导致启动第一个AWT-EventQueue-NU程QEDT-(setDaemon-false)Q?
EDT启动后将一直从EventQueue获取AWTEVENTq行dispatch分发处理Q处理过E中若遇到某些意外或被强制中断都有可能导致EDT熄火Q此时AWT-Shutdown被notify彻底终止AWT的时机是否到来,若不满条g新的EDTQAWT-EventQueue-N+1被启动?
以上徏立v界面GUI的基本运行框架。上qC子的mainU程很快退出,而EDTU程处理InvocationEvent,处理q程x执行Runable.runҎ体?
在EDT中,JFrame被构造,在其构造过E中Q会q加dispose记录?
(addRecord(JFrame.anchorObject, WindowDisposerRecord(appContext,this));)qJava2D Disposer以在失去引用旉攄口资源?
随后JFrame被setvisibleQ在setvisibleq程中,通过WToolkit createFramePeerQƈ注册在AWT-Shutdown的peerMap中以支持AWT-AutoShutDown机制?
Setvisible中将促调用peer.pShow-native代码Q即发送给win32h昄H口Q窗口被打开后awt_windowsU程在eventloop中得到wm_paint消息q行处理Q这是一个异步过E?
awt_windows处理中将有选择地通过RepaintManager加入重画记录区几何区?
RepaintManager. nativeAddDirtyRegioq调度重ȝE单位在EDT中进行绘?
postEvent-InvocationEvent(ProcessingRunnable)QProcessingRunnable随后
在EDT中run时将Ҏ重画录执行可能的H口内容l制--卛_子组件回调paintq程?
上述是SWING层重量U容器组件的一个绘制场景,可以看到是经由awt-windows eventloopC底层事g后触发paintl制Q然而对轻量UswinglgQ其paint都是通过java代码中对repaint调用而触发,其会向RepaintManager.addDirtyRegionQ同时scheduleProcessingRunnable。这是整个GUI生命周期内对l制的两U不同的触发方式Q但触发后的处理都是交由RepaintManager?
回过头去看,JFrame被构造的时候就会创建root pane, layered pane,content pane, glass pane{?q些没有对等体的轻量USwinglg在构造时都将repaint。虽然在创徏windows对等H口之前q些Swinglg已l在要求l制Q但是RepaintManager能够协调好这个步调(具体x当收到repainth时要判断情况Q像q时的请求因为顶层容器还没有l制则不会记录到重画区)。所以最l效果就是在peer.pshow的时候只能看C个空H口Q随后底层消息到来后通过paint回调画这些子lgQ最后hello world才显C出来。如果眼好Q能够看有一?#8220;闪烁”?
q是一个最单的swing应用E序的基本运行机制分析,下面再具体分析?
Swing的GUIL由顶层容器组件和轻量Uswinglgl合建立Q顶层容器和其他lg区别主要在于层容器没有自n的paint逻辑?
所有顶层容器都是通过使用底层pȝAPI来绘制对{体的方式进行paintQ自w没有java2d的paint逻辑实现Q对{体L什么样层容器是什么样Q它只是可以控制对等体的一些可配显C属性。所以效果就是比如在windowsq_上画一个jframeQ除在桌面上昄一个窗口还会在d栏上昄一个条目。Swing?个顶层容器都是在addNotify时才会getToolkit().createPeer(this)QFrame/Dialog/WindowQ?而addNotifyq不是在构造时被调用,而是在pack/show或setvisibleQ这3个所谓的realizedL化方法)时被调用。创Z对等体peer后还要通过peer.pShowQshow/setVisible(true)调用Q调用才会要求底层系l进行显C(所以只有pack是不会显C窗口的Q。在昄H口后底层消息队列得到通知Q此后随着H口被最化后恢复或被遮盖后恢复{系l操作后同样能从底层消息得到通知Q这时的监听处理有选择地通知lRepaintManager一个重画请求进行窗口内?子组仉甅R?
而轻量swinglg绘制有关的职责都委托给了ui成员对象Qui对象使用JAVA2D API q行l制Qpaint成什么样那就是这个组件的样子。具体就是在构造的时候即要updateUI{setUI(UIManger.getUI(this))}。UIManger会根据当前L&F的选择Q根据this.uiClassID来得到ui成员cdƈ建立实例Q以后的paint回调{都推托lui成员cpaintQ这也算是一U策略模式。Setui的过E中除了保存q个ui实例外,repaint来通知RepaintManagerq行paint回调完成lgl制。轻量swinglg在addNotify时也会去创徏对等体getToolkit().createPeer(this)( LightWeightPeer)Q但q个peer的实玎ͼNullComponentPeerQ是个空壛_Q只是作Z个轻量lg的标讎ͼ以后的很多事件处理等都要判断peer是否instance of LightWeightPeer从而能够进行不同处理。同LAddnotify也不是在构造时被调用,而是在被加入container时被调用?
注意Q构造方法本w就是状态模式的W一状态,所以GUIlg的构造方法里应该要努力完成自n的绘制来W合自己的地位。轻量lg是按这个意义在构造方法里去通知repaintmanagerq行自nl制的,但是层容器却将真正的绘制意图createPeer延迟CLҎ里。这是因为首先一个合乎思维的表N辑是先有容器,再将子组件向容器里添加, 所以最层容器L先行构造出来,然后再被一层层地追加轻量子组件。如果最层容器在构造时去LQ则p求后l的构造都应该在EDT中进行,而且每次add子组仉要导致revalidateQ但若将最层容器的绘制分dgq到LҎ里,则可以表达是在容器里盛满了要昄的子lg后再一股脑Ll制出来的概念,cM于在q行一ơweb面的完整加?然后注意在具现方法执行后如果要操作组仉在EDT中进行即可,而且层容器提供一个特有的packҎQ用来一ơ性对所有子lg验证大小位置q行重布局Qpack之后再showQ这L一ơ性计展现是最有效率的?
层容器和轻量lg是q样诞生q绘制的Q在此后的生命周期里Q都按事g监听机制完成GUI随需而变Q无论是pȝ事gQ还是因为repaint调用dpost事gQ事件到来后再在EDT中执行监听器里的paintl制。Swing已经提供的顶层容器和轻量U组件因各自的定义已l注册了各自的paint监听Q开发h员可以再行维护或按此模式开发新lg从而满_用的需要。比如,jbutton默认有mousepress listenerQ在mousepress事g到来后,监听响应中会讄鼠标颜色加深来表C按下,然后再调用repaint要求重画Q随后在EDT中执行jbutton的paint回调Q此时按深颜色绘Ӟ于是一个被按下的效果就出来了?
下面在具体分析各cM件的处理?
对于层容器的受底层事g消息的触发,当得到的通知是因为expose暴露隐藏区(暴露被遮蔽的部分或恢复最化或第一ơ绘制等Q时Q处理过E会涉及到双~存的处理,卛_果可能,直接使用~存中的旧图像信息进行覆盖而不再重新绘制?
所谓双~存机制是将一整片的显C内Ҏ时写入一张内存空间里Q然后一ơ性内存拷入显C区来进行显C,q样处理是因为如果直接写入显C区Q随着昄该写入线E逐渐写入Q可能经历多ơ屏q刷斎ͼD每次h都Ş成过E图像,lh眼造成闪烁感觉Q同时一个副收益是可以针对每个H口都做~存待用Q而不仅仅是针对一个屏q双~存Q,当窗口被遮挡的部分重现时直接拯~存来覆盖,不用再执行绘画逻辑Q提高了效率?
现在的OS一般都提供双缓存机制支持,如果底层pȝ自n支持以每个窗口ؓ单位做双~存Q则该expose消息被本地处理Q不需要通知q行子组件的l制Q如果底层不支持Q则该消息会到达wcomponetpeer.handleexpose中进行回调处理,此时swing机制下有一个参数控制的双缓存机制可以提供。这里的参数控制需要从RepaintManager的构造过E说赗?
首先RepaintManager可以通过static setCurrentManager(SomeCurrentManager)来进行全局指定。默认情况用currentRepaintManager(){new RepaintManager(BUFFER_STRATEGY_TYPE)}得到一个gq创建的单例。RepaintManager有一D静态类初始化过E,涉及到双~存讄Q?
static {
nativeDoubleBuffering = "true".equals(AccessController.doPrivileged(
new GetPropertyAction("awt.nativeDoubleBuffering")));//JVM的启动参数控Ӟ默认false
String bs = AccessController.doPrivileged(
new GetPropertyAction("swing.bufferPerWindow"));//是否每窗口缓存?
if (headless) {
BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
}
else if (bs == null) {
BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_NOT_SPECIFIED;
}
else if ("true".equals(bs)) {
BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_ON;
}
else {
BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
}
}
private RepaintManager(short bufferStrategyType) {
// If native doublebuffering is being used, do NOT use
// Swing doublebuffering.
doubleBufferingEnabled = !nativeDoubleBuffering;
this.bufferStrategyType = bufferStrategyType;
}
public void setDoubleBufferingEnabled(boolean aFlag) {
doubleBufferingEnabled = aFlag;
doubleBufferingEnabledQ开启双~存Q,nativeDoubleBufferingQ利用本地双~存机制Q,bufferStrategyTypeQ每H口双缓存策略)
q几个参数将影响到RepaintManager的成员对象paintManager的选择Q也是一个策略模式,该paintManager是负责绘制的核心cR?
private synchronized PaintManager getPaintManager() {
if (paintManager == null) {
PaintManager paintManager = null;
if (doubleBufferingEnabled && !nativeDoubleBuffering) {
switch (bufferStrategyType) {
case BUFFER_STRATEGY_NOT_SPECIFIED:
if (((SunToolkit)Toolkit.getDefaultToolkit()).
useBufferPerWindow()) {//windows下是否禁用vista dwm,在没有声明bufferPerWindow的情况下由windowspȝҎ确定paintmanager?
paintManager = new BufferStrategyPaintManager();
}
break;
case BUFFER_STRATEGY_SPECIFIED_ON:
paintManager = new BufferStrategyPaintManager();
break;
default:
break;
}
}
// null case handled in setPaintManager
setPaintManager(paintManager);
}
return paintManager;
}
void setPaintManager(PaintManager paintManager) {
if (paintManager == null) {
paintManager = new PaintManager();
}
}
回到上文Q当handleexposeӞ通过getPaintEventDispatcher 来createPaintEventQ在UIManager.initializeҎRepaintManager.HANDLE_TOP_LEVEL_PAINTQ属性swing.handleTopLevelPaintQ确定是SwingPaintEventDispatcherq是直接使用PaintEventDispatcher?
若ؓfalse,在PaintEventDispatcher中,直接创建PaintEvent-PAINT提交Q此后该事gl合q后由wcomponentpeer.handleEventQ该处理通过一个自w维护的paintArea几何脏区域进行重d域优化,最l委托给Containerq行子组件绘Ӟq是非SWING模式-即AWT模式Q没有双~存的概c?
补充Q在Swing和它的RepainManager出现以前QGUI的模?AWT模式L要先形成一个PaintEventQ触发可能来自底层消?PAINTcdQ也可能来自repaint-UPDATEcdQ,postlEventQueueQƈl织一ơ合qӞ
public abstract class WComponentPeer{
void handlePaint(int x, int y, int w, int h) {
System.out.println("handlePaint>>>"+x+":"+y+":"+w+":"+h);
postPaintIfNecessary(x, y, w, h);
}
private void postPaintIfNecessary(int x, int y, int w, int h) {
if ( !ComponentAccessor.getIgnoreRepaint( (Component) target) ) {
PaintEvent event = PaintEventDispatcher.getPaintEventDispatcher().
createPaintEvent((Component)target, x, y, w, h);
if (event != null) {
postEvent(event);
}
}
}
public class PaintEventDispatcher {
public PaintEvent createPaintEvent(Component target, int x, int y, int w,
int h) {
return new PaintEvent((Component)target, PaintEvent.PAINT,
new Rectangle(x, y, w, h));
}
public abstract class Component{
public void repaint(long tm, int x, int y, int width, int height) {
if (this.peer instanceof LightweightPeer) {
~~~
parent.repaint(tm, px, py, pwidth, pheight);
}
} else {
if (isVisible() && (this.peer != null) &&
(width > 0) && (height > 0)) {
PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE,
new Rectangle(x, y, width, height));
Toolkit.getEventQueue().postEvent(e);
}
}
}
public class EventQueue{
private void postEvent(AWTEvent theEvent, int priority) {
if (coalesceEvent(theEvent, priority)) {//post之前L需要合q?
return;
}
private boolean coalesceEvent(AWTEvent e, int priority) {
if (e instanceof PaintEvent) {
return coalescePaintEvent((PaintEvent)e);//对painteventq行一轮合q处理,D同一重量U组件的多次paintevent被合qؓ一个paintevent{待dispatch。以提高效率
}
然后EDT中在Component.dispatchImpl中委托给wcomponentpeer处理?
public abstract class Component{
dispatchEventImpl{
/*
* 9. Allow the peer to process the event.
* Except KeyEvents,
*/
if (tpeer != null) {
tpeer.handleEvent(e);
}
public abstract class WComponentPeer{
public void handleEvent(AWTEvent e) {
switch(id) {
case PaintEvent.PAINT:
// Got native painting
paintPending = false;
// Fallthrough to next statement
case PaintEvent.UPDATE:
// Skip all painting while layouting and all UPDATEs
// while waiting for native paint
if (!isLayouting && ! paintPending) {
paintArea.paint(target,shouldClearRectBeforePaint());
}
return;
default:
break;
}
Peer处理q程中将利用自nl护的PaintAreaq行重画区域的优化,q执行子lgpaint回调?
/**
* Invokes paint and update on target Component with optimal
* rectangular clip region.
* If PAINT bounding rectangle is less than
* MAX_BENEFIT_RATIO times the benefit, then the vertical and horizontal unions are
* painted separately. Otherwise the entire bounding rectangle is painted.
*
* @param target Component to <code>paint</code> or <code>update</code>
* @since 1.4
*/
public void paint(Object target, boolean shouldClearRectBeforePaint) {
Component comp = (Component)target;
~~~
if (ra.paintRects[HORIZONTAL] != null && ra.paintRects[VERTICAL] != null) {
Rectangle paintRect = ra.paintRects[HORIZONTAL].union(ra.paintRects[VERTICAL]);
int square = paintRect.width * paintRect.height;
int benefit = square - ra.paintRects[HORIZONTAL].width
* ra.paintRects[HORIZONTAL].height - ra.paintRects[VERTICAL].width
* ra.paintRects[VERTICAL].height;
// if benefit is comparable with bounding box
if (MAX_BENEFIT_RATIO * benefit < square) {
ra.paintRects[HORIZONTAL] = paintRect;
ra.paintRects[VERTICAL] = null;
}
}
for (int i = 0; i < paintRects.length; i++) {
if (ra.paintRects[i] != null
&& !ra.paintRects[i].isEmpty())
{
// Should use separate Graphics for each paint() call,
// since paint() can change Graphics state for next call.
Graphics g = comp.getGraphics();
if (g != null) {
try {
g.setClip(ra.paintRects[i]);
if (i == UPDATE) {
updateComponent(comp, g);
} else {
if (shouldClearRectBeforePaint) {
g.clearRect( ra.paintRects[i].x,
ra.paintRects[i].y,
ra.paintRects[i].width,
ra.paintRects[i].height);
}
paintComponent(comp, g);
}
} finally {
g.dispose();
}
}
}
}
}
若ؓtrue,在SwingPaintEventDispatcher.createPaintEventQ?
if (component instanceof RootPaneContainer) {//如果是顶层容?
AppContext appContext = SunToolkit.targetToAppContext(component);
RepaintManager rm = RepaintManager.currentManager(appContext);
if (!SHOW_FROM_DOUBLE_BUFFER ||//参数swing.showFromDoubleBuffer控制Q默认true定swing//是否会考虑双缓存支?
!rm.show((Container)component, x, y, w, h)) {
rm.nativeAddDirtyRegion(appContext, (Container)component,
x, y, w, h);
}
return new IgnorePaintEvent(component, PaintEvent.PAINT,
new Rectangle(x, y, w, h));//q回一个将被忽略的假事件提?
如果SHOW_FROM_DOUBLE_BUFFER 考虑双缓存支持,进行rm.showQ其交给getPaintManager().showQ这时的paintmanager是经q了前面所说的几个参数选择的,也就是说Q考虑当前是否当前正能双~存doubleBufferingEnabledQ是否不使用本地双缓存nativeDoubleBufferingQ?BUFFER_STRATEGY_TYPE是否指定了每H口~存的双~存支持{略Q如果没有指定策略是否或本地windowspȝ环境没有开启vista dwm效果Q如果都满用BufferStrategyPaintManagerQ借由swing提供每窗口双~存机制Q检查swing记录中是否具有有效缓存,若存在则会要求该区直接拷贝flip卛_Q如果没有成功执行双~存拯Q则加入Repaintmanager重画区域q行swing模式的重甅R?
层容器除了在对{体发过消息后处理paintQ也h自己的repaintҎM动创造绘L机?
public void repaint(long time, int x, int y, int width, int height) {
if (RepaintManager.HANDLE_TOP_LEVEL_PAINT) {//属性swing.handleTopLevelPaint定Q默认true
RepaintManager.currentManager(this).addDirtyRegion(
this, x, y, width, height);
}
else {
super.repaint(time, x, y, width, height);
}
}
q里的repaint首先确定RepaintManager.HANDLE_TOP_LEVEL_PAINT-如果不支持将委托lComponent.repaintQŞ成PaintEventq进行提交走AWT模式。支持的话将促RepaintManager加入重画区后通过调度走SWING模式。SWING模式是走RepaintManager的方式。自w的repaint不会去考虑每窗口双~存直接拯区域Q因时的需求就是要求重新绘甅R?
轻量Uswinglg在自qrepaintҎM动创造绘L机?
JComponent.Repaint{RepaintManager.currentManager(this).addDirtyRegion}走SWING模式处理?
SWING模式都是借由RepaintManager来安排绘画,它维护了一个几何区域ƈ负责重画的框架。外界L要求先加入RepaintManager重绘区,在加入的同时Ȁ发v一个调度重ȝ
SunToolkit.getSystemEventQueueImplPP(context).
postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
processingRunnable))
InvocationEvent?
注意Q通过上文分析Q对于顶层容器处理底层消息的触发Ӟ走swing处理模式而通过swingpaintEventdispatcherd建painitevent旉向repaintmanager登记脏区Q如果不使用每窗口双~存{略Q外Q还要额外post一个IgnorePaintEvent。该paintevent在随后的EDT里按awt模式走peer处理时ƈ没有加入awt的重画脏区,实际上忽略掉了绘制意义,q样做避免了在swing和awt两种模式的重复绘Ӟ但同时Ş成依然将paint事g通知到组件的效果?
public void coalescePaintEvent(PaintEvent e) {
Rectangle r = e.getUpdateRect();
if (!(e instanceof IgnorePaintEvent)) {
paintArea.add(r, e.getID());
}
Swing API的设计目标是强大、灵zd易用。特别地Q我们希望能让程序员们方便地建立新的SwinglgQ不论是从头开始还是通过扩展我们所提供的一些组件?
Zq个目的Q我们不要求Swinglg支持多线E访问。相反,我们向组件发送请求ƈ在单一U程中执行请求?
本文讨论U程和Swinglg。目的不仅是Z帮助你以U程安全的方式用Swing APIQ而且解释了我们ؓ什么会选择现在q样的线E方案?
本文包括以下内容Q?
单线E规则:SwingU程在同一时刻仅能被一个线E所讉K。一般来_q个U程是事件派发线E(event-dispatching threadQ?
规则的例外:有些操作保证是线E安全的?
事g分发Q如果你需要从事g处理Qevent-handlingQ或l制代码以外的地方访问UIQ那么你可以使用SwingUtilitiescȝinvokeLater()或invokeAndWait()Ҏ?
创徏U程Q如果你需要创Z个线E――比如用来处理一些耗费大量计算能力或受I/O能力限制的工作――你可以使用一个线E工L如SwingWorker或Timer?
Z么我们这样实现SwingQ我们用一些关于Swing的线E安全的背景资料来结束这文章?
Swing的规则是Q?
一旦Swinglg被具现化QrealizedQ,所有可能媄响或依赖于组件状态的代码都应该在事gz֏U程中执行?
q个规则可能听v来有点吓人,但对许多单的E序来说Q你用不着为线E问题操心。在我们深入如何撰写Swing代码之前Q让我们先来定义两个术语Q具现化QrealizedQ和事gz֏U程Qevent-dispatching threadQ?
L化的意思是l徏的paint()Ҏ已经或可能会被调用。一个作为顶U窗口的Swinglg当调用以下方法时被L化:setVisible(true)、show()或(可能令你惊奇Qpack()。当一个窗口被L化,它包含的所有组仉被具现化。另一个具现化一个组件的Ҏ是将它放入到一个已l具现化的容器中。稍后你会看C些对lgL化的例子?
事gz֏U程是执行绘制和事g处理的线E。例如,paint()和actionPerformed()Ҏ会自动在事gz֏U程中执行。另一个将代码攑ֈ事gz֏U程中执行的Ҏ是用SwingUtilitiescȝinvokeLater()Ҏ?
所有可能媄响一个已L化的Swinglg的代码都必须在事件派发线E中执行。但q个规则有一些例外:
有些Ҏ是线E安全的Q在Swing API的文档中Q线E安全的Ҏ用以下文字标讎ͼ
This method is thread safe, although most Swing methods are not.
Q这个方法是U程安全的,管大多数SwingҎ都不是。)
一个应用程序的GUI常常可以在主U程中构建和昄Q下面的典型代码是安全的Q只要没有(Swing或其他)lg被具现化Q?
public class MyApplication {
public static void main(String[] args) {
JFrame f = new JFrame("Labels");
// 在这里将各组?nbsp;
// 加入C框架……
f.pack();
f.show();
// 不要再做MGUI工作……
}
}
上面所C的代码全部?#8220;main”U程中运行。对f.pack()的调用得JFrame以下的组仉被具现化。这意味着Qf.show()调用是不安全的且应该在事件派发线E中执行。尽如此,只要E序q没有一个看得到的GUIQJFrame或它的里面的lg几乎不可能在f.show()q回前收C个paint()调用。因为在f.show()调用之后不再有Q何GUI代码Q于是所有GUI工作都从ȝE{C事gz֏U程Q因此前面所讨论的代码实际上是线E安全的?
一个applet的GUI可以在init()Ҏ中构造和昄Q现有的览器都不会在一个applet的init()和start()Ҏ被调用前l制它。因而,在一个applet的init()Ҏ中构造GUI是安全的Q只要你不对applet中的对象调用show()或setVisible(true)Ҏ?
要顺便一提的是,如果applet中用了SwinglgQ就必须实现为JApplet的子cRƈ且,lg应该d到的JApplet内容H格Qcontent paneQ中Q而不要直接添加到JApplet。对MappletQ你都不应该在init()或start()Ҏ中执行费时的初始化操作;而应该启动一个线E来执行Ҏ的Q务?
下述JComponentҎ是安全的Q可以从MU程调用Qrepaint()、revalidate()、和invalidate()。repaint()和revalidate()ҎZ件派发线E对h排队Qƈ分别调用paint()和validate()Ҏ。invalidate()Ҏ只在需要确认时标记一个组件和它的所有直接祖先?
监听者列表可以由MU程修改Q调用addListenerTypeListener()和removeListenerTypeListener()ҎL安全的。对监听者列表的d/删除操作不会对进行中的事件派发有M影响?
注意Qrevalidate()和旧的validate()Ҏ之间的重要区别是Qrevalidate()会缓存请求ƈl合成一ơvalidate()调用。这和repaint()~存q组合绘制请求类伹{?
大多数初始化后的GUI工作自然地发生在事gz֏U程。一旦GUI成ؓ可见Q大多数E序都是׃仉动的Q如按钮动作或鼠标点击,q些L在事件派发线E中处理的?
不过QL些程序需要在GUI成ؓ可见后执行一些非事g驱动的GUI工作。比如:
在成为可用前需要进行长旉初始化操作的E序Q这cȝ序通常应该在初始化期间显C出GUIQ然后更新或改变GUI。初始化q程不应该在事gz֏U程中进行;否则Q重l组件和事gz֏会停止。尽如此,在初始化之后QGUI的更?改变q是应该在事件派发线E中q行Q理由是U程安全?
必须响应非AWT事g来更新GUI的程序:例如Q想象一个服务器E序从可能运行在其他机器上的E序得到h。这些请求可能在M时刻到达Qƈ且会引v在一些可能未知的U程中对服务器的Ҏ调用。这个方法调用怎样更新GUI呢?在事件派发线E中执行GUI更新代码?
SwingUtilitiescL供了两个Ҏ来帮助你在事件派发线E中执行代码Q?
invokeLater()Q要求在事gz֏U程中执行某些代码。这个方法会立即q回Q不会等待代码执行完毕?
invokeAndWait()Q行ZinvokeLater()cMQ除了这个方法会{待代码执行完毕。一般地Q你可以用invokeLater()来代替这个方法?
下面是一些用这几个API的例子。请同时参阅《The Java Tutorial》中?#8220;BINGO example”Q尤其是以下几个c:CardWindow、ControlPane、Player和OverallStatusPane?
使用invokeLater()Ҏ
你可以从MU程调用invokeLater()Ҏ以请求事件派发线E运行特定代码。你必须把要q行的代码放C个Runnable对象的run()Ҏ中,q将此Runnable对象设ؓinvokeLater()的参数。invokeLater()Ҏ会立卌回,不等待事件派发线E执行指定代码。这是一个用invokeLater()Ҏ的例子:
Runnable doWorkRunnable = new Runnable() {
public void run() { doWork(); }
};
SwingUtilities.invokeLater(doWorkRunnable);
使用invokeAndWait()Ҏ
invokeAndWait()Ҏ和invokeLater()Ҏ很相|除了invokeAndWait()Ҏ会等事gz֏U程执行了指定代码才q回。在可能的情况下Q你应该量用invokeLater()来代替invokeAndWait()。如果你真的要用invokeAndWait()Q请保调用invokeAndWait()的线E不会在调用期间持有M其他U程可能需要的锁?
q是一个用invokeAndWait()的例子:
void showHelloThereDialog()
throws Exception {
Runnable showModalDialog = new
Runnable() {
public void run() {
JOptionPane.showMessageDialog(
myMainFrame, "Hello There");
}
};
SwingUtilities.invokeAndWait
(showModalDialog);
}
cM圎ͼ假设一个线E需要对GUI的状态进行存取,比如文本域的内容Q它的代码可能类DP
void printTextField() throws Exception {
final String[] myStrings =
new String[2];
Runnable getTextFieldText =
new Runnable() {
public void run() {
myStrings[0] =
textField0.getText();
myStrings[1] =
textField1.getText();
}
};
SwingUtilities.invokeAndWait
(getTextFieldText);
System.out.println(myStrings[0]
+ " " + myStrings[1]);
}
如果你能避免使用U程Q最好这样做。线E可能难于用,q得程序的debug更困难。一般来_对于严格意义下的GUI工作Q线E是不必要的Q比如对lg属性的更新?
不管怎么_有时候线E是必要的。下列情冉|使用U程的一些典型情况:
执行一费时的d而不必将事gz֏U程锁定。例子包括执行大量计的情况Q会D大量c被装蝲的情况(如初始化Q,和ؓ|络或磁盘I/O而阻塞的情况?
重复地执行一Ҏ作,通常在两ơ操作间间隔一个预定的旉周期?
要等待来自客L消息?
你可以用两个类来帮助你实现U程Q?
SwingWorkerQ创Z个后台线E来执行Ҏ的操作?
TimerQ创Z个线E来执行或多ơ执行某些代码,在两ơ执行间间隔用户定义的gq?
使用SwingWorkerc?
SwingWorkercdSwingWorker.java中实玎ͼq个cdƈ不包含在Java的Q何发行版中,所以你必须单独下蝲它?
SwingWorkercd了所有实C个后台线E所需的肮脏工作。虽然许多程序都不需要后台线E,后台U程在执行费时的操作时仍然是很有用的Q它能提高程序的性能观感?
SwingWorker's get() method. Here's an example of using SwingWorker:
要用SwingWorkerc,你首先要实现它的一个子cR在子类中,你必d现construct()Ҏq包含你的长旉操作。当你实例化SwingWorker的子cLQSwingWorker创徏一个线E但q不启动它。你要调用你的SwingWorker对象的start()Ҏ来启动线E,然后start()Ҏ会调用你的construct()Ҏ。当你需要construct()Ҏq回的对象时Q可以调用SwingWorkercȝget()Ҏ。这是一个用SwingWorkercȝ例子Q?
...// 在mainҎ中:
final SwingWorker worker =
new SwingWorker() {
public Object construct() {
return new
expensiveDialogComponent();
}
};
worker.start();
...// 在动作事件处理方法中:
JOptionPane.showMessageDialog
(f, worker.get());
当程序的main()Ҏ调用start()ҎQSwingWorker启动一个新的线E来实例化ExpensiveDialogComponent。main()Ҏq构造了׃个窗口和一个按钮组成的GUI?
当用LL钮,E序阻塞,如果必要Q阻塞到ExpensiveDialogComponent创徏完成。然后程序显CZ个包含ExpensiveDialogComponent的模式对话框。你可以在MyApplication.java扑ֈ整个E序?
使用Timerc?
Timerc通过一个ActionListener来执行或多次执行一Ҏ作。你创徏定时器的时候可以指定操作执行的频率Qƈ且你可以指定定时器的动作事g的监听者(action listenerQ。启动定时器后,动作监听者的actionPerformed()Ҏ会被Q多ơ)调用来执行操作?
定时器动作监听者(action listenerQ定义的actionPerformed()Ҏ在事gz֏U程中调用。这意味着你不必在其中使用invokeLater()Ҏ?
q是一个用TimercL实现动画循环的例子:
public class AnimatorApplicationTimer
extends JFrame implements
ActionListener {
...//在这里定义实例变?
Timer timer;
public AnimatorApplicationTimer(...) {
...
// 创徏一个定时器?nbsp;
// 来调用此对象action handler?
timer = new Timer(delay, this);
timer.setInitialDelay(0);
timer.setCoalesce(true);
...
}
public void startAnimation() {
if (frozen) {
// 什么都不做。应用户要求
// 停止变换囑փ?
} else {
// 启动Q或重启动)动画Q?
timer.start();
}
}
public void stopAnimation() {
// 停止动画U程?
timer.stop();
}
public void actionPerformed
(ActionEvent e) {
// q到下一帧动甅R?
frameNumber++;
// 昄?
repaint();
}
...
}
在一个线E中执行所有的用户界面代码有这样一些优点:
lg开发者不必对U程~程有深入的理解Q像ViewPoint和Trestleq类工具包中的所有组仉必须完全支持多线E访问,使得扩展非常困难Q尤其对不精通线E编E的开发者来说。最q的一些工具包如SubArctic和IFCQ都采用和SwingcM的设计?
事g以可预知的次序派发:invokeLater()排队的runnable对象从鼠标和键盘事g、定时器事g、绘制请求的同一个队列派发。在一些组件完全支持多U程讉K的工具包中,lg的改变被变化无常的线E调度程序穿插到事g处理q程中。这使得全面试变得困难甚至不可能?
更低的代P试心锁住临界区的工具包要p实的时间和I间在锁的管理上。每当工具包中调用某个可能在客户代码中实现的ҎӞ如publiccM的Q何public和protectedҎQ,工具包都要保存它的状态ƈ释放所有锁Q以便客户代码能在必要时获得锁。当控制权交回到工具包,工具包又必须重新抓住它的锁ƈ恢复状态。所有应用程序都不得不负担这一代hQ即使大多数应用E序q不需要对GUI的ƈ发访问?
q是的SubArctic Java Toolkit的作者对在工具包中支持多U程讉K的问题的描述Q?
我们的基本信条是Q当设计和徏造多U程应用E序Q尤其是那些包括GUIlg的应用程序时Q必M证极端小心。线E的使用可能会很有欺骗性。在许多情况下,它们表现得能够极好的化编成,使得设计“专注于单一d的简单自d?#8221;成ؓ可能。在一些情况下它们的确化了设计和编码。然而,在几乎所有的情况下,它们都得调试、测试和l护的困隑֤大增加甚xZ可能。无论大多数E序员所受的训练、他们的l验和实践,q是我们用来帮助自己的工P都不是能够用来对付非军_论的。例如,全面试Q这L困难的)在bug依赖于时间时是几乎不可能的。尤其对于Java来说Q一个程序要q行在许多不同类型的机器的操作系l^CQƈ且每个程序都必须在抢先和非抢先式调度下都能正常工作?
׃q些固有的困难,我们力劝你三思是否绝Ҏ使用U程的必要。尽如此,有些情况下用线E是必要的(或者是被其他Y件包强加的)Q所以subArctic提供了一个线E安全的讉K机制。本章讨Zq一机制和怎样在一个独立线E中安全地操作交互树?
他们所说的U程安全机制非常cM于SwingUtilitiescL供的invokeLater()和invokeAndWait()Ҏ?/font>
主要内容Q事Ӟl制Q渲染,双缓ԌU程
一?事g
1、事件来源:本地pȝQ应用程序本w?/font>
2、所有的事g都存储在一个队列里?/font>
3、Java.awt.EventQueue从队列中取数据ƈz֏QEventQueue的派发机制由单独的线EEDTQEvent Dispatch ThreadQ处理?/font>
二、绘?/font>
1、绘制请求发Pawt或者swing本n、对swing发vh
2、异步重l请求:所有异步请求都是repaint()的变体,包括整个l徏的绘制和l徏某个区域的绘?/font>
3、同步绘制请求:Jcomponent.paintImmediately()和Component.paint()
三、渲?/font>
1、画家算?/font>
2、paintComponent()
3、paint()
4、setpaque()
四、双~冲
五、线E?/font>
1、swingU程包括Q主U程Q应用程序的L法)Q工具包U程Q扑Ll事ӞQEDTQ将扑获的线E派发)
2、线E模型:单一的规则,所以要遵从此规则,量事件让EDTq行z֏?/font>
3、invokeLater()
4、isEventDispatchThread()
5、invokeAndWait
六、定时器和事件派?/font>
1、java.util.Timer
2、javax.swing.Timer
七、SwingWorker
1、done()
2、doInBackground()
3、get
4、process
文档对这个布局理器介l很详细Q但是最痛苦的是英文。不q幸好它有实例,l过在网上查阅和推敲实例Q终于对GridBagLayout的用有了一个成型的了解Q拿出来与大家分享?/p>
GridBagLayout是一个灵zȝ布局理器,部g如果惛_入其中需借助GridBagConstraintsQ其中有若干个参敎ͼ解释如下Q?/p>
gridx/gridyQ组件的横纵坐标
gridwidthQ组件所占列敎ͼ也是lg的宽?/p>
gridheightQ组件所占行敎ͼ也是lg的高?/p>
fillQ当lg在其格内而不能撑满其格时Q通过fill的值来讑֮填充方式Q有四个?/p>
ipadxQ?lg间的横向间距
ipadyQ组仉的纵向间?/p>
insetsQ当lg不能填满其格Ӟ通过insets来指定四周(即上下左叻I所留空?/p>
anchorQ同h当组件不能填满其格时Q通过anchor来设|组件的位置Qanchor有两U|l对和相对的值分别有 若干个,文档中有Q可自行查看
weightxQ行的权重,通过q个属性来军_如何分配行的剩余I间
weightyQ列的权重,通过q个属性来军_如何分配列的剩余I间
q是文档实用Q用例子来说?/p>
import java.awt.*;
import java.util.*;
import java.applet.Applet;
public class GridBagEx1 extends Applet {
protected void makebutton(String name,
GridBagLayout gridbag,
GridBagConstraints c) {
Button button = new Button(name);
gridbag.setConstraints(button, c);
add(button);
}
public void init() {
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setFont(new Font("SansSerif", Font.PLAIN, 14));
setLayout(gridbag);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
makebutton("Button1", gridbag, c);
makebutton("Button2", gridbag, c);
makebutton("Button3", gridbag, c);
c.gridwidth = GridBagConstraints.REMAINDER; //end row
makebutton("Button4", gridbag, c);
c.weightx = 0.0; //reset to the default
makebutton("Button5", gridbag, c); //another row
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last in row
makebutton("Button6", gridbag, c);
c.gridwidth = GridBagConstraints.REMAINDER; //end row
makebutton("Button7", gridbag, c);
c.gridwidth = 1; //reset to the default
c.gridheight = 2;
c.weighty = 1.0;
makebutton("Button8", gridbag, c);
c.weighty = 0.0; //reset to the default
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.gridheight = 1; //reset to the default
makebutton("Button9", gridbag, c);
makebutton("Button10", gridbag, c);
setSize(300, 100);
}
public static void main(String args[]) {
Frame f = new Frame("GridBag Layout Example");
GridBagEx1 ex1 = new GridBagEx1();
ex1.init();
f.add("Center", ex1);
f.pack();
f.setSize(f.getPreferredSize());
f.setVisible(true);
}
}
可以自行q行Q查看其l果
文档对其各个按钮的参数设定解释如下:
Button1, Button2, Button3: weightx = 1.0
Button4: weightx = 1.0, gridwidth = GridBagConstraints.REMAINDER
Button5: gridwidth = GridBagConstraints.REMAINDER
Button6: gridwidth = GridBagConstraints.RELATIVE
Button7: gridwidth = GridBagConstraints.REMAINDER
Button8: gridheight = 2, weighty = 1.0
Button9, Button 10: gridwidth = GridBagConstraints.REMAINDER
对照着E序和运行结果,q有其参数设定,我的理解如下Q?
W一行:W一行之所以有四个按钮Q关键点在于Qweightx=1.0Q这样就可以在前边的按钮后l加入按钮,而button4成ؓ行尾是因为其gridwidth = GridBagConstraints.REMAINDERQ这句话p定它是行的末?
W二行:既然W一行都有末了Q那么再加入按钮的话Q必定是另v一行了Q这个道理)。此时加入了button5Q而button5又被讑֮Z本行的最后一个(gridwidth = GridBagConstraints.REMAINDERQ,加之它又是第二行的第一个按钮,所以第二行只有一个按钮,是button5?
W三行:button6不可避免的成ZW一个按钮,它被讑֮了gridwidth = GridBagConstraints.RELATIVEQ表明button6要紧挨它前边的那个按钮和最后的那个按钮Q也是说它一定是倒数W二个按钮(为最后一个按钮的出现做好了准备)。button7出现了,׃有gridwidth = GridBagConstraints.REMAINDERQ它׃ؓW三行封了口。第三行l束?
W四行:q一行有一个特D的按钮button8Q它的设定ؓgridheight = 2, weighty = 1.0Q即它占用两行一列(其实q个一列和两行都是相对的)。这一行还没封口,所以后面来的button9加在了这一行,因ؓ它gridwidth = GridBagConstraints.REMAINDERQ所以第四行口?
W五行:q一行button8已经占据了第一个的位置Q因为button8的gridheight=2Q,所以后来的button10加在W二Q同L于gridwidth = GridBagConstraints.REMAINDERQ第五行口?
要理解GridBagLayoutQ最好从例子的理解开始,呵呵?
|上q有另外几篇介绍它的文章Q大家也可参?
http://blog.163.com/everlee@126/blog/static/263574220089621157826/
http://hi.baidu.com/zml1003/blog/item/43728f6ee02a7bd980cb4afc.html
文章出处QDIY部落(http://www.diybl.com/course/3_program/java/javajs/20090405/164188.html)
开发中q接数据库的方式有多?可以直接用jdbcq接,也可以配|服务器的连接池.写法也有多种,可以直接写在jsp面?但通常都会写个专用的类,用单态之cȝ设计模式把他装h.但其中的基本q接驱动形式代码是不变的,下面是些CZ代码Q你一定将jdbc的驱动程序放到服务器的类路径里,然后要在数据库里Z个表test,有两个字D|如ؓtest1Qtest2Q可以用下面SQL?
create table test(test1 varchar(20),test2 varchar(20)
然后向这个表写入一条测试纪?
那么现在开始我们的jsp和数据库之旅吧?
一、jspq接Oracle8/8i/9i数据库(用thin模式Q?
testoracle.jsp如下Q?
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*"%>
<html>
<body>
<%Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url="jdbc:oracle:thin:@localhost:1521:orcl";
//orclZ的数据库的SID
String user="scott";
String password="tiger";
Connection conn= DriverManager.getConnection(url,user,password);
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from test";
ResultSet rs=stmt.executeQuery(sql);
while(rs.next()) {%>
您的W一个字D内容ؓQ?lt;%=rs.getString(1)%>
您的W二个字D内容ؓQ?lt;%=rs.getString(2)%>
<%}%>
<%out.print("数据?**作成功,恭喜?);%>
<%rs.close();
stmt.close();
conn.close();
%>
</body>
</html>
二、jspq接Sql Server7.0/2000数据?
testsqlserver.jsp如下Q?
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*"%>
<html>
<body>
<%Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();
String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=pubs";
//pubsZ的数据库?
String user="sa";
String password="";
Connection conn= DriverManager.getConnection(url,user,password);
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from test";
ResultSet rs=stmt.executeQuery(sql);
while(rs.next()) {%>
您的W一个字D内容ؓQ?lt;%=rs.getString(1)%>
您的W二个字D内容ؓQ?lt;%=rs.getString(2)%>
<%}%>
<%out.print("数据?**作成功,恭喜?);%>
<%rs.close();
stmt.close();
conn.close();
%>
</body>
</html>
三、jspq接DB2数据?
testdb2.jsp如下Q?
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*"%>
<html>
<body>
<%Class.forName("com.ibm.db2.jdbc.app.DB2Driver ").newInstance();
String url="jdbc:db2://localhost:5000/sample";
//sampleZ的数据库?
String user="admin";
String password="";
Connection conn= DriverManager.getConnection(url,user,password);
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from test";
ResultSet rs=stmt.executeQuery(sql);
while(rs.next()) {%>
您的W一个字D内容ؓQ?lt;%=rs.getString(1)%>
您的W二个字D内容ؓQ?lt;%=rs.getString(2)%>
<%}%>
<%out.print("数据?**作成功,恭喜?);%>
<%rs.close();
stmt.close();
conn.close();
%>
</body>
</html>
四、jspq接Informix数据?
testinformix.jsp如下Q?
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*"%>
<html>
<body>
<%Class.forName("com.informix.jdbc.IfxDriver").newInstance();
String url =
"jdbc:informix-sqli://123.45.67.89:1533/testDB:INFORMIXSERVER=myserver;
user=testuser;password=testpassword";
//testDBZ的数据库?
Connection conn= DriverManager.getConnection(url);
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from test";
ResultSet rs=stmt.executeQuery(sql);
while(rs.next()) {%>
您的W一个字D内容ؓQ?lt;%=rs.getString(1)%>
您的W二个字D内容ؓQ?lt;%=rs.getString(2)%>
<%}%>
<%out.print("数据?**作成功,恭喜?);%>
<%rs.close();
stmt.close();
conn.close();
%>
</body>
</html>
五、jspq接Sybase数据?
testmysql.jsp如下Q?
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*"%>
<html>
<body>
<%Class.forName("com.sybase.jdbc.SybDriver").newInstance();
String url =" jdbc:sybase:Tds:localhost:5007/tsdata";
//tsdataZ的数据库?
Properties sysProps = System.getProperties();
SysProps.put("user","userid");
SysProps.put("password","user_password");
Connection conn= DriverManager.getConnection(url, SysProps);
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from test";
ResultSet rs=stmt.executeQuery(sql);
while(rs.next()) {%>
您的W一个字D内容ؓQ?lt;%=rs.getString(1)%>
您的W二个字D内容ؓQ?lt;%=rs.getString(2)%>
<%}%>
<%out.print("数据?**作成功,恭喜?);%>
<%rs.close();
stmt.close();
conn.close();
%>
</body>
</html>
六、jspq接MySQL数据?
testmysql.jsp如下Q?
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*"%>
<html>
<body>
<%Class.forName("org.gjt.mm.mysql.Driver").newInstance();
String url ="jdbc:mysql://localhost/softforum?user=soft&password=soft1234&useUnicode=true&characterEncoding=8859_1"
//testDBZ的数据库?
Connection conn= DriverManager.getConnection(url);
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from test";
ResultSet rs=stmt.executeQuery(sql);
while(rs.next()) {%>
您的W一个字D内容ؓQ?lt;%=rs.getString(1)%>
您的W二个字D内容ؓQ?lt;%=rs.getString(2)%>
<%}%>
<%out.print("数据?**作成功,恭喜?);%>
<%rs.close();
stmt.close();
conn.close();
%>
</body>
</html>
七、jspq接PostgreSQL数据?
testmysql.jsp如下Q?
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*"%>
<html>
<body>
<%Class.forName("org.postgresql.Driver").newInstance();
String url ="jdbc:postgresql://localhost/soft"
//softZ的数据库?
String user="myuser";
String password="mypassword";
Connection conn= DriverManager.getConnection(url,user,password);
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from test";
ResultSet rs=stmt.executeQuery(sql);
while(rs.next()) {%>
您的W一个字D内容ؓQ?lt;%=rs.getString(1)%>
您的W二个字D内容ؓQ?lt;%=rs.getString(2)%>
<%}%>
<%out.print("数据?**作成功,恭喜?);%>
<%rs.close();
stmt.close();
conn.close();
%>
</body>
</html>
?q接access例子
public class AccessTest {
public static void main(String args[]) {
try {
String strurl = "jdbc:odbc:MS Access Database;DBQ=d:\\books.mdb";
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn = DriverManager.getConnection(strurl, "", "");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from books");
while (rs.next())
{
System.out.print(rs.getString(1) + " ");
System.out.print(rs.getString(2));
}
} catch (Exception e) {
System.out.println(e);
}
}
}
http://haiyupeter.javaeye.com/blog/306572
关键? dom4j实例 国外的dom4j已经很流行了Q国内的相关资料相对较少Q但普及风暴也即到来。我们公司(老外开的)解析XML是用的dom4j?br />
今天公司没事做,自己也写一个小例子贴上来,嘻~~
books.xml:
下面我们使用dom4j的xPath来解析:
segment of ParseXML.java:
Document doc = reader.read("books.xml");的意思是加蝲XML文档Q此是可以用doc.asXML()来查看,它将打印整个xml文档?br />
Node root = doc.selectSingleNode("/books");是读取刚才加载的xml文档内的books节点下的所有内容,对于本例也是整个xml文档?br />
当然我们也可以加?books下的某一个节点,如:book节点
Node root = doc.selectSingleNode("/books/book");
或:Node root = doc.selectSingleNode("/books/*");
注意Q如果有多个book节点Q它只会dW一?br />
root.asXML()打华ͼ
<book show="yes" url="lucene.net">
<title id="456">Lucene Studing</title>
</book>
既然加蝲了这么多Q那我怎么_的得到我惌的节点呢Q别急,看下面:
List list = root.selectNodes("book[@url='dom4j.com']");
它的意思就是读取books节点下的book节点Q且book的节点的url属性ؓdom4j.com
Z么用list来接收呢Q如果有两个book节点Q且它们的url属性都为dom4j.comQ此时就闭到list里了?br />
如果惌取books下的所有book节点Q可以这P
List list = root.selectNodes("book");
如果惌取books节点下的book节点下的title节点Q可以这P
List list2 = root.selectNodes("book[@url='dom4j.com']/title[@id='123']");
注意QselectNodes()参数的格式:
节点名[@属性名='属性?]Q如Qbook[@url='dom4j.com']
如果有多个节点,?#8220;/”分开Q如Qbook[@url='dom4j.com']/title[@id='123']
最q就是读取封闭在List里的内容了,可以用Node来读取,也可以用Element来{换?br />
attributeValue("属?)是读取该节点的属性?br />
getText()是读取节点的的内宏V?/p>
用法Q?br />
<div id="test">
<span style="color:red">test1</span> test2
</div>
在JS中可以用:
test.innerHTML:
也就是从对象的v始位|到l止位置的全部内?包括Html标签?
上例中的test.innerHTML的g是“<span style="color:red">test1</span> test2 ”?br />
test.innerText:
从v始位|到l止位置的内? 但它去除Html标签
上例中的text.innerTest的g是“test1 test2”, 其中span标签去除了?
test.outerHTML:
除了包含innerHTML的全部内容外, q包含对象标{本w?/p>
上例中的text.outerHTML的g是<div id="test"><span style="color:red">test1</span> test2</div>
完整CZQ?/strong>
<div id="test">
<span style="color:red">test1</span> test2
</div>
<a href="javascript:alert(test.innerHTML)">innerHTML内容</a>
<a href="javascript:alert(test.innerText)">inerHTML内容</a>
<a href="javascript:alert(test.outerHTML)">outerHTML内容</a>
特别说明Q?/strong>
innerHTML是符合W3C标准的属性,而innerText只适用于IE览器,因此Q尽可能地去使用innerHTMLQ而少用innerTextQ如果要输出不含HTML标签的内容,可以使用innerHTML取得包含HTML标签的内容后Q再用正则表辑ּ去除HTML标签Q下面是一个简单的W合W3C标准的示例:
<a href="javascript:alert(document.getElementById('test').innerHTML.replace(/<.+?>/gim,''))">无HTML,W合W3C标准</a>
-------------------------------------------------------------------------------------------------------------------------------
<html>
<head></head>
<frameset frameborder="yes" frameborder="1" rows="40%,*">
<frame name="top" src="1.html">
<frame name="bottom" src="2.html">
</frameset>
</html>
<html>
<head>
<script language="javascript">
function init()
{
var aaa = parent.window.frames[0].document.body.innerHTML;
alert(aaa);
}
</script>
</head>
<body>
<p align="center">nothing</p>
<p align="center"><input type="button" onclick="init()"; value="click"></p>
</body>
</html>
<html>
<center>汽R 房 女h</center>
</html>
本h对dwr 中文文档关于dwr.xml做的一个整?Ua体力z,有用得着的朋友别忘顶一下,留个联系方式一起交学习! dwr.xml是DWR的配|文件。默认情况下Q应该把它放到WEB-INF目录(web.xml的目??/p> 创徏dwr.xml文gdwr.xml文g的结构如? <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"><dwr> <!-- init is only needed if you are extending DWR --> <init> <creator id="..." class="..."/> <converter id="..." class="..."/> </init> <!-- without allow, DWR isn't allowed to do anything --> <allow> <create creator="..." javascript="..."/> <convert converter="..." match="..."/> </allow> <!-- you may need to tell DWR about method signatures --> <signatures> ... </signatures></dwr> 术语q里是一些必ȝ解的术语 - 参数会被convertedQ远EBean会被created。所以如果你有一个叫A的beanQ它有一个方法叫A.blah(B) 那么你需要一个A?strong>creator和一个B?strong>converter?/p> 一Q?lt;allow>allowD落里面定义的试DWR可以创徏和{换的cR?/p> 二:Creatorsdwr.xml文g中的create元素的结构如下: <allow> <create creator="..." javascript="..." scope="..."> <param name="..." value="..."/> <auth method="..." role="..."/> <exclude method="..."/> <include method="..."/> </create> ...</allow> q里的多数元素都是可选的 - 你真正必ȝ道的是指定一个creator和一个javascript名字?/p> creator属?/strong> 是必ȝ - 它用来指定用那U创造器?/p>
默认情况下DWR1.1?U创造器。它们是Q?/p>
如果你需要写自己的创造器Q你必须在init部分注册它?/p>
javascript属?/strong> 用于指定览器中q个被创造出来的对象的名字。你不能使用Javascript的关键字?/p>
scope属?/strong> 非常cMservlet规范中的scope?它允怽指定q个bean在什么生命范围。选项?application", "session", "request" ?page"。这些值对于Servlet和JSP开发者来说应该相当熟悉了?/p>
scope属性是可选的。默认是"page"。如果要使用"session"需要cookies。当前的DWR不支持ULR重写?/p>
param元素 被用来指定创造器的其他参敎ͼ每种构造器各有不同。例如,"new"创造器需要知道要创徏的对象类型是什么。每一个创造器的参数在各自的文档中能找到。请查看上面的链接?/p>
include和exclude元素 允许创造器来限制类中方法的讉K。一个创造器必须指定include列表或exclude列表之一。如果是include列表则暗C默认的讉K{略?拒绝"Q如果是exclude列表则暗C默认的讉K{略?允许"?/p>
例如要拒l防范除?em>setWibble()以外的所有方法,你应该把如下内容d到dwr.xml中?/p>
对于加入到create元素中的cȝ所有方法都是默认可见的?/p>
auth元素 允许你指定一个J2EE的角色作为将来的讉K控制? new创造器在DWR中已l默认声明了Q?strong><creator id="new" class="uk.ltd.getahead.dwr.create.NewCreator"/> q个创造器通过c默认的够早函数创造对象实例。用new创造器有一些好处: 你可以通过下面的方式用new创造器来创造远E调用BeanQ?/p>
q些代码?java.util.Date 映射成JavascriptQƈ且命名ؓBlahQ所以在Javascript中当你调用Blah.toString(reply) 那么一个新?java.util.Date ׃通过默认的构造函数创造出来, 然后 toString() Ҏ被调用, 然后l果数据q回lreplyҎ(在这个例子中date是字W串格式)?/p>
none'创造器不创ZQ何对?- 它会假设你不需要创建对象。这有可能是对的Q有两个原因?/p>
你可能在使用的scope不是"page"(看上?Qƈ在在前面已经把这个对象创建到q个scope中了Q这时你׃需要再创徏对象了?/p>
q有一U情冉|要调用的Ҏ是静态的Q这时也不需要创建对象。DWR会在调用创徏器之前先查一下这个方法是不是静态的?/p>
对于上诉两种情况Q你仍然需要class参数Q用来告诉DWR它是在操作的对象cd是什么?/p>
scripted创造器在DWR中已l默认声明了Q?lt;creator id="script" class="uk.ltd.getahead.dwr.create.ScriptedCreator"/> q个创造器用BSF来执行脚本得到BeanQ例如: script创造器有如下参敎ͼ
要用这个创造器Q你需要把一些辅助库攑ֈWEB-INF/lib文g夹下Q?span class="nobr">BSF的jar?sup> 当一个类是用script创造出来的Qƈ且scope是session或applicationQ如果你的脚本改变,session中的cdscript中的cd不一致了。这样会出现错误。虽然web容器不用重启Q但是用户需要先d(或以某种方式清空session)Q然后再d?/p>
当clazz参数不ؓI,q且用来创造新实例QDWR单的调用 class.newInstance() Ҏ。这U方法是没问题的Q除非脚本正在用某个参数创徏一个类Q或者调用某个函数来配置q个cR?不幸的是Q每ơ请求都要重新运行scriptq成上面的问题?/p>
|