4.设计模式-代理模式(结构型)

资料:https://gitee.com/chuanqi1995/java

先说下如果没有代理模式的代码

package start.service;

public interface WuPinService {
    void goumai();
}
package start.service.impl;

import start.service.WuPinService;

public class MingServiceImpl implements WuPinService {
    @Override
    public void goumai() {
        System.out.println("明购买物品");
        before();
        after();

        //可不可以把快递这部分交给别人来做呢?所以我们要用代理模式 先说静态代理
    }

    private void after() {
        System.out.println("快递员取货");
    }

    private void before() {
        System.out.println("快递员送货");
    }
}

我们可不可以把before();、after();等无关的方法交给别人来处理呢?相当于找一个房屋中介,来处理这些事情,使调用者无感知,那么下面就说静态代理模式

静态代理

抽象主题角色(被代理类和代理类都要实现的接口)

public interface WuPinService {
    void goumai();
}

真实主题角色(被代理类)

package staticproxy.service.impl;

import staticproxy.service.WuPinService;

public class MingServiceImpl implements WuPinService {
    @Override
    public void goumai() {
        System.out.println("明购买物品");
    }
}

代理主题角色 (代理类)

package staticproxy.proxy;

import staticproxy.service.WuPinService;
import staticproxy.service.impl.MingServiceImpl;

public class WuPinProxy implements WuPinService {
    private MingServiceImpl mingServiceImpl;
    public WuPinProxy(MingServiceImpl mingService){
        mingServiceImpl = new MingServiceImpl();
    }
    @Override
    public void goumai() {
        mingServiceImpl.goumai();
        before();
        after();
    }
    private void after() {
        System.out.println("快递员送货");
    }

    private void before() {
        System.out.println("快递员取货");
    }
}

从代理类来看的话,如果又来一个人也要买东西,也需要快递员送货取货,这个时候这个代理类是实现不了的,所以引出动态代理的概念

动态代理

JDK

接口

package dynamicproxy.jdk.service;

public interface WuPinService {
    void goumai();
}

被代理类

package dynamicproxy.jdk.service.impl;


import dynamicproxy.jdk.service.WuPinService;

public class MingServiceImpl implements WuPinService {
    @Override
    public void goumai() {
        System.out.println("明购买物品");
    }
}
package dynamicproxy.jdk.service.impl;

import dynamicproxy.jdk.service.WuPinService;

public class YuServiceImpl implements WuPinService {
    @Override
    public void goumai() {
        System.out.println("雨购买物品");
    }
}

代理类

package dynamicproxy.jdk.proxy;

import dynamicproxy.jdk.service.WuPinService;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class WuPinProxy implements InvocationHandler {
    private WuPinService wuPinService;
    public WuPinService getInstance(WuPinService wuPinService){
        this.wuPinService = wuPinService;
        Class<? extends WuPinService> clazz = wuPinService.getClass();
        return (WuPinService) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object obj = method.invoke(this.wuPinService, args);
        before();
        after();
        return obj;
    }

    private void after() {
        System.out.println("快递员送货");
    }

    private void before() {
        System.out.println("快递员取货");
    }
}

调用

package dynamicproxy.jdk;

import dynamicproxy.jdk.proxy.WuPinProxy;
import dynamicproxy.jdk.service.WuPinService;
import dynamicproxy.jdk.service.impl.MingServiceImpl;
import dynamicproxy.jdk.service.impl.YuServiceImpl;

public class Test {
    public static void main(String[] args) {
        WuPinProxy wuPinProxy = new WuPinProxy();
        WuPinService wuPinService = wuPinProxy.getInstance(new YuServiceImpl());
        wuPinService.goumai();
    }
}

步骤

  1. 通过反射获取到被代理类的接口
  2. jdk动态代理重新生成一个新的类,这个新的类实现上面第1部所说的所有接口
  3. 动态生成java代码的同时,把新加的业务添加进去
  4. 编译生成class文件,然后执行

所以,jdk代理的目标类(重新生成的那个类)必须要实现被代理类的接口,因为jdk代理是根据被代理对象的所有接口去生成新的方法!

通过Proxy.newProxyInstance()的返回可以看出,返回的是接口,所以,jdk代理只能代理接口

CGLib

被代理类

package dynamicproxy.cglib.service.impl;


public class MingServiceImpl {
    public void goumai() {
        System.out.println("明购买物品");
    }
}
package dynamicproxy.cglib.service.impl;

