JAVA动态代理技术

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA动态代理技术相关的知识,希望对你有一定的参考价值。

JAVA代理

JAVA代理技术是JAVA核心技术之一,也是JAVA core中非常重要的一部分,对于学习Spring等JAVA生态圈的学习起着非常重要的作用,比如说AOP,cglib。动态代理技术就是产生对象的代理对象的。举例现实场景就是:一个明星在出名之前可能没有很多商演找他,所以商家可以直接联系明星本人进行商谈,但随着明星知名度越来越高,商演越来越多,那么这个时候明星本人就无法处理过多的商谈,那么这个时候就需要一个角色经纪人(明星代理人),而明星只要做商演即可。说道这里是不是代理的作用就出来了:拦截直接访问真实业务对象(被代理对象)。

代理分类

根据加载被代理类的时机不同,将代理分为静态代理和动态代理。
1.静态代理
如果在编译时就确定被代理的类,那么就可以直接使用静态代理。

2.动态代理

如果编译时不能确定,那么使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制。

静态代理实现

1.ITask接口

/**
 * @ClassName ITask
 * @Description TODO:描述该接口职责
 * @Author ckmike
 * @Date 18-12-5 下午5:15
 * @Version 1.0
 * @Copyright ckmike
 **/
public interface ITask {

    void writelog(String msg);
}

2.PersonTask类

public class PeopleTask implements ITask {

    private String username;
    private String age;

    public PeopleTask() {
    }

    @Override
    public void writelog(String msg) {
        System.out.println("Hello! My name is"+username+",age is "+age);
    }

    public PeopleTask(String username, String age) {
        this.username = username;
        this.age = age;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public String getAge() {
        return age;
    }
}

3.TaskProxy代理类

/**
 * TaskProxy 简要描述
 * <p> TODO:描述该类职责 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-5 下午5:20
 * @copyright ckmike
 **/
public class TaskProxy implements ITask {

    private ITask tasker;

    public TaskProxy(ITask tasker) {
        this.tasker = tasker;
    }

    @Override
    public void writelog(String msg) {
        tasker.writelog(msg);
    }

    public void writelog(String msg,String singname){
        tasker.writelog(msg);
        sing(singname);
    }

    private void sing(String singname) {
        System.out.println("唱歌:"+singname);
    }
}

4.测试

TaskProxy taskProxy = new TaskProxy(new PeopleTask("Mike","18"));
taskProxy.writelog("静态代理测试...","莫妮卡");

这种静态代理的方式,非常适合做增强老旧复杂代码。比如说一个复杂的业务代码已经写完了,经过很久后有一天需要对业务方法进行扩展,那么这个时候不管出于风险还是出于业务复杂性代码可读性考虑都不适合去动原本已经稳定的代码,这个时候进行代码增强进行代理就非常合适。其实这个模型就是Spring AOP的雏形,你看我调用writelog方法在执行了增强方法sing方法,是不是有点像@After?

实现动态代理

JAVA中是通过java.lang.reflect.Proxy中的newProxyInstance方法创建一个代理对象。

1.StarAction接口

/**
 * @ClassName IStarAction
 * @Description TODO:描述该接口职责
 * @Author ckmike
 * @Date 18-12-7 下午5:46
 * @Version 1.0
 * @Copyright ckmike
 **/
public interface IStarAction {

    // 歌唱
    void sing(String name);

    // 跳舞
    void dance(String name);

    // 拍电影
    void movie(String name);

}

2.Star类

/**
 * Star 简要描述
 * <p> TODO:描述该类职责 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-7 下午5:51
 * @copyright ckmike
 **/
public class Star implements IStarAction {

    private String name;

