通过一个工具类更深入理解动态代理和Threadlocal

时间:2024-10-06 23:33:02

动态代理和Threadlocal

一个代理类返回指定的接口,将方法调用指定的调用处理程序的代理类的实例。返回的是一个代理类,由指定的类装载器的定义和实现指定接口指定代理实例调用处理程序最近用到一个工具类,里面用到两个动态代理,以前一个动态代理还是用过,上两个,看来就必要好好研究一把了,这是一个连接数据源的工具类,用到动态代理,主要是为了为了更好的实现service和dao的解耦,同时也避免了一些冗余的代码,这个工具类的作用主要是在service层中一些方法可能用到事务,一些方法可能不用到事务,但是它们都要与数据源连接,传统的做法就是要用到事务管理的时候,就用QueryRunner让事务去连接,因为在事务管理中,涉及到并发和同时处理多个事件,那么每次都要为其单独写一个事务连接,那么有没有一种更好的方法,即能实现开启事务时,也能用,普通的不开启事务时,也能用用到动态代理,这是一种通过一个代理对象的方式,当代理对象被Invocation时,在它动态代理的invoke方法中,我们再对其判断进行处理,通过该方法是带有标记的具有事务管理的。我们就让其开启事务,并在此用事务去单独连接数据源。并在结束后,作回滚操作。而另一方面,我们对不开启事务的方法,就按正常的方式DataSource中的source连接数据源

这样的话就可以两全其美,我重点想说的是,两个动态代理同时运行时,执行过程是怎样的?

