Java动态代理

Java动态代理

Java有三个动态特性:

  • 反射
  • 注解
  • 动态代理

本文主要介绍动态代理的知识。


代理是什么

代理背后一般至少有一个实际对象,代理的外部功能和实际对象一般是一样的,用户与代理打交道,不直接接触实际对象,虽然外部功能和实际对象一样,但代理有它存在的价值:
-节省成本比较高的实际对象的创建开销,按需延迟加载,创建代理时并不真正创建实际对象,而只是保存实际对象的地址,在需要时再加载或创建
- 执行权限检查,代理检查权限后,再调用实际对象
- 屏蔽网络差异和复杂性,代理在本地,而实际对象在其他服务器上,调用本地代理时,本地代理请求其他服务器

动态代理是什么

首先我们来看看动态代理的定义。
动态代理:

动态代理可以在运行时动态地创建一个类,这个类实现一个或多个接口,在不修改原有类的基础上动态地为该类获取的对象添加方法,修改行为。


动态代理应用

为什么要介绍动态代理呢?因为动态代理是面向切面编程(AOP)的基础。是不是很耳熟,没错,相信你在学习Spring的时候已经接触到了Spring AOP的概念了。

什么是Spring AOP呢?

在Java编程中,有很多切面如日志,权限,数据库事务等在程序中都会用到,代码都差不多,但是与具体的业务代码关系不大。
如果在每个地方都写这些代码会显得很冗余,难以维护。
Spring AOP将切面与主体逻辑的业务代码分离。

Spring AOP就是动态代理的应用之一,实际上动态代理广泛应用于各种系统程序、框架和库中,如Spring,Hibernate,Mybatis,Guice等。


动态代理实现的两种方式

动态代理有两种实现方式,一种是Java SDK提供的,另外一种是第三方库如cglib提供的。
介绍这两种方式之前先介绍下静态代理。

静态代理

静态代理代码结构比较简单,需要有三个要素:

  1. 共同的接口:定义方法不提供实现,供外部调用;
  2. 实现类:实现上述接口,提供上述方法的具体实现;
  3. 代理类:注入的是实现类,调用接口的方法实际是调用实现类的方法的实现。

下面看代码实现:

package com.wgs.静态代理;

/**
 * 静态代理
 * @author GenshenWang.nomico
 * @time 2017/05/28
 *
 */
public class StaticProxyDemo {

    //公共接口,没有任何实现,必须.
    static interface CommonService{
        public void sayHello();
    } 

    //公共接口的具体实现
    static class RealService implements CommonService{
        @Override
        public void sayHello() {
            System.out.println("hello,静态代理!");
        }

    }

    //产生一个代理类,此类必须实现公共接口,作用:具有sayHello功能的类
    static class StaticProxy implements CommonService{

        private CommonService realService;

        //通过构造器注入接口,实际注入接口的是RealService
        public StaticProxy(CommonService service){
            this.realService = service;
        }

        //此处模拟的即为AOP编程思想。sayHello()方法实现不变,只需在其上下加上一些诸如日志事务等代码,
        //无需在sayHello()中加上与业务代码无关的代码
        @Override
        public void sayHello() {
            System.out.println("模拟日志输出1.....");
            realService.sayHello();
            System.out.println("模拟日志输出2.....");
        }

    }

    //测试类
    public static void main(String[] args) {
        CommonService service = new RealService();
        CommonService proxy = new StaticProxy(service);
        proxy.sayHello();

    }

}

输出:

模拟日志输出1.....
hello,静态代理!
模拟日志输出2.....

在上述例子中我们可以看到,在不修改sayHello()方法源码的情况下即可在其上下加上调试语句,这个过程是通过代理类实现的。

为什么叫静态代理呢?
可以看到我们在程序中固定的为CommonServiceA接口和RealServiceA实现类造出了一个代理类StaticProxy。
缺点就是我们如果再想为CommonServiceB类,CommonServiceC类…实现代理类,必须得另建对应代理类,就会显得很麻烦,这时候就需要动态代理去实现它了。

动态代理实现方式一—Java SDK动态代理

在静态代理中,代理类Proxy是直接定义在代码中的,而在动态代理中,代理是动态生成的,代码实现如下:
1. 首先需要一个公共接口:

package com.wgs.动态代理;

public interface CommonService {

    void sayHello();

}

2. 公共接口的实现类,是被代理的实际对象:

package com.wgs.动态代理;

public class CommonServiceImpl implements CommonService {

    @Override
    public void sayHello() {
        System.out.println("hello 动态代理!");
    }

}

3. 动态产生代理类:

package com.wgs.动态代理;

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

/**
 * 动态代理类作用: 为一个真实对象产生一个类。
 * 这个类具有真实对象的某个或者所有的功能
 * 每一个动态代理类都要实现一个InvocationHandler
 * @author GenshenWang.nomico
 * @time 2017/05/28
 */
public class DynamicProxyHanlder implements InvocationHandler{

    //要代理的真实对象
    private Object obj;

