yuyee

2010年11月3日 #

适配器

适配器模式:将一个现有类实现的功能接口转变为客户希望的接口

场景:你想使用一个已经存在的类,但是这个类的接口不符合需求,所以需要适配

2中实现:一种是继承,一种是委托,先来看看继承

   

第一步:系统现有功能

package com.google.desginpattern.adapter;
/**
 * 现有系统提供的功能
 * 
 * 
@author Administrator
 * 
 
*/
public class BMWCar {
public void quickDriver() {
System.out.println(
"宝马太快");
}
}

 

第二步:客户需要的接口

package com.google.desginpattern.adapter;
/**
 * 客户需要的接口
 * 
@author Administrator
 *
 
*/
public interface Car {
public void driver();
public void brake();
}

 

第三步:实现客户需要的功能

package com.google.desginpattern.adapter;
/**
 * 匹配客户需求的实现
 * 
@author Administrator
 *
 
*/
public class CarAdapter extends BMWCar implements Car {
@Override
public void brake() {
System.out.println(
"刹车");
}
@Override
public void driver() {
quickDriver();
}
}

 

测试类:

 

package com.google.desginpattern.adapter;


public class Test {

public static void main(String[] args) {


Car car 
= new CarAdapter();

car.brake();

car.driver();

}


}

输出:

刹车

宝马太快

如果是委托的方式,改写adapter

package com.google.desginpattern.adapter;
/**
 * 匹配客户需求的实现
 * 
 * 
@author Administrator
 * 
 
*/
public class CarAdapter implements Car {
private BMWCar car;
@Override
public void brake() {
System.out.println(
"刹车");
}
@Override
public void driver() {
car.quickDriver();
}
public BMWCar getCar() {
return car;
}
public void setCar(BMWCar car) {
this.car = car;
}
}

 

posted @ 2010-11-29 22:28 羔羊| 编辑 收藏

装饰器


装饰器:装饰器模式主要用于系统扩张功能用,在系统原有的功能上,扩展出其他的功能,JDKIO包用到很多,比如datainputstream之类,需要用其他流进行构造的上层类,符合面向对象设计的开闭原则

下面我来写个例子:

首先,写一个Car模版,定义基本属性及行为功能driver

package com.google.desginpattern.decoration;
//其实这是个模版
public abstract class Car {
private int spreed;
public int getSpreed() {
return spreed;
}
public void setSpreed(int spreed) {
this.spreed = spreed;
}
public abstract void driver();
}

 

第二步:具体车比如宝马,这是目前系统中这个类能提供的功能

package com.google.desginpattern.decoration;
//目前系统中此类的功能
public class BMWCar extends Car {
@Override
public void driver() {
System.out.println(
"我开着宝马车");
}
}

 

现在我想在这个类上扩展出其他功能,比如:泡妞

第三步:定义一个装饰模板,为什么给他定义个模板呢~因为可以给这个BMWCar类装饰很不同的功能,不只泡妞一个~

继承Car父类,覆盖driver功能,调用Car引用完成driver功能

package com.google.desginpattern.decoration;
//装饰器父类
public abstract class DecorationCar extends Car {
// 引入car
private Car car;
@Override
public void driver() {
car.driver();
// 调用此car来完成装饰器的功能
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
}

 

第四步:具体的装饰功能,添加一个构造函数,参数为Car,为装饰父类Car引用赋值,其实就是原来具体的功能类,回想下IO包里经常new的代码段就明白~~

package com.google.desginpattern.decoration;
//具体的装饰类,添加额外的泡妞功能
public class DecorationBMWCar extends DecorationCar {
public DecorationBMWCar(Car car) {
super.setCar(car);
}
@Override
public void driver() {
// TODO Auto-generated method stub
super.driver();// 调用原来的功能
System.out.println("泡妞");// 添加额外的功能
}
}

 

测试类:构造的方法很像IO包里的流

package com.google.desginpattern.decoration;
public class Test {
public static void main(String[] args) {
Car car 
= new DecorationBMWCar(new BMWCar());
car.driver();
}
}

 

输出:

我开着宝马车

泡妞

posted @ 2010-11-29 21:36 羔羊| 编辑 收藏

IOC初始化和互相引用解决