public class YuServiceImpl {
    public void goumai() {
        System.out.println("雨购买物品");
    }
}

代理类

package dynamicproxy.cglib.proxy;

import dynamicproxy.jdk.service.WuPinService;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class WuPinProxy implements MethodInterceptor {
    public Object getInstance(Class<?> clazz){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        after();
        Object obj = methodProxy.invokeSuper(o, objects);
        return obj;
    }

    private void after() {
        System.out.println("快递员送货");
    }

    private void before() {
        System.out.println("快递员取货");
    }


}

调用

package dynamicproxy.cglib;

import dynamicproxy.cglib.proxy.WuPinProxy;
import dynamicproxy.cglib.service.impl.MingServiceImpl;
import dynamicproxy.cglib.service.impl.YuServiceImpl;

import java.util.*;

public class Test {
    public static void main(String[] args) {
        WuPinProxy wuPinProxy = new WuPinProxy();
        MingServiceImpl service = (MingServiceImpl) wuPinProxy.getInstance(MingServiceImpl.class);
        service.goumai();

        // JDK Proxy代理需要实现接口
        // CGLib除了final修饰的方法都可以代理

        // JDK Proxy是JDK自带的
        // CGLib属于第三方组件
    }
}

步骤

  1. 创建被代理的类及方法
  2. 创建一个实现MethodInterceptor 的代理类,重写 intercept 方法;
  3. 创建获取被代理类的方法 getInstance(Object target);
  4. 获取代理类,调用代理类方法。

CGLib代理的不需要被代理对象实现任何接口,它会重写被代理类的所有方法,所以不能代理final修饰的方法或者

因为被final修饰后,表明这个类不能被继承

动态代理

JDK动态代理

  1. 目标类一定要实现接口
  2. 通过目标类得到代理类,三个参数:1)类加载器,2)目标类实现的接口 3)InvocationHandler
  3. 生成的代理类是final修饰的
  4. jdk只生成一个代理类文件
  5. 通过反射去调用方法

CGLib动态代理

  1. 生成的代理类会去继承目标类
  2. 通过index去找到方法
  3. cglib生成三个文件

生成代理类时jdk更快,cglib慢

调用时jdk用的反射,cglib用的fastClass机制通过index去定位,cglib会更快

jdk1.8之后(反射有了很大的优化)

CGLib为什么要生成三个文件?

  1. 代理类的文件
  2. 先去找到我们的索引
  3. 通过索引定位到我们的方法
  4. 还有个继承了Factory

我们的代理类还能再被代理吗?

  1. jdk代理后的类再次被jdk代理?

    jdk不能再次被jdk代理,因为会造成handler调用死循环。如果使用不同的InvocationHandler可以,mybatis中的
    多重插件就是这么做的

  2. jdk代理后的类再次被cglib代理?
    jdk生成代理类是final,cglib生成代理类去继承目标类,所以不能被代理

  3. cglib代理后的类再次被cglib代理?
    会报方法名重复 Duplicate method name "newInstance" with signature

  4. cglib代理后的类再次被jdk代理?
    类的签名已经改变,没有目标方法了

只有jdk代理能再次被jdk代理,必须使用不同的InvocationHandler,其他都不可以

什么样类不能被代理?

  1. 对于jdk,必须有接口实现
  2. final修飾的方法不能被cglib代理
  3. 方法不是public的

接口能够被代理吗?

接口能被代理,比如mybatis中的dao接口就是这样做

你工作当中有没有用过?

这个后期总结下!!!

JDK代理和CGLib代理的区别?

  1. JDK动态代理通过反射去实现被代理对象的所有接口,所以JDK只能代理实现了接口的对象,CGLlib对代理类
    所有方法重写
  2. JDK动态代理跟CGLib代理都是在运行期生成新的字节码,但是CGLib实现更为复杂,用的是ASM框架,生成
    代理类的效率比JDK低
  3. JDK动态代理是通过反射机制去查询所有被代理对象的接口,CGLib代理是通过FastClass机制直接调用方法,
    所有执行效率,CGLib比JDK代理要高

Spring用的是哪个代理模式?

bean有实现接口的时候用jdk,没有实现的话模式cglib,也可以强制用cglib

posted @ 2022-03-23 16:32  Java-Legend  阅读(44)  评论(0编辑  收藏  举报