Java静态代理与JDK+cglib动态代理

简介

代理模式是一种十分经典和常用的设计模式,在Java的许多框架中有着十分重要的作用,比如Spring和MyBatis等。本文将从静态代理,JDK动态代理,cglib动态代理三种实现方式进行展示,并观察JDK动态代理的实现方式。

首先来看静态代理

1.首先编写一个Teacher接口,并拥有一个实现类TeacherImpl:

/**
 * 被代理的接口
 */
public interface Teacher {
    public void techStu();
}

/**
 * 代理接口的真实实现类
 */
public class TeacherImpl implements Teacher{

    @Override
    public void techStu() {
        System.out.println("TeacherImpl.techStu()");
    }
}

2.然后编写其代理类:

/**
 * AProxyTeacher的代理类
 * 他继承了Teacher并重写了其方法
 * 使它的行为看起来同Teacher的真正实现类一样
 */
public class AProxyTeacher implements Teacher {

    // 但是实际上其持有了一个私有的Teacher实例,这才是动作的真正执行者
    private Teacher teacherImpl;

    // 该实例可以从外部获得,也可以通过其他方式产生
    public AProxyTeacher(Teacher teacherImpl) {
        this.teacherImpl = teacherImpl;
    }

    // 该实例可以从外部获得,也可以通过其他方式产生
    public AProxyTeacher() {
        this.teacherImpl =new TeacherImpl();
    }

    @Override
    public void techStu() {
        // 在真正操作的执行前后可以进行附加操作
        System.out.println("------AProxyTeacher.techStu()------");
        // 这才是动作的真正执行者
        teacherImpl.techStu();
        System.out.println("------AProxyTeacher.techStu().finished------");
    }

}

3.最后通过一个简单的测试:

public class TestStaticProxy {

    public static void main(String[] args) {
        TeacherImpl teacherImpl = new TeacherImpl();
        AProxyTeacher proxyTeacher = new AProxyTeacher(teacherImpl);
        proxyTeacher.techStu();
        // 为了表示真实的代理对象不一定从外部获得
        AProxyTeacher proxyTeacher2 = new AProxyTeacher();
        proxyTeacher2.techStu();
    }
}

4.显示控制台输出:

------AProxyTeacher.techStu()------
TeacherImpl.techStu()
------AProxyTeacher.techStu().finished------
------AProxyTeacher.techStu()------
TeacherImpl.techStu()
------AProxyTeacher.techStu().finished------

小结:观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,(其实一个代理类也可以为多个接口服务,但是会造成代理类代码过多)。而且,静态代理中每代理一个方法就要去重写一遍该方法,是十分繁琐的。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时使用动态代理完成是很好的。

JDK动态代理

JDK动态代理的基本思路同静态代理相似,只是将重写方法的步骤交由JDK去完成,而我们只需要关注对于真是对象的方法调用环节。
1.首先创建接口和其实现类

/**
 * 接口
 */
public interface Subject {

    public void doSomething();
}

/**
 * 实现类
 */
public class RealSubject implements Subject {

    @Override
    public void doSomething() {
        System.out.println("RealSubject.doSomething()");
    }
}

2.然后编写代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
 * 代理的处理类
 */
public class SubjectProxyHandler implements InvocationHandler {

    /**
     * 被代理的真实对象存放处
     */
    private Object proxied;

    public SubjectProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    // 我们只需要关注对于真实对象的方法调用环节
    @Override
    public Object invoke(Object proxyObj, Method method, Object[] args)
            throws Throwable {
        // 在之前可以做的事情
        System.out.println("SubjectProxyHandler.invoke()--before");
        // 注意此处调用的是proxied(属性)而不是proxyObj(变量)
        Object obj = method.invoke(proxied, args);

        System.out.println("SubjectProxyHandler.invoke()--after");
        //在之后可以做的事情
        return obj;
    }
}

4.然后编写测试类
测试类的关键在于Proxy.newProxyInstance生成了代理类,在这其中需要传入ClassLoader,Interface数组和代理处理对象InvocationHandler,尽管在一些文章中该方法出现的位置不一样,但是这就是JDK动态代理最为关键的一步。(我们在之前已经定义了代理处理调用的方式SubjectProxyHandler.invoke,生成的代理会按照该方式进行处理)

public class TestInterfaceProxy {

    public static void main(String[] args) {
        RealSubject subject = new RealSubject();
        // Proxy.newProxyInstance(ClassLoader,Class<?>[],InvocationHandler)
        Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class<?>[]{Subject.class}, new SubjectProxyHandler(subject));

        System.out.println("------------proxySubject.doSomething------------");
        proxySubject.doSomething();
        System.out.println("------------执行完成------------");
    }
}

测试结果:

------------proxySubject.doSomething------------
SubjectProxyHandler.invoke()--before
RealSubject.doSomething()
SubjectProxyHandler.invoke()--after
------------执行完成------------

小结:实现InvocationHandler接口和调用Proxy.newProxyInstance生成代理是JDK动态代理中的关键步骤。由于调用Proxy.newProxyInstance需要传入接口数组,这就造成了JDK动态代理的局限性,就是只能代理接口而不能代理实体类或者抽象类。

cglib动态代理

cglib动态代理作为JDK动态代理机制的补充,填补了JDK只能代理接口而不能代理实体类或者抽象类的缺陷。实例如下:
1.创建抽象类和实现类

/**
* 抽象类
*/
public abstract class Lession {

    public abstract void teachLession();
}

/**
 * 具体继承类
 */
public class InstanceLession extends Lession{

    @Override
    public void teachLession() {
        System.out.println("InstanceLession.teachLession()");
    }
}

2.创建代理类
创建该代理类需要引入CGLIB相关类,这里由于有Spring中的相同类,就没有再引用,没有的请自行下载cglib.XX.jar

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

/**
 * 代理类
 */
public class InstProxyHandler implements MethodInterceptor{

    private Object proxied;

    public Object getInstance(Object proxied){
        this.proxied=proxied;
        Enhancer enhancer = new Enhancer();
        // 设置其代理的超类
        enhancer.setSuperclass(this.proxied.getClass());
        // 设置其回调对象
        enhancer.setCallback(this);
        // 最关键的一句与jdk模式的.newStance()作用相同
        return enhancer.create();
    }

    @Override
    public Object intercept(Object proxyObj, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        // 可以在invoke之前做一些事情
        System.out.println("--------InstProxyHandler.intercept()--------");

        Object obj = methodProxy.invoke(proxied, args);

        // 可以在invoke之后做一些事情
        System.out.println("--------proxyHandler.finished--------");
        return obj;
    }

}

3.然后编写测试用例

public class TestCglibProxy {

    public static void main(String[] args) {
        InstanceLession insLession = new InstanceLession();
        Lession proxyLession = (Lession) new InstProxyHandler().getInstance(insLession);

        proxyLession.teachLession();
    }
}

4.测试结果:

--------InstProxyHandler.intercept()--------
InstanceLession.teachLession()
--------proxyHandler.finished--------

与JDK动态代理模式相比较,cglib动态代理同样的拿到了一个私有的proxied对象,不同的是其通过Enhancer设置了代理的超类与回调对象,并调用.create创建了代理对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值