精华内容
下载资源
问答
  • 代理模式 – cglib代理
    2021-02-28 11:22:44

    代理模式 – cglib代理

     目录

     cglib代理

     例子-常规

    代码:

    问候 Greet

    问候实现 GreetIml

    商店 Shop

    商店实现 ShopImpl

    cglib代理 CGLibProxy

    测试:,

    结果:

    例子-单例模式

    代码:

    cglli代理 CGLibProxySingle

    测试:

    结果:

    总结:


     cglib代理

    在使用的是静态代理动态代理都需要目标对象是一个实现了接口的目标对象,当目标对象可能只是一个单独的对象,并没有实现任何的接口,那如何进行代理? 需要使用cglib代理

     

    Cglib代理是什么?

       Cglib代理,也叫做子类代理,它是在内存中构件一个子类对象,从而实现对目标对象的功能拓展。 cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类,不鼓励直接只使用ASM,因为它要求你必须对JVM内部结构,包括class文件的格式和指令集都很熟悉。

     

    如何实现呢?

       在Java中要想实现cglib代理机制,需要org.springframework.cglib.proxy.MethodInterceptor接口和 org.springframework.cglib.proxy.Enhancer 类的支持

     

     例子-常规

     

    代码:

    问候 Greet

    public interface Greet {
        void say(String name);
    }

     

    问候实现 GreetIml

    public class GreetImpl implements Greet {
        @Override
        public void say(String name) {
            System.out.println("Hi! " + name);
        }
    }

     

    商店 Shop

    public interface Shop {
        void sell(String wares);
    }

     

    商店实现 ShopImpl

    public class ShopImpl implements Shop {
        @Override
        public void sell(String foodName) {
            System.out.println("sell the " + foodName);
        }
    }

     

    cglib代理 CGLibProxy

    public class CGLibProxy implements MethodInterceptor {
    
        public <T> T getProxy(Class<T> cls) {
            return (T) Enhancer.create(cls, (Callback) this);
        }
    
        // CGLib 给我们提供的是方法级别的代理,也可以理解为为对方法的拦截
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            before();
            Object result = methodProxy.invokeSuper(obj, args);
            after();
            return result;
        }
    
        private void before() {
            System.out.println("Before");
        }
    
        private void after() {
            System.out.println("After");
        }
    }
    

    在cglib代理类中,实现MethodInterceptor 的接口,调用 methodProxy.invokeSuper(obj, args)

     

    测试:,

    public   class TestCglib {
        public static void main(String[] args) throws Exception {
            CGLibProxy cgLibProxy = new CGLibProxy();
            System.out.println("========== greet proxy ==========");
            Greet greetProxy = cgLibProxy.getProxy(GreetImpl.class);
            greetProxy.say("cglib");
    
            System.out.println("========== shop proxy ==========");
            Shop shopProxy = cgLibProxy.getProxy(ShopImpl.class);
            shopProxy.sell(" potato chips");
        }
    }
    

     

    结果:

    ========== greet proxy ==========
    
    Before
    
    Hi! cglib
    
    After
    
    ========== shop proxy ==========
    
    Before
    
    sell the  potato chips
    
    After

     

    例子-单例模式

     

    代码:

    cglli代理 CGLibProxySingle

    public class CGLibProxySingle implements MethodInterceptor {
        private static class CGLibProxySingleHandler {
            private static CGLibProxySingle staticInnerSingle = new CGLibProxySingle();
        };
    
        private CGLibProxySingle() {
        }
    
        public static CGLibProxySingle getInstance() {
            return CGLibProxySingleHandler.staticInnerSingle;
        }
    
        public <T> T getProxy(Class<T> cls) {
            return (T) Enhancer.create(cls, (Callback) this);
        }
    
        // CGLib 给我们提供的是方法级别的代理,也可以理解为为对方法的拦截
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            before();
            Object result = methodProxy.invokeSuper(obj, args);
            after();
            return result;
        }
    
        private void before() {
            System.out.println("Before");
        }
    
        private void after() {
            System.out.println("After");
        }
    }
    

     结合单例模式的静态内部类的引用

    测试:

    public class TestCglibSingle {
        public static void main(String[] args) throws Exception {
            System.out.println("========== greet proxy ==========");
            Greet greetProxy = CGLibProxySingle.getInstance().getProxy(GreetImpl.class);
            greetProxy.say("cglib singleton");
    
            System.out.println("========== shop proxy ==========");
            Shop shopProxy = CGLibProxySingle.getInstance().getProxy(ShopImpl.class);
            shopProxy.sell("fried chicken");
        }
    }
    

     

    结果:

    ========== greet proxy ==========
    
    Before
    
    Hi! cglib singleton
    
    After
    
    ========== shop proxy ==========
    
    Before
    
    sell the fried chicken
    
    After

     

    总结:

      当目标对象可能只是一个单独的对象,并没有实现任何的接口,这个时候需要cglib代理方式,即使用目标对象子类的方式实现代理.

    动态代理和cgli代理进行比较:

    1,对象不同:

          Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);

          CGLIB能够代理普通类;

    2,实现方式不同:

          Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;

          CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效

     

    更多相关内容
  • Spring AOP、动态代理中的CGLib代理依赖的所有jar包
  • NULL 博文链接:https://jummy.iteye.com/blog/255628
  • cglib代理jar包

    2018-10-18 10:17:05
    cglib代理jar包
  • 项目简介 Async 是一款 Java 异步处理框架 设计目的 并行执行可以大幅度...CGLIB 代理异步 CGLIB 代理 和 Spring 的 AOP 实现一样 我们有些方法是没有实现接口的这时就需要依赖于字节码技术 此处选择 CGLIB 实现 支持
  • 本文阐述了3种代理模式的定义,并编写了代码测试案例; 代理其实是一种设计模式,可以在访问目标对象的方法上下文添加处理逻辑(扩展目标对象的功能),是 切面编程的基石; 【举个例子】 抖音直播带货就是一种...

    【README】

    本文阐述了3种代理模式的定义,并编写了代码测试案例;

    代理其实是一种设计模式,可以在访问目标对象的方法上下文添加处理逻辑(扩展目标对象的功能),是 切面编程的基石;

    【举个例子】 抖音直播带货就是一种代理模式;主播代理了工厂对象,提供了购买商品的方法;

    主播可以下调商品价格(因为走量),类似于在购买商品方法上文添加了逻辑;

    主播可以赠送其他商品,类似于在购买商品方法下文添加了逻辑;

    【代理模式URL】

    1.定义一个带有代理方法的接口;

    2.定义目标对象实现这个接口;

    3.定义代理对象实现这个接口,且代理对象调用目标对象的对应方法;

    4.客户端调用代理对象的方法,代理对象接着调用目标对象的方法,且代理对象可以在上下文添加逻辑(代理模式的目的所在)


     【1】静态代理(编译时生成代理类class文件)

    1,代码结构: 需要代理对象和目标对象实现相同接口;

    2,优点:可以在不修改目标对象的前提下,扩展其功能;

    3,缺点:

    • 冗余:每次代理都要定义一个代理类,会产生过多代理类;
    • 不移维护: 一旦接口方法有增删改,则代理对象和目标对象都需要修改;

    【1.1】静态代理代码示例

    // 接口
    public interface IUserDao {
        public void sayHello();
    }
    
    // 目标类
    public class UserDaoImpl implements IUserDao {
        @Override
        public void sayHello() {
            System.out.println("i am method named sayHello.");
        }
    }
    
    // 代理类 
    public class UserDaoStaticProxy implements IUserDao {
    
        private IUserDao target;
        public UserDaoStaticProxy(IUserDao target) {
            this.target = target;
        }
    
        @Override
        public void sayHello() {
            System.out.println("before");
            target.sayHello();
            System.out.println("after");
        }
    }

    main方法

    public class StaticProxyTest {
        public static void main(String[] args) {
            // 目标对象
            IUserDao target = new UserDaoImpl();
            // 代理对象
            UserDaoStaticProxy proxy = new UserDaoStaticProxy(target);
            // 调用代理方法
            proxy.sayHello();
        }
    }

    执行结果 

    before
    i am method named sayHello.
    after

    【2】动态代理(运行时生成代理对象)

    1,为了解决静态代理会有多个代理类,不易维护的缺点,我们引入了动态代理;

    2,动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。
    3,静态代理与动态代理的区别主要在:

    • 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件;
    • 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中;

    4,优点: 不需要代理对象实现接口,但目标对象必须实现接口;

    5,底层实现: 底层调用了

    // 生成代理对象 
    Proxy.newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
    • loader: 目标对象的类加载器;
    • interfaces: 目标对象实现的接口class数组;
    • InvocationHandler:将目标方法调用分配到的调用处理器对象;

    6,调用处理类实现接口 InvocationHandler ;

    类描述:InvocationHandler 是代理对象关联的调用处理程序所实现的接口。
    每个代理对象都有一个关联的调用处理程序。
    当在代理对象上调用方法时,方法调用被编码并分派到其调用处理程序的调用方法。

    public interface InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }

     方法描述:处理代理对象上的方法调用并返回结果。

    当在与其关联的代理对象上调用方法时,将在调用处理程序上调用本方法。

    【2.1】动态代理代码示例

    // 目标对象的接口
    public interface IUserDao2 {
        public String sayHello();
    }
    
    // 目标对象所属类
    public class UserDaoImpl2  implements IUserDao2 {
        @Override
        public String sayHello() {
            String msg = "i am method named sayHello. by dynamic proxy"; 
            System.out.println(msg);
            return msg;
        }
    }
    
    // 生成代理对象工厂
    public class DynamicProxyFactory {
        private DynamicProxyFactory(){}
    
        public static Object getProxyInstance (Object target) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        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;
                        }
                    });
        }
    } 

    main方法;

    public class DynamicProxyTest {
        public static void main(String[] args) {
            // 目标对象
            UserDaoImpl2 target = new UserDaoImpl2();
            // 代理对象
            IUserDao2 proxy = (IUserDao2) DynamicProxyFactory.getProxyInstance(target);
            // 调用代理方法
            String result = proxy.sayHello();
            System.out.println(result);
        }
    }

    运行结果:

    before
    i am method named sayHello. by dynamic proxy
    after
    i am method named sayHello. by dynamic proxy


    【3】cglib代理

    1,cglib定义, cglib 是一个强大、高性能和高质量的代码生成库。 它可以扩展 JAVA 类并在运行时实现接口;

    2,特点

    1. JDK的动态代理有一个限制,就是使用动态代理的目标对象必须实现一个或多个接口。如果想目标对象不实现接口,就可以使用CGLIB实现
    2. CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口;它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的 interception(拦截);
    3. CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉;

    3,cglib与动态代理最大的区别:

    • 使用动态代理的对象必须实现一个或多个接口;使用cglib代理的对象则无需实现接口,达到代理类无侵入;

    【3.1】cglib代理代码示例

    1,maven 引入 cglib 依赖;

    <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.2.5</version>
            </dependency>

    2,没有实现接口的目标类

    public class UserDaoImpl3 {
        public String sayHello() {
            String msg = "i am method named sayHello. test cglib  proxy.";
            System.out.println(msg);
            return msg;
        }
    }

    3,代理工厂

    public class CglibProxyFactory implements MethodInterceptor {
        private Object target;
    
        public CglibProxyFactory(Object target) {
            this.target = target;
        }
    
        public Object getProxyInstance () {
            this.target = target;
            // 工具类
            Enhancer enhancer = new Enhancer();
            // 设置父类
            enhancer.setSuperclass(target.getClass());
            // 设置回调函数
            enhancer.setCallback(this);
            // 创建子类对象代理
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("before");
            Object result = method.invoke(target, args);
            System.out.println("after");
            return result;
        }
    }

    4,main方法

    public class CglibProxyTest {
        public static void main(String[] args) {
            // 目标对象
            UserDaoImpl3 target = new UserDaoImpl3();
            // 代理对象
            UserDaoImpl3 proxy = (UserDaoImpl3) new CglibProxyFactory(target).getProxyInstance();
            // 调用代理方法
            String result = proxy.sayHello();
            System.out.println(result);
        }
    }

    执行结果:

    before
    i am method named sayHello. test cglib  proxy.
    after
    i am method named sayHello. test cglib  proxy.


    【4】java代理实现小结

    1)静态代理

    • 1.1)实现较简单,只要代理对象对目标对象进行包装,即可实现增强功能,但静态代理只能为一个目标对象服务,如果目标对象过多,则会产生很多代理类。
    • 1.2)静态代理在编译时产生class字节码文件,可以直接使用,效率高。

    2)动态代理

    • 2.1)需要目标对象实现业务接口,代理对象关联的调用处理类需要实现InvocationHandler接口
    • 2.2)动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。

    3)cglib代理

    • 无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。

    【reference】

    https://segmentfault.com/a/1190000011291179icon-default.png?t=M0H8https://segmentfault.com/a/1190000011291179

    展开全文
  • NULL 博文链接:https://pluto418.iteye.com/blog/1692218
  • NULL 博文链接:https://lochen514.iteye.com/blog/1271194
  • 代理模式(Proxy Pattern)是一种结构性模式。代理模式为一个对象提供了一个替身,以控制对这个对象的访问。即通过代理对象访问目标目标对象,可以在目标对象实现的基础上,增强额外的功能操作,即扩展...cglib代理 应用

    在这里插入图片描述

    代理模式


    代理模式(Proxy Pattern)是一种结构性模式。代理模式为一个对象提供了一个替身,以控制对这个对象的访问。即通过代理对象访问目标目标对象,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

    被代理的对象可以是远程对象、创建开销答得对象或需要安全控制得对象。代理模式主要有三种形式,分别是静态代理动态代理(也称JDK代理、接口代理)和cglib代理(在内存动态创建对象而不需要实现接口,也可属于动态代理得范畴)

    类图
    在这里插入图片描述

    静态代理


    静态代理是定义父类或者接口,然后被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。代理对象与目标对象实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

    • 优点:可不修改目标对象的功能,通过代理对象对目标功能扩展。
    • 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,目标对象与代理对象都要维护。

    比如老师代课的案例:
    在这里插入图片描述

    • ITeacherDao:接口
    • TeacherDao:目标对象,实现接口ITeacherDao
    • TeacherDAOProxy:代理对象,也实现ITeacherDao接口,并且聚合ITeacherDao属性,通过构造器传参设置值,调用的时候通过调用代理对象的方法来调用目标对象。

    代码

    1. 接口
    public interface ITeacherDao {
    	void teach(); // 授课的方法
    }
    
    1. 被代理对象
    public class TeacherDao implements ITeacherDao {
    	@Override
    	public void teach() {
    		System.out.println("一键三连");
    	}
    }
    
    1. 代理对象
    public class TeacherDaoProxy implements ITeacherDao {
    	private ITeacherDao target; //通过接口聚合目标对象
    	public TeacherDaoProxy(ITeacherDao target) {
    		this.target = target;
    	}
    	@Override
    	public void teach() { //重写接口
    		System.out.println("静态代理开始");
    		target.teach();
    		System.out.println("静态代理结束");
    	}
    }
    
    1. 测试
    public class Client {
    	public static void main(String[] args) {
    		//创建被代理对象
    		TeacherDao teacherDao = new TeacherDao();
    
    		//创建代理对象, 同时将被代理对象传递给代理对象
    		TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
    
    		//通过代理对象,调用到被代理对象的方法
    		teacherDaoProxy.teach();
    	}
    }
    /*运行结果:
    静态代理开始
    一键三连
    静态代理结束
    */
    

    动态代理


    动态代理也叫JDK代理、接口代理。它使代理对象不需要实现接口(但目标对象要实现接口),代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。

    即使用JDK包java.lang.reflect.Proxy中的newProxyInstance方法来动态的创建目标对象(被代理对象),该方法需要如下接收三个参数:

    1. ClassLoader loader
      指定当前目标对象使用的类加载器
    2. Class<?>[] interfaces
      目标对象实现的接口类型,使用泛型方法确认类型
    3. InvocationHandler h
      事情处理,执行目标对象的方法时,会触发事情处理器方法,把当前执行的目标对象方法作为参数传入
    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
    

    类图:
    在这里插入图片描述

    核心是getProxyInstacne()

    1. 根据传入的对象TeacherDao目标对象
    2. 利用返回机制,返回一个代理对象
    3. 然后通过代理对象,调用目标对象方法

    代码:

    1. 接口
    public interface ITeacherDao {
    	void teach();
    	void tesst(String name);
    }
    
    1. 目标对象
    public class TeacherDao implements ITeacherDao {
    	@Override
    	public void teach() {
    		System.out.println("一键三连");
    	}
    	@Override
    	public void tesst(String name) {
    		System.out.println("传参测试:" + name);
    	}
    }
    

    插播反爬信息 )博主CSDN地址:https://wzlodq.blog.csdn.net/

    1. 代理对象
    public class ProxyFactory {
    	//维护一个目标对象 , Object
    	private Object target;
    	//构造器 , 对target 进行初始化
    	public ProxyFactory(Object target) {
    		this.target = target;
    	}
    	//动态生成一个代理对象
    	public Object getProxyInstance() {
    		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    				target.getClass().getInterfaces(),
    				new InvocationHandler() { //匿名类重写invoke方法
    					@Override
    					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    						System.out.println("动态代理开始");
    						Object returnVal = method.invoke(target, args);//反射机制调用目标对象的方法
    						System.out.println("动态代理结束");
    						return returnVal;
    					}
    				});
    	}
    }
    
    1. 测试
    public class Client {
    	public static void main(String[] args) {
    		//创建目标对象
    		ITeacherDao target = new TeacherDao();
    
    		//创建代理对象
    		ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
    
    		//内存中动态生成了代理对象
    		System.out.println(proxyInstance.getClass());
    
    		//通过代理对象,调用目标对象的方法
    		proxyInstance.teach();
    		proxyInstance.tesst("一键三连");
    	}
    }
    /*运行结果:
    动态代理开始
    一键三连
    动态代理结束
    动态代理开始
    传参测试:一键三连
    动态代理结束
    */
    

    cglib代理


    Cglib代理也叫作子类代理,它使目标对象不需要实现接口,是在内存中构建一个子类对象从而实现对目标对象功能扩展,有的也将Cglib代理归属到动态代理。

    Cglib是一个高性能的代码生成包,它可以在运行期扩展java类与实现java接口。被许多AOP的框架使用(如Spring AOP)。Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。

    特别注意:代理的类不能为final,否则报错java.lang.IllegalArgumentException ,如果目标对象的方法如果为final或static,那么就不会被拦截(即不会执行目标对象额外的业务方法)。

    添加cglib包:
    下载以下jar包,复制到项目中,然后右键add to library
    https://github.com/wuzelong/CSDNBLOG(顺便求个star)
    在这里插入图片描述

    在这里插入图片描述

    不一样的是代理对象实现了MethodInterceptor接口,重写了intercept()方法实现对被代理对象(目标对象)的方法掉调用。

    代码:

    1. 目标对象
    public class TeacherDao {
    	public String teach() {
    		System.out.println("一键三连");
    		return "好";
    	}
    }
    
    1. 代理对象
    public class ProxyFactory implements MethodInterceptor {
    	//目标对象
    	private Object target;
    	public ProxyFactory(Object target) {
    		this.target = target;
    	}
    	//返回target对象的代理对象
    	public Object getProxyInstance() {
    		//创建一个工具类
    		Enhancer enhancer = new Enhancer();
    
    		//设置父类
    		enhancer.setSuperclass(target.getClass());
    
    		//设置回调函数
    		enhancer.setCallback(this);
    
    		//创建子类对象,即代理对象
    		return enhancer.create();
    	}
    	//重写intercept方法,会调用目标对象的方法
    	@Override
    	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
    		System.out.println("Cglib代理开始");
    		Object returnVal = method.invoke(target, args);
    		System.out.println("Cglib代理结束");
    		return returnVal;
    	}
    }
    
    1. 测试
    public class Client {
    	public static void main(String[] args) {
    		//创建目标对象
    		TeacherDao target = new TeacherDao();
    
    		//获取到代理对象,并且将目标对象传递给代理对象
    		TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();
    
    		//执行代理对象的方法,触发intecept 方法,从而实现对目标对象的调用
    		proxyInstance.teach();
    		System.out.println(res);
    	}
    }
    /*运行结果:
    Cglib代理开始
    一键三连
    Cglib代理结束
    好
    */
    

    应用


    • 防火墙代理
      内网通过代理穿透防火墙,实现对公网的访问。
    • 缓存代理
      请求资源时先到缓存代理中取,如果取不到再到数据库或公网取,然后更新缓存(如Redis)。
    • 远程代理
      通过网络和真正的远程对象沟通信息(如远程连接服务器)。
    • 同步代理
      用在多线程编程中,完成多线程间同步工作。

    原创不易,请勿转载本不富裕的访问量雪上加霜
    博主首页:https://wzlodq.blog.csdn.net/
    微信公众号:吾仄lo咚锵
    如果文章对你有帮助,记得一键三连❤

    展开全文
  • Cglib代理Controller后导致空指针异常原始程序加入切面问题排查问题解决引申 首先我们看下没有使用CGLIB对Controller进行代理时代码的样子 原始程序 @SpringBootApplication public class CglibProxyProblemMain { ...

    Cglib代理Controller后导致空指针异常

    首先我们看下没有使用CGLIBController进行代理时代码的样子

    原始程序

    @SpringBootApplication
    public class CglibProxyProblemMain {
        public static void main(String[] args) {
            SpringApplication.run(CglibProxyProblemMain.class, args);
        }
    }
    @Service
    public class TestService {
    
        public String test(){
            return "test";
        }
    
        public String test2(){
            return "test2";
        }
    }
    @RestController
    public class TestController {
    
        @Autowired
        private TestService testService;
    
        @GetMapping("/test")
        private String test(){
            return testService.test();
        }
    
        @GetMapping("/test2")
        public String test2(){
            return testService.test2();
        }
    }
    

    上面的逻辑很简单,CglibProxyProblemMain 作为Spring的启动入口,TestController中依赖了TestService。然后我们运行一下程序,并测试一下接口是否正常。
    在这里插入图片描述
    在这里插入图片描述

    从curl的结果上看,我们TestController应该是生效了

    加入切面

    然后,我们引入一个切面,并在TestController的方法执行前先输出一个“before …”

    @Component
    @Aspect
    public class TestAspect {
    
        @Before("pointCut()")
        public void beforeTest(){
            System.out.println("before ...");
        }
    
        @Pointcut("execution(public * com..TestController.*(..))")
        public void pointCut() {
            //定义切点
        }
    }
    

    然后我们再跑一次程序。服务启动正常:
    在这里插入图片描述
    接下来再测试一下之前的接口,然后我们的\test接口报错了:
    在这里插入图片描述
    在这里插入图片描述
    神奇的空指针问题就这么出现了…

    问题排查

    既然是空指针,那我们先调试一下吧:
    在这里插入图片描述
    从调试的堆栈信息上看,this指向的当前对象是一个被CGLIB代理之后的对象。因为这个类没有接口,同时我们又启用了切面,所以当前对象是一个被CGLIB代理之后的对象感觉也没什么问题。
    但是问题是为什么testServicenull呢?难道没有注入成功?那我们换个注入方式吧,使用构造器注入,同时将testService使用final修饰,这样testService应该就不会null了。
    修改之后TestController是这样的:

    在这里插入图片描述
    稳妥起见,我们再跟踪一次服务启动,确认一下testService确实被成功注入了。
    在这里插入图片描述
    这次应该没问题了,我们再测试一下接口:
    在这里插入图片描述
    依旧是一个空指针的报错,再调试一次程序,发现testService依旧是null,就像下面这样:
    在这里插入图片描述
    这就很诡异了…
    我们再测试一下/test2这个接口,更加神奇的现象出现了,/test2完全没问题
    在这里插入图片描述
    难道testService又有了?我们接着调试一下:
    在这里插入图片描述
    从调试信息中可以看到,在执行test2时,testService确实是存在的,而且this指向并不是被CGLIB代理之后的类。

    问题解决

    这下就找到问题的原因了

    CGLIB无法代理被privatefinal修饰的方法

    在这里插入图片描述
    由于test方法是private的,无法被CGLIB代理,所以当这个方法被调用时,其实调用的是代理类的同名方法。但是test2由于是public的,可以被正常代理,所以始终都没有问题。

    引申

    我们再深入一下这个问题,为什么代理类执行test方法时,testServicenull呢?难道没有依赖注入成功吗?
    要解决这个问题,首先我们得看下代理是什么时机生成的:
    在这里插入图片描述
    从调试信息中可以看到,生成代理时,bean已经实例化完毕,bean中的testService已经有值了。但是生成的代理类中的testService却是空的。
    在这里插入图片描述
    我们都知道CGLIB是基于继承的。也就是生成的代理类是被代理类的子类。但是我们既然写了构造器注入,为什么代理类的testService依旧是null呢?
    在这里插入图片描述
    原因是CGLIB作弊了,通过CGLIB生成的类的构造器中没有super(...),所以testServicenull了。饶了这么大一圈,其实最后还是因为对CGLIB代理的原理不够清晰才导致了这样的问题,-_-||

    展开全文
  • aop - cglib代理(实例)

    2020-04-06 11:44:09
    cglib代理- 通过继承的方式实现代理类 MethodInterceptor :通过MethodProxy.invokeSuper()反射调用目标对象的方法 代码实现: 1、创建接口 package jdk; public interface Subject { void request(); void ...
  • CGLib代理jar包

    2017-07-26 16:18:44
    CGLib动态代理需要的jar包
  • cglib代理

    2019-07-12 18:48:31
    cglib代理 ​ 在此之前,我们学习了JDK动态代理,而JDK动态代理有一定的局限性,因为使用JDK动态代理时,被代理类必须实现接口,然后动态代理生成的代理类同时实现该接口实现代理模式,但在特定情况下没办法让被代理...
  • Cglib代理解密

    千次阅读 2020-09-29 23:32:09
    Cglib代理原理解密 cglib与动态代理功能类似,都是为了做方法拦截的,从而增强方法功能,本文讲解cglib的原理 我有一个CglibDemo类,也就是main方法的测试类, 他有一个属性name和他的get set方法 import ...
  • Cglib代理

    2019-08-22 20:58:33
    1.cglib代理目标类和代理类都不需要实现接口; 简单实例代码如下: 目标类: package com.fxy.cglib.proxy; public class TeacherDao { public void teach() { System.out.println("老师正在授课中,这是cglib...
  • cglib代理的依赖jar包

    2019-04-29 17:59:14
    内含asm-3.3.1.jar和cglib-2.2.2.jar,添加到项目即可,亲测可用
  • Java SDK动态代理的局限性     我们知道如果要使用java sdk的动态代理,那么我们必须要创建...cglib动态代理 首先来看它的实例 public class SimpleCGLibDemo { //表示被代理的类 static class RealService..
  • 1、使用 cglib代理 使用 cglib代理完成上一章 静态代理的事务管理的功能。 沿用上一章的几个类: 实体类:User 接口:UserService 接口实现类: UserServiceImpl 引入依赖: <dependency> <...
  • Cglib 代理详解及案例

    2020-10-14 12:40:24
    Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。 Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java类与实现 java 接口.它...
  • 在理解他们区别之前,首先要清楚...CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承) 二、Spring在选择用JDK还是CGLiB的依据: (1)当Bean实现接口时,Spring就会用JDK的动态代理 (2)当Bea
  • cglib是使用ASM框架来生成代理类,目标类无须实现接口,生成的代理类会继承目标类,不能是final修饰的类,方法不能是非public,由于生成了几个类文件,所以生成时慢,之所以有几个类文件,因为cglib在生成代理类的...
  • JDK代理和CGLIB代理的区别 区别 JDK: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 CGKIB: cglib动态代理是利用asm开源包,对代理对象类的class文件加载...
  • CGLIB代理例子

    2019-04-28 11:27:53
    CGLIB代理例子
  • Java动态代理Cglib代理

    2021-03-27 19:49:43
    JDK 动态代理有一个最致命的问题是其...很多知名的开源框架都使用到了CGLIB, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。 在 CGLIB 动态代理机制中 M
  • 1.1 CGLIB代理相关的类 1.2 CGLIB动态代理的基本原理 1.3 使用MethodInterceptor接口实现方法回调 1.3.1 实现MethodInterceptor接口 1.4 使用CGLIB代理最核心类Enhancer生成代理对象 1.5 使用CGLIB继进行动态...
  • jdk 和 cglib的简单动态代理,闲来无事 写写。有需要的朋友可以看看
  • Jdk代理和CGLIB代理的区别

    千次阅读 2020-12-09 12:42:34
    cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。核心是实现MethodInterceptor接口,使用intercept()方法进行面向切面的处理,调用相应的通知。 如果目标对象...
  • Cglib代理模式实现步骤 1) 需要引入cglib的jar文件 2) 在内存中动态构建子类,注意代理的类不能为final,否则报错 java.lang.IllegalArgumentException: 3) 目标对象的方法如果为final/static,那么就不会被拦截,即...
  • spring的cglib代理

    2020-10-14 19:57:03
    有接口的是proxy的动态代理,没有接口的是cglib代理 没有接口spring自动应用cglib package org.example; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype....
  • CGLIB代理使用与原理详解

    千次阅读 2018-08-26 11:26:49
    这就有CGLIB的诞生了,前面说的JDK的动态代理的实现方式是实现相关的接口成为接口的实现类,那么我们自然可以想到用继承的方式实现相关的代理类。 【1】CGLIB简单实现 ① pom依赖如下 &amp;amp;amp;amp;amp;...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 81,980
精华内容 32,792
关键字:

cglib代理

友情链接: Moving Load.zip