在这一章中,我们不会贸然开始SpringAOP的旅程。在开头我将带来些许入门必备知识。当然对AOP的解释是不能少的百度百科:
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
简单来说AOP是一种新的模块化机制,同时我们还需要知道的一个点就是:动态代理是实现AOP的基础。我们接下来的代码也是为了学习动态代理而展开的(这里省去静态代理【案例了解,点这里】)。
动态代理
那什么是代理呢?什么又是动态代理呢?我们阔以来一个简单实例:大D哥去买饮料,他不会直接前往工厂购买饮料,而是会前往商店购买饮料。在这里商店就相当于代理了工厂对饮料的销售。但我们知道这瓶饮料背后所有的生产、处理、运输工作都是由工厂完成的,在这商店仅仅是提供了一个代理出售而已。
但如果只卖一种饮料的话,这个商店肯定是不行的,用户也不会满意于此。因为我们都希望去商品更齐全的,性价比更高的商店。商店的每个货架上不可能只卖一种固定的商品,所以商店应该动态地向用户出售当前需要的商品。这样商店的代理模式就从静态的转变成了动态的。
最后我们将上面的图简化一下下,同时我们再对上面进行一下总结,代理到底干了什么?有什么用?
在上面案例中(下面讨论仅限于实体经济情况下),大D哥之所以不去工厂购买是为什么呢,可能有许多原因对不,诸如距离、交易量、售后服务等等,但总结起来是不是对终端消费者的销售能力上的不足,但我们的代理商店是不是就是弥补这一弱势的存在。所以我们的代理应该是:增强代码 + 目标对象(原对象):
代码的实现
在代码的实现上分为: Java SDK动态代理和cglib动态代理
文末附上源码地址。
Java SDK动态代理实现:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SimpleJDKDynamicProxyDemo {
//A工厂完成对A商品的实际生产
static interface FactoryA {
public void produceA();
}
//B工厂完成对B商品的实际生产
static class FactoryAImpl implements FactoryA {
@Override
public void produceA() {
System.out.println("工厂A完成了对商品A的生产、装配、运输");
}
}
static interface FactoryB {
public void produceB();
}
static class FactoryBImpl implements FactoryB {
@Override
public void produceB() { System.out.println("工厂B完成了对商品B的生产、装配、运输"); }
}
//看起来这里将充当我们的代理商店
static class SimpleInvocationHandler implements InvocationHandler {
private Object realObj;
public SimpleInvocationHandler(Object realObj) {
this.realObj = realObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("您选购了"+ method.getName()+"商品");
System.out.println("进入 " + realObj.getClass()
.getSimpleName() + "::" + method.getName());
//这里通过反射代理了实际的工厂生产
Object result = method.invoke(realObj, args);
System.out.println("完成 " + realObj.getClass()
.getSimpleName() + "::" + method.getName());
System.out.println("您完成了"+ method.getName()+"商品的购买");
return result;
}
}
//这里是获取我们的动态代理某件商品的商店
private static <T> T getProxy(Class<T> intf, T realObj) {
return (T) Proxy.newProxyInstance(intf.getClassLoader(),
new Class<?>[] { intf }, new SimpleInvocationHandler(realObj));
}
public static void main(String[] args) throws Exception {
FactoryA a = new FactoryAImpl();
//我们想要购买FactoryA生产的produce
FactoryA aProxy = getProxy(FactoryA.class, a);
aProxy.produceA();
FactoryB b = new FactoryBImpl();
//我们想要购买FactoryB生产的produce
FactoryB bProxy = getProxy(FactoryB.class, b);
bProxy.produceB();
}
}
运行结果:
cglib动态代理实现:
cglib动态代理的实现需要自己引入第三方依赖cglib,所有勿忘依赖得引入,若不是采用Maven坐标引入,而采用jar引入的话,会报一个错java.lang.NoClassDefFoundError: org/objectweb/asm/Type,因为缺少asm.jar。
代码如下:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class SimpleCGLibDemo {
static class FactoryA {
public void produceA() {
System.out.println("工厂A完成了对商品A的生产、装配、运输");
}
}
static class FactoryB {
public void produceB() {
System.out.println("工厂B完成了对商品B的生产、装配、运输");
}
}
//看起来这里将充当我们的代理商店
static class SimpleInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("您选购了"+ method.getName()+"商品");
System.out.println("entering " + method.getName());
//这里通过反射代理了实际的工厂生产
Object result = proxy.invokeSuper(object, args);
System.out.println("leaving " + method.getName());
System.out.println("您完成了"+ method.getName()+"商品的购买");
return result;
}
}
//这里是获取我们的动态代理某件商品的商店
private static <T> T getProxy(Class<T> cls) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls);
enhancer.setCallback(new SimpleInterceptor());
return (T) enhancer.create();
}
public static void main(String[] args) throws Exception {
//我们想要购买FactoryA生产的produce
FactoryA proxyA = getProxy(FactoryA.class);
proxyA.produceA();
//我们想要购买FactoryB生产的produce
FactoryB proxyB = getProxy(FactoryB.class);
proxyB.produceB();
}
}
输出结果:
看起来两种方式的代理都完成了我们的要求,但两种方式还是有很大的不同:
JDK动态代理是面向接口,CGlib是代理是面向类
形成这中不同的原因,主要是两个方法在底层实现上的不同,这里简单提一嘴,JDK是通过生成一个被代理接口的实现类并对所有方法进行重写,而CGlib则是生成一个对被代理类的继承类。这里将不再展开,因为主要是进行一个简单介绍和实现方法,后续将填这个坑。
在下一篇文章我们将进一步探讨SpringAOP的实现。
源码地址:https://github.com/sanshi-07/simple-spring/tree/master/simplr-spring-v1.0.0/src/dynamicproxy