    //注入的是实际对象
    public DynamicProxyHanlder(Object realObj){
        this.obj = realObj;
    }

    /**
     * @proxy: Object proxy没有太大作用
     * @method: 想要调用的方法
     * @args:传入的参数值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("模拟日志输出1.....");
        //实际执行的业务代码,可能有返回值.注入实际接口与参数值
        method.invoke(obj, args);
        System.out.println("模拟日志输出2.....");
        return null;
    }






}

4. 产生动态代理对象并测试Proxy.newProxyInstance:

package com.wgs.动态代理;

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

/**
 * 测试类
 * @author GenshenWang.nomico
 * @time 2017/05/28
 *
 */
public class Client {

    public static void main(String[] args) {
        //代理的真实对象
        CommonService realService = new CommonServiceImpl();
        //产生一个代理类
        InvocationHandler proxyHandler = new DynamicProxyHanlder(realService);

        //产生一个代理对象
        CommonService proxyService = 
                (CommonService) Proxy.newProxyInstance(
                        CommonService.class.getClassLoader(),
                        new Class<?>[]{CommonService.class}, 
                        proxyHandler);

        proxyService.sayHello();


    }

}

这里有两个地方要详细解释下。
1. InvocationHandler
第一个就是生成动态代理类的时候类需要实现InvocationHandler,它的构造方法接受一个参数realObj表示被代理的对象,
invoke方法处理所有的接口调用,它有三个参数:

  • proxy:表示代理对象本身,需要注意,它不是被代理的对象,这个参数一般用处不大
  • method:表示正在被调用的方法
  • args :表示方法的参数

2. Proxy.newProxyInstance
使用java.lang.reflect包中的Proxy类的静态方法newProxyInstance来创建代理对象,方法定义如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
三个参数:

loader:表示类加载器
interfaces:表示代理类要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类,例子中只有一个IService
h:h的类型为InvocationHandler,它是一个接口,也定义在java.lang.reflect包中,它只定义了一个方法invoke,对代理接口所有方法的调用都会转给该方法

注:newProxyInstance的返回值类型为Object,可以强制转换为interfaces数组中的某个接口类型,这里我们强制转换为了IService类型,需要注意的是,它不能强制转换为某个类类型,比如RealService,即使它实际代理的对象类型为RealService。

动态代理实现方式二—cglib动态代理

Java SDK动态代理的局限在于:

它只能为接口创建代理,返回的代理对象也只能转换到某个接口类型,如果一个类没有接口,或者希望代理非接口中定义的方法,那就没有办法了。

有一个第三方的类库cglib(https://github.com/cglib/cglib)可以做到这一点,Spring,Hibernate等都使用该类库。

cglib:
cglib实现机制与Java SDK不同,它是通过继承实现的。

cglib动态地创建了一个类,但这个类的父类是被代理的类,代理类重写了父类所有public非final得方法,改为调用callback中的相关方法。

代码实现:

package com.wgs.动态代理;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 动态代理实现方式二---cglib
 * @author GenshenWang.nomico
 * @time 2017/5/30
 *
 */
public class CGLibDemo {

    /**
     * 被代理的类,不需要实现接口
     * @author GenshenWang.nomico
     *
     */
    static class RealService{
        public void sayHello(){
            System.out.println("hello,cglib");
        }
    }


    static class SimpleInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object object, Method method,
                Object[] args, MethodProxy proxy) throws Throwable {

            System.out.println("模拟日志输出1.。。。");
            Object result = proxy.invokeSuper(object, args);
            System.out.println("模拟日志输出2.。。。");

            return result;
        }

    }

    /**
     * 为一个类生产代理对象
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    private static <T> T getProxy(Class<T> clazz){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new SimpleInterceptor());

        return (T)enhancer.create();
    }

    /**
     * 测试
     */
     public static void main(String[] args) throws Exception {
         RealService proxyService = getProxy(RealService.class);
         proxyService.sayHello();
     }


}

RealService:被代理的类,没有接口。
getProxy() : 为一个类(RealService)生成代理对象;
cglib的Enhancer类:
setSuperclass设置被代理的类;
setCallback设置被代理类的public非final方法被调用时的处理类

Enhancer支持多种类型,这里使用的类实现了MethodInterceptor接口,它与Java SDK中的InvocationHandler有点类似,方法名称变成了intercept,多了一个MethodProxy类型的参数。

与前面的InvocationHandler不同,SimpleInterceptor中没有被代理的对象,它通过MethodProxy的invokeSuper方法调用被代理类的方法:
Object result = proxy.invokeSuper(object, args);


Java SDK代理与cglib代理比较

Java SDK代理:面向的是一组接口,
它为这些接口动态创建了一个实现类,接口的具体实现逻辑是通过自定义的InvocationHandler实现的,这个实现是自定义的,也就是说,其背后都不一定有真正被代理的对象,也可能多个实际对象,根据情况动态选择。
cglib代理:面向的是一个具体的类,它动态创建了一个新类,继承了该类,重写了其方法。


参考:http://www.cnblogs.com/swiftma/p/6869790.html

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值