动态代理和Threadlocal

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

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

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

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. import java.sql.Connection;
  5.  
  6. import javax.sql.DataSource;
  7.  
  8. import com.mchange.v2.c3p0.ComboPooledDataSource;
  9.  
  10. public class DaoUtils {
  11.  
  12. private static DataSource source=new ComboPooledDataSource();
  13. private DaoUtils(){
  14.  
  15. }
  16. //普通情况
  17. /*
  18. public static DataSource getSource(){
  19. return source;
  20. }*/
  21.  
  22. /**
  23. * 改造此方法,在调用这个方法时检查,当前线程是否开启过事务
  24. * 如果没有开启事务,返回的是最普通的数据源
  25. * 如果开启过过事务,则返回改造过的数据源--改造底层获取连接的getConnection
  26. */
  27. public static DataSource getSource(){
  28. if(TransactionManager.hasStarTran()){
  29. //开启过事务,返回一个改造getConnection--每次返回都开启了事务的的连接,此方法每次都返回,当前线程
  30. DataSource proxy= (DataSource) Proxy.newProxyInstance(source.getClass().getClassLoader(),
  31. source.getClass().getInterfaces(),
  32. new InvocationHandler() {
  33.  
  34. @Override
  35. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  36. //if("getconnction".equals(method.getName())){
  37.  
  38. if("getConnection".equals(method.getName())){
  39. //当前调用getConnection方法,使它每次都返回当前的线程变量中保存当前线程中使用开启事务的连接
  40. final Connection conn=TransactionManager.getconn();
  41.  
  42. Connection proxy2= (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(),
  43. conn.getClass().getInterfaces(),
  44. new InvocationHandler() {
  45.  
  46. @Override
  47. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  48.  
  49. if("close".equals(method.getName())){
  50. return null;//什么也不做
  51. }else{
  52. return method.invoke(conn, args) ;
  53. }
  54.  
  55. }
  56. });
  57.  
  58. return proxy2;
  59. }else{
  60. return method.invoke(source, args);
  61. }
  62.  
  63. }
  64. });
  65. return proxy;
  66.  
  67. }else{//如果没有开启事务,就返回
  68. return source;
  69. }
  70.  
  71. }
  72.  
  73. public static Connection conn(){
  74. try {
  75. return source.getConnection();
  76. } catch (Exception e) {
  77. e.printStackTrace();
  78. throw new RuntimeException(e);
  79. }
  80. }
  81.  
  82. }

事务的工具类:

  1. import java.sql.Connection;
  2.  
  3. import javax.sql.DataSource;
  4.  
  5. import org.apache.commons.dbutils.DbUtils;
  6.  
  7. public class TransactionManager {
  8.  
  9. /*因为存在多个线程共用一个连接,一个ThreadLocal代表一个变量,
  10. 故其中里只能放一个数据,你有两个变量都要线程范围内共享,
  11. 则要定义两个ThreadLocal对象。如果有一个百个变量要线程共享呢?
  12. 那请先定义一个对象来装这一百个变量,然后在ThreadLocal中存储这一个对象。*/
  13.  
  14. /*private static ThreadLocal<Connection> conn_local=new ThreadLocal<Connection>(){
  15.  
  16. protected Connection initialValue() {
  17. return DaoUtils.conn();//连接数据库
  18. }
  19. };*/
  20.  
  21. private static ThreadLocal<Connection> conn_local=new ThreadLocal<Connection>(){
  22.  
  23. @Override
  24. protected Connection initialValue() {
  25. return DaoUtils.conn();
  26.  
  27. };
  28. };
  29.  
  30. //判断是否开启事务
  31. private static ThreadLocal<Boolean> hasStarTran_local=new ThreadLocal<Boolean>(){
  32.  
  33. @Override
  34. protected Boolean initialValue() {
  35.  
  36. return false;
  37. };
  38.  
  39. };
  40.  
  41. private TransactionManager(){
  42.  
  43. }
  44.  
  45. //开启事务
  46. public static void startTran(){
  47. try {
  48. conn_local.get().setAutoCommit(false);
  49. //开启事务
  50. hasStarTran_local.set(true);
  51. } catch (Exception e) {
  52. e.printStackTrace();
  53. throw new RuntimeException(e);
  54. }
  55. }
  56. //判断是否开启事务
  57. public static boolean hasStarTran(){
  58. return hasStarTran_local.get();
  59. }
  60.  
  61. //提交事务
  62. public static void commit(){
  63. try {
  64. conn_local.get().commit();
  65. } catch (Exception e) {
  66. e.printStackTrace();
  67. throw new RuntimeException(e);
  68. }
  69. }
  70. //回滚事务
  71. public static void rollback(){
  72. try {
  73. conn_local.get().rollback();
  74. } catch (Exception e) {
  75. e.printStackTrace();
  76. throw new RuntimeException(e);
  77. }
  78. }
  79.  
  80. public static Connection getconn(){
  81. return conn_local.get();
  82. }
  83.  
  84. //释放连接
  85. public static void release(){
  86. DbUtils.closeQuietly(conn_local.get());
  87. conn_local.remove();
  88. hasStarTran_local.remove();
  89. }
  90.  
  91. }

