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

121 阅读6分钟

jdk与cglib

承接上文

上文代理公司生成售货员的前提是冰淇淋机必须有接口(jdk动态代理,只能对有接口的类生成代理)。为了解决这个问题,代理公司只能技术革新了 代理公司引入新技术叫cglib,可以再没有接口的情况下生成代理,公司分成两个部门,一个还是用老jdk技术,一个使用新cglib技术,代理公司也升级为代理工厂,并设置一个调度人员是否有接口来交给不同的部门处理。 代理工厂又设置一个职位专门负责收集需求(需求人员),发给调度人员调度人员再根据需求人员提供的信息把工作分配给不同的部门(同时下发需求配置给工作部门)。

画个图梳理下整个过程:

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
	 */
	public Object getProxy() {
		// 让部门培训售货员
		return this.aopProxyFactory.createAopProxy(this).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~