静态代理和动态代理

1、代理模式

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能

这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法

在这里插入图片描述

2、静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类

下面举个案例来解释:

模拟修改动作,定义一个修改动作的接口:IPersonDao.java,然后目标对象实现这个接口的方法PersonDao.java,此时如果使用静态代理方式,就需要在代理对象(PersonDaoProxy.java)中也实现IPersonDao接口,调用的时候通过调用代理对象的方法来调用目标对象

需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法

代码示例:

接口:Person.java

public interface Person {
    void update();
}

被代理对象:PersonDao.java

public class PersonDao implements Person {
    @Override
    public void update() {
        System.out.println("修改个人信息");
    }
}

代理对象:PersonDaoProxy.java

public class PersonDaoProxy implements Person {
    private Person person;
    public PersonDaoProxy(Person person){
        this.person=person;
    }
    @Override
    public void update(){
        System.out.println("修改个人信息前记录日志");
        this.person.update();
        System.out.println("修改个人信息后记录日志");
    }
}

测试代码:

class Test{
    public static void main(String[] args) {
        PersonDao personDao = new PersonDao();
        PersonDaoProxy personDaoProxy = new PersonDaoProxy(personDao);
        personDaoProxy.update();
    }
}

输出为:

修改个人信息前记录日志
修改个人信息
修改个人信息后记录日志

静态代理总结:
1、优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展
2、缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护.。

如何解决静态代理中的缺点呢?答案是可以使用动态代理方式。

3、动态代理

静态代理有个很大的缺陷,就是代理类需要自己去写,假如实际项目中用到的类跟我们这里测试的一样的简单就好了,那自己写就自己写吧!然而实际中一个类中的方法可能有几十个几百个,来,你去试试写个代理类。。。简直坑爹,而且写的代码还都差不多,这就意味着又要为另外一个类写代理的时候再重复写一遍,简直太糟糕了!

为了弥补这个缺陷,一些大佬就设计出了可以自动生成代理类的手段,这就很舒服了,这个手段是比较厉害的,但是有点儿不好理解,要仔细想想!而动态代理有两种方式,JDK动态代理和CGLib动态代理,下面说的是JDK动态代理。。。。

首先JDK动态代理就不止有代理类和目标类了,还有一个中间类,这个中间类有什么用呢?我们可以画个图看看:
在这里插入图片描述

上图可以简单的知道调用代理类中的所有方法实际上都是调用中间类的invoke方法,而在invoke方法中才是真正去调用对应的目标类的目标方法

在这里插入图片描述

如上图所示,代理对象调用方法时,JVM将方法导向invole()方法,在invoke()方法里面可以做一些方法增强,方法执行完后,JVM将invoke()的返回值导向给代理对象调用方法的结果

下面分析一下整个过程:
在这里插入图片描述
1、通过上图可以知道,想创建一个实例吗,最关键的是得到对应的Class对象。Class对象包含了一个类的所有信息,比如构造器、方法、字段等。因为代理类和目标类理应实现同一组接口,因为这样可以尽可能保证代理对象的内部结构和目标对象一致,这样我们对代理对象的操作都可以转移到目标对象身上。
2、接口拥有代理对象和目标对象共同的类信息,所以可以从接口那里得到代理类的信息,但是接口无法创建对象,这就需要用到反射了
JDK提供了java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类,这两个相互配合就可以完成动态代理对象的获取
Proxy类提供了一个newProxyInstance()方法,这个方法需要传递三个参数,分别为类加载器、一组接口和InvocationHandler类型的参数
因为每次代理对象调用方法的时候,最终都会调用InvocationHandler的invoke()方法,所以我们要重invoke(0方法

动态代理的代码如下:

public static Object getDynamicProxy(final Object target){
        Object proxy= Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println(method.getName()+"方法开始执行。。。");
                        Object result = method.invoke(target, args);
                        System.out.println(method.getName()+"方法执行结束了。。。。。");
                        return result;
                    }
                }
        );
        return proxy;
    }

4、Cglib动态代理

JDK动态代理是基于接口的方式,换句话来说就是代理类和目标类都实现同一个接口,那么代理类和目标类的方法名就一样了。而CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法;

看一下CGLib的基本结构,下图所示,代理类去继承目标类,每次调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类的该方法的逻辑,结构还是一目了然的;
在这里插入图片描述

4.1、CGLib动态代理的基本使用

使用一下CGLib,在JDK动态代理中提供一个Proxy类来创建代理类,而在CGLib动态代理中也提供了一个类似的类Enhancer;

首先进行导包:

<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
</dependency>

目标类:

public class Dog{
    public void eat() {
        System.out.println("狗----eat");
    }
}

方法拦截器:

import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class MyMethodInterceptor implements MethodInterceptor{

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("这里是对目标类进行增强!!!");
        //注意这里的方法调用,不是用反射哦!!!
        Object object = proxy.invokeSuper(obj, args);
        return object;
    }  
}

测试类:

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class CgLibProxy {
    public static void main(String[] args) {
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(Dog.class);
        //设置回调函数
        enhancer.setCallback(new MyMethodInterceptor());
        
        //这里的creat方法就是正式创建代理类
        Dog proxyDog = (Dog)enhancer.create();
        //调用代理类的eat方法
        proxyDog.eat();       
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值