最简单的动态代理运用

  1. public class MyInvocationHandler implements InvocationHandler {
  2.  
  3. // 目标对象
  4. private Object target;
  5.  
  6. /**
  7. * 构造方法
  8. * @param target 目标对象
  9. */
  10. public MyInvocationHandler(Object target) {
  11. super();
  12. this.target = target;
  13. }
  14.  
  15. /**
  16. * 执行目标对象的方法
  17. */
  18. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  19.  
  20. // 在目标对象的方法执行之前
  21. System.out.println("------------------before------------------");
  22.  
  23. // 执行目标对象的方法
  24. Object result = method.invoke(target, args);
  25.  
  26. // 在目标对象的方法执行之后
  27. System.out.println("-------------------after------------------");
  28.  
  29. return result;
  30. }
  31.  
  32. /**
  33. * 获取目标对象的代理对象
  34. * @return 代理对象
  35. */
  36. public Object getProxy() {
  37. return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
  38. target.getClass().getInterfaces(), this);
  39. }
  40. }

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

 

   

  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class<?>[] interfaces,
  3. InvocationHandler h)
  4. throws IllegalArgumentException
  5. {
  6. if (h == null) {
  7. throw new NullPointerException();
  8. }
  9.  
  10. /*
  11. * Look up or generate the designated proxy class.
  12. */
  13. Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor
  14.  
  15. /*
  16. * 调用它的构造函数指定调用处理程序。
  17. */
  18. try {
  19. final Constructor<?> cons = cl.getConstructor(constructorParams);
  20. final InvocationHandler ih = h;
  21. SecurityManager sm = System.getSecurityManager();
  22. if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
  23. // create proxy instance with doPrivilege as the proxy class may
  24. // implement non-public interfaces that requires a special permission
  25. return AccessController.doPrivileged(new PrivilegedAction<Object>() {
  26. public Object run() {
  27. return newInstance(cons, ih);
  28. }
  29. });
  30. } else {
  31. return newInstance(cons, ih);
  32. }
  33. } catch (NoSuchMethodException e) {
  34. throw new InternalError(e.toString());
  35. }
  36. }
  37.  
  38. private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
  39. try {
  40. return cons.newInstance(new Object[] {h} );
  41. } catch (IllegalAccessException | InstantiationException e) {
  42. throw new InternalError(e.toString());
  43. } catch (InvocationTargetException e) {
  44. Throwable t = e.getCause();
  45. if (t instanceof RuntimeException) {
  46. throw (RuntimeException) t;
  47. } else {
  48. throw new InternalError(t.toString());
  49. }
  50. }
  51. }
  52.  
  53. /**
  54. * Returns true if and only if the specified class was dynamically
  55. * generated to be a proxy class using the {@code getProxyClass}
  56. * method or the {@code newProxyInstance} method.
  57. *
  58. * <p>The reliability of this method is important for the ability
  59. * to use it to make security decisions, so its implementation should
  60. * not just test if the class in question extends {@code Proxy}.
  61. *
  62. * @param cl the class to test
  63. * @return {@code true} if the class is a proxy class and
  64. * {@code false} otherwise
  65. * @throws NullPointerException if {@code cl} is {@code null}
  66. */
  67. public static boolean isProxyClass(Class<?> cl) {
  68. if (cl == null) {
  69. throw new NullPointerException();
  70. }
  71.  
  72. return proxyClasses.containsKey(cl);
  73. }
  74.  
  75. /**
  76. * Returns the invocation handler for the specified proxy instance.
  77. *
  78. * @param proxy the proxy instance to return the invocation handler for
  79. * @return the invocation handler for the proxy instance
  80. * @throws IllegalArgumentException if the argument is not a
  81. * proxy instance
  82. */
  83. public static InvocationHandler getInvocationHandler(Object proxy)
  84. throws IllegalArgumentException
  85. {
  86. /*
  87. * Verify that the object is actually a proxy instance.
  88. */
  89. if (!isProxyClass(proxy.getClass())) {
  90. throw new IllegalArgumentException("not a proxy instance");
  91. }
  92.  
  93. Proxy p = (Proxy) proxy;
  94. return p.h;
  95. }
  96.  
  97. 进去ProxyGenerator类的静态方法generateProxyClass,这里是真正生成代理类class字节码的地方。
  98. 你可以去你的web-info目录下的classes中看到用到动态代理的类,都会多出一个字节码文件。带$的,
  99. 可以用ju-gui(反编译工具)看到,里面就是一个代理对象。
  100. public static byte[] generateProxyClass(final String name,
  101. Class[] interfaces)
  102. {
  103. ProxyGenerator gen = new ProxyGenerator(name, interfaces);
  104.  
  105. final byte[] classFile = gen.generateClassFile();
  106.  
  107. // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上
  108. if (saveGeneratedFiles) {
  109. java.security.AccessController.doPrivileged(
  110. new java.security.PrivilegedAction<Void>() {
  111. public Void run() {
  112. try {
  113. FileOutputStream file =
  114. new FileOutputStream(dotToSlash(name) + ".class");
  115. file.write(classFile);
  116. file.close();
  117. return null;
  118. } catch (IOException e) {
  119. throw new InternalError(
  120. "I/O exception saving generated file: " + e);
  121. }
  122. }
  123. });
  124. }
  125.  
  126. // 返回代理类的字节码
  127. return classFile;
  128. }