     摘要: 观察IOC中容器初始化某个Bean顺序,现先一个JAVABean类,看看控制台输出:package com.google.aop.exception.ioc; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory...  阅读全文

posted @ 2010-11-10 16:27 羔羊| 编辑 收藏

AbstractQueuedSynchronizer看看


API中的解释:为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁定和相关同步器(信号量、事件,等等)提供一个框架。此类的设计目标是成为依靠单个原子 int 值来表示状态的大多数同步器的一个有用基础。子类必须定义更改此状态的受保护方法,并定义哪种状态对于此对象意味着被获取或被释放。假定这些条件之后,此类中的其他方法就可以实现所有排队和阻塞机制。子类可以维护其他状态字段,但只是为了获得同步而只追踪使用 getState()getState()getState()setState(int)compareAndSetState(int, int) 方法来操作以原子方式更新的 int 值。
此类采用模板模式设计,此类为一个抽象类,但是没抽象方法,每个sync子类需要实现5个受保护的方法

这个5个方法在AbstractQueuedSynchronizer 都抛出throw new UnsupportedOperationException();
AbstractQueuedSynchronizer 中有3个属性:主要声明一个状态和一个wait queue,通过

wait queue中Node 为一个双向链表,需要去理解Node中几个静态字段值的意义,下面为他的源码:
static final class Node {
        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

        /**
         * Status field, taking on only the values:
         *   SIGNAL:     The successor of this node is (or will soon be)
         *               blocked (via park), so the current node must
         *               unpark its successor when it releases or
         *               cancels. To avoid races, acquire methods must
         *               first indicate they need a signal,
         *               then retry the atomic acquire, and then,
         *               on failure, block.
         *   CANCELLED:  This node is cancelled due to timeout or interrupt.
         *               Nodes never leave this state. In particular,
         *               a thread with cancelled node never again blocks.
         *   CONDITION:  This node is currently on a condition queue.
         *               It will not be used as a sync queue node until
         *               transferred. (Use of this value here
         *               has nothing to do with the other uses
         *               of the field, but simplifies mechanics.)
         *   0:          None of the above
         *
         * The values are arranged numerically to simplify use.
         * Non-negative values mean that a node doesn't need to
         * signal. So, most code doesn't need to check for particular
         * values, just for sign.
         *
         * The field is initialized to 0 for normal sync nodes, and
         * CONDITION for condition nodes.  It is modified only using
         * CAS.
         */
        volatile int waitStatus;

        /**
         * Link to predecessor node that current node/thread relies on
         * for checking waitStatus. Assigned during enqueing, and nulled
         * out (for sake of GC) only upon dequeuing.  Also, upon
         * cancellation of a predecessor, we short-circuit while
         * finding a non-cancelled one, which will always exist
         * because the head node is never cancelled: A node becomes
         * head only as a result of successful acquire. A
         * cancelled thread never succeeds in acquiring, and a thread only
         * cancels itself, not any other node.
         */
        volatile Node prev;

        /**
         * Link to the successor node that the current node/thread
         * unparks upon release. Assigned once during enqueuing, and
         * nulled out (for sake of GC) when no longer needed.  Upon
         * cancellation, we cannot adjust this field, but can notice
         * status and bypass the node if cancelled.  The enq operation
         * does not assign next field of a predecessor until after
         * attachment, so seeing a null next field does not
         * necessarily mean that node is at end of queue. However, if
         * a next field appears to be null, we can scan prev's from
         * the tail to double-check.
         */
        volatile Node next;

        /**
         * The thread that enqueued this node.  Initialized on
         * construction and nulled out after use.
         */
        volatile Thread thread;