    public Star(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public void sing(String name) {
        System.out.println(this.name+"演唱歌曲:"+ name);
    }

    @Override
    public void dance(String name) {
        System.out.println(this.name+"表演舞蹈:"+ name);
    }

    @Override
    public void movie(String name) {
        System.out.println(this.name+"参演电影:"+ name);
    }
}

3.StarProxy类

/**
 * StarProxy 简要描述
 * <p> TODO:描述该类职责 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-7 下午5:58
 * @copyright ckmike
 **/
public class StarProxy implements InvocationHandler {

    // 代理目标对象
    private Star target;

    public Object bind(Star target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result=null;
        if(method.getName().equals("sing")){
            System.out.println("邀请"+target.getName()+"演唱歌曲,每首报价60w!");
            return method.invoke(target,args);
        }else if(method.getName().equals("dance")){
            System.out.println("邀请"+target.getName()+"演唱跳舞,每场报价200w!");
            return method.invoke(target,args);
        }else if(method.getName().equals("movie")){
            System.out.println("邀请"+target.getName()+"出演电影,每部报价8000w!");
            return method.invoke(target,args);
        }
        return result;
    }

}

4.测试用例

/**
 * Test 简要描述
 * <p> TODO:描述该类职责 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-7 下午6:15
 * @copyright ckmike
 **/
public class Test {
    public static void main(String[] args) {

        // 这里特别要注意,只能转为接口,因为代理返回的就是接口的代理对象,无法转为Star对象
        IStarAction star = (IStarAction) new StarProxy().bind(new Star("刘德华"));
        star.sing("冰雨");
        star.dance("MoonWalk");
        star.movie("旺角");
    }
}

技术分享图片

总结:
所有的对被代理对象接口的访问都会执行invoke方法,那么也就是通过代理对象拦截了对被代理对象接口的访问,这个代理对象类似于拦截器。根据上面的静态和动态代理,我们发现一个问题,JAVA 的API代理要求每个被代理类必须实现某个接口,这也是JAVA面向接口编程的一种表现,但是如果我们某个类没有实现任何接口,那么我们还能实现它的代理吗?答案是肯定的,cglib就是一个非常成熟流行的代表。接下来我们就来讲讲cglib代理。

cglib代理

cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理,也无法代理final修饰的成员方法。


/**
 * CglibProxy 简要描述
 * <p> TODO:描述该类职责 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-5 下午5:40
 * @copyright ckmike
 **/
public class CglibProxy implements MethodInterceptor {
    private Object target;

    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 o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result=null;
        System.out.println("播放电影院广告...");
        result = methodProxy.invokeSuper(o,objects);
        System.out.println("播放电影类似新电影广告...");
        return result;
    }

}

public class Movie {

    public void play(String name){
        System.out.println("播放电影:"+name);
    }

        public final void show(String info){
        System.out.println(info);
    }

}

public class Test {

    public static void main(String[] args) {
        // cglib代理
        Movie movie = (Movie) new CglibProxy().getInstance(new Movie());
        movie.play("十面埋伏");
        movie.show("hello,大家好!");
    }
}

技术分享图片

JAVA动态代理应用场景

上面我们以及了解了JAVA静态代理,JAVA动态代理以及cglib的动态代理。那么我们通常什么时候会使用代理技术呢?什么场景上会使用呢?
1.设计模式中对关闭开放原则,如果要在原来稳定业务代码中进行扩展或者增强,对于进入原功能方法进行修改是不应许的,所以这个时候采用代理技术就可以非常方便的实现这一原则。
2.Spring中的AOP面向切面编程就是使用的动态代理技术实现的。

总结

1.静态代理、动态代理都是需要通过接口来实现,面向接口编程的方式。
2.cglib可以代理未实现任何接口的类的代理,但是该类不能是final类,并且final方法无法被代理。
3.Spring中AOP技术就是通过动态代理实现的。

以上是关于JAVA动态代理技术的主要内容,如果未能解决你的问题,请参考以下文章

Java动态代理学习Spring AOP基础之一

java动态代理技术

动态代理

JDK动态代理与CGLib动态代理

java中的动态代理机制

学习Spring必学的Java基础知识----动态代理