静态代理、JDK的动态代理以及Cglib的动态代理

一,问题

最近想看一下Spring的AOP源码,因为它里面涉及到了代理模式,所以就先了解一下我们常用的代理模式。其中包括静态代理、JDK的动态代理和Cglib的动态代理。在这个文章会简要地介绍这三种代理模式,同时会提供相应的实例案例。

二,解决方案

2.1 静态代理

①特点:

代理类的代码,在程序未运行前就已经处理好

②实现:

UserInterface

public interface UserInterface {

    public void eat();

    public void sleep(); 
}

UserDao

public class UserDao implements UserInterface{

    @Override
    public void eat() {
        System.out.println("我在吃饭!!!");
    }

    @Override
    public void sleep() {
        System.out.println("我在睡觉!");
    }
}

UserProxy

public class UserProxy implements UserInterface {

    private UserDao userDao  = new UserDao();

    @Override
    public void eat() {

        System.out.println("我在上班");

        userDao.eat();

        System.out.println("我吃完了");

    }

    @Override
    public void sleep() {

        System.out.println("我在开party");

        userDao.sleep();

        System.out.println("我起床了!");

    }
}

StaticUserMain

public class StaticUserMain {

    public static void main(String[] args){

        UserProxy userProxy = new UserProxy();

        userProxy.eat();

        System.out.println("======================");

        userProxy.sleep();

    }
}

③静态代理的优缺点:

优点:思路简单,实现简单
缺点:在UserInterface接口中增加方法或者删除方法,那么目标对象跟代理对象所对应的类都需要
进行修改

2.2 JDK的动态代理

①特点:

在程序运行期间,JDK会自动帮我们实现同目标对象同样的接口,然后再根据我们在代理工厂中配置的
规则,对指定的方法进行增强。

②实现:

PersonInterface

public interface PersonInterface {

    public void work();

    public void rest();

}

ExtraInterface

public interface ExtraInterface {
    public void extraMethod();
}

PersonDao

public class PersonDao implements PersonInterface ,ExtraInterface{

    @Override
    public void work() {
        System.out.println("我在工作呢,别打扰我!");
    }

    @Override
    public void rest() {
        System.out.println("我在休息呢,请打扰我!");
    }

    @Override
    public void extraMethod() {
        System.out.println("这是额外的方法!");
    }
}

PersonProxyFactory

public class PersonProxyFactory {

    //接收目标对象
    private Object target;

    PersonProxyFactory(Object target){
        this.target = target;
    }

    //根据目标对象生成代理对象
    public Object getProxyInstance(){

        Object proxy = Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {       //当代理对象执行方法的时候会调用
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //判断需要增强的方法,然后进行方法增强操作
                        String methodName = method.getName();
                        //方法执行的结果
                        Object result  = null;
                        if ("work".equals(methodName)){
                            System.out.println("我在搭地铁上班呢");
                            method.invoke(target,args);
                            System.out.println("我下班了!");
                        }

                        if ("rest".equals(methodName)){
                            System.out.println("还有23小时就休息了!");
                            method.invoke(target,args);
                            System.out.println("不用上班的日子真难受,好想加班!");
                        }

                        if ("extraMethod".equals(methodName)){
                            method.invoke(target,args);

                            System.out.println("这个方法竟然会被执行?");
                        }

                        //代理对象执行对应方法的计算结果
                        return result;
                    }
                });
            return proxy;
    }



}

JDKDynamicProxyMain

public class JDKDynamicProxyMain {

