• Java的动态代理


     为什么JDK代理只能对接口实现代理?

    1、动态代理的几种方式 

     Java主要有两种代理,JDK和Cglib动态代理。先看JDK代理实例如下:

    JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。

    JDK创建代理有一个限制,即它只能为接口创建代理实例。举个例子如下:

    public interface Advice {
    	void beforeMethod();
    	void afterMethod();
    }
    
    public class TimeAdvice implements Advice {
    	long startTime;
    	long endTime;
    
    	public void beforeMethod() {
    		startTime = System.nanoTime(); // 获取开始时间
    		System.out.println("开始计算程序运行时间" + startTime);
    	}
    
    	public void afterMethod() {
    		endTime = System.nanoTime(); // 获取结束时间
    		System.out.println("计算程序运行时间: " + (endTime - startTime) + "ns");
    	}
    }
    
    public interface SalaryInterface {  
        public void doSalary();  
    }
    public class Salary  implements SalaryInterface{  
        public void doSalary() {  
              System.out.println("进行薪资计算的逻辑处理");  
          }  
    }
    

      

    import java.lang.reflect.InvocationHandler;  
    import java.lang.reflect.Method;  
    import java.lang.reflect.Proxy;  
    /* 
     * 每一个代理实例都必须指定一个调用处理器,代理对象调用方法时, 
     * 该方法会指派到调用处理器的invoke()中去。代理的方法封装成 
     * invoke中的method对象,其中的参数封装成Object[]. 
     */  
    public class MyProxy implements InvocationHandler{  
    	
         private Object obj;   // 希望被代理的对象  
         private Advice advice;  
         
        // 绑定代理对象  
        public Object bind(Object obj, Advice advice) {  
            this.obj = obj;  
            this.advice = advice;  
            return Proxy.newProxyInstance(  
                    obj.getClass().getClassLoader(), // 类加载器    
                    obj.getClass().getInterfaces(),  // 创建目标类所需要使用的一组接口  
                    this  // 一个实现InvocationHandler的实例,用来整合横切与业务逻辑  
                );  
            }  
      
        /* 
         * 实现代理
         * method为方法名,args为代理实例某一方法的入参数组,而obj为所属的实例对象 
         */  
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
            Object result = null;  
            try {  
                advice.beforeMethod();  
                result = method.invoke(obj, args);   
                advice.afterMethod();  
            } catch (Exception e){  
                e.printStackTrace();  
            }  
            return result;  
        }  
    }  
    
    public class Bootstrap {
    
    	public static void main(String[] args) {
    		
            Advice advice = new TimeAdvice();   
            SalaryInterface p = new Salary();  
            // Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象  
            MyProxy proxy = new MyProxy();  
              
            SalaryInterface y = (SalaryInterface)proxy.bind(p, advice);  
            y.doSalary(); // 相当于调用proxy.invoke(proxy, "doSalary, null);  
    	}
    }
    
    开始计算程序运行时间43041762316001
    进行薪资计算的逻辑处理
    计算程序运行时间: 882610ns
    

      

    CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。  

    CGGlib创建的代理对象要比JDK的性能高很多,但是创建时所花费的时间却比JDK动态代理要多。所以对于singleton的代理对象或者具有实例池的代码,由于无须频繁创建代码对象,用CGLib比较合适。也就是生命周期长的实例用CGLib比较合适。并且无法对final方法进行代理

    /** 
     * 使用cglib动态代理 
     */  
    public class Cglib implements MethodInterceptor {  
        private Object target;  
        long startTime;
       	long endTime;
      
        /** 
         * 创建代理对象 
         */  
        public Object getInstance(Object target) {  
            this.target = target;  
            Enhancer enhancer = new Enhancer();  
            enhancer.setSuperclass(this.target.getClass());  // 设置需要创建子类的类 
            enhancer.setCallback(this);   // 回调方法   
            return enhancer.create();     // 通过字节码技术动态创建子类实例   
        }  
      
        @Override  
        // 回调方法 ,拦截所有的父类方法调用 
        public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {  
        	startTime = System.nanoTime(); // 获取开始时间
    		System.out.println("开始计算程序运行时间" + startTime);
            Object result = proxy.invokeSuper(obj, args);   // 通过代码类调用父类中的方法
            endTime = System.nanoTime();   // 获取结束时间
    		System.out.println("计算程序运行时间: " + (endTime - startTime) + "ns");
            return result;  
        }  
       
    }  
    

      

    /*
     * 采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截
     * 所有父类方法的调用,并顺势织入横切逻辑
     * 
     * singletom的代理对象或者具有实例池的代理,因为无须频繁创建代理对象,比较适合用CGLib动态代理技术
     * 
     * 由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行代理
     */
    public class TestCglib {
    	   
        public static void main(String[] args) {  
            Cglib cglib=new Cglib();  
            Salary salary=(Salary)cglib.getInstance(new Salary());  
            salary.doSalary();  
        }  
    }
    
  • 相关阅读:
    Convert CString to std::string
    VC 使用预编译头
    [转]Windows下使用doxygen阅读和分析C/C++代码
    [SCOI2016]背单词
    Linux配置日志服务器
    网络学习day02_OSI七层模型及数据的传输过程
    网络学习day04_VLSM、子网划分
    XSS闯关游戏准备阶段及XSS构造方法
    网络学习day03_IP地址概述与应用
    网络学习day01_计算机网络与分层思想
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/9633014.html
Copyright © 2020-2023  润新知