美文网首页AOP
用一个小故事模拟Spring Aop(二): 代理工厂jdk和c

用一个小故事模拟Spring Aop(二): 代理工厂jdk和c

作者: pq217 | 来源:发表于2022-01-19 18:14 被阅读0次

jdk与cglib

承接上文

厂家代理公司合作了一段时间,由于代理公司技术有缺陷,厂家的所有冰淇淋机想去代理公司生成售货员的前提是冰淇淋机必须有个说明书(接口),代理公司实际上是通过这个说明书培训处的售货员(对应这个说明书实际就是类实现的接口,代理公司的技术实际上就是上文的jdk动态代理,只能对有接口的类生成代理)。
麻烦又出现了,厂家生产的新冰淇淋机没有说明书,这种情况代理公司没有办法给他配置售货员(代理)了,没办法,毕竟是客户,代理公司只能技术革新了。
于是代理公司引入了一项新技术叫cglib,它可以做到再没有规范的情况下生成代理,当然原来的jdk动态代理技术还保留,只不过公司分成两个部门,一个还是用jdk技术,一个使用新cglib技术,代理公司也升级为代理工厂(为了和spring命名对应),并设置一个调度人员跟据机器是否有规范来交给不同的部门处理。
每个部门要培训售货员都需要知道用户需求啊,所以代理工厂雇佣了一个人专门负责收集需求(需求人员),发给调度人员调度人员再根据需求人员提供的信息把工作分配给不同的部门(同时下发需求配置给工作部门)。

画个图梳理下整个过程:

image.png

首先来用代码模拟下这个需求整理需求人员

/**
 * @Author wmf
 * @Date 2022/1/19 17:05
 * @Description 需求人员
 */
public class ProxyConfig {
    /**
     * 附加工作列表
     */
    List<MethodInterceptor> interceptors = new ArrayList<>();
    /**
     * 绑定的机器
     */
    Object target;
    /**
     * 是否有规范(是否有实现的接口)
     */
    Boolean isImpl;
    /**
     * 设置拦截计划
     * @param interceptor
     */
    public void addInterceptor(MethodInterceptor interceptor) {
        this.interceptors.add(interceptor);
    }
    /**
     * 获取拦截计划
     */
    List<MethodInterceptor> getInterceptors() {
        return interceptors;
    }
    /**
     * 绑定机器
     * @param target
     */
    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * 设置是否有规范(是否有实现的接口)
     * @param impl
     */
    public void setImpl(Boolean impl) {
        isImpl = impl;
    }
}

可以告诉需求人员绑定的机器,可以设置工作列表,是否有规范(其实可以根据target自动识别,但是懒着写了,都配吧)
然后之前的ProxyCompany改成jdk动态部门,并把之前的需求设置也都不要了,有问题直接问需求人员

/**
 * @Author wmf
 * @Date 2022/1/12 18:23
 * @Description 原代理公司成员变为jdk部门
 */
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { // AopProxy相当于国家给所有代理公司下发的一个标准

    public JdkDynamicAopProxy(ProxyConfig config) {
        this.config = config;
    }

    /**
     * 存储需求人员的电话
     */
    private ProxyConfig config;

    /**
     * 生成售货员(代理)
     * @return
     */
    @Override
    public Object getProxy() {
        Object target = config.target;
        // 使用jdk动态代理技术
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        return null;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 使用通灵术召唤一个调度员,并把打包的工作也交给调度员
        ReflectiveMethodInvocation dispatcher = new ReflectiveMethodInvocation(config.target, method, args, config.getInterceptors());
        // 需求来了之后按拦截计划去执行
        return dispatcher.proceed();
    }
}

新增一个使用cglib技术的部门(不懂cglib自己补)

/**
 * @Author wmf
 * @Date 2022/1/12 18:23
 * @Description 新组织的cglib部门
 */
public class CglibAopProxy implements AopProxy, MethodInterceptor { // AopProxy相当于国家给所有代理公司下发的一个标准

    public CglibAopProxy(ProxyConfig config) {
        this.config = config;
    }

    /**
     * 存储需求人员的电话
     */
    private ProxyConfig config;

    /**
     * 生成售货员(代理)
     * @return
     */
    @Override
    public Object getProxy() {
        Object target = config.target;
        // 使用cglib代理技术
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallbacks(new Callback[] {this});
        return enhancer.create();
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        return null;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 使用通灵术召唤一个调度员,并把打包的工作也交给调度员
        ReflectiveMethodInvocation dispatcher = new ReflectiveMethodInvocation(config.target, method, args, config.getInterceptors());
        // 需求来了之后按拦截计划去执行
        return dispatcher.proceed();
    }
}

两个部门都实现了AopProxy(spring的),因为部门虽然用的技术不一样,但是都是生成售货员(代理),有统一的规范

public interface AopProxy {

    /**
     * Create a new proxy object.
     * <p>Uses the AopProxy's default class loader (if necessary for proxy creation):
     * usually, the thread context class loader.
     * @return the new proxy object (never {@code null})
     * @see Thread#getContextClassLoader()
     */
    Object getProxy();

    /**
     * Create a new proxy object.
     * <p>Uses the given class loader (if necessary for proxy creation).
     * {@code null} will simply be passed down and thus lead to the low-level
     * proxy facility's default, which is usually different from the default chosen
     * by the AopProxy implementation's {@link #getProxy()} method.
     * @param classLoader the class loader to create the proxy with
     * (or {@code null} for the low-level proxy facility's default)
     * @return the new proxy object (never {@code null})
     */
    Object getProxy(@Nullable ClassLoader classLoader);

}

我们再模拟一下调度人员,他的工作是根据配置区分工作该分给哪个部门

