设计模式(3):代理模式

1.代理模式

代理模式是一种常见的设计模式,它提供了对目标对象的另外一种访问方式,即通过代理对象来访问目标对象,这样做的好处是:可以在访问目标对象的基础上,增强额外的操作,但不更改目标对象。符合编程思想的开(扩展)闭(修改)原则。

在java中由三种实现代理模式的方式:

  • 静态代理
  • jdk动态代理
  • cglib代理

1.1 静态代理

代理示例:

/**
 * 接口
 */
public interface IndexDao {
    void query();
}

/**
 * 目标对象
 */
public class IndexDaoImpl implements IndexDao{
    @Override
    public void query() {
        System.out.println("查找数据库");
    }
}

/**
 * 代理对象
 */
public class IndexDaoProxy  implements IndexDao{
    //目标对象
    private IndexDao target;
    public IndexDaoProxy(IndexDao target) {
        this.target = target;
    }

    public void query() {
        System.out.println("开始");
        target.query();//执行目标对象的方法
        System.out.println("结束");
    }
}

public class Test {
    public static void main(String[] args) {
        IndexDaoImpl indexDao=new IndexDaoImpl();
        IndexDaoProxy indexDaoProxy =new IndexDaoProxy(indexDao);
        indexDaoProxy.query();
    }
}

静态代理比较简单,可以实现在不修改目标对象的情况下,完成对目标对象的增强,但也存在以下缺点:一旦接口增加其他方法,目标对象和代理对象都需要手动去维护。那么如何解决这个缺点呢?可以使用动态代理。

1.2 动态代理

实现动态代理的方式主要有两种:

  • jdk动态代理
  • cglib代理
jdk动态代理

代码如下

//定义一个接口
public interface Dog {
    void info();
    void run();
}

//定义一个实现类,实现Dog接口,也就是本文中需要被代理的对象
public class BigDog implements Dog{
    @Override
    public void info() {
        System.out.println("我很大");
    }

    @Override
    public void run() {
        System.out.println("我跑的快");
    }
}

public class MyInvocationHandle implements InvocationHandler {
    //需要被代理的对象
    private Object target;
    public void setTarget(Object target){
        this.target=target;
    }

    //执行动态代理的所有方法,都会被替换成执行下面的invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("-----我是在调用之前执行的-----");

        //这一行很关键,已target作为主调来执行menthod的invoke方法,target就是被代理对象,也就是执行被代理对象里面的方法
        Object result = method.invoke(target, args);

        System.out.println("-----我是在调用之后执行的-----");
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        //创建一个InvocationHandler对象
        MyInvocationHandle myInvocationHandle=new MyInvocationHandle();
        Dog dog=new BigDog();
        //为InvocationHandler对象设置target,也就是设置被代理对象
        myInvocationHandle.setTarget(dog);

        //生成一个代理对象
        Dog proDog=(Dog) Proxy.newProxyInstance(dog.getClass().getClassLoader(),dog.getClass().getInterfaces(),myInvocationHandle);
        proDog.info();
        proDog.run();

        /**
         * 很明显我们发现一个问题,JDK的动态代理只能为接口创建代理对象,换句话说
         * 也就是说,被代理的对象必须要作为一个接口的实现类,在这种情况下,我们才能使用JDk动态代理
         */
    }

/*    下面这些为代码中的打印

-----我是在调用之前执行的-----
我很大
-----我是在调用之后执行的-----
-----我是在调用之前执行的-----
我跑的快
-----我是在调用之后执行的-----

*/

jdk动态代理是java自带的,非常方便,但有一个要求是目标对象一定要实现接口,否则就不能用动态代理。那么如果目标对象没有实现接口,有没有方式可以实现代理呢?答案是有的,就是下面的cglib代理。

cglib代理

添加一个cglib的代理

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
/**
 * 目标对象
 */
public class UserDao {
    public void save(){
        System.out.println("插入一条数据");
    }
}

/**
 * 代理对象
 */
public class UserDaoProxy implements MethodInterceptor {
    //目标对象
    private Object target;

    public UserDaoProxy(Object target) {
        this.target = target;
    }

    //为目标对象生成代理对象
    public Object getProxyInstance() {
        //工具类
        Enhancer en = new Enhancer();
        //设置父类
        en.setSuperclass(target.getClass());
        //设置回调函数
        en.setCallback(this);
        //创建子类对象代理
        return en.create();
    }

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

        System.out.println("我是在之前被调用的");
        method.invoke(target,args);//执行目标对象的方法
        System.out.println("我是在之后被调用的");
        return null;
    }
}

//测试类
public class Test {
    public static void main(String[] args) {

        UserDao target =new UserDao();
        UserDao proxy=(UserDao) new UserDaoProxy(target).getProxyInstance();
        proxy.save();
    }
}

我们发现可以解决上面说的当目标对象没有实现接口的时候也可以完成代理,但此时目标类是不能被final修饰的,因为生成的代理类会是目标类的子类,如果目标类被final修饰的话,就不能被继承,就无法完成代理。
所以说每种代理模式都有各自的优缺点,具体选用哪种,依情况而定。

posted @ 2022-01-03 14:38  提莫_队长  阅读(39)  评论(0)    收藏  举报