精华内容
下载资源
问答
  • java动态代理中的invoke方法

    万次阅读 2021-01-13 09:56:14
    静态代理这模式本身有大问题,如果类方法数量越来越的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题 二、动态代理 Java中动态代理的实现,关键就是这两东西:Proxy、InvocationHandler...

    前言

    记录一篇好的文章
    https://blog.csdn.net/zcc_0015/article/details/22695647
    同时推荐狂神说的视频,对于动态代理讲解的很详细
    https://www.bilibili.com/video/BV1WE411d7Dv?p=19

    一、动态代理与静态代理的区别

    (1)Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;
    (2)可以实现AOP编程,这是静态代理无法实现的;
    (3)解耦,如果用在web业务下,可以实现数据层和业务层的分离。
    (4)动态代理的优势就是实现无侵入式的代码扩展。
    静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题

    二、动态代理

    Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下面从InvocationHandler接口中的invoke方法入手,简单说明一下Java如何实现动态代理的。
    首先,invoke方法的完整形式如下:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
    {  
    method.invoke(obj, args);  
    
        return null;  
    }  
    

    首先猜测一下,method是调用的方法,即需要执行的方法;args是方法的参数;proxy,这个参数是什么?以上invoke()方法的实现即是比较标准的形式,我们看到,这里并没有用到proxy参数。查看JDK文档中Proxy的说明,如下:

    A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance's invocation handler, passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments.  
    

    由此可以知道以上的猜测是正确的,同时也知道,proxy参数传递的即是代理类的实例。
    为了方便说明,这里写一个简单的例子来实现动态代理。

    //抽象角色(动态代理只能代理接口)  
    public interface Subject {  
    public void request();  
    }  
    
    //真实角色:实现了Subject的request()方法  
    public class RealSubject implements Subject{  
      public void request(){  
            System.out.println("From real subject.");  
        }  
    }  
    
    //实现了InvocationHandler  
    public class DynamicSubject implements InvocationHandler  
    {  
        private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象  
        public DynamicSubject()  
        {  
        }  
      
        public DynamicSubject(Object obj)  
        {  
            this.obj = obj;  
        }  
      
        //这个方法不是我们显示的去调用  
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
        {  
            System.out.println("before calling " + method);  
      
            method.invoke(obj, args);  
      
            System.out.println("after calling " + method);  
      
            return null;  
        }  
      
    }  
    
    //客户端:生成代理实例,并调用了request()方法  
    public class Client {  
      
        public static void main(String[] args) throws Throwable{  
            // TODO Auto-generated method stub  
      
            Subject rs=new RealSubject();//这里指定被代理类  
            InvocationHandler ds=new DynamicSubject(rs);  
            Class<?> cls=rs.getClass();  
              
            //以下是一次性生成代理  
              
            Subject subject=(Subject) Proxy.newProxyInstance(  
                    cls.getClassLoader(),cls.getInterfaces(), ds);  
              
            //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口  
            System.out.println(subject instanceof Proxy);  
              
            //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口  
            System.out.println("subject的Class类是:"+subject.getClass().toString());  
              
            System.out.print("subject中的属性有:");  
              
            Field[] field=subject.getClass().getDeclaredFields();  
            for(Field f:field){  
                System.out.print(f.getName()+", ");  
            }  
              
            System.out.print("\n"+"subject中的方法有:");  
              
            Method[] method=subject.getClass().getDeclaredMethods();  
              
            for(Method m:method){  
                System.out.print(m.getName()+", ");  
            }  
              
            System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass());  
              
            System.out.print("\n"+"subject实现的接口是:");  
              
            Class<?>[] interfaces=subject.getClass().getInterfaces();  
              
            for(Class<?> i:interfaces){  
                System.out.print(i.getName()+", ");  
            }  
      
            System.out.println("\n\n"+"运行结果为:");  
            subject.request();  
        }  
    }  
    
    运行结果如下:此处省略了包名,***代替  
    true  
    subject的Class类是:class $Proxy0  
    subject中的属性有:m1, m3, m0, m2,   
    subject中的方法有:request, hashCode, equals, toString,   
    subject的父类是:class java.lang.reflect.Proxy  
    subject实现的接口是:cn.edu.ustc.dynamicproxy.Subject,   
      
    运行结果为:  
    before calling public abstract void ***.Subject.request()  
    From real subject.  
    after calling public abstract void ***.Subject.request()  
    

    PS:这个结果的信息非常重要,至少对我来说。因为我在动态代理犯晕的根源就在于将上面的subject.request()理解错了,至少是被表面所迷惑,没有发现这个subject和Proxy之间的联系,一度纠结于最后调用的这个request()是怎么和invoke()联系上的,而invoke又是怎么知道request存在的。其实上面的true和class ProxyProxy就能解决很多的疑问,再加上下面将要说的ProxyProxy的源码,完全可以解决动态代理的疑惑了。

    从以上代码和结果可以看出,我们并没有显示的调用invoke()方法,但是这个方法确实执行了。下面就整个的过程进行分析一下:

    从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:

    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 = getProxyClass(loader, interfaces);  
      
        /* 
         * Invoke its constructor with the designated invocation handler. 
         */  
        try {  
               /* 
                * Proxy源码开始有这样的定义: 
                * private final static Class[] constructorParams = { InvocationHandler.class }; 
                * cons即是形参为InvocationHandler类型的构造方法 
               */  
            Constructor cons = cl.getConstructor(constructorParams);  
            return (Object) cons.newInstance(new Object[] { h });  
        } catch (NoSuchMethodException e) {  
            throw new InternalError(e.toString());  
        } catch (IllegalAccessException e) {  
            throw new InternalError(e.toString());  
        } catch (InstantiationException e) {  
            throw new InternalError(e.toString());  
        } catch (InvocationTargetException e) {  
            throw new InternalError(e.toString());  
        }  
    }  
    
    
            Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事. 
            (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类. 
            (2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下: 
    Java代码  收藏代码
    class Proxy{  
        InvocationHandler h=null;  
        protected Proxy(InvocationHandler h) {  
            this.h = h;  
        }  
        ...  
    }  
    

    来看一下这个继承了Proxy的ProxyProxy的源代码:

    public final class $Proxy0 extends Proxy implements Subject {  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
      
        static {  
            try {  
                m1 = Class.forName("java.lang.Object").getMethod("equals",  
                        new Class[] { Class.forName("java.lang.Object") });  
      
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                        new Class[0]);  
      
                m3 = Class.forName("***.RealSubject").getMethod("request",  
                        new Class[0]);  
      
                m2 = Class.forName("java.lang.Object").getMethod("toString",  
                        new Class[0]);  
      
            } catch (NoSuchMethodException nosuchmethodexception) {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            } catch (ClassNotFoundException classnotfoundexception) {  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        } //static  
      
        public $Proxy0(InvocationHandler invocationhandler) {  
            super(invocationhandler);  
        }  
      
        @Override  
        public final boolean equals(Object obj) {  
            try {  
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final int hashCode() {  
            try {  
                return ((Integer) super.h.invoke(this, m0, null)).intValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        public final void request() {  
            try {  
                super.h.invoke(this, m3, null);  
                return;  
            } catch (Error e) {  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final String toString() {  
            try {  
                return (String) super.h.invoke(this, m2, null);  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
    }  
    

    接着把得到的ProxyProxy实例强制转换成Subject,并将引用赋给subject。当执行subject.request()方法时,就调用了ProxyProxy类中的request()方法,进而调用父类Proxy中的h的invoke()方法.即InvocationHandler.invoke()。

    PS:1、需要说明的一点是,Proxy类中getProxyClass方法返回的是Proxy的Class类。之所以说明,是因为我一开始犯了个低级错误,以为返回的是“被代理类的Class类”- -!推荐看一下getProxyClass的源码,很长=。=
    2、从$Proxy0的源码可以看出,动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。

    Q:到现在为止,还有一个疑问,invoke方法中的第一个参数是Proxy的实例(准确说,最终用到的是$Proxy0的实例),但是有什么用呢?或者说,程序内是怎样显示出作用的?
    A:就本人目前的水平看来,这个proxy参数并没有什么作用,在整个动态代理机制中,并没有用到InvocationHandler中invoke方法的proxy参数。而传入的这个参数实际是代理类的一个实例。我想可能是为了让程序员在invoke方法中使用反射来获取关于代理类的一些信息吧。

    原文链接
    https://blog.csdn.net/zcc_0015/article/details/22695647

    展开全文
  • 相信很在学习SSM框架的人都免不了碰到如何理解动态代理的问题,今天简单总结一下动态代理的newProxyInstances方法。 首先,我们来看看这个方法的官方解释(下附翻译) /** * Returns an instance of a proxy ...

    相信很多在学习SSM框架的人都免不了碰到如何理解动态代理的问题,今天简单总结一下动态代理的newProxyInstances方法。

    首先,我们来看看这个方法的官方解释(下附翻译)

    /**
         * Returns an instance of a proxy class for the specified interfaces
         * that dispatches method invocations to the specified invocation
         * handler.
         *
         * <p>{@code Proxy.newProxyInstance} throws
         * {@code IllegalArgumentException} for the same reasons that
         * {@code Proxy.getProxyClass} does.
         *
         * @param   loader the class loader to define the proxy class
         * @param   interfaces the list of interfaces for the proxy class
         *          to implement
         * @param   h the invocation handler to dispatch method invocations to
         * @return  a proxy instance with the specified invocation handler of a
         *          proxy class that is defined by the specified class loader
         *          and that implements the specified interfaces
         * @throws  IllegalArgumentException if any of the restrictions on the
         *          parameters that may be passed to {@code getProxyClass}
         *          are violated
         * @throws  SecurityException if a security manager, <em>s</em>, is present
         *          and any of the following conditions is met:
         *          <ul>
         *          <li> the given {@code loader} is {@code null} and
         *               the caller's class loader is not {@code null} and the
         *               invocation of {@link SecurityManager#checkPermission
         *               s.checkPermission} with
         *               {@code RuntimePermission("getClassLoader")} permission
         *               denies access;</li>
         *          <li> for each proxy interface, {@code intf},
         *               the caller's class loader is not the same as or an
         *               ancestor of the class loader for {@code intf} and
         *               invocation of {@link SecurityManager#checkPackageAccess
         *               s.checkPackageAccess()} denies access to {@code intf};</li>
         *          <li> any of the given proxy interfaces is non-public and the
         *               caller class is not in the same {@linkplain Package runtime package}
         *               as the non-public interface and the invocation of
         *               {@link SecurityManager#checkPermission s.checkPermission} with
         *               {@code ReflectPermission("newProxyInPackage.{package name}")}
         *               permission denies access.</li>
         *          </ul>
         * @throws  NullPointerException if the {@code interfaces} array
         *          argument or any of its elements are {@code null}, or
         *          if the invocation handler, {@code h}, is
         *          {@code null}
         */
    
    *@return一个具有指定调用处理程序的代理实例
    *由指定的类加载程序定义的代理类
    *实现了指定的接口
    *@throws IllegalArgumentException如果任何限制
    *可以传递给{@code getproxyclass}的参数
    *被违反
    *@throws SecurityException如果存在安全管理器<em>s</em>
    *并符合下列任何一项条件:
    * < ul >
    *<li>给定的{@code loader}为{@code null}
    *调用方的类加载程序不是{@code null}
    *调用{@link securitymanager#checkpermission
    * s。checkPermission} with
    *{@code RuntimePermission ("getclassloader")}权限
    *拒绝访问;
    *<li>对于每个代理接口,{@code intf},
    *调用者的类加载程序
    * {@code intf}类加载程序的祖先
    *调用{@link securitymanager#checkpackageaccess
    * s.checkpackageaccess()拒绝访问{@code intf};</li>
    *<li>任何给定的代理接口都是非公共的,并且 caller类不在同一个{@linkplain包运行时包}中作为非公开
    接口和调用
    *{@link securitymanager#checkpermission s。checkPermission} with
    *{@code ReflectPermission ("newproxyinpackage"。{package name} ")}
    *权限拒绝访问。
    *@throws NullPointerException如果{@code接口}数组
    *参数或其任何元素为{@code null},或
    *如果调用处理程序{@code h}为
    * {@code null}
    

    在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

    然后,我们编写两段代码来进行对比。

    第一段

    创建一个InvocationHandler对象

     InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
     //创建一个与代理对象相关联的InvocationHandler
    

    使用Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass

     Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[] {Person.class});
    

    获得stuProxyClass 中一个带InvocationHandler参数的构造器constructor

    Constructor<?> constructor = PersonProxy.getConstructor(InvocationHandler.class);
    

    通过构造器constructor来创建一个动态实例stuProxy

    Person stuProxy = (Person) cons.newInstance(stuHandler);
    

    就此,一个动态代理对象就创建完毕。

    下面,我们通过Proxy类的newProxyInstances方法来简化这段代码:

    第二段:

     //创建一个与代理对象相关联的InvocationHandler
      InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
    
    //创建一个代理对象stuProxy,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
      Person stuProxy= (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler)
    

    将两段代码仔细对比,再来看看newProxyInstances的源码

        @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }
    

    相信大家看完应该会发现,其实在这个方法里面,就已经实现了创建一个动态实例以及获取构造器的操作,结合源码再去看上面的两段代码对比,就会对newProxyInstances方法有一定的理解了。

    总结一下:其实这个方法就是实现了创建一个动态实例以及获取构造器的操作,调用这个方法也是一种基于子类的动态代理。

    注:在使用Proxy类时,要记得引入cglib库,否则无法使用此方法。

    展开全文
  • 动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC远程调用、Java注解...JDK提供的动态代理功能只能针对(一个或多个)接口提供代理。使用步骤如下: (1) 新建一个接口 ...

          动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC远程调用、Java注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。本文主要介绍Java中几种常见的动态代理方式:

    一、JDK的动态代理

    JDK提供的动态代理功能只能针对(一个或多个)接口提供代理。使用步骤如下:

    (1) 新建一个接口

    public interface ISubject {
    	public void print();
    }

    (2) 为接口创建一个实现类

    public class SubjectImpl implements ISubject {
    
    	@Override
    	public void print() {
    		System.out.println("hello world!");
    	}
    
    }

    (3) 创建拦截类,实现java.lang.reflect.InvocationHandler接口

    public class MyInvokeHandler implements InvocationHandler {
    	
    	private Object instance;
    	
    	public MyInvokeHandler(Object o) {
    		this.instance=o;
    	}
    	
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		this.before(method);
    		Object result=method.invoke(this.instance, args);
    		this.after(method);
    		return result;
    	}
    
    	private void before(Method method) {
    		System.out.println("run before method:"+method.getName());
    	}
    	
    	private void after(Method method) {
    		System.out.println("run after method:"+method.getName());
    	}
    }

    (4) 使用Proxy.newProxyInstance()函数生成代理对象

    public class JdkProxyTester {
    
    	public static void main(String[] args) {
    		ISubject subject=new SubjectImpl();
    		InvocationHandler handler=new MyInvokeHandler(subject);
    		ClassLoader classLoader = ISubject.class.getClassLoader();
    		Class<?>[] interfaces = { ISubject.class };
    		ISubject proxy = (ISubject) Proxy.newProxyInstance(classLoader,interfaces,handler);
    		proxy.print();
    	}
    
    }

    二、CGlib的动态代理

    CGlib也可以实现动态代理,但与JDK的不同之处是: JDK只能为接口的实现类提供代理,而CGlib可以为类的实现类提供代理,原理上使用字节码技术,不能对 final类进行继承。

    (1) 添加pom依赖

    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
    	<groupId>cglib</groupId>
    	<artifactId>cglib</artifactId>
    	<version>3.2.5</version>
    </dependency>

    (2) 新建一个需要被代理的类

    public class MySubject {
    	
    	private String name;
    
    	public MySubject(String name) {
    		this.name=name;
    	}
    
    	public void print() {
    		System.out.println("hello world!,name=" + this.name);
    	}
    
    }

    (3) 创建拦截类,实现net.sf.cglib.proxy.MethodInterceptor接口

    public class MyInvokeHandler implements MethodInterceptor {
    
    	@Override
    	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    		this.before(method);
    		Object result = proxy.invokeSuper(obj, args);
    		this.after(method);
    		return result;
    	}
    
    	private void before(Method method) {
    		System.out.println("run before method:" + method.getName());
    	}
    
    	private void after(Method method) {
    		System.out.println("run after method:" + method.getName());
    	}
    }

    (4) 使用net.sf.cglib.proxy.Enhancer的create()方法生成代理对象

    public class CglibTester {
    
    	public static void main(String[] args) {
    		MethodInterceptor handler = new MyInvokeHandler();
    		Enhancer enhancer = new Enhancer();
    		enhancer.setSuperclass(MySubject.class);
    		enhancer.setCallback(handler);
    		Class<?>[] argsType = new Class<?>[] { String.class };
    		Object[] argsValue = new Object[] { "aa" };
    		MySubject proxy= (MySubject)enhancer.create(argsType, argsValue);
    		proxy.print();
    	}
    }

    三、JAVAssist的动态代理

    Java代码编译完生成.class文件,就JVM(准确说是JIT)会解释执行这些字节码(转换为机器码并执行),由于字节码的解释执行是在运行时进行的,而Javassist正式再运行时改字节码来实现的。它同样可以对类进行代理。

    (1) 添加pom依赖

    <!-- https://mvnrepository.com/artifact/javassist/javassist -->
    <dependency>
        <groupId>javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.12.1.GA</version>
    </dependency>

    (2) 新建一个需要被代理的类

    public class MySubject {
    	
    	private String name;
    
    	public MySubject(String name) {
    		this.name=name;
    	}
    
    	public void print() {
    		System.out.println("hello world!,name=" + this.name);
    	}
    
    }

    (3) 创建拦截类,实现javassist.util.proxy.MethodHandler接口

    public class MyMethodHandler implements MethodHandler {
    
    	@Override
    	public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
    		this.before(thisMethod);
    		// thisMethod为被代理方法 ,proceed为代理方法, self为代理实例 ,args为方法参数
    		Object result = proceed.invoke(self, args);
    		this.after(thisMethod);
    		return result;
    	}
    
    	private void before(Method method) {
    		System.out.println("run before method:" + method.getName());
    	}
    
    	private void after(Method method) {
    		System.out.println("run after method:" + method.getName());
    	}
    }

    (4) 使用javassist.util.proxy.ProxyFactory的createClass()方法生成代理对象

    public class JavassistTester {
    
    	public static void main(String[] args) throws Exception {
    		ProxyFactory factory = new ProxyFactory();
    		factory.setSuperclass(MySubject.class);
    		factory.setFilter(new MethodFilter() {
    
    			@Override
    			public boolean isHandled(Method m) {
    				return m.getName().equals("print");
    			}
    		});
    		Class<?> clazz = factory.createClass();
    		Class<?>[] argsType = new Class<?>[] { String.class };
    		Object[] argsValue = new Object[] { "aa" };
    		Constructor<?> constructor=clazz.getConstructor(argsType);
    		MySubject proxy = (MySubject) constructor.newInstance(argsValue);
    		proxy.print();
    	}
    
    }

    四、Java动态代理实现的原理

    Java动态代理实现的原理:在编译期或运行期间操作修改java的字节码。

    操作java字节码的工具有两个比较流行,一个是ASM,一个是Javassit 。

    • ASM

    直接操作字节码指令,执行效率高,要是使用者掌握Java类字节码文件格式及指令,对使用者的要求比较高。

    官网地址:https://asm.ow2.io/index.html

    • Javassit

    提供了更高级的API,执行效率相对较差,但无需掌握字节码指令的知识,对使用者要求较低。

    官网地址:http://www.javassist.org/

    应用层面来讲一般使用建议优先选择Javassit,如果后续发现Javassit 成为了整个应用的效率瓶颈的话可以再考虑ASM。

    更多Java字节码操作开源框架介绍请参考:

       https://www.cnblogs.com/zj2lzh/p/3844681.html

     

    五、Java常见几种动态代理的对比

    参考文章:https://zhuanlan.zhihu.com/p/87393183

    展开全文
  • 个方法中的所有操作都应该用同一个连接。 编写一个工具类,使得一个线程中只有一个能控制事务的对象 package com.wei.utils; import javax.sql.DataSource; import java.sql.Connection; /** * 连接的工具类...

    dbutils的QueryRunner对象每次都要创建新的,在执行操作时都要从数据源中拿出一个连接,导致很多连接(多例),没出异常的连接提交了,出现异常后数据没有从一个一致的状态跳到另一个一致的状态。一个方法中的所有操作都应该用同一个连接。

    编写一个工具类,使得一个线程中只有一个能控制事务的对象

    package com.wei.utils;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    
    /**
     * 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定
     */
    public class ConnectionUtils {
    
        private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    
        private DataSource dataSource;
    
        public void setDataSource(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        /**
         * 获取当前线程上的连接
         * @return
         */
        public Connection getThreadConnection() {
            try{
                //1.先从ThreadLocal上获取
                Connection conn = tl.get();
                //2.判断当前线程上是否有连接
                if (conn == null) {
                    //3.从数据源中获取一个连接,并且存入ThreadLocal中
                    conn = dataSource.getConnection();
                    tl.set(conn);
                }
                //4.返回当前线程上的连接
                return conn;
            }catch (Exception e){
                throw new RuntimeException(e);
            }
        }
    
        /**
         * 把连接和线程解绑
         */
        public void removeConnection(){
            tl.remove();
        }
    }

    与事务相关的工具类,与conn连接有关,编写事务管理类

    package com.wei.utils;
    
    /**
     * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
     */
    public class TransactionManager {
    
        private ConnectionUtils connectionUtils;
    
        public void setConnectionUtils(ConnectionUtils connectionUtils) {
            this.connectionUtils = connectionUtils;
        }
    
        /**
         * 开启事务
         */
        public  void beginTransaction(){
            try {
                connectionUtils.getThreadConnection().setAutoCommit(false);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        /**
         * 提交事务
         */
        public  void commit(){
            try {
                connectionUtils.getThreadConnection().commit();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        /**
         * 回滚事务
         */
        public  void rollback(){
            try {
                connectionUtils.getThreadConnection().rollback();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
    
        /**
         * 释放连接
         */
        public  void release(){
            try {
                connectionUtils.getThreadConnection().close();//还回连接池中
                connectionUtils.removeConnection();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

    数据库一般用的是连接池,将消耗时间创建连接的部分放在一开始,保证了connection的使用效率。用完还回池中。此时该线程绑着一个连接,线程用完后要将线程和连接进行解绑。

    改造业务层(刚开始事务是在持久层的,现在提升到业务层)

    package com.wei.service.impl;
    
    import com.wei.dao.IAccountDao;
    import com.wei.domain.Account;
    import com.wei.service.IAccountService;
    import com.wei.utils.TransactionManager;
    
    import java.util.List;
    
    /**
     * 账户的业务层实现类
     *
     * 事务控制应该都是在业务层
     */
    public class AccountServiceImpl_OLD implements IAccountService{
    
        private IAccountDao accountDao;
        private TransactionManager txManager;
    
        public void setTxManager(TransactionManager txManager) {
            this.txManager = txManager;
        }
    
        public void setAccountDao(IAccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public List<Account> findAllAccount() {
            try {
                //1.开启事务
                txManager.beginTransaction();
                //2.执行操作
                List<Account> accounts = accountDao.findAllAccount();
                //3.提交事务
                txManager.commit();
                //4.返回结果
                return accounts;
            }catch (Exception e){
                //5.回滚操作
                txManager.rollback();
                throw new RuntimeException(e);
            }finally {
                //6.释放连接
                txManager.release();
            }
    
        }
    
        @Override
        public Account findAccountById(Integer accountId) {
            try {
                //1.开启事务
                txManager.beginTransaction();
                //2.执行操作
                Account account = accountDao.findAccountById(accountId);
                //3.提交事务
                txManager.commit();
                //4.返回结果
                return account;
            }catch (Exception e){
                //5.回滚操作
                txManager.rollback();
                throw new RuntimeException(e);
            }finally {
                //6.释放连接
                txManager.release();
            }
        }
    
        @Override
        public void saveAccount(Account account) {
            try {
                //1.开启事务
                txManager.beginTransaction();
                //2.执行操作
                accountDao.saveAccount(account);
                //3.提交事务
                txManager.commit();
            }catch (Exception e){
                //4.回滚操作
                txManager.rollback();
            }finally {
                //5.释放连接
                txManager.release();
            }
    
        }
    
        @Override
        public void updateAccount(Account account) {
            try {
                //1.开启事务
                txManager.beginTransaction();
                //2.执行操作
                accountDao.updateAccount(account);
                //3.提交事务
                txManager.commit();
            }catch (Exception e){
                //4.回滚操作
                txManager.rollback();
            }finally {
                //5.释放连接
                txManager.release();
            }
    
        }
    
        @Override
        public void deleteAccount(Integer acccountId) {
            try {
                //1.开启事务
                txManager.beginTransaction();
                //2.执行操作
                accountDao.deleteAccount(acccountId);
                //3.提交事务
                txManager.commit();
            }catch (Exception e){
                //4.回滚操作
                txManager.rollback();
            }finally {
                //5.释放连接
                txManager.release();
            }
    
        }
    
        @Override
        public void transfer(String sourceName, String targetName, Float money) {
            try {
                //1.开启事务
                txManager.beginTransaction();
                //2.执行操作
    
                //2.1根据名称查询转出账户
                Account source = accountDao.findAccountByName(sourceName);
                //2.2根据名称查询转入账户
                Account target = accountDao.findAccountByName(targetName);
                //2.3转出账户减钱
                source.setMoney(source.getMoney()-money);
                //2.4转入账户加钱
                target.setMoney(target.getMoney()+money);
                //2.5更新转出账户
                accountDao.updateAccount(source);
    
                int i=1/0;
    
                //2.6更新转入账户
                accountDao.updateAccount(target);
                //3.提交事务
                txManager.commit();
    
            }catch (Exception e){
                //4.回滚操作
                txManager.rollback();
                e.printStackTrace();
            }finally {
                //5.释放连接
                txManager.release();
            }
    
    
        }
    }
    

    根据改造重新配置配置文件

    代码很臃肿,为了引出Spring的AOP技术。先引出动态代理的概念。通过代理方式对数据库方法操作的行为进行方法增强,所有的方法叫做连接点,被增强的方法叫做切入点,对切入点方法进行拦截增强

    动态代理(含某马案例)

    AOP的一些基本概念

    #使用动态代理实现事务控制

    编写用于创建service的代理对象工厂

    添加代码

    package com.wei.factory;
    
    import com.wei.service.IAccountService;
    import com.wei.utils.TransactionManager;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 用于创建Service的代理对象的工厂
     */
    public class BeanFactory {
    
        private IAccountService accountService;
    
        private TransactionManager txManager;
    
        public void setTxManager(TransactionManager txManager) {
            this.txManager = txManager;
        }
    
    
        public final void setAccountService(IAccountService accountService) {
            this.accountService = accountService;
        }
    
        /**
         * 获取Service代理对象
         * @return
         */
        public IAccountService getAccountService() {
            return (IAccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                    accountService.getClass().getInterfaces(),
                    new InvocationHandler() {
                        /**
                         * 添加事务的支持
                         *
                         * @param proxy
                         * @param method
                         * @param args
                         * @return
                         * @throws Throwable
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                            if("test".equals(method.getName())){
                                return method.invoke(accountService,args);
                            }
    
                            Object rtValue = null;
                            try {
                                //1.开启事务
                                txManager.beginTransaction();
                                //2.执行操作
                                rtValue = method.invoke(accountService, args);
                                //3.提交事务
                                txManager.commit();
                                //4.返回结果
                                return rtValue;
                            } catch (Exception e) {
                                //5.回滚操作
                                txManager.rollback();
                                throw new RuntimeException(e);
                            } finally {
                                //6.释放连接
                                txManager.release();
                            }
                        }
                    });
    
        }
    }
    

    配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--配置代理的service-->
        <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>
    
        <!--配置beanfactory-->
        <bean id="beanFactory" class="com.itheima.factory.BeanFactory">
            <!-- 注入service -->
            <property name="accountService" ref="accountService"></property>
            <!-- 注入事务管理器 -->
            <property name="txManager" ref="txManager"></property>
        </bean>
    
         <!-- 配置Service -->
        <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
            <!-- 注入dao -->
            <property name="accountDao" ref="accountDao"></property>
        </bean>
    
        <!--配置Dao对象-->
        <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
            <!-- 注入QueryRunner -->
            <property name="runner" ref="runner"></property>
            <!-- 注入ConnectionUtils -->
            <property name="connectionUtils" ref="connectionUtils"></property>
        </bean>
    
        <!--配置QueryRunner-->
        <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean>
    
        <!-- 配置数据源 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <!--连接数据库的必备信息-->
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></property>
            <property name="user" value="root"></property>
            <property name="password" value="1234"></property>
        </bean>
    
        <!-- 配置Connection的工具类 ConnectionUtils -->
        <bean id="connectionUtils" class="com.itheima.utils.ConnectionUtils">
            <!-- 注入数据源-->
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 配置事务管理器-->
        <bean id="txManager" class="com.itheima.utils.TransactionManager">
            <!-- 注入ConnectionUtils -->
            <property name="connectionUtils" ref="connectionUtils"></property>
        </bean>
    </beans>

     

    测试类

    /**
     * 使用Junit单元测试:测试我们的配置
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:bean.xml")
    public class AccountServiceTest {
    
        @Autowired
        @Qualifier("proxyAccountService")
        private  IAccountService as;
    
        @Test
        public  void testTransfer(){
            as.transfer("aaa","bbb",100f);
        }
    
    }
    展开全文
  • 动态代理

    2020-12-10 11:10:39
    动态代理 可以在运行时创建一个类,实现一个 或者多个接口,可以在不修改原来类的基础上动态的为通过该类获取的对象添加方法或者修改行为。要理解动态代理 ,先要理解静态代理,静态代理是啥?静态代理:动态代理的...
  • 上篇介绍了一下静态代理:Java中的代理模式——静态...2、如果一Service中有很多方法需要事务(增强动作),发现代理对象的方法中还是有很重复的代码 3、由第一点和第二点可以得出:静态代理的重用性不强 那怎...
  • JDK动态代理 核心思想:通过实现被代理类的所有接口,生成一个字节码文件后构造一个代理对象,通过持有反射...缺点:JDK动态代理的对象必须实现一个或多个接口 流程图 知识点 JDK实现动态代理需要实现类通过接...
  • java 动态代理 为什么在debug 时会次执行invoke 内部方法 最近被一同事问道该问题,有些模糊了,前来验证记录下。 copy了一网上实例进行验证 package com.huilong.hrs.portal.study.amn.jdkpox; import java....
  • 动态代理 动态代理动态代理工具 是 java.lang.reflect 包的一部分,在 JDK 1.3 版本中添加到 JDK,它允许程序创建 代理对象,代理对象能实现一个或多个已知接口,并用反射代替内置的虚方法分派,编程地分派对...
  • 利用动态代理模式来增强方法

    千次阅读 2016-09-28 18:31:56
    在使用装饰着模式去增强某个类的时候会发现当被增强的那个类所实现的接口中含有的方法有很多个时,我们就需要将全部的方法都进行重写,显然这是不符合开发的习惯的,那有没有一种方式可以之增强我们需要的那个方法呢...
  • 一、增强一对象有几种方法? 1)继承:  条件:需要知道被继承的类 2)装饰者模式:  条件:1、不需要知道父类,只需知道接口 ... 2、装饰者和被装饰者实现同一接口;...3)动态代理: ...
  • 今天项目经理发下任务,需要测试 20 接口,看看推送和接收数据是否...推送比较好做数据,队友们都写好代码,但是有问题,方法要的值都大致相同,封装的方式不一致,多人开发,有的封装对象里面,有的直接使用 M...
  • 如果多个类需要进行方法增强,静态代理则需要创建多个物理类,占用磁盘空间。而动态代理则是在内存中创建,不会对磁盘进行影响。 静态代理和JDK动态代理需要有接口。 CGLIB动态代理无需有接口的情况下进行代理。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,899
精华内容 759
关键字:

动态代理多个方法