zoukankan      html  css  js  c++  java
  • jdk动态代理源码分析(一)---代理的定义

    最近在看rpc的实现原理,发现大部分通用的rpc框架在实现远程调用的时候,都是通过java动态代理封装好了通信细节,让用户可以像调用本地服务一样调用远程服务。但是关于java动态代理有两个问题想不通:jdk动态代理中的invoke方法是如何被自动调用的?jdk动态代理为什么只针对实现了接口的类?带着这两个问题,我仔细读了下源码。
    代理的基本定义
    代理模式定义:给某个原对象提供一个接口实现类(代理类),客户端不直接访问这个对象,而是通过代理类来间接访问。
    代理模式涉及到了四个对象:
    Client:服务调用方,通过代理类调用真实服务(serviceImpl)。
    IService:功能接口类,类中包含了功能点的抽象。是serviceImpl 和 Proxy都要去实现的接口。
    ServiceImpl:功能接口的具体实现类。
    Proxy:代理类。其中包含了对功能时间类(ServiceImpl)的引用,从而可以操作真实服务接口。并且可以在调用真实服务处的前后进行一系列其他操作。
    对象之间关系为:
     
     
    代理的分类与使用
       java代理主要分为静态代理和动态代理两类。
      1、静态代理:所谓静态是指,在程序运行前已经写好并编译成了.class文件,而不是动态产生的。
       静态代理使用方式为手动实现一个继承了IService接口类的代理类Proxy。并在Proxy代理类中手动引用ServiceImpl 功能接口的具体实现类。
      代码:
      
    package com.xiaosong.proxy.demo.service;
    
    /**
     * Hello world!
     *
     */
    public interface IHello {
    	
    	public void sayHello();
    	
    }
    

      

    package com.xiaosong.proxy.demo.service.impl;
    
    import com.xiaosong.proxy.demo.service.IHello;
    
    public class Hello implements IHello {
    
    	public void sayHello() {
    		// TODO Auto-generated method stub
    		System.out.println("Hello KuGou!");
    	}
    }
    

      

    package com.xiaosong.proxy.demo.jdkproxy;
    
    import com.xiaosong.proxy.demo.service.IHello;
    import com.xiaosong.proxy.demo.service.impl.Hello;
    
    public class StaticProxy implements IHello{
    
    	public Hello hello;
    	public StaticProxy (Hello hello){
    		this.hello=hello;
    	}
    	
    	public void sayHello() {
    		
    		System.out.println("before Hello...");
    		hello.sayHello();
    		System.out.println("after Hello...");
    	
    	}
    
    }
    

      

      2、动态代理:所谓动态是指,不在代码编译期确定被加载的类,而是在代码运行期通过反射的方式来加载。这就是和静态编译最根本的区别。
           动态代理的使用方式分为3步:
          (1)写一个 InvocationHandler接口的实现类实现invoke方法,并在这个实现类中引用一个Object对象(该对象在运行期可能被赋值为各种各样的ServiceImpl 接口实现类对 
            象),在invoke方法通过反射调用Object对象中的方法。
          (2)调用Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this) 方法,object为1中实现类的对象,获取一个代理类Proxy。
          (3)将(2)中获取到的Object对象强转为IService类,并调用IService中的方法,就可以实现对ServiceImpl 的调用了。
        
    package com.xiaosong.proxy.demo.jdkproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class MyInvocationHandler implements InvocationHandler {
    
    	//代理目标对象
    	public Object object;
    	
    	public MyInvocationHandler(Object object) {
    		// TODO Auto-generated constructor stub
    		this.object=object;
    	}
    	
    	//获取代理对象工具方法
    	public Object getProxyObject(){
    
    		//object.getClass().getClassLoader()确保类加载器为代理目标的加载器
    		return Proxy.newProxyInstance(object.getClass().getClassLoader(), 
    									  object.getClass().getInterfaces(), 
    									  this);
    	}
    	
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		
    		System.out.println("前置方法...");
    		
    		//这里入参要是object,不能是proxy,否则造成死循环,后续会详细分析原因
    		Object rs = method.invoke(object, args);
    		
    		System.out.println("后置方法...");
    		
    		return rs;
    	}
    
    }
    

      

    相对于静态代理,动态代理有以下几个优点:
    1、动态代理的代码量不会随着接口的增加而无限制的扩大。
    2、由于代理类是固定通用的,所以动态代理可以有更多的用途,比如spring AOP,比如RPC远程调用。
     
    缺点是什么呢?
    上述的静态代理和动态代理的实现都是依赖接口的,如果需要代理一个没有实现接口的方法,JDK静态代理就无计可施了。那么问题来了,为什么JDK动态代理必须是依赖接口呢?我们继续往下看。
     
  • 相关阅读:
    【ASP.Net MVC】在AspNet Mvc使用JQuery AutoComplete组件
    Jquery AutoComplete的使用方法实例
    .Net使用Redis详解之ServiceStack.Redis(七)
    Redis系列之key操作命令与Redis中的事务详解(六)
    Redis数据结构详解之Zset(五)
    redis数据结构详解之Hash(四)
    Redis数据结构详解之Set(三)
    Redis数据结构详解之List(二)
    Redis数据结构详解(一)
    WCF配置文件详解(一)
  • 原文地址:https://www.cnblogs.com/penglaihaibiandexiaoxiami/p/8312887.html
Copyright © 2011-2022 走看看