        /**
         * Link to next node waiting on condition, or the special
         * value SHARED.  Because condition queues are accessed only
         * when holding in exclusive mode, we just need a simple
         * linked queue to hold nodes while they are waiting on
         * conditions. They are then transferred to the queue to
         * re-acquire. And because conditions can only be exclusive,
         * we save a field by using special value to indicate shared
         * mode.
         */
        Node nextWaiter;

        /**
         * Returns true if node is waiting in shared mode
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * Returns previous node, or throws NullPointerException if
         * null.  Use when predecessor cannot be null.
         * @return the predecessor of this node
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }


    }
获取锁定调用的方法,其实这个方法是阻塞的:
  public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
如果获取不成功则调用如下方法:
   final boolean acquireQueued(final Node node, int arg) {
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {//当节点是头节点且独占时才返回
                    setHead(node);
                    p.next = null; // help GC
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())//阻塞并判断是否打断,其实这个判断才是自旋锁真正的猥琐点,
意思是如果你的前继节点不是head,而且当你的前继节点状态是Node.SIGNAL时,你这个线程将被park(),直到另外的线程release时,发现head.next是你这个node时,才unpark,你才能继续循环并获取锁
                    interrupted = true;
            }
shouldParkAfterFailedAcquire这个方法删除所有waitStatus>0也就是CANCELLED状态的Node,并设置前继节点为signal
 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int s = pred.waitStatus;
        if (s < 0)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park
             */
            return true;
        if (s > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
   do {
node.prev = pred = pred.prev;
   } while (pred.waitStatus > 0);
   pred.next = node;
}
        else
            /*
             * Indicate that we need a signal, but don't park yet. Caller
             * will need to retry to make sure it cannot acquire before
             * parking.
             */
            compareAndSetWaitStatus(pred, 0, Node.SIGNAL);
        return false;
    }


使用LockSupport.park(this),禁用当前线程
private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);//block
        return Thread.interrupted();
    }
释放锁:
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);//unblock
            return true;
        }
        return false;
    }
private void unparkSuccessor(Node node) {
        /*
         * Try to clear status in anticipation of signalling.  It is
         * OK if this fails or if status is changed by waiting thread.
         */
        compareAndSetWaitStatus(node, Node.SIGNAL, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }


        } catch (RuntimeException ex) {
            cancelAcquire(node);
            throw ex;
        }
    }
  

看下ReentrantLock锁中sync的实现:
 static abstract class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is
         * implemented in subclasses, but both need nonfair
         * try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes this lock instance from a stream.
         * @param s the stream
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }
非公平规则下nonfairTryAcquire,获取当前锁的state,通过CAS原子操作设置为1,并将当前线程设置为独占线程,如果当前线程已经拿了锁,则state增加1
公平锁中 有如下判断:
if (isFirst(current) &&//判断头元素
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
在获取锁步骤:
1.调用tryAcquire来获取,如果失败,则进入2
2.调用addWaiter,以独占模式将node放到tail位置
3.调用acquireQueued方法,此方法阻塞,直到node的pre为head,并成功获取锁定,也可能存在阻塞并打断情况
释放锁的步骤:
1.放弃排他锁持有权
2.unpark 节点的下一个blocked节点

公平锁与非公平锁:从代码上看,非公平锁是让当前线程优先独占,而公平锁则是让等待时间最长的线程优先,非公平的可能让其他线程没机会执行,而公平的则可以让等待时间最长的先执行,但是性能上会差点

posted @ 2010-11-09 15:30 羔羊| 编辑 收藏

linkedhashmap看看

linkedhashmap继承自hashmap,他的底层维护的一个链表,    private transient Entry<K,V> header 来记录元素的插入顺序和访问顺序;
hashmap的构造函数中调用init()方法,而linkedhashmap中重写了init(),将head元素初始化
   void init() {
        header = new Entry<K,V>(-1, null, null, null);
        header.before = header.after = header;
    }

private final boolean accessOrder这个属性表示是否要根据访问顺序改变线性结构
在linkedhashmap中改写了hashmap的get()方法,增加了 e.recordAccess(this),这个方法主要是根据accessOrder的值判断是否需要实现LRU,
 void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }

