精华内容
下载资源
问答
  • 动态代理几种实现方式及优缺点

    千次阅读 2020-02-13 17:32:05
    动态代理:是使用反射和字节码,在运行期间创建指定接口或类的子类以及它的实例...java的动态代理技术的实现主要有两种方式: JDK原生动态代理 CGLIB动态代理 JDK原生动态代理: 使用到一个类Proxy和一个接口Invoc...

    动态代理:是使用反射和字节码,在运行期间创建指定接口或类的子类以及它的实例对象的一项技术,通过这个技术可以对代码进行无侵入式的增强。

    源文件生成实例对象的过程如下:

    在这里插入图片描述
    关于动态代理的底层原理,在另一篇文章中已经介绍过了。 你必须要学会的动态代理

    java的动态代理技术的实现主要有两种方式:

    • JDK原生动态代理
    • CGLIB动态代理

    JDK原生动态代理:

    使用到一个类Proxy和一个接口InvocationHandler。Proxy是所有动态类的父类,它提供一个静态方法来创建动态代理的class对象和实例,这个方法就是newProxyInstance(),

    InvocationHandler:每个动态代理实例都有一个相关联的InvocationHandler方法,在代理实例调用时,方法调用将被转发到InvocationHandler的invole方法。

    代码实验:

    User类有两个属性,name和age,代码省略

    UserService:

    public interface UserService {
        public void addUser(User user);
    }
    

    UserServiceImpl:

    public class UserServiceImpl implements UserService {
        @Override
        public void addUser(User user) {
            System.out.println("新增用户成功,数据为:"+user.toString());
        }
    }
    

    UserServiceProxy:实现InvocationHandler,重写invoke方法,做前置增强,判断用户年龄是否大于0,如果小于等于0就报运行时异常

    public class UserServiceProxy implements InvocationHandler {
    
        private Object realObj;
        private static Logger logger = Logger.getLogger(UserServiceProxy.class.getName());
    
        public Object getRealObj() {
            return realObj;
        }
    
        public void setRealObj(Object realObj) {
            this.realObj = realObj;
        }
    
        public UserServiceProxy(Object realObj) {
            super();
            this.realObj = realObj;
        }
    
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            if(objects!=null && objects.length>0 && objects[0] instanceof User){
                User user = (User)objects[0];
                if(user.getAge()<=0){
                    throw new RuntimeException("用户年龄不符合");
                }
            }
            Object ret = method.invoke(realObj,objects);
            logger.info("数据添加成功");
            return ret;
        }
    }
    

    Client:

    public class Client {
        public static void main(String[] args) {
            User user  =new User();
            user.setName("张三");
            user.setAge(0);
            UserService us = new UserServiceImpl();
            UserServiceProxy usp = new UserServiceProxy(us);
            UserService proxy = (UserService) Proxy.newProxyInstance(us.getClass().getClassLoader(),us.getClass().getInterfaces(),usp);
            proxy.addUser(user);
        }
    }
    

    运行结果:

    Exception in thread "main" java.lang.RuntimeException: 用户年龄不符合
    	at DynamicProxy.JDK.UserServiceProxy.invoke(UserServiceProxy.java:30)
    	at com.sun.proxy.$Proxy0.addUser(Unknown Source)
    	at DynamicProxy.JDK.Client.main(Client.java:13)
    

    看来过滤起了作用。将年龄改为大于0的数时,能够正常运行。

    CGLIB动态代理

    CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理:

    • Enhancer:来指定要代理的目标对象,实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象的所有非final方法的调用都会转发给MethodInterceptor;
    • MethodInterceptor:动态代理对象的方法调用丢回转发到intercept方法进行增强

    UserServiceImpl类:无需接口

    public class UserServiceImpl {
    
        public void addUser(User user) {
            System.out.println("新增用户成功,数据为:"+user.toString());
        }
    }
    

    UserServiceProxy:逻辑几乎一样

    public class UserServiceProxy implements MethodInterceptor {
    
        private static Logger logger = Logger.getLogger(UserServiceProxy.class.getName());
    
        public Object intercept(Object o, Method method, Object[] objects,MethodProxy proxy) throws Throwable {
            if(objects!=null && objects.length>0 && objects[0] instanceof User){
                User user = (User)objects[0];
                if(user.getAge()<=0){
                    throw new RuntimeException("用户年龄不符合");
                }
            }
            Object ret = proxy.invokeSuper(realObj,objects);
            logger.info("数据添加成功");
            return ret;
        }
    }
    

    Client

    public class Client {
        public static void main(String[] args) {
            User user  =new User();
            user.setName("张三");
            user.setAge(0);
            Enhancer enhancer = new EnHancer();
            enhancer.setSuperClass(UserServiceImpl.class);
            enhancer.setCallback(new UserServiceProxy());
            UserServiceImpl usi = (UserServiceImpl) enhancer.create();
            usi.addUser(user);
        }
    }
    

    总结:

    • JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理(需要代理的对象必须实现于某个接口)
    • CGLIB通过继承的方式进行代理(让需要代理的类成为Enhancer的父类),无论目标对象有没有实现接口都可以代理,但是无法处理final的情况。
    展开全文
  • Spring的两种动态代理:Jdk和Cglib 的区别和实现 一、原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 而cglib动态代理是利用asm开源包,对代理...

    Spring的两种动态代理:Jdk和Cglib 的区别和实现

    一、原理区别:

    java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

    而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 
    2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 

    3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

    如何强制使用CGLIB实现AOP?
     (1)添加CGLIB库,SPRING_HOME/cglib/*.jar
     (2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

    JDK动态代理和CGLIB字节码生成的区别?
     (1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
     (2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
       因为是继承,所以该类或方法最好不要声明成final 

    二、代码实现

    用户管理接口

    复制代码

    package com.lf.shejimoshi.proxy.entity;
    //用户管理接口
    public interface UserManager {
        //新增用户抽象方法
        void addUser(String userName,String password);
        //删除用户抽象方法
        void delUser(String userName);
        
    }

    复制代码

    用户管理接口实现类

    复制代码

    package com.lf.shejimoshi.proxy.entity;
    //用户管理实现类,实现用户管理接口
    public class UserManagerImpl implements UserManager{
        //重写新增用户方法
        @Override
        public void addUser(String userName, String password) {
            System.out.println("调用了新增的方法!");
            System.out.println("传入参数为 userName: "+userName+" password: "+password);
        }
        //重写删除用户方法
        @Override
        public void delUser(String userName) {
            System.out.println("调用了删除的方法!");
            System.out.println("传入参数为 userName: "+userName);
        }
        
    }

    复制代码

    JDK动态代理

    复制代码

    package com.lf.shejimoshi.proxy.jdk;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import com.lf.shejimoshi.proxy.entity.UserManager;
    import com.lf.shejimoshi.proxy.entity.UserManagerImpl;
    //JDK动态代理实现InvocationHandler接口
    public class JdkProxy implements InvocationHandler {
        private Object target ;//需要代理的目标对象
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("JDK动态代理,监听开始!");
            Object result = method.invoke(target, args);
            System.out.println("JDK动态代理,监听结束!");
            return result;
        }
        //定义获取代理对象方法
        private Object getJDKProxy(Object targetObject){
            //为目标对象target赋值
            this.target = targetObject;
            //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
        }
        
        public static void main(String[] args) {
            JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象
            UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象
            user.addUser("admin", "123123");//执行新增方法
        }
        
    }

    复制代码

    JDK动态代理运行结果

    Cglib动态代理(需要导入两个jar包,asm-5.2.jar,cglib-3.2.5.jar。版本自行选择)

    复制代码

    package com.lf.shejimoshi.proxy.cglib;
    
    import java.lang.reflect.Method;
    
    import com.lf.shejimoshi.proxy.entity.UserManager;
    import com.lf.shejimoshi.proxy.entity.UserManagerImpl;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    //Cglib动态代理,实现MethodInterceptor接口
    public class CglibProxy implements MethodInterceptor {
        private Object target;//需要代理的目标对象
        
        //重写拦截方法
        @Override
        public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {
            System.out.println("Cglib动态代理,监听开始!");
            Object invoke = method.invoke(target, arr);//方法执行,参数:target 目标对象 arr参数数组
            System.out.println("Cglib动态代理,监听结束!");
            return invoke;
        }
        //定义获取代理对象方法
        public Object getCglibProxy(Object objectTarget){
            //为目标对象target赋值
            this.target = objectTarget;
            Enhancer enhancer = new Enhancer();
            //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
            enhancer.setSuperclass(objectTarget.getClass());
            enhancer.setCallback(this);// 设置回调 
            Object result = enhancer.create();//创建并返回代理对象
            return result;
        }
        
        public static void main(String[] args) {
            CglibProxy cglib = new CglibProxy();//实例化CglibProxy对象
            UserManager user =  (UserManager) cglib.getCglibProxy(new UserManagerImpl());//获取代理对象
            user.delUser("admin");//执行删除方法
        }
        
    }

    复制代码

    Cglib动态代理运行结果

     

    AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。

    那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。

    AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。

    jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。

    总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

    1、定义接口和实现
    package com.proxy;

    public interface UserService {
        public String getName(int id);

        public Integer getAge(int id);
    }

    package com.proxy;

    public class UserServiceImpl implements UserService {
        @Override
        public String getName(int id) {
            System.out.println("------getName------");
            return "riemann";
        }

        @Override
        public Integer getAge(int id) {
            System.out.println("------getAge------");
            return 26;
        }
    }

    2、jdk动态代理实现
    jdk动态代理是jdk原生就支持的一种代理方式,它的实现原理,就是通过让target类和代理类实现同一接口,代理类持有target对象,来达到方法拦截的作用,这样通过接口的方式有两个弊端,一个是必须保证target类有接口,第二个是如果想要对target类的方法进行代理拦截,那么就要保证这些方法都要在接口中声明,实现上略微有点限制。

    package com.proxy;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;

    public class MyInvocationHandler implements InvocationHandler {

        public Object target;

        MyInvocationHandler() {
            super();
        }

        MyInvocationHandler(Object target) {
            super();
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("getName".equals(method.getName())) {
                System.out.println("++++++before " + method.getName() + "++++++");
                Object result = method.invoke(target, args);
                System.out.println("++++++after " + method.getName() + "++++++");
                return result;
            } else {
                Object result = method.invoke(target, args);
                return result;
            }
        }
    }

    package com.proxy;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;

    public class Main1 {
        public static void main(String[] args) {
            UserService userService = new UserServiceImpl();
            InvocationHandler invocationHandler = new MyInvocationHandler(userService);
            UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                    userService.getClass().getInterfaces(),invocationHandler);
            System.out.println(userServiceProxy.getName(1));
            System.out.println(userServiceProxy.getAge(1));
        }
    }

    输出结果:

    ++++++before getName++++++
    ------getName------
    ++++++after getName++++++
    riemann
    ------getAge------
    26

    3、cglib动态代理实现
    Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:

    cglib有两种可选方式,继承和引用。第一种是基于继承实现的动态代理,所以可以直接通过super调用target方法,但是这种方式在spring中是不支持的,因为这样的话,这个target对象就不能被spring所管理,所以cglib还是才用类似jdk的方式,通过持有target对象来达到拦截方法的效果。

    CGLIB的核心类:
    net.sf.cglib.proxy.Enhancer – 主要的增强类
    net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
    net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
    Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。

    net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
    public Object intercept(Object object, java.lang.reflect.Method method,
    Object[] args, MethodProxy proxy) throws Throwable;

    第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

    package com.proxy.cglib;

    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
     
    public class CglibProxy implements MethodInterceptor {
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
            System.out.println(method.getName());
            Object o1 = methodProxy.invokeSuper(o, args);
            System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
            return o1;
        }
    }

    package com.proxy.cglib;
     
    import com.meituan.hyt.test3.service.UserService;
    import com.meituan.hyt.test3.service.impl.UserServiceImpl;
    import net.sf.cglib.proxy.Enhancer;
     
    public class Main2 {
        public static void main(String[] args) {
            CglibProxy cglibProxy = new CglibProxy();
     
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(UserServiceImpl.class);
            enhancer.setCallback(cglibProxy);
     
            UserService o = (UserService)enhancer.create();
            o.getName(1);
            o.getAge(1);
        }
    }

    输出结果:

    ++++++before CGLIB$getName$0++++++
    getName
    ------getName------
    ++++++before CGLIB$getName$0++++++
    ++++++before CGLIB$getAge$1++++++
    getAge
    ------getAge------
    ++++++before CGLIB$getAge$1++++++

    4、需要注意的问题
    需要注意的是,当一个方法没有被aop事务包裹,在该方法内部去调用另外一个有aop事务包裹的方法时,这个方法的aop事务不会生效。比如:

    public void register() {
        aopRegister();
    }
     
    @Transactional
    public void aopRegister() {
     
    }

    因为通过上面的分析我们知道,在spring中,无论通过jdk的形式还是cglib的形式,代理类对target对象的方法进行拦截,其实都是通过让代理类持有target对象的引用,当外部引用aop包围的方法时,调用的其实是代理类对应的方法,代理类持有target对象,便可以控制target方法执行时的全方位拦截。

    而如果在target的内部方法register调用一个aop包围的target方法aopRegister,调用的其实就是target自身的方法,因为这时候的this指针是不可能指向代理类的。所以事务是不能生效的。

    5、优缺点
    速度比较
    //测试结果
    //创建代理的速度
    Create JDK Proxy: 13 ms  
    Create CGLIB Proxy: 217 ms  //较慢
    Create JAVAASSIST Proxy: 99 ms  
    Create JAVAASSIST Bytecode Proxy: 168 ms   //较慢
    Create ASM Proxy: 3 ms  //最快

    ================  
    Run JDK Proxy: 2224 ms, 634,022 t/s  //很慢,jdk生成的字节码考虑了太多的条件,所以字节码非常多,大多都是用不到的
    Run CGLIB Proxy: 1123 ms, 1,255,623 t/s  //较慢,也是因为字节码稍微多了点
    Run JAVAASSIST Proxy: 3212 ms, 438,999 t/s   //非常慢,完全不建议使用javassist的代理类来实现动态代理
    Run JAVAASSIST Bytecode Proxy: 206 ms, 6,844,977 t/s  //和asm差不多的执行速度,因为他们都是只生成简单纯粹的执行字节码,非常少(所以直接用javassist生成代理类的方式最值得推荐[从速度上讲])
    Run ASM Bytecode Proxy: 209 ms, 6,746,724 t/s   //asm什么都是最快的,毕竟是只和最底层打交道
     

    展开全文
  • AOP的拦截功能是由java中的动态代理实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。...

    欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

    AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。

    那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。

    AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。

    jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。

    总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

    1、定义接口和实现

    package com.proxy;
    
    public interface UserService {
        public String getName(int id);
    
        public Integer getAge(int id);
    }
    
    package com.proxy;
    
    public class UserServiceImpl implements UserService {
        @Override
        public String getName(int id) {
            System.out.println("------getName------");
            return "riemann";
        }
    
        @Override
        public Integer getAge(int id) {
            System.out.println("------getAge------");
            return 26;
        }
    }
    

    2、jdk动态代理实现

    jdk动态代理是jdk原生就支持的一种代理方式,它的实现原理,就是通过让target类和代理类实现同一接口,代理类持有target对象,来达到方法拦截的作用,这样通过接口的方式有两个弊端,一个是必须保证target类有接口,第二个是如果想要对target类的方法进行代理拦截,那么就要保证这些方法都要在接口中声明,实现上略微有点限制。

    package com.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class MyInvocationHandler implements InvocationHandler {
    
        public Object target;
    
        MyInvocationHandler() {
            super();
        }
    
        MyInvocationHandler(Object target) {
            super();
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("getName".equals(method.getName())) {
                System.out.println("++++++before " + method.getName() + "++++++");
                Object result = method.invoke(target, args);
                System.out.println("++++++after " + method.getName() + "++++++");
                return result;
            } else {
                Object result = method.invoke(target, args);
                return result;
            }
        }
    }
    
    package com.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class Main1 {
        public static void main(String[] args) {
            UserService userService = new UserServiceImpl();
            InvocationHandler invocationHandler = new MyInvocationHandler(userService);
            UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                    userService.getClass().getInterfaces(),invocationHandler);
            System.out.println(userServiceProxy.getName(1));
            System.out.println(userServiceProxy.getAge(1));
        }
    }
    

    输出结果:

    ++++++before getName++++++
    ------getName------
    ++++++after getName++++++
    riemann
    ------getAge------
    26
    

    3、cglib动态代理实现

    Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:

    cglib有两种可选方式,继承和引用。第一种是基于继承实现的动态代理,所以可以直接通过super调用target方法,但是这种方式在spring中是不支持的,因为这样的话,这个target对象就不能被spring所管理,所以cglib还是才用类似jdk的方式,通过持有target对象来达到拦截方法的效果。

    CGLIB的核心类:
    net.sf.cglib.proxy.Enhancer – 主要的增强类
    net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
    net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
    Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。

    net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
    public Object intercept(Object object, java.lang.reflect.Method method,
    Object[] args, MethodProxy proxy) throws Throwable;

    第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

    package com.proxy.cglib;
    
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
     
    public class CglibProxy implements MethodInterceptor {
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
            System.out.println(method.getName());
            Object o1 = methodProxy.invokeSuper(o, args);
            System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
            return o1;
        }
    }
    
    package com.proxy.cglib;
     
    import com.meituan.hyt.test3.service.UserService;
    import com.meituan.hyt.test3.service.impl.UserServiceImpl;
    import net.sf.cglib.proxy.Enhancer;
     
    public class Main2 {
        public static void main(String[] args) {
            CglibProxy cglibProxy = new CglibProxy();
     
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(UserServiceImpl.class);
            enhancer.setCallback(cglibProxy);
     
            UserService o = (UserService)enhancer.create();
            o.getName(1);
            o.getAge(1);
        }
    }
    

    输出结果:

    ++++++before CGLIB$getName$0++++++
    getName
    ------getName------
    ++++++before CGLIB$getName$0++++++
    ++++++before CGLIB$getAge$1++++++
    getAge
    ------getAge------
    ++++++before CGLIB$getAge$1++++++
    

    4、需要注意的问题

    需要注意的是,当一个方法没有被aop事务包裹,在该方法内部去调用另外一个有aop事务包裹的方法时,这个方法的aop事务不会生效。比如:

    public void register() {
        aopRegister();
    }
     
    @Transactional
    public void aopRegister() {
     
    }
    

    因为通过上面的分析我们知道,在spring中,无论通过jdk的形式还是cglib的形式,代理类对target对象的方法进行拦截,其实都是通过让代理类持有target对象的引用,当外部引用aop包围的方法时,调用的其实是代理类对应的方法,代理类持有target对象,便可以控制target方法执行时的全方位拦截。

    而如果在target的内部方法register调用一个aop包围的target方法aopRegister,调用的其实就是target自身的方法,因为这时候的this指针是不可能指向代理类的。所以事务是不能生效的。

    5、优缺点

    • 速度比较
    //测试结果
    //创建代理的速度
    Create JDK Proxy: 13 ms  
    Create CGLIB Proxy: 217 ms  //较慢
    Create JAVAASSIST Proxy: 99 ms  
    Create JAVAASSIST Bytecode Proxy: 168 ms   //较慢
    Create ASM Proxy: 3 ms  //最快
    
    ================  
    Run JDK Proxy: 2224 ms, 634,022 t/s  //很慢,jdk生成的字节码考虑了太多的条件,所以字节码非常多,大多都是用不到的
    Run CGLIB Proxy: 1123 ms, 1,255,623 t/s  //较慢,也是因为字节码稍微多了点
    Run JAVAASSIST Proxy: 3212 ms, 438,999 t/s   //非常慢,完全不建议使用javassist的代理类来实现动态代理
    Run JAVAASSIST Bytecode Proxy: 206 ms, 6,844,977 t/s  //和asm差不多的执行速度,因为他们都是只生成简单纯粹的执行字节码,非常少(所以直接用javassist生成代理类的方式最值得推荐[从速度上讲])
    Run ASM Bytecode Proxy: 209 ms, 6,746,724 t/s   //asm什么都是最快的,毕竟是只和最底层打交道
    
    • 便捷性比较

    排序(只是生成字节码,语义性):javassist>asm
    排序(动态代理):cglib=jdk>javassist bytecode>javassist proxy>asm

    展开全文
  • Java动态代理的两种实现方式

    千次阅读 2019-01-19 16:09:11
    一般而言,动态代理有以下两种实现方式 一、基于接口的动态代理(JDK动态代理) 要求:被代理类最少实现一个接口 提供者:jdk官方 例: /** * 演员类 */ public class Actor implements IActor{ public void ...

    动态代理是为了实现在不改变源码的基础上,对已有方法增强,它是AOP思想的底层实现技术。一般而言,动态代理有以下两种实现方式

    一、基于接口的动态代理(JDK动态代理)

    要求:被代理类最少实现一个接口
    提供者:jdk官方
    例:

    /**
     * 演员类
     */
    public class Actor implements IActor{
        public void basicAct(float money){
            System.out.println("拿到"+money+"钱,开始初级表演");
        }
        public void advancedAct(float money){
            System.out.println("拿到"+money+"钱,开始高级表演");
        }
    }
    
    /**
     * 演员类要实现的接口,即标准
     */
    public interface IActor {
        public void basicAct(float money);
        public void advancedAct(float money);
    }
    
    /**
     * 剧组类,主方法
     */
    public class Client {
        public static void main(String[] args) {
            final Actor actor = new Actor();
            /**
             * 剧组找演员,通过经纪公司,它就是代理
             * 涉及的类:Proxy
             * 创建代理对象的方法:newProxyInstance()
             * 该方法的参数:
             *      ClassLoader:;类加载器。和被代理对象使用相同的类加载器
             *      Class[]:字节码数组。被代理类实现的接口,要求代理对象和被代理对象具有相同的行为
             *      InvocationHandler:用于我们提供增强代码的接口。一般会写一个该接口的实现类。
             *                        实现类可以是匿名内部类。它的含义就是如何代理。这里的代码只能是谁用谁提供
             */
            IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(),
                    actor.getClass().getInterfaces(),
                    new InvocationHandler() {
                        /**
                         * 执行被代理对象的任何方法都会经过该方法,该方法有拦截的功能
                         * Object proxy:代理对象的引用。不一定每次都会有
                         * Method method:当前执行的方法
                         * Object[] args:当前执行方法所需的参数
                         * @return 当前执行方法的返回值
                         */
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Object ret=null;
                            //1.取出执行方法中的参数
                            Float money = (Float) args[0];
                            //2.判断当前执行的什么方法
                            if ("basicAct".equals(method.getName())){
                                if (money>10000)
                                    ret = method.invoke(actor,money);
                            }
                            if ("advancedAct".equals(method.getName())){
                                if (money>50000)
                                    ret = method.invoke(actor,money);
                            }
                            return ret;
                        }
                    });
            proxyActor.basicAct(20000);
        }
    }
    

    二、基于子类的动态代理(Cglib动态代理)

    要求:该代理类不能是最终类,不能被final修饰
    提供者:第三方Cglib

    改写上面的代码,此时Actor类不需要实现IActor接口

    /**
     * 剧组类,主方法
     */
    public class Client {
        public static void main(String[] args) {
            final Actor actor = new Actor();
            /**
             * 涉及的类:Enhancer
             * 创建代理对象的方法:create()
             * 该方法的参数:
             *        Class:被代理对象的字节码
             *        Callback:如何代理,作用同方法一的InvocationHandler
             */
            Actor proxyActor  = (Actor)Enhancer.create(actor.getClass(), new MethodInterceptor() {
                /**
                 * 执行被代理对象的方法都会经过该方法,作用同方法一的invoke()
                 * Object proxy:代理对象的引用。不一定每次都会有
                 * Method method:当前执行的方法
                 * Object[] args:当前执行方法所需的参数
                 * MethodProxy methodProxy:当前执行方法的代理对象,一般不用
                 * @return
                 * @throws Throwable
                 */
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    Object ret=null;
                    //1.取出执行方法中的参数
                    Float money = (Float) args[0];
                    //2.判断当前执行的什么方法
                    if ("basicAct".equals(method.getName())){
                        if (money>10000)
                            ret = method.invoke(actor,money);
                    }
                    if ("advancedAct".equals(method.getName())){
                        if (money>50000)
                            ret = method.invoke(actor,money);
                    }
                    return ret;
                }
            });
            proxyActor.advancedAct(500000);
        }
    }
    
    
    展开全文
  • 动态代理实现的三种方式

    千次阅读 2017-06-22 13:18:05
    动态代理实现有三种方式,jdk动态代理(基于接口),cglib动态代理(基于继承),javassist(hibernate中使用这种方式实现动态代理一 jdk实现动态代理package com.lzzl.jdkproxy;public interface Pet { public ...
  • 代理(Proxy)是一设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用到编程中的一个思想:不要...
  • JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢? 这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的...
  • Spring AOP两种动态代理对象实现方式

    千次阅读 2018-09-04 11:58:51
    一、JDK生成AopProxy代理对象 通过JDK的Proxy类的静态方法newProxyInstance方法得到...一个Proxy回调方法所在的对象handler:这个对象需要实现InvocationHandler接口,该接口实现类定义了invoke()方法,提供代理...
  • 代理模式的角色组成代理模式有三角色组成:抽象角色:通过接口或抽象类声明真实角色实现的业务方法。 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的...
  • 动态代理的两种方式以及区别

    万次阅读 多人点赞 2018-09-19 12:37:09
    动态代理的两种方式,以及区别。 JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将...
  • Spring AOP动态代理实现方式

    千次阅读 2019-06-06 23:20:57
    JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类 CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的...
  • Spring事务的几种实现方式

    万次阅读 多人点赞 2019-03-14 14:38:56
    1.、事务几种实现方式 (1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。 (2)基于 ...
  • 代理的三种实现方式

    千次阅读 2018-07-23 18:05:34
    代理(Proxy)是一设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 简单来说就是说代理就是增强方法,...
  • 代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。  按照代理的创建时期,代理类可以...
  • java动态代理:JDK接口实现方式

    千次阅读 2018-06-11 11:26:21
    具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 商户----&gt;消防部门119(代理)----&gt;消防员这种模式。我们可以不通过直接与消防对话的情况下,而通过...
  • python 使用代理几种方式

    千次阅读 2021-01-04 10:45:46
    本文介绍几种在Python里使用代理方式,假定代理运行在本机,HTTP代理端口为1231, SOCKS5代理端口为8080。 HTTP全局代理:环境变量方式 在命令行里配置如下环境变量,然后执行Python脚本,Python在进行网络请求时...
  • JDK动态代理如何实现?2.1 主要的实现过程3. 如何选择? 1. 实现方式 JDK 动态代理实现和 cglib 实现 2. JDK动态代理如何实现? 只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。 ...
  • 动态代理是 AOP(Aspect Orient Programming)编程思想,理解...CGLIB通过继承的方式进行代理,无论目标对象有没有实现接口都可以代理,但是无法代理final对象与final方法。(final类型不能有子类,final方法不能被重载)
  • iOS搜索功能的几种实现方式

    千次阅读 2016-09-08 15:16:38
    iOS搜索功能的几种实现方式,包含自定义视图时需要的部分参数设置
  • 1、动态代理的原理 ...Java提供2中动态代理方式,一是基于接口实现(JDK动态代理),一是基于继承实现(Cglib)。 2、基于接口的动态代理模式 JDK代理模式中,有两个重要的点;一个类(Proxy)和一个...
  • 设计模式之代理模式:三种代理模式的实现方式 前言:代理模式和另外一种设计模式--装饰者模式十分相像,他们都... 代理模式有三种实现方式:继承目标对象的同一个接口,引入目标对象,重写方法;使用JDK动态代理的...
  • Java代理几种方式

    千次阅读 2019-05-25 21:37:46
    什么是代理 什么是代理呢,其实很好理解,就是不直接访问目标,而是通过一个中间层来访问,就好像下面这样: Java的静态代理 举个例子,如果我们一些水果,比如:香蕉、苹果等,写成Java代码,大概是下面这个样子:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 328,291
精华内容 131,316
关键字:

动态代理的几种实现方式