通过一个工具类更深入理解动态代理和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)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处 ...
随机推荐
- Map,HashMap,TreeMap
一.HashMap,TreeMap差别 1.两种常规Map性能 HashMap:适用于在Map中插入.删除和定位元素. Treemap:适用于按自然顺序或自定义顺序遍历键(key). 2.总结 Has ...
- Mysql优化--慢查询日志
Mysql 系列文章主页 =============== 默认没有开启慢查询日志功能.如果不是调优需要的话,一般不建议开启. 查看是否开启慢查询日志: SHOW VARIABLES LIKE '%sl ...
- ThreadLocal基本原理及运用
ThreadLocal提供本地线程变量.这个变量里面的值(通过get方法获取)是和其他线程分割开来的,变量的值只有当前线程能访问到,不像一般的类型比如Person,Student类型的变量,只要访问到 ...
- Eclipse 一直不停 building workspace完美解决总结
一.产生这个问题的原因多种1.自动升级 2.未正确关闭 3.maven下载lib挂起 等.. 二.解决总结(1).解决方法 方法1.修改eclipse启动文件 eclipse.ini ...
- FastDFS+Nginx安装配置
下载相关包: libevent-2.0.22-stable.tar.gz => https://github.com/libevent/libevent/releases/download/re ...
- Luogu P3740 [HAOI2014]贴海报_线段树
线段树版的海报 实际上这个与普通的线段树相差不大,只是貌似数据太水,暴力都可以过啊 本来以为要离散的,结果没打就A了 #include<iostream> #include<cstd ...
- 搭建一个交互式的前端构建环境.md
为了提高开发效率.减少重复的操作,现在几乎全部的前端项目都需要依赖一些构建工具来实现自动化打包,主流的有webpack, gulp, grunt等.加上各种各样的配置文件就会形成了一个相对复杂的构建环 ...
- dubbo服务的发布和调用
Dubbo是分布式服务架构,是一个优秀的开源服务型框架,使得应用可以通过高性能的rpc实现服务的输入和输出功能.其实dubbo就是资源调度和治理中心的管理工具. 发布dubbo服务:在提供服务的应用中 ...
- Android碎裂的粒子效果
最近看到一段时间都没怎么更新文章了,一直在学习iOS相关内容.偶然间看到一个碎裂的粒子效果,觉得很有意思,就查了查,参考下网上的思路自己撸了个轮子. 好了,说了这么多,先看看效果吧~ 依惯例,先说下行 ...
- 高仿腾讯QQ最终版
之前写过一篇关于高仿腾讯QQ的博客,不知道的看这:http://blog.csdn.net/htq__/article/details/51840273 ,主要是从界面上高仿了腾讯QQ,在UI上基本上 ...