精华内容
下载资源
问答
  • Java两种动态代理JDK动态代理和CGLIB动态代理

    万次阅读 多人点赞 2018-08-07 15:33:35
    JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一对象A通过持有另一对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一接口,A也会去实现接口。但是B是...

    目录

    代理模式

    JDK动态代理

    cglib动态代理

    测试


    代理模式

    代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。

    使用代理好处还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。代理其他的应用可能还有很多。

    上述例子中,类A写死持有B,就是B的静态代理。如果A代理的对象是不确定的,就是动态代理。动态代理目前有两种常见的实现,jdk动态代理和cglib动态代理。

    JDK动态代理

    jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。

    首先有个“明星”接口类,有唱、跳两个功能:

    package proxy;
    
    public interface Star
    {
        String sing(String name);
        
        String dance(String name);
    }
    

    再有个明星实现类“刘德华”:

    package proxy;
    
    public class LiuDeHua implements Star
    {   
        @Override
        public String sing(String name)
        {
             System.out.println("给我一杯忘情水");
    
            return "唱完" ;
        }
        
        @Override
        public String dance(String name)
        {
            System.out.println("开心的马骝");
    
            return "跳完" ;
        }
    }
    

    明星演出前需要有人收钱,由于要准备演出,自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的明星接口,无法对外服务,它仅仅是一个wrapper。

    package proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class StarProxy implements InvocationHandler
    {
        // 目标类,也就是被代理对象
        private Object target;
        
        public void setTarget(Object target)
        {
            this.target = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里可以做增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }
        
        // 生成代理类
        public Object CreatProxyedObj()
        {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }  
       
    }
    

    上述例子中,方法CreatProxyedObj返回的对象才是我们的代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个CreatProxyedObj方法不一定非得在我们的StarProxy类中,往往放在一个工厂类中。上述代理的代码使用过程一般如下:

    1、new一个目标对象

    2、new一个InvocationHandler,将目标对象set进去

    3、通过CreatProxyedObj创建代理对象,强转为目标对象的接口类型即可使用,实际上生成的代理对象实现了目标接口。

            Star ldh = new LiuDeHua();
    
            StarProxy proxy = new StarProxy();
    
            proxy.setTarget(ldh); 
      
            Object obj = proxy.CreatProxyedObj();
            
            Star star = (Star)obj;

    Proxy(jdk类库提供)根据B的接口生成一个实现类,我们成为C,它就是动态代理类(该类型是 $Proxy+数字 的“新的类型”)。生成过程是:由于拿到了接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它调用另一个对象的方法。当然这个被调用的对象不能是对象B,如果是对象B,我们就没法增强了,等于饶了一圈又回来了。

    所以它调用的是B的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,上述例子中就是StarProxy, 这个接口里面有个方法,它是所有Target的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。

    看下我们的实现,我们在InvocationHandler里调用了对象B(target)的方法,调用之前增强了B的方法。

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }

    所以可以这么认为C代理了InvocationHandler,InvocationHandler代理了我们的类B,两级代理。

    整个JDK动态代理的秘密也就这些,简单一句话,动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。

    下面看下动态代理类到底如何调用的InvocationHandler的,为什么InvocationHandler的一个invoke方法能为分发target的所有方法。C中的部分代码示例如下,通过反编译生成后的代码查看,摘自链接地址。Proxy创造的C是自己(Proxy)的子类,且实现了B的接口,一般都是这么修饰的:

    public final class XXX extends Proxy implements XXX
    

    一个方法代码如下:

    
      public final String SayHello(String paramString)
      {
        try
        {
          return (String)this.h.invoke(this, m4, new Object[] { paramString });
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
    

    可以看到,C中的方法全部通过调用h实现,其中h就是InvocationHandler,是我们在生成C时传递的第三个参数。这里还有个关键就是SayHello方法(业务方法)跟调用invoke方法时传递的参数m4一定要是一一对应的,但是这些对我们来说都是透明的,由Proxy在newProxyInstance时保证的。留心看到C在invoke时把自己this传递了过去,InvocationHandler的invoke的第一个方法也就是我们的动态代理实例类,业务上有需要就可以使用它。(所以千万不要在invoke方法里把请求分发给第一个参数,否则很明显就死循环了)

    C类中有B中所有方法的成员变量

      private static Method m1;
      private static Method m3;
      private static Method m4;
      private static Method m2;
      private static Method m0;
    

    这些变量在static静态代码块初始化,这些变量是在调用invocationhander时必要的入参,也让我们依稀看到Proxy在生成C时留下的痕迹。

    static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
          m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
    

    从以上分析来看,要想彻底理解一个东西,再多的理论不如看源码,底层的原理非常重要。

    jdk动态代理类图如下

    cglib动态代理

    我们了解到,“代理”的目的是构造一个和被代理的对象有同样行为的对象,一个对象的行为是在类中定义的,对象只是类的实例。所以构造代理,不一定非得通过持有、包装对象这一种方式。

    通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以cglib实现的代理也是可以被正常使用的。

    先看下代码

    package proxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxy implements MethodInterceptor
    {
        // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
        public Object CreatProxyedObj(Class<?> clazz)
        {
            Enhancer enhancer = new Enhancer();
            
            enhancer.setSuperclass(clazz);
            
            enhancer.setCallback(this);
            
            return enhancer.create();
        }
        
        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        } 
    }
    

    从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。

    cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。

    这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

    C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。

    这里有个疑问就是intercept的四个参数,为什么我们使用的是arg3而不是arg1?

        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        }

     因为如果我们通过反射 arg1.invoke(arg0, ...)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, ...)很明显会死循环。

    所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。

    fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。

    对比JDK动态代理,cglib依然需要一个第三者分发请求,只不过jdk动态代理分发给了目标对象,cglib最终分发给了自己,通过给method编号完成调用。cglib是继承的极致发挥,本身还是很简单的,只是fastclass需要另行理解。

    参考

    https://blog.csdn.net/jiankunking/article/details/52143504

    http://www.php.cn/java-article-407212.html

    https://www.cnblogs.com/chinajava/p/5880887.html

    https://rejoy.iteye.com/blog/1627405

    测试

       public static void main(String[] args)
        {
            int times = 1000000;
            
            Star ldh = new LiuDeHua();
            StarProxy proxy = new StarProxy();
            proxy.setTarget(ldh);
            
            long time1 = System.currentTimeMillis();
            Star star = (Star)proxy.CreatProxyedObj();
            long time2 = System.currentTimeMillis();
            System.out.println("jdk创建时间:" + (time2 - time1));
            
            CglibProxy proxy2 = new CglibProxy();
            long time5 = System.currentTimeMillis();
            Star star2 = (Star)proxy2.CreatProxyedObj(LiuDeHua.class);
            long time6 = System.currentTimeMillis();
            System.out.println("cglib创建时间:" + (time6 - time5));
            
            long time3 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star.sing("ss");
                
                star.dance("ss");
            }
            long time4 = System.currentTimeMillis();
            System.out.println("jdk执行时间" + (time4 - time3));
            
            long time7 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star2.sing("ss");
                
                star2.dance("ss");
            }
            
            long time8 = System.currentTimeMillis();
            
            System.out.println("cglib执行时间" + (time8 - time7));   
        }

    经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。

    展开全文
  • 上篇介绍了一下静态代理:Java中的代理模式——静态...2、如果一Service中有很多方法需要事务(增强动作),发现代理对象的方法中还是有很重复的代码 3、由第一点和第二点可以得出:静态代理的重用性不强 那怎...

    视频功能审核通过了,可以看视频啦!记得点关注啊~

    注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了

    记得点关注啊,视频里的wx二维码失效了,wx搜索:“聊5毛钱的java 或 扫码关注公众号,欢迎一起学习交流

    快扫码关注啦!关注可领取博主的Java学习视频+资料,保证都是干货

    SpringAOP系列,帮你了解SpringAOP的来龙去脉

    上篇介绍了一下静态代理:Java中的代理模式——静态代理以及分析静态代理的缺点

    静态代理是程序运行前,代理类的.class文件已经存在了。

    动态代理是,在程序运行时运用反射机制动态创建而成,无需手动编写代码

    也分析了一下静态代理的缺点:

    1、由于静态代理中的代理类是针对某一个类去做代理的,那么假设一个系统中有100个Service,则需要创建100个代理类

    2、如果一个Service中有很多方法需要事务(增强动作),发现代理对象的方法中还是有很多重复的代码

    3、由第一点和第二点可以得出:静态代理的重用性不强

    那怎么解决呢?

    用动态代理就可以很好的解决上述问题,本篇介绍一下:Java中的动态代理

    动态代理实现的目的和静态代理一样,都是对目标方法进行增强,而且让增强的动作和目标动作分开,达到解耦的目的

    动态代理分为JDK的动态代理和cglib动态代理

    它俩有略微的差别:JDK动态代理产生的代理类和目标类实现了相同的接口;cglib动态代理产生的代理类是目标对象的子类

    下面分别介绍JDK的动态代理(是JDK的代码实现的)和cglib动态代理(是cglib的jar包实现的)

    1、JDK的动态代理

    package com.cj.study.proxyjdk;
    
    public interface PersonService {
    	
    	public String savePerson();
    	
    	public void updatePerson();
    	
    	public void deletePerson();
    	
    }
    package com.cj.study.proxyjdk;
    
    public class PersonServiceImpl implements PersonService{
    
    	@Override
    	public String savePerson() {
    		System.out.println("添加");
    		return "保存成功!";
    	}
    
    	@Override
    	public void updatePerson() {
    		System.out.println("修改");
    	}
    
    	@Override
    	public void deletePerson() {
    		System.out.println("删除");
    	}
    
    }
    
    package com.cj.study.proxyjdk;
    
    public class MyTransaction {
    	public void beginTransaction(){
    		System.out.println("开启事务 ");
    	}
    	public void commit(){
    		System.out.println("提交事务");
    	}
    }
    

    注意:在动态代理在生成代理对象的时候需要一个拦截器 InvocationHandler 因此咱们需要写一个拦截器

    package com.cj.study.proxyjdk;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class PersonServiceInterceptor implements InvocationHandler{
    	//目标类
    	private Object target;
    	//增强类
    	private MyTransaction myTransaction;
    	//构造函数注入目标类和增强类
    	public PersonServiceInterceptor(Object target,MyTransaction myTransaction){
    		this.target = target;
    		this.myTransaction = myTransaction;
    	}
    
    	//代理类的每一个方法被调用的时候都会调用下边的这个invoke方法
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable {
    		this.myTransaction.beginTransaction();
    		Object returnValue = method.invoke(this.target, args);
    		this.myTransaction.commit();
    		return returnValue;
    	}
    	
    }
    

    注意:

    1、当客户端执行代理对象.方法时,进入到了拦截器的invoke方法体

    2、拦截器中invoke方法体的内容就是代理对象方法体的内容

    3、拦截器中invoke方法的method参数是在调用的时候赋值的

    package com.cj.study.proxyjdk;
    
    import java.lang.reflect.Proxy;
    import org.junit.Test;
    
    public class ProxyTest {
    	@Test
    	public void test(){
    		Object target = new PersonServiceImpl();
    		MyTransaction myTransaction = new MyTransaction();
    		PersonServiceInterceptor interceptor = new PersonServiceInterceptor(target, myTransaction);
    		PersonService personService = (PersonService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),interceptor);
    		String returnValue = (String)personService.savePerson();
    		System.out.println(returnValue);
    	}
    }
    

    可以打断点看下

    发现生成的对象是 $Proxy4 说明返回的已经是我们的代理对象了

    最后的运行结果

    2、cglib动态代理

    首先需要导入cglib的jar包:cglib-nodep-2.1_3.jar

    package com.cj.study.proxycglib;
    
    public interface PersonService {
    	
    	public String savePerson();
    	
    	public void updatePerson();
    	
    	public void deletePerson();
    	
    }
    
    package com.cj.study.proxycglib;
    
    public class PersonServiceImpl implements PersonService{
    
    	@Override
    	public String savePerson() {
    		System.out.println("添加");
    		return "保存成功!";
    	}
    
    	@Override
    	public void updatePerson() {
    		System.out.println("修改");
    	}
    
    	@Override
    	public void deletePerson() {
    		System.out.println("删除");
    	}
    
    }
    
    package com.cj.study.proxycglib;
    
    public class MyTransaction {
    	public void beginTransaction(){
    		System.out.println("开启事务 ");
    	}
    	public void commit(){
    		System.out.println("提交事务");
    	}
    }
    
    package com.cj.study.proxycglib;
    
    import java.lang.reflect.Method;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class PersonServiceInterceptor implements MethodInterceptor{
    	//目标类
    	private Object target;
    	//增强类
    	private MyTransaction myTransaction;
    	//构造函数注入目标类和增强类
    	public PersonServiceInterceptor(Object target,MyTransaction myTransaction){
    		this.target = target;
    		this.myTransaction = myTransaction;
    	}
    	
    	public Object createProxy(){
    		Enhancer enhancer = new Enhancer();
    		enhancer.setCallback(this);
    		enhancer.setSuperclass(this.target.getClass());
    		return enhancer.create();
    	}
    
    	@Override
    	public Object intercept(Object arg0, Method arg1, Object[] arg2,
    			MethodProxy arg3) throws Throwable {
    		myTransaction.beginTransaction();
    		Object returnValue = arg1.invoke(this.target, arg2);
    		myTransaction.commit();
    		return returnValue;
    	}
    	
    }
    
    package com.cj.study.proxycglib;
    
    import org.junit.Test;
    
    public class ProxyTest {
    	@Test
    	public void test(){
    		Object target = new PersonServiceImpl();
    		MyTransaction myTransaction = new MyTransaction();
    		PersonServiceInterceptor interceptor = new PersonServiceInterceptor(target, myTransaction);
    		PersonService personService =(PersonService) interceptor.createProxy();
    		String returnValue = (String)personService.savePerson();
    		System.out.println(returnValue);
    	}
    }
    

    最后的运行结果

    最后需要知道的点:

    1、JDK动态代理,要求目标类实现接口,但是有时候目标类直接一个单独的对象,并没有实现任何的接口,这时就得使用CGLib动态代理
    2、JDK动态代理是JDK里自带的,CGLib动态代理需要引入第三方的jar包

    3、CGLib动态代理,它是在内存中构建一个子类对象,从而实现对目标对象功能的扩展
    4、CGLib动态代理,是基于继承来实现代理,所以无法对final类、private方法和static方法进行代理

    以上就是动态代理的两种实现。

    我们用上边的做法去实现目标方法的增强,实现代码的解耦,是没有问题的,但是还是需要自己去生成代理对象,自己手写拦截器,在拦截器里自己手动的去把要增强的内容和目标方法结合起来,这用起来还是有点繁琐,有更好的解决方案吗?

    答案是:有的!那就是Spring的AOP,这才是咱们最终想引出来的重点!

    有了Spring的AOP后,就不用自己去写了,只需要在配置文件里进行配置,配置好后Spring按照你的配置去帮你生成代理对象,按照你的配置把增强的内容和目标方法结合起来。就相当于自己写代码也能实现和aop类似的功能,但是有了Spring aop以后有些事情Spring帮你做了,而且人家Spring做成了可配置化,用起来非常简单而且很灵活

    咱们上边用的JDK动态代理和cglib动态代理,这两种在Spring的AOP里都有用到,Spring是根据不同的情况去决定是使用JDK的动态代理生成代理对象,还是使用cglib去生成代理对象,具体的内容下一篇会讲到。

    下一篇我们继续:讲一下Spring中的AOP以及切入点表达式和各种通知

    铁子们,如果觉得文章对你有所帮助,可以点关注,点赞

    也可以关注下公众号:扫码或 wx搜索:“聊5毛钱的java”,欢迎一起学习交流,关注公众号可领取博主的Java学习视频+资料,保证都是干货

    3Q~

    纯手敲原创不易,如果觉得对你有帮助,可以打赏支持一下,哈哈,感谢~

               

    展开全文
  • java动态代理

    万次阅读 2020-12-14 17:59:02
    最近在看一些技术源码的时候,发现很地方都是动态代理, 真可谓是一切皆代理啊,所以我们需要弄明白代理模式这样在看源码的时候会好受很。 2、基本概念 代理(Proxy)模式提供了间接访问目标对象的方式,即通过...

    1、引言

    最近在看一些技术源码的时候,发现很多地方都是动态代理, 真可谓是一切皆代理啊,所以我们需要弄明白代理模式这样在看源码的时候会好受很多。

    2、基本概念

    代理(Proxy)模式提供了间接访问目标对象的方式,即通过代理对象访问目标对象,这样做的好处是:可以在目标对象的功能上,增加额外的功能补充,即扩展目标对象的功能。

    这就符合了设计模式低开闭原则,即在对既有代码不改动的情况下进行功能扩展。

    举个我们平常非常常见的例子, 明星和经纪人就是被代理和代理的关系,明细出演活动的时候,明细就是一个目标对象,他只负责活动中的节目,而其他的琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想 中的一个例子。

    3、静态代理

    静态代理的特点:代理类和目标类必须实现 同一个接口或者继承相同的父类。所以我们需要 定义一个接口。下面来看具体实现demo。

    接口类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:28
     */
    public interface Istart {
    
        void sing();
    }

    目标类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1416:07
     */
    public class LDHStar implements Istart {
    
        @Override
        public void sing() {
            System.out.println("华仔唱歌");
        }
    }
    

    代理类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1416:08
     */
    public class StaticPorxyManager implements Istart {
    
        private Istart target;
        public StaticPorxyManager(Istart target) {
            this.target = target;
        }
    
        @Override
        public void sing() {
            System.out.println("演唱会之前。。。。");
            this.target.sing();
            System.out.println("演唱会之后。。。。");
    
        }
    }

    测试类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:46
     */
    public class StaticTeasMain {
    
        public static void main(String[] args) {
            Istart ldhStar = new LDHStar();
    
            StaticPorxyManager staticPorxyManager = new StaticPorxyManager(ldhStar);
            staticPorxyManager.sing();
    
        }
    }

    静态代理类优缺点:

    有点:可以在不修改目标代码的情况下,扩折额外 功能

    缺点:因为代理类和目标类必须实现相同的接口,所以会有很多代理类,类太多, 同时,一旦接口增加方法,目标对象很代理对象都需要维护。而动态代理可以解决上面的问题。

    4、JDK动态代理

    动态代理的主要特点就是能够在程序运行时JVM才为目标对象生成代理对象。

    常说的动态代理也叫做JDK 代理也是一种接口代理,之所以叫做接口代理,是因为被代理的对象也就是目标对象必须实现至少一个接口,没有实现接口不能使用这种方式生成代理对象,JDK中生成代理对象的代理类就是Proxy,所在包是java.lang.reflect.

    接口:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:28
     */
    public interface Istart {
    
        void sing();
    }
    

    目标类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:29
     */
    public class WangFengStar implements Istart {
        @Override
        public void sing() {
            System.out.println("汪峰唱歌。。。");
        }
    }
    
    InvocationHandler类:
    import java.lang.annotation.Target;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:32
     */
    public class StarInvocationHandler implements InvocationHandler {
    
        private  Object target;
        public StarInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            System.out.println("演唱会前工作");
            //调用目标对象的目标方法
            method.invoke(target,args);
            System.out.println("演唱会后工作");
            return null;
        }
    }
    

    代理工厂:

    import java.lang.reflect.Proxy;
    
    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:30
     */
    public class ProxyFactory {
    
        private Object target;
        private StarInvocationHandler starInvocationHandler;
        public ProxyFactory( Object target) {
            this.target = target;
            this.starInvocationHandler = new StarInvocationHandler(target);
        }
    
        public Object getProxyObject(){
           return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),starInvocationHandler);
        }
    }

    测试方法:

    public class TeasMain {
    
        public static void main(String[] args) {
            Istart wangfengStar = new WangFengStar();
            Istart proxyObject = (Istart)new ProxyFactory(wangfengStar).getProxyObject();
            proxyObject.sing();
        }
    }

    JDK代理方式不需要代理对象实现接口,但是目标对象一定要实现接口,但是我们在项目中有很多需要代理的类并没有实现接口,所以这也算是这种代理方式的一种缺陷。

    5、cglib动态代理

    如果我们 需要给没有是实现任何接口的目标类生成代理对象,JDK方式是做不到的。这是就可以使用继承目标类以目标对象子类的方式实现代理,这种方法就叫做Cglib代理,也叫做子类代理,他是在内存中构建一个子类对象从而 实现对目标对象功能的扩展。

    Cglib是一个强大的高性能代码生成包,他可以在运行期扩展java类和扩展java接口。它广泛的被许多AOP框架使用,例如Sring AOP和synaop,为他们提供方法的intercepion(拦截)

    Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类。

    Cglib子类代理实现方法:

    1.需要引入cglib的jar文件 cglib-2.2.2.jar asm-3.3.1.jar,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core-3.2.5.jar即可.
    2.引入功能包后,就可以在内存中动态构建子类
    3.代理的类不能为final,否则报错
    4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

    目标类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:57
     */
    public class JieLunStar {
    
        public void sing(){
            System.out.println("杰伦唱歌");
        }
    }

    代理工厂:

    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:58
     */
    public class CglibProxyFactory implements MethodInterceptor {
    
        //维护目标对象
        private Object target;
    
        public CglibProxyFactory(Object target) {
            this.target = target;
        }
    
        /**
         * @Description:获得目标类的代理对象
         * @author: zhenghao
         * @date: 2020/12/14 11:02
        */
        public Object getProxyObject(){
            //1、工具类
            Enhancer enhancer = new Enhancer();
            //2、设置父类
            enhancer.setSuperclass(target.getClass());
            //3、设置回调
            enhancer.setCallback(this);
            //4、创建代理类
            Object proxyObject = enhancer.create();
            return proxyObject;
    
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
            System.out.println("演唱会前工作");
            //执行目标对象的目标方法
            method.invoke(target,objects);
            System.out.println("演唱后前工作");
            return null;
        }

    测试类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:46
     */
    public class CglibTeasMain {
    
        public static void main(String[] args) {
            JieLunStar jieLunStar = new JieLunStar();
            JieLunStar proxyObject = (JieLunStar)new CglibProxyFactory(jieLunStar).getProxyObject();
            proxyObject.sing();
        }
    }
    

    到这三种代理方式我们都介绍完了,下面总结一下:

    1、如果目标对象实现了接口,我们就采用JDK方式实现动态代理

    2、如果目标对象没有实现接口,我们就需要采用cglib方式实现动态代理;

    6、目标是接口+注解的动态代理

    注解类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1411:33
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Select {
    
        String value() default "";
    }

    目标类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1411:32
     */
    public interface IUserDao {
    
        @Select("select * from user")
        String getUser();
    
      
    }
    InvocationHandler类:
    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:32
     */
    public class UserInvocationHandler implements InvocationHandler {
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //创建类的是一个具体的实现类
            if (Object.class.equals(method.getDeclaringClass())){
                return method.invoke(this,args);
            }else {
                //得到所有注解
                String value = method.getAnnotation(Select.class).value();
                System.out.println("接口上面的注解的内容:" + value);
    
                //实现具体的业务逻辑 如远程http调用实现等
                return "user info";
            }
    
        }
    }
    

    代理工厂:

    import java.lang.reflect.Proxy;
    
    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1411:47
     */
    public class InterfaceProxyFactory  {
    
        private  Class<?> target;
        private UserInvocationHandler userInvocationHandler;
        public InterfaceProxyFactory( Class<?> target) {
            this.target = target;
            this.userInvocationHandler = new UserInvocationHandler();
        }
    
        public Object getProxyObject(){
            return Proxy.newProxyInstance(target.getClassLoader(),new  Class[]{target},userInvocationHandler);
        }
    }

    测试类:

    /**
     * @author zhenghao
     * @description:
     * @date 2020/12/1410:46
     */
    public class InterfaceProxyTeasMain {
    
        public static void main(String[] args) {
            IUserDao proxyObject = (IUserDao) new InterfaceProxyFactory(IUserDao.class).getProxyObject();
            System.out.println(proxyObject.getUser());
        }
    }

     

    展开全文
  • Java动态代理实现接口方法

    千次阅读 2015-03-30 11:50:53
    最近正在看Mybatis源码,重点研究了自定义Mapper接口里方法实现如何与xml配置文件进行绑定,最后了解到是通过java动态代理实现了接口方法。 主要使用了Proxy.newProxyInstance(loader, interfaces, ...

    最近正在看Mybatis源码,重点研究了自定义Mapper接口里方法实现如何与xml配置文件进行绑定,最后了解到是通过java动态代理实现了接口方法。

    主要使用了Proxy.newProxyInstance(loader, interfaces, invocationHandler方法来实现实例化,第一个参数为classLoader,第二个参数为要实例化的接口,第三个是InvocationHanler接口,需要实现此接口来实现方法的具体操作。例:

    public void proxyTest() throws ClassNotFoundException,
    			InstantiationException, IllegalAccessException {
    		MethodProxy invocationHandler = new MethodProxy();
    		Object newProxyInstance = Proxy.newProxyInstance(
    				IUserService.class.getClassLoader(),
    				new Class[] { IUserService.class }, invocationHandler);
    		IUserService userService = (IUserService) newProxyInstance;
    		User query = userService.query();
    		System.out.println("query result->" + query.toString());
    	}
    执行结果:



    具体代码实现如下(仅为了说明实现方式,请忽略结构层次和命名规范):

    public class MethodProxy implements InvocationHandler {
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable {
    		// TODO Auto-generated method stub
    		System.out.println("执行前");
    		if (Object.class.equals(method.getDeclaringClass())) {
    			try {
    				return method.invoke(this, args);
    			} catch (Throwable t) {
    				t.printStackTrace();
    			}
    		} else {
    //			System.out.println("调用方法名:"+method.getName());
    			return run(method, args);
    		}
    		System.out.println("执行后");
    		return null;
    	}
    	
    	UserDao dao = null;
    	//具体实现通过方法名绑定。
    	public Object run(Method method,Object[] args){
    		if(dao == null){
    			dao = new UserDao();
    		}
    		Object ret = null;
    		String name = method.getName();
    		if(name.equals("add")){
    			dao.add(args);
    		}else if(name.equals("delete")){
    			dao.delete();
    		}else if(name.equals("query")){
    			ret = dao.query();
    		}
    		return ret;
    	}
    	
    	class UserDao {
    		public void add(Object[] args){
    			System.out.println("dao add");
    			for (Object object : args) {
    				System.out.print(object+",");
    			}
    		}
    		public void delete(){
    			System.out.println("dao delete");
    		}
    		public User query(){
    			return new User("123", "济南", 22, "备注");
    		}
    	}
    }
    

    public interface IUserService {
    
    	public void add(User user);
    	
    	public User query();
    	
    	public void delete(int id);
    
    }

    public class User {
    	private String id;
    	private String username;
    	private int age;
    	private String remarks;
    
    	public User(String id, String username, int age, String remarks) {
    		super();
    		this.id = id;
    		this.username = username;
    		this.age = age;
    		this.remarks = remarks;
    	}
     ……
           @Override
           public String toString() {
               return "User [id=" + id + ", username=" + username + ", age=" + age
                    + ", remarks=" + remarks + "]";
        }
    }
    本demo所展示的是具体接口的实现方法,为保证通用性,则可通过泛型实现接口实例化的通用方法。  (参考mybatis-src)

    例地:

    展开全文
  • 轻松学,Java 中的代理模式及动态代理

    万次阅读 多人点赞 2017-06-29 22:08:55
    前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。...
  • java InvocationHandler invoke方法的第一参数有什么用?java InvocationHandler invoke方法的第一参数有什么用? 想知道它有什么用,就必须的先知道它到底是什么… 那InvocationHandler invoke的第一参数...
  • 动态代理:1 经纪人如何代理 N 明星

    千次阅读 热门讨论 2016-08-27 12:14:07
    在 代理模式:女朋友这么漂亮,你缺经纪人吗? 中我们用宝强的例子介绍了静态代理模式的概念。 本来我的目的是通过大家耳熟能详的例子来加深理解... OK,这篇文章我们将结合其他例子介绍动态代理模式。: ) 回顾静态
  • jdk动态代理和cglib动态代理详解

    千次阅读 多人点赞 2018-08-22 15:14:11
    如何实现一HashMap的动态代理类 cglib动态代理实现 jdk和cglib代理的区别 动态代理和静态代理的区别 spring如何选择jdk和cglib代理 如上图,代理模式可分为动态代理和静态代理,我们比较常用的有动态代理中...
  • 动态代理

    千次阅读 2018-08-30 19:06:38
    1.代理模式深入学习(一)——动态代理的实现及解析 原文链接:https://blog.csdn.net/wangyy130/article/details/48828595 一、代理模式 分为静态和动态代理。静态代理,我们通常都很熟悉。有一写好的代理类...
  • JDK动态代理

    千次阅读 2018-02-11 12:20:04
    JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一借口才能产生一对象,也就是说JDK动态代理是对接口的代理。一般要使用JDK动态代理,首先得定义接口,然后再对这接口的实现类对象进行代理,产生...
  • jdk动态代理invoke方法自动运行原因

    千次阅读 多人点赞 2019-10-17 09:00:20
    想要知道 invoke方法为什么会自动调用我们先要来了解一下这个方法 public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) 首先 该方法来自于接口...
  • CGLIB动态代理

    千次阅读 2018-02-11 15:31:25
    JDK动态代理必须提供接口才可以使用,但是在某些环境下,接口这条件是无法满足的,这时候JDK动态代理就无法使用了,只能采取第三方技术,比如CGLIB动态代理技术。它的最大的优势就是不需要提供接口,只要一非...
  • 什么是动态代理? 当我们需要给某个类或者接口中的方法添加一些额外的功能比如日志、事务的时候,可以通过创建一代理类来实现这些... java动态代理缺点就是要被代理的类必须实现一接口,否则没法代理 2.cgli...
  • 静态代理,JDK动态代理,Cglib动态代理详解

    千次阅读 热门讨论 2021-02-12 13:21:39
    文章目录一、代理模式二、静态代理三、动态代理 一、代理模式 代理模式(Proxy Pattern)是程序设计中的一种设计模式,他的特征是代理类和委托类实现有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、...
  • 关于JDK动态代理方法的调用

    千次阅读 2018-01-18 21:27:23
    这篇文章关于JDK动态代理的演示让我有点迷惑,又重新看了下动态代理 1.首先JDK动态代理是针对接口的, 用提供的被代理对象获得该对象所有实现了的接口,重新生成的一类 针对这,像spring注解事务应该放在接口...
  • 相关文章:静态代理和动态代理的区别和联系 一、动态代理与静态代理的区别。 (1)Proxy类的代码被固定下来,不会...静态代理这模式本身有大问题,如果类方法数量越来越的时候,代理类的代码量是十分庞大的。...
  • JDK 动态代理和 CGLIB 动态代理

    千次阅读 2020-04-12 18:25:13
    我们在阅读一些 Java 框架的源码时,基本上常会看到使用动态代理机制,它可以无感的对既有代码进行方法的增强,使得代码拥有更好的拓展性。 通过从静态代理、JDK 动态代理、CGLIB 动态代理来进行本文的分析。 静态...
  • 实例理解JDK动态代理和Cglib动态代理及其区别 深入理解设计模式之代理模式 代理商代理化妆品生产理解JDK动态代理 代理商代理汽车制造理解Cglib动态代理
  • Java动态代理、cglib动态代理

    千次阅读 2016-02-24 14:17:57
    动态代理,需要先清楚静态代理。所谓静态代理就是程序员提前实现好的代理类,编译后class文件是已经存在的。 实现原理,利用Java代理模式,由一代理类持有委托类的实例,并实现委托类一样的接口,来实现增强...
  • AOP之JDK动态代理和CGLib动态代理

    千次阅读 2015-04-23 14:24:22
    转载请注明出处http://blog.csdn.net/evankaka一、JAVA的动态代理 1.1 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给...
  • Java动态代理的两种实现方法

    万次阅读 2017-09-13 20:29:37
    Java动态代理的两种实现方法 注:文章转载自:http://blog.csdn.net/heyutao007/article/details/49738887 一、代理的概念  动态代理技术是整个java技术中最重要的一技术,它是学习java框架的基础,不会...
  • 动态代理是 AOP(Aspect Orient Programming)编程思想,理解动态代理原理,对学习AOP框架至关重要。 JDK动态代理不需要任何外部依赖,但是只能基于接口进行代理;CGLIB通过继承的方式进行代理,无论目标对象有没有...
  • 一、概念梳理 1、Socket是什么? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一门面模式,它...在业务中使用动态代理,一般是为了给需要实现的方法添加预处理
  • 上篇我们将了一下代理模式的静态代理,然后碰到一代码重复的问题。  回头在来看下我们用代理实现事务的封装,它们都遵循这样的一结构: System.out.println("------获取连接------"); System.out....
  • 秒懂Java代理与动态代理模式

    万次阅读 多人点赞 2018-06-30 17:08:23
    什么是动态代理模式?二者什么关系?具体如何实现?什么原理?如何改进?这即为我们学习一项新知识的正确打开方式,我们接下来会以此展开,让你秒懂。 概念 什么是代理模式 定义:为其他对象提供一种代理以控制...
  • Mybatis使用JDK动态代理来实现Mapper接口,事先保存好Mapper接口,和接口声明的方法,返回值,参数类型,然后代理类的方法调用的时候使用MapperMethod这事先放入方法缓存里的对象来真实调用功能。 笔者极度简化了...
  • Spring的静态代理与动态代理

    万次阅读 多人点赞 2018-06-26 23:34:00
    AOP是可以横向扩展功能的,能够在不修改原来代码的前提下实现功能扩展,AOP的实现原理即是动态代理+反射。 为什么要使用代理 1.什么是代理  代理即是将被代理对象进一步封装后,隐藏被代理对象,在不...
  • 动态代理什么梗

    千次阅读 2018-03-30 17:38:48
    哪些玩的很niu(一声)的spring-dog,竟不不晓得动态代理啥子玩意,那… 代理模式 动态代理 代理设计模式 代理模式是JAVA23种设计模式中的一种。来梗:n年前面试,问我java设计模...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 286,824
精华内容 114,729
热门标签
关键字:

动态代理多个方法