第二个代理对象是Connection,因为每次在事务管理中用Threadlocal(线程本地变量)。ThreadLocal多用在多线程的并发问题,对于同一个静态,多个线程并发访问数据,在连接数据库时,须要考虑到同一时刻,多个用户进来进行连接,我们用threadlocal就可以很方便的解决了不用等到这个用户用完下一个用户再来用连接的问题,我们知道,线程是程序内部处理事务的流程,每个线程里都有一个map对象,打个比方,如果说线程是一条河流里的水,threadlocal就是一个载着信息的小船,每当有用户来访问连接时,就给用户开启一条小船,带着它所请求的信息,到达想要去的地方,而每一个threadlocal都可以同时在河流上开启。。这样的话,就能为不同的用户传递不同的信息,就能保证每个线程都是用的都是自己的变量。我们知道,一般都是请求一次连接,然后再把这个连接给关上,然而,在事务阶段,当一个连接被打开后,可能还有下一条事务要进行处理,如果你把它关了话,
就会进行事务回滚,达不到我们所想要的目的。所以第二个动态代理,主要是用来管理代理对象Connection中的close方法。。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class DaoUtils {

	private static DataSource source=new ComboPooledDataSource();
	private DaoUtils(){

	}
	//普通情况
	/*
	public static DataSource getSource(){
		return source;
	}*/

	/**
	 * 改造此方法,在调用这个方法时检查,当前线程是否开启过事务
	 * 如果没有开启事务,返回的是最普通的数据源
	 * 如果开启过过事务,则返回改造过的数据源--改造底层获取连接的getConnection
	 */
	public static DataSource getSource(){
		if(TransactionManager.hasStarTran()){
			//开启过事务,返回一个改造getConnection--每次返回都开启了事务的的连接,此方法每次都返回,当前线程
			DataSource proxy= (DataSource) Proxy.newProxyInstance(source.getClass().getClassLoader(),
		    		source.getClass().getInterfaces(),
		    		new InvocationHandler() {

						@Override
						public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						//if("getconnction".equals(method.getName())){

							if("getConnection".equals(method.getName())){
							//当前调用getConnection方法,使它每次都返回当前的线程变量中保存当前线程中使用开启事务的连接
							final Connection conn=TransactionManager.getconn();

							Connection	proxy2= (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(),
									conn.getClass().getInterfaces(),
									new InvocationHandler() {

										@Override
										public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

											if("close".equals(method.getName())){
												return null;//什么也不做
											}else{
												return method.invoke(conn, args) ;
											}

										}
									});

							return proxy2;
						}else{
							return method.invoke(source, args);
						}

						}
					});
		    return proxy;

		}else{//如果没有开启事务,就返回
			return source;
		}

	}

	public static Connection conn(){
		try {
			return source.getConnection();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

}

事务的工具类:

import java.sql.Connection;

import javax.sql.DataSource;

import org.apache.commons.dbutils.DbUtils;

public class TransactionManager {

	/*因为存在多个线程共用一个连接,一个ThreadLocal代表一个变量,
	故其中里只能放一个数据,你有两个变量都要线程范围内共享,
	则要定义两个ThreadLocal对象。如果有一个百个变量要线程共享呢?
	那请先定义一个对象来装这一百个变量,然后在ThreadLocal中存储这一个对象。*/

	/*private static ThreadLocal<Connection> conn_local=new ThreadLocal<Connection>(){

		protected Connection initialValue() {
			return DaoUtils.conn();//连接数据库
		}
	};*/

	private static ThreadLocal<Connection> conn_local=new ThreadLocal<Connection>(){

		@Override
		protected Connection initialValue() {
			return DaoUtils.conn();	

		};
	};

	//判断是否开启事务
	private static ThreadLocal<Boolean> hasStarTran_local=new ThreadLocal<Boolean>(){

		@Override
		protected Boolean initialValue() {

			return false;
		};

	};

	private TransactionManager(){

	}

	//开启事务
	public static void startTran(){
		try {
			conn_local.get().setAutoCommit(false);
			//开启事务
			hasStarTran_local.set(true);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	//判断是否开启事务
	public static boolean hasStarTran(){
		return hasStarTran_local.get();
	}

	//提交事务
	public static void commit(){
		try {
			conn_local.get().commit();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	//回滚事务
	public static void rollback(){
		try {
			conn_local.get().rollback();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	public static Connection getconn(){
		return conn_local.get();
	}

	//释放连接
	public static void release(){
		DbUtils.closeQuietly(conn_local.get());
		conn_local.remove();
		hasStarTran_local.remove();
	}

}

最简单的动态代理运用

public class MyInvocationHandler implements InvocationHandler {  

    // 目标对象
    private Object target;  

    /**
     * 构造方法
     * @param target 目标对象
     */
    public MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }  

    /**
     * 执行目标对象的方法
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  

        // 在目标对象的方法执行之前
        System.out.println("------------------before------------------");  

        // 执行目标对象的方法
        Object result = method.invoke(target, args);  

        // 在目标对象的方法执行之后
        System.out.println("-------------------after------------------");  

        return result;
    }  

    /**
     * 获取目标对象的代理对象
     * @return 代理对象
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(), this);
    }
}  

下面是动态代理的源码:类加载器,一大堆接口,还有就是代理的实现类

 

   

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
		{
        if (h == null) {
            throw new NullPointerException();
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor

        /*
         * 调用它的构造函数指定调用处理程序。
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            SecurityManager sm = System.getSecurityManager();
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

    private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
        try {
            return cons.newInstance(new Object[] {h} );
        } catch (IllegalAccessException | InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString());
            }
        }
    }

    /**
     * Returns true if and only if the specified class was dynamically
     * generated to be a proxy class using the {@code getProxyClass}
     * method or the {@code newProxyInstance} method.
     *
     * <p>The reliability of this method is important for the ability
     * to use it to make security decisions, so its implementation should
     * not just test if the class in question extends {@code Proxy}.
     *
     * @param   cl the class to test
     * @return  {@code true} if the class is a proxy class and
     *          {@code false} otherwise
     * @throws  NullPointerException if {@code cl} is {@code null}
     */
    public static boolean isProxyClass(Class<?> cl) {
        if (cl == null) {
            throw new NullPointerException();
        }

        return proxyClasses.containsKey(cl);
    }

    /**
     * Returns the invocation handler for the specified proxy instance.
     *
     * @param   proxy the proxy instance to return the invocation handler for
     * @return  the invocation handler for the proxy instance
     * @throws  IllegalArgumentException if the argument is not a
     *          proxy instance
     */
    public static InvocationHandler getInvocationHandler(Object proxy)
        throws IllegalArgumentException
    {
        /*
         * Verify that the object is actually a proxy instance.
         */
        if (!isProxyClass(proxy.getClass())) {
            throw new IllegalArgumentException("not a proxy instance");
        }

        Proxy p = (Proxy) proxy;
        return p.h;
    }

	进去ProxyGenerator类的静态方法generateProxyClass,这里是真正生成代理类class字节码的地方。
	你可以去你的web-info目录下的classes中看到用到动态代理的类,都会多出一个字节码文件。带$的,
	可以用ju-gui(反编译工具)看到,里面就是一个代理对象。
	public static byte[] generateProxyClass(final String name,
                                           Class[] interfaces)
   {
       ProxyGenerator gen = new ProxyGenerator(name, interfaces);  

       final byte[] classFile = gen.generateClassFile();  

    // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上
       if (saveGeneratedFiles) {
           java.security.AccessController.doPrivileged(
           new java.security.PrivilegedAction<Void>() {
               public Void run() {
                   try {
                       FileOutputStream file =
                           new FileOutputStream(dotToSlash(name) + ".class");
                       file.write(classFile);
                       file.close();
                       return null;
                   } catch (IOException e) {
                       throw new InternalError(
                           "I/O exception saving generated file: " + e);
                   }
               }
           });
       }  

    // 返回代理类的字节码
       return classFile;
   }