通过一个工具类更深入理解动态代理和Threadlocal
动态代理和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; }
通过一个工具类更深入理解动态代理和Threadlocal的更多相关文章
- 用Java开发一个工具类,提供似于js中eval函数功能的eval方法
今天在看到<Java疯狂讲义>中一个章节习题: 开发一个工具类,该工具类提供一个eval()方法,实现JavaScript中eval()函数的功能--可以动态运行一行或多行程序代码.例如: ...
- java中使用反射做一个工具类,来为指定类中的成员变量进行赋值操作,使用与多个类对象的成员变量的赋值。
//------------------------------------------------我是代码的分割线 // 首选是一个工具类,在该工具类里面,定义了一个方法,public void s ...
- Java三种代理模式:静态代理、动态代理和cglib代理
一.代理模式介绍 代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 简言之,代理模式就是 ...
- java的静态代理、jdk动态代理和cglib动态代理
Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...
- JAVA高级架构师基础功:Spring中AOP的两种代理方式:动态代理和CGLIB详解
在spring框架中使用了两种代理方式: 1.JDK自带的动态代理. 2.Spring框架自己提供的CGLIB的方式. 这两种也是Spring框架核心AOP的基础. 在详细讲解上述提到的动态代理和CG ...
- Java动态代理和CGLib代理
本文参考 在上一篇"Netty + Spring + ZooKeeper搭建轻量级RPC框架"文章中涉及到了Java动态代理和CGLib代理,在这篇文章中对这两种代理方式做详解 下 ...
- JDK动态代理和CGLib动态代理简单演示
JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...
- SpringAOP-JDK 动态代理和 CGLIB 代理
在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类. 1.JDK 动态代理 那么接 ...
- 静态代理、动态代理和cglib代理
转:https://www.cnblogs.com/cenyu/p/6289209.html 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处 ...
随机推荐
- 【python教程01】 编辑器
工欲善其事,必先利其器.学习python,首先应该安装好开发中使用的编辑器. 那么在这里说一下我们推荐的两款:sublime text && pycharm 为什么推荐这两款编辑 ...
- JQuery插件的写法和规范
首先,在具体说明编写插件之前,我们先假定一个使用场景:有一个HTML页面(或.aspx页面),页面上放置了一个5行3列的表格,即:<table></table>标记,具体代码如 ...
- input text focus去掉默认光影
input:focus { outline:none; }
- 解决 Popup 位置不随窗口移动更新的问题
Popup弹出后,因业务需求设置了StaysOpen=true后,移动窗口位置或者改变窗口大小,Popup的位置不会更新. 如何更新位置? 获取当前Popup的Target绑定UserControl所 ...
- jsp&servlet——session监听
session监听,需要实现HttpSessionAttributeListener接口 attributeAdded:监听添加session attributeRemoved:监听删除session ...
- 【python标准库模块三】Os模块和Sys模块学习
Os模块 导入os模块 import os 获取当前工作目录 os.getcwd() 切换目录,跟linux中的cd一样 os.chdir("文件夹名") 递归生成文件夹 os.m ...
- java中的final和volatile详解
相比synchronized,final和volatile也是经常使用的关键字,下面聊一聊这两个关键字的使用和实现 1.使用 final使用: 修饰类表示该类为终态类,无法被继承 修饰方法表示该方法无 ...
- linux系统性能监控--I/O利用率
尽管整体的处理器速度. 内存大小以及 I/O执行速度在不断提高,但 I/O操作的吞吐率和延迟性能仍然要比等价的内存访问操作低多个数量级.另外,由于许多工作负荷都拥有重要的I/O组件,I/O处理很容易成 ...
- A discussion of Dead Connection Detection, Resource Limits, V$SESSION, V$PROCESS and OS processes
A discussion of Dead Connection Detection, Resource Limits, V$SESSION, V$PROCESS and OS processes (文 ...
- PGM:部分有向模型之条件随机场与链图模型
http://blog.csdn.net/pipisorry/article/details/52529287 贝叶斯网与马尔可夫网 [PGM:无向图模型:马尔可夫网]中例3.8和例4.8显示,贝叶斯 ...