通过一个工具类更深入理解动态代理和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() {@Overridepublic 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() {@Overridepublic 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>(){@Overrideprotected Connection initialValue() {return DaoUtils.conn(); };};//判断是否开启事务private static ThreadLocal<Boolean> hasStarTran_local=new ThreadLocal<Boolean>(){@Overrideprotected 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 permissionreturn 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相关推荐
- 利用代码分别实现jdk动态代理和cglib动态代理_面试之动态代理
大家好!我是CSRobot,从今天开始,我将会发布一些技术文章,内容就是结合春招以来的面试所遇到的问题进行分享,首先会对知识点进行一个探讨和整理,在最后会给出一些面试题并作出解答,希望可以帮助到大家! ...
- 一个工具类实现自定义Tablayout的下划线宽度
** 只改变Tablayout的下划线的宽度,只需要一个工具类就可以可满足** 1.写个工具类 封装: /*** @author FX* @date 2018/07/19 11:11* @fuctio ...
- VUE 爬坑之旅 -- 用 ES6 语法写一个工具类,并全局引用
在我前面的有一篇文章里有说过怎么引入外部的 JS 文件,详情见 VUE 爬坑之旅– 如何对公共JS,CSS进行统一管理,全局调用 .这里所说的外部 JS 文件指的是用 ES6 之前的老语法编写的各种 ...
- 一个工具类实现钉钉群头像效果
DingDingImage 项目地址:qiaoyhh/DingDingImage 简介:一个工具类实现钉钉群头像效果 更多:作者 提 Bug 标签: 拼图-钉钉- 描述 由几张图片拼成一张圆形图 ...
- 装饰器/使用类和对象封装一个工具类
# coding:utf-8 # 装饰器是以@开头,@结构称为语法糖,装饰器的作用主要是给现有的函数增加一些额外的功能. # @classmethod # @staticmethod # @prope ...
- 关于棋牌游戏的福建十三水的一个工具类(牌型分析、大小对比、自动摆牌、获取所有牌型组合)
关于棋牌游戏的福建十三水的一个工具类(牌型分析.大小对比.自动摆牌.获取所有牌型组合) 写的比较简陋,仅仅只是实现了而已,谈不上美观,大神们不要喷我 直接上图(个人研究使用,不允许用于不正当途径) 1 ...
- 什么是代理模式?代理模式有什么用?通过一个小程序分析静态代理和动态代理。自己简单实现动态代理。JDK动态代理和CGLIB动态代理的区别。
1. 代理模式有什么用 ①功能增强,在实现目标功能的基础上,又增加了额外功能.就像生活中的中介一样,他跟两边客户会有私下的交流. ②控制访问,代理不让用户直接和目标接触.就像中间商一样,他们不会让我们 ...
- Java中的原生动态代理和CGLIB动态代理的原理,我不信你全知道!
作者:CarpenterLee cnblogs.com/CarpenterLee/p/8241042.html 动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询 ...
- cglib动态代理jar包_Java中的原生动态代理和CGLIB动态代理的原理,我不信你全知道!...
作者:CarpenterLee cnblogs.com/CarpenterLee/p/8241042.html 动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询 ...
- Spring AOP之---基于JDK动态代理和CGLib动态代理的AOP实现
AOP(面向切面编程)是OOP的有益补充,它只适合那些具有横切逻辑的应用场合,如性能监测,访问控制,事物管理,日志记录等.至于怎么理解横切逻辑,敲完实例代码也就明白了. 为什么要使用AOP,举个栗子: ...
最新文章
- matlab的边缘检测方法,MATLAB应用—Matlab多种图像边缘检测方法
- ASP.Net生成静态HTML页!
- 复制Excel分类汇总结果的三种方法
- IDEA中使用Maven构建SSM项目
- 导航系统辅助设备行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
- 电子测量与仪器第四版pdf_电子技术经典资料汇总:模电篇800M
- python扫描端口hack_一款集http端口扫描和目录批量扫描为一体的自动化工具
- 百望税控服务器管理系统安装,电子发票客户端安装和配置操作说明
- C4D实现宜家闹钟建模
- 菜鸟点评-FILCO 忍者二代机械键
- 提高 Python 代码的可读性,你需要知道这10个技巧
- 一次搞懂什么是大数据
- Unity插件篇:Pocket RPG Weapon Trails(武器拖尾特效)部分解读以及基本用法
- hdmi怎么支持2k分辨率_27寸2k分辨率的护眼显示器推荐:飞利浦275E9使用测评
- 什么是抖音SEO,抖音seo优化怎么做?抖音seo实操教程
- windows下gromacs中文教程(simulate chain A of insulin (PDB ID: 1ZNI).
- TOP 云平台虚拟机内外网互通
- 刘利刚-什么是计算机图形学?
- Python小游戏-Las Vegas Black Jack- CASINO (21点)
- DNA与剑桥的老鹰酒吧
热门文章
- 矩阵运算_SLAM中用到的矩阵计算_基本公式及知识汇总
- 算法笔记_面试题_16. 二叉树相关_模板及示例十几道
- 【一天一个C++小知识】002.C++中const和volatile关键字
- particle filtering---粒子滤波(讲的很通俗易懂)
- 在手机上实现实时的单眼3D重建
- Asp.Net Core 入门(八)—— Taghelper
- 第四次作业——04树
- python 单例模式,一个类只能生成唯一的一个实例,重写__new__方法详解
- VS2015编译VS2013工程文件出错
- .11-Vue源码之patch(1)