通过一个工具类更深入理解动态代理和Threadlocal的更多相关文章

  1. 用Java开发一个工具类,提供似于js中eval函数功能的eval方法

    今天在看到<Java疯狂讲义>中一个章节习题: 开发一个工具类,该工具类提供一个eval()方法,实现JavaScript中eval()函数的功能--可以动态运行一行或多行程序代码.例如: ...

  2. java中使用反射做一个工具类,来为指定类中的成员变量进行赋值操作,使用与多个类对象的成员变量的赋值。

    //------------------------------------------------我是代码的分割线 // 首选是一个工具类,在该工具类里面,定义了一个方法,public void s ...

  3. Java三种代理模式:静态代理、动态代理和cglib代理

    一.代理模式介绍 代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 简言之,代理模式就是 ...

  4. java的静态代理、jdk动态代理和cglib动态代理

    Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...

  5. JAVA高级架构师基础功:Spring中AOP的两种代理方式:动态代理和CGLIB详解

    在spring框架中使用了两种代理方式: 1.JDK自带的动态代理. 2.Spring框架自己提供的CGLIB的方式. 这两种也是Spring框架核心AOP的基础. 在详细讲解上述提到的动态代理和CG ...

  6. Java动态代理和CGLib代理

    本文参考 在上一篇"Netty + Spring + ZooKeeper搭建轻量级RPC框架"文章中涉及到了Java动态代理和CGLib代理,在这篇文章中对这两种代理方式做详解 下 ...

  7. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  8. SpringAOP-JDK 动态代理和 CGLIB 代理

    在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类. 1.JDK 动态代理 那么接 ...

  9. 静态代理、动态代理和cglib代理

    转:https://www.cnblogs.com/cenyu/p/6289209.html 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处 ...

随机推荐

  1. 关于Matchvs一些使用心得与建议

    我的项目是类似<贪吃蛇>玩法的一款IO游戏,就是几个玩家在游戏界面中可以吃食物,也可以相互吃,吃了食物或对方都会变大这样子.我是在用cocos creator做完前端开发的部分后,开始接入 ...

  2. Spring常用接口和类

    一.ApplicationContextAware接口 当一个类需要获取ApplicationContext实例时,可以让该类实现ApplicationContextAware接口.代码展示如下: p ...

  3. Nginx 安装 配置 使用

    Nginx 安装 配置 使用 基本的HTTP服务器特性 处理静态文件,索引文件以及自动索引:打开文件描述符缓存(缓存元数据和文件描述符,下一次可以直接从内存找到数据或者文件的位置): 使用缓存加速反向 ...

  4. C++框架_之Qt的信号和槽的详解

    C++_之Qt的信号和槽的详解 1.概述 信号槽是 Qt 框架引以为豪的机制之一.所谓信号槽,实际就是观察者模式.当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal ...

  5. 关于html+ashx开发中几个问题的解决方法的感想和总结

    1.针对上篇文章中的服务端处理不敢苟同.仍然坚持使用反射,建立BaseHandler.ashx并在默认process方法中写上反射方法以及权限验证方法.针对具体的情况返回对应的值.服务端其他handl ...

  6. 毕业回馈-89c51之定时器/计数器(Timer/Count)

    今天分享的是89c51系列单片机的内部资源定时器/计数器,在所有的嵌入式系统中都包含这两个内部功能. 首先先了解几个定时器/计数器相关的概念: •时钟周期:时钟周期 T 是时序中最小的时间单位,具体计 ...

  7. 编写高性能的Lua代码

    编写高性能的Lua代码 Posted on2014/04/18· 10 Comments 前言 Lua是一门以其性能著称的脚本语言,被广泛应用在很多方面,尤其是游戏.像<魔兽世界>的插件, ...

  8. ViewPager滑动后,可移动的Imageview会回到初始化的位置

    知乎看到的原文http://www.zhihu.com/question/37398770?sort=created ViewPager滑动后,可移动的Imageview会回到初始化的位置? < ...

  9. ERROR: In <declare-styleable> MenuView, unable to find attribute android:preserveIconSpacing

    eclipse  sdk从低版本切换到高版本sdk的时候   v7包会包这个错ERROR: In <declare-styleable> MenuView, unable to find ...

  10. 从操作系统内核看Java非阻塞IO事件检测

    非阻塞服务器模型最重要的一个特点是,在调用读取或写入接口后立即返回,而不会进入阻塞状态.在探讨单线程非阻塞IO模型前必须要先了解非阻塞情况下Socket事件的检测机制,因为对于非阻塞模式最重要的事情是 ...