    public static void main(String[] args){

        PersonDao personDao = new PersonDao();

        System.out.println("目标对象的HashCode:"+ Objects.hashCode(personDao.getClass()));

        //这个地方不能强转成目标对象,也不能用目标对象来接收。否则会报错java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to DynamicProxy.JDKDynamicProxy
        //应该强转成目标对象实现的接口类,以及用接口类来接收
        //个人估计原因,未看源码:因为这里返回的是代理对象,而代理对象跟目标对象并不是同一个对象,所以如果我们把它强转成目标对象的话,那么在进行方法调用的时候,它就会抛出异常
        PersonInterface proxyObject = (PersonInterface) new PersonProxyFactory(personDao).getProxyInstance();

        System.out.println("代理对象的HashCode:"+ Objects.hashCode(proxyObject.getClass()));

        proxyObject.work();

        System.out.println("=================================");

        proxyObject.rest();

        System.out.println("==================================");

        //将JDK自动生成的代理对象的class文件保存到D盘,然后我们反编译查看它的内容
        String path = "D:/$Proxy0.class";

        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", PersonDao.class.getInterfaces());

        FileOutputStream out;

        try {
            out = new FileOutputStream(path);
            out.write(classFile);
            out.flush();
        }catch (Exception e) {
            e.printStackTrace();
        }

    }


}

③JDK动态代理的优缺点:

优点:自动帮我们实现跟目标对象一样的接口,同时对该对象的方法进行增强
缺点:目标对象必须实现接口

2.3 Cglib的动态代理

①特点:

在程序运行时,动态生成子类去继承目标对象,实现增强目标对象的方法

②实现

导入依赖:

<dependency>
   <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
	<groupId>org.ow2.asm</groupId>
	<artifactId>asm</artifactId>
	<version>5.0.3</version>
</dependency>

User

public  class User {
    public String work(){
        System.out.println("我在工作!");
        return "123";
    }
}

UserProxy

public class UserProxy implements MethodInterceptor {

    public static <T> T newInstance(Class<? extends User> clazz) {
        //创建一个生成动态代理的对象
        Enhancer e = new Enhancer();
        //设置动态代理对象的父类
        e.setSuperclass(clazz);
        //设置动态代理对象的回调对象
        e.setCallback(new UserProxy());
        //返回动态代理对象
        return (T) e.create(); // Generate a class object
    }

    /**
     * sub:cglib生成的代理对象
     * method:被代理对象方法
     * objects:方法入参
     * methodProxy: 代理方法
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

        String methodName = method.getName();

        if ("work".equals(methodName)){

            System.out.println("我在上班!");

            String  str = (String)proxy.invokeSuper(obj, args);

            System.out.println("原方法执行的结果为:"+str);

            System.out.println("我在做小程序开发!");

        }

        //返回的结果
        return "111";
    }
}

UserProxyMain

public class UserProxyMain {
    public static void main(String[] args) {
        User user = new User();
        User userProxy = UserProxy.newInstance(user.getClass());
        System.out.println(userProxy.work());
        
    }
}

③Cglib动态代理的优缺点:

优点:对于可以继承的类都可以实现方法增强。
缺点:目标类不能被final修饰

2.4 小总结

对于静态代理,它的实现是挺简单的。但是问题就在于,目标类和代理类它们都实现了同一个接口,那么当这个接口添加或删除了某个方法的时候,目标类和代理类的代码都需要修改。麻烦指数2颗星。

对于JDK的动态代理,我觉得它是基于静态代理来进行优化的一种方法,其实经过反编译之后,我们就会发现JDK自动帮我们生成的代理类其实也是实现了跟目标类一样的接口。但是这种方式跟静态代理相比就没这么麻烦了,起码到时候,我们如果要在接口上增加或删除某个方法,那可以只修改目标类的代码。另外,那个自动生成的代理类在调用目标类的方法时,是根据反射的机制去实现的。麻烦指数1颗星。

对于Cglib的动态代理,我觉得它是基于JDK动态代理来进行优化的一种方法,因为JDK的动态代理,一定要目标类实现接口,那对于没有实现接口的目标类,就没用了。所以Cglib使用的方式是继承目标类,Cglib的代理范围要比JDK的方式更广一些。它会在程序运行期间,自动帮我们创建一个代理类,让代理类去继承这个目标类。同时,在代理类调用目标类的方法时,它也是通过反射机制去实现的。麻烦指数1颗星。

对于上述的总结,可能会存在一些小偏差,但是时间有限,后面再不断学习,不断完善。有问题,欢迎大家指出。谢谢!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值