/**
 * @Author wmf
 * @Date 2022/1/19 17:27
 * @Description 接待员
 */
public class DefaultAopProxyFactory {
    /**
     * 根据配置返回不同的部门
     * @param config 需求配置
     * @return
     */
    public AopProxy createAopProxy(ProxyConfig config) {
        if (config.isImpl) {
            return new JdkDynamicAopProxy(config);
        } else {
            return new CglibAopProxy(config);
        }
    }
}

最后模拟代理工厂,由于代理工厂对于外界冰淇淋厂家来说也充当一个需求整理人员,所以继承了ProxyConfig

/**
 * @Author wmf
 * @Date 2022/1/19 16:50
 * @Description 代理公司升级为代理工厂, 代理工厂对于厂家来说也是充当一个需求整理人员,所以继承了ProxyConfig
 */
public class ProxyFactory extends ProxyConfig {
    /**
     * 接待员
     */
    DefaultAopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
    /**
     * 让接待员去分配部门
     * @return
     */
    private AopProxy createAopProxy() {
        return this.aopProxyFactory.createAopProxy(this);
    }
    /**
     * 生成售货员(代理)
     * @return
     */
    public Object getProxy() {
        // 让部门培训售货员
        return createAopProxy().getProxy();
    }
}

好了现在再测试一下,先测试一下老机器

public class ChainApplication {
    public static void main(String[] args) {
        // 厂家的一代冰淇淋机
        IceCreamMachine machine = new IceCreamMachine1();
        // 厂家定制食品监督计划
        MethodInterceptor interceptor1 = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("记录需求至食品监督本:"+invocation.getArguments()[0]);
                Object proceed = invocation.proceed();
                System.out.println("拍照传给厂家微信:"+proceed);
                return proceed;
            }
        };
        // 厂家定制市场调研计划
        MethodInterceptor interceptor2 = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("记录需求至市场调研本:"+invocation.getArguments()[0]);
                return invocation.proceed();
            }
        };
        // 代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        // 绑定冰淇淋机
        proxyFactory.setTarget(machine);
        proxyFactory.setImpl(true);
        // 绑定两个拦截计划
        proxyFactory.addInterceptor(interceptor1);
        proxyFactory.addInterceptor(interceptor2);
        // 生成售货员(机器的代理)
        IceCreamMachine saler = (IceCreamMachine) proxyFactory.getProxy();
        String iceCream = saler.eggCone("原味", "中");
    }
}

输出跟原来一模一样:

记录需求至食品监督本:原味
记录需求至市场调研本:原味
开始生产蛋筒冰淇淋
拍照传给厂家微信:原味 蛋筒冰淇淋(中)

再用代码模拟一个新冰淇淋机,2代冰淇淋机没有规范,代码上反应就是没有接口

/**
 * @Author wmf
 * @Date 2022/1/18 15:37
 * @Description 模拟一个2代冰淇淋机,没有接口
 */
public class IceCreamMachine2 {
    /**
     * 模拟生产杯装冰淇淋
     * @param taste 草莓/原味/巧克力
     * @param size 大/中/小
     * @return 冰淇淋
     */
    public String cup(String taste, String size) {
        System.out.println("2代:开始生产杯装冰淇淋");
        return taste + " 杯装冰淇淋("+size+")";
    }

    /**
     * 模拟生产蛋筒冰淇淋
     * @param taste 草莓/原味/巧克力
     * @param size 大/中/小
     * @return 冰淇淋
     */
    public String eggCone(String taste, String size) {
        System.out.println("2代:开始生产蛋筒冰淇淋");
        return taste + " 蛋筒冰淇淋("+size+")";
    }
}

测试一下2代冰淇淋机

/**
 * @Author wmf
 * @Date 2022/1/18 15:45
 * @Description 整个chain的测试类
 */
@SuppressWarnings("ALL")
public class ChainApplication {
    public static void main(String[] args) {
        // 厂家的冰淇淋机
        IceCreamMachine2 machine = new IceCreamMachine2();
        // 厂家定制食品监督计划
        MethodInterceptor interceptor1 = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("记录需求至食品监督本:"+invocation.getArguments()[0]);
                Object proceed = invocation.proceed();
                System.out.println("拍照传给厂家微信:"+proceed);
                return proceed;
            }
        };
        // 厂家定制市场调研计划
        MethodInterceptor interceptor2 = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("记录需求至市场调研本:"+invocation.getArguments()[0]);
                return invocation.proceed();
            }
        };
        // 代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        // 绑定冰淇淋机
        proxyFactory.setTarget(machine);
        // 没有规范
        proxyFactory.setImpl(false);
        // 绑定两个拦截计划
        proxyFactory.addInterceptor(interceptor1);
        proxyFactory.addInterceptor(interceptor2);
        // 生成售货员(机器的代理)
        IceCreamMachine2 saler = (IceCreamMachine2) proxyFactory.getProxy();
        String iceCream = saler.eggCone("原味", "中");
    }
}

输出:

记录需求至食品监督本:原味
记录需求至市场调研本:原味
2代:开始生产蛋筒冰淇淋
拍照传给厂家微信:原味 蛋筒冰淇淋(中)

还是符合预期,说明代理工厂的这次技术改造成功了!!!

对比spring

ProxyFactory 对比 ProxyFactory


image.png
image.png
image.png
image.png

DefaultAopProxyFactory 对比 DefaultAopProxyFactory


image.png

over~

相关文章

网友评论

    本文标题:用一个小故事模拟Spring Aop(二): 代理工厂jdk和c

    本文链接:https://www.haomeiwen.com/subject/ukarhrtx.html