addBefore这个方法是把刚访问的元素放到head的前面
 private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }
put方法继承自hashmap,hashmap预留了 e.recordAccess(this)这个方法:
     public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

并通过重写 addEntry(hash, key, value, i)这个方法,实现LRU中的删除动作:
    void addEntry(int hash, K key, V value, int bucketIndex) {
        createEntry(hash, key, value, bucketIndex);

        // Remove eldest entry if instructed, else grow capacity if appropriate
        Entry<K,V> eldest = header.after;//找到最老的元素,这个在addBefore里确定,初次赋值是当只有一个head时候,你插入一个元素
        if (removeEldestEntry(eldest)) {//这个是受保护的方法,需要自己制定删除策略,比如size() > 最大容量,可自己实现,默认为false,也就是不开启
            removeEntryForKey(eldest.key);
        } else {
            if (size >= threshold)
                resize(2 * table.length);
        }
    }
自己重写这个方法,指定删除策略:
 protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }
因此,可用linkedhashmap 构建一个基于LRU算法的缓存。


package com.google.study.cache;

import java.util.LinkedHashMap;
import java.util.concurrent.locks.ReentrantLock;

public class SimpleCache<K, V> extends LinkedHashMap<K, V> {

private int maxCapacity;
private ReentrantLock lock = new ReentrantLock();

public SimpleCache(int maxCapacity, float load_factory) {
super(maxCapacity, load_factory, true);
this.maxCapacity = maxCapacity;
}

public int getMaxCapacity() {
return maxCapacity;
}

public void setMaxCapacity(int maxCapacity) {
this.maxCapacity = maxCapacity;
}

@Override
protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
// TODO Auto-generated method stub
return super.removeEldestEntry(eldest);
}

public V get(Object key) {
lock.lock();
try {
return super.get(key);
} finally {
lock.unlock();
}

}

public V put(K k, V v) {
lock.lock();
try {
return super.put(k, v);
} finally {
lock.unlock();
}
}

@Override
public void clear() {
lock.lock();
try {
super.clear();
} finally {
lock.unlock();
}
}

@Override
public int size() {

lock.lock();
try {
return super.size();
} finally {
lock.unlock();
}
}

}



posted @ 2010-11-08 23:14 羔羊| 编辑 收藏

IOC监管Bean

1.InitializingBean接口,在初始化Bean时容器会调用前者的afterPropertiesSet()方法

2.DisposableBean接口,在析构Bean时容器会调用destroy()方法

3.BeanFactoryAware接口,当它被BeanFactory创建后,它会拥有一个指向创建它的BeanFactory的引用

4.BeanPostProcessor接口,这个接口两个方法,postProcessBeforeInitialization(Object bean, String beanName)和postProcessAfterInitialization(Object bean, String beanName) 在其他Bean构造前后执行

5.BeanFactoryPostProcessor接口,Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其它的bean之前读取配置元数据,并有可能修改它

package com.google.springioctest;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class A implements BeanPostProcessor, InitializingBean,
BeanFactoryAware, BeanFactoryPostProcessor,DisposableBean {

public A() {
System.out.println("Class A");
}
private void init(){
System.out.println("Class A init");
}

@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("Class A afterPropertiesSet()");

}

@Override
public void destroy() throws Exception {
System.out.println("Class A destroy()");

}

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("Class A setBeanFactory()");
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("Class A postProcessAfterInitialization");
return null;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("Class A postProcessBeforeInitialization");
return null;
}


@Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("Class A postProcessBeanFactory");
}

}
执行结果:
Class A
Class A setBeanFactory()
Class A afterPropertiesSet()
Class A init
Class A postProcessBeanFactory
创建一个B类,由A来监管B类,B实现 InitializingBean,BeanFactoryAware,BeanFactoryPostProcessor
package com.google.springioctest;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class B implements InitializingBean, BeanFactoryAware,
BeanFactoryPostProcessor {

public B() {
System.out.println("Class B");
}

public void init() {
System.out.println("Class B init");
}

@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Class B afterPropertiesSet");

}

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("Class B beanFactory");

}

@Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("Class B postProcessBeanFactory");
}

}
执行结果:
Class A
Class A setBeanFactory()
Class A afterPropertiesSet()
Class A init
Class B
Class B beanFactory
Class B afterPropertiesSet
Class B init
Class A postProcessBeanFactory
Class B postProcessBeanFactory

可以看出A并没有监管B类,也就是没调用BeanPostProcessor这个接口的2个方法
再来去掉B上的BeanFactoryPostProcessor接口
package com.google.springioctest;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class B implements InitializingBean,
BeanFactoryAware{

public B() {
System.out.println("Class B");
}

public void init() {
System.out.println("Class B init");
}

@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Class B afterPropertiesSet");

}

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("Class B beanFactory");

}

public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("Class B postProcessBeanFactory");
}


}
执行输出:Class A
Class A setBeanFactory()
Class A afterPropertiesSet()
Class A init
Class A postProcessBeanFactory
2010-11-3 21:33:31 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@8916a2: defining beans [A,B]; root of factory hierarchy
Class B
Class B beanFactory
Class A postProcessBeforeInitialization
2010-11-3 21:33:31 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
信息: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@8916a2: defining beans [A,B]; root of factory hierarchy
Class A destroy()
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'B' defined in class path resource [icoContext.xml]: Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1338)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:429)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at com.google.springioctest.Test.main(Test.java:8)
Caused by: java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1393)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1375)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
... 15 more
抛异常了。。。。。
原因是A类里的postProcessBeforeInitialization,postProcessAfterInitialization2个方法没有返回bean,修改下
执行输出:
Class A
Class A setBeanFactory()
Class A afterPropertiesSet()
Class A init
Class A postProcessBeanFactory
2010-11-3 21:37:10 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1193779: defining beans [A,B]; root of factory hierarchy
Class B
Class B beanFactory
Class A postProcessBeforeInitialization
Class B afterPropertiesSet
Class B init
Class A postProcessAfterInitialization
在B类被初始化之后,也就是调用afterPropertiesSet之前那段时间,属性初始化完成后,进行了回调,控制B类

注意:在写被监控的Bean的时候,不能实现BeanFactoryPostProcessor这个接口,没看源码,其实也不知道是什么原因,哈哈,只能硬记了

posted @ 2010-11-03 17:04 羔羊| 编辑 收藏

嵌套事务

在所有使用 spring 的应用中, 声明式事务管理可能是使用率最高的功能了, 但是, 从我观察到的情况看, 绝大多数人并不能深刻理解事务声明中不同事务传播属性配置的的含义, 让我们来看一下TransactionDefinition 接口中的定义 ,在 spring 中一共定义了六种事务传播属性, 如果你觉得看起来不够直观, 那么我来转贴一个满大街都有的翻译 
PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 
PROPAGATION_SUPPORTS 
-- 支持当前事务,如果当前没有事务,就以非事务方式执行。 
PROPAGATION_MANDATORY 
-- 支持当前事务,如果当前没有事务,就抛出异常。 
PROPAGATION_REQUIRES_NEW 
-- 新建事务,如果当前存在事务,把当前事务挂起。 
PROPAGATION_NOT_SUPPORTED 
-- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 
PROPAGATION_NEVER 
-- 以非事务方式执行,如果当前存在事务,则抛出异常。 
PROPAGATION_NESTED 
-- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。 
它要求事务管理器或者使用JDBC 
3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager) 

在我所见过的误解中, 最常见的是下面这种: 
假如有两个业务接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一个方法实现如下 
/** 
* 事务属性配置为 PROPAGATION_REQUIRED 
*/
 
void methodA() 
// 调用 ServiceB 的方法 
ServiceB.methodB(); 
}
 
那么如果 ServiceB 的 methodB  如果配置了事务, 就必须配置为 PROPAGATION_NESTED 


这种想法可能害了不少人, 认为 Service 之间应该避免互相调用, 其实根本不用担心这点,PROPAGATION_REQUIRED 已经说得很明白, 
如果当前线程中已经存在事务, 方法调用会加入此事务, 果当前没有事务,就新建一个事务, 所以 ServiceB#methodB() 的事务只要遵循最普通的规则配置为 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我们称之为内部事务, 为下文打下基础) 抛了异常, 那么 ServiceA#methodA(我们称之为外部事务) 如果没有特殊配置此异常时事务提交 (即 +MyCheckedException的用法), 那么整个事务是一定要 rollback 的, 什么 Service 只能调 Dao 之类的言论纯属无稽之谈, spring 只负责配置了事务属性方法的拦截, 它怎么知道你这个方法是在 Service 还是 Dao 里 ? 

最容易弄混淆的其实是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么这两种方式又有何区别呢? 我简单的翻译一下 Juergen Hoeller 的话 : 
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. 

    另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务,  它是已经存在事务的一个真正的子事务. 潜套事务开始执行时,  它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交. 

    由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.

 那么外部事务如何利用嵌套事务的 savepoint 特性呢, 我们用代码来说话

 

ServiceA {       
    
/**  
     * 事务属性配置为 PROPAGATION_REQUIRED  
     
*/
  
    
void methodA() {   
        ServiceB.methodB();    }
   
 }
     
ServiceB 
{      
    
/**  
     * 事务属性配置为 PROPAGATION_REQUIRES_NEW  
     
*/
    
    
void methodB() {   
    }
         
}
   

这种情况下, 因为 ServiceB#methodB 的事务属性为 PROPAGATION_REQUIRES_NEW, 所以两者不会发生任何关系, ServiceA#methodA 和 ServiceB#methodB 不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务, 在 ServiceB#methodB 执行时 ServiceA#methodA 的事务已经挂起了 (关于事务挂起的内容已经超出了本文的讨论范围, 有时间我会再写一些挂起的文章) . 

ServiceA {      
    
/**  
     * 事务属性配置为 PROPAGATION_REQUIRED  
     
*/
  
    
void methodA() {   
        ServiceB.methodB();   
    }
   
}
     
ServiceB 
{         
    
/**  
     * 事务属性配置为 PROPAGATION_NESTED  
     
*/
    
    
void methodB() {   
    }
         
}
 

ServiceB#methodB 如果 rollback, 那么内部事务(即 ServiceB#methodB) 将回滚到它执行前的 SavePoint(注意, 这是本文中第一次提到它, 潜套事务中最核心的概念), 而外部事务(即 ServiceA#methodA) 可以有以下两种处理方式: 
1. 改写 ServiceA 如下 

ServiceA {      
    
/**  
     * 事务属性配置为 PROPAGATION_REQUIRED  
     
*/
  
    
void methodA() {   
        
try {   
            ServiceB.methodB();   
        }
 catch (SomeException) {   
            
// 执行其他业务, 如 ServiceC.methodC();   
        }
   
    }
    
}
  

这种方式也是潜套事务最有价值的地方, 它起到了分支执行的效果, 如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(), 而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点. (题外话 : 看到这种代码, 似乎似曾相识, 想起了 prototype.js 中的 Try 函数 ) 

2. 代码不做任何修改, 那么如果内部事务(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此), 外部事务(即 ServiceA#methodA) 将根据具体的配置决定自己是 commit 还是 rollback (+MyCheckedException). 
上面大致讲述了潜套事务的使用场景, 下面我们来看如何在 spring 中使用 PROPAGATION_NESTED, 首先来看 AbstractPlatformTransactionManager 

JdbcTransactionObjectSupport 告诉我们必须要满足两个条件才能 createSavepoint : 
  
2. java.sql.Savepoint 必须存在, 即 jdk 版本要 1.4+ 
3. Connection.getMetaData().supportsSavepoints() 必须为 true, 即 jdbc drive 必须支持 JDBC 3.0 


确保以上条件都满足后, 你就可以尝试使用 PROPAGATION_NESTED 了.

posted @ 2010-11-03 00:11 羔羊| 编辑 收藏