反向思考之——aop
什么是aop,aop的定义网上很多,总结也说就是面向切面的变成,在业务代码与基础代码的分离。业务场景也很多,比如日志系统,事务系统,权限系统等。这些业务场景一个共同的特点是:
1、范围广,相关联的操作比较多;
2、与业务逻辑无关,他们的存在与不存在,不会影响整个业务逻辑的实现;
3、代码相同,作用在一部分的业务逻辑代码上的操作基本一样。
因此,我们从这三个方面思考,如何实现这样的一个需求?
首先,对于代码相同的操作,我们应该及时抽取成公共方法,多个操作相近的组可以抽取成一个公共类,已备需要使用的类进行操作。因此,第一步,我们应该将这类操作单独放在一个独立的包中,和独立的类中。但这样的公共方法多种多样,而且我需要动态的切换不同的操作,比如日志系统中,我不想用debug等级,想切到info等级,如果我依赖了debug方法的具体实现,就需要停机改代码。因此我们抽出来一个接口,所有的需要的操作,都要继承自这个接口。
其次,这些代码与业务逻辑无关,我们不能写入业务代码,这样就会导致更多的耦合,对于后续的代码维护以及系统升级不利。所以,思路就是增强业务代码类,通过一种方式,在调用业务代码方法的不同时机,进行这些操作的使用。在所有设计模式当中代理是最符合我们的预期。
代理模式:就是将类的方法调用交由代理类进行,客户方使用代理类进行类方法的调用。简单的代理模式实现如下:
interface ITarget{
void doSome();
}
class SimpleTarget implements ITarget {
@Override
public void doSome() {
System.out.println(“Do Some Things”);
}
}
class TargetProxy implements ITarget{
private ITarget target = new SimpleTarget();
public void doSome(){
System.out.println("do other ");
target.doSome();
}
}
public void test(){
ITarget target = new TargetProxy();
target.doSome();
}
代理模式分为静态代理和动态代理两种,静态代理即我们创建代理时,被代理对象已知,且存在,代理类持有被代理对象然后进行操作。而动态代理是我们创建代理类之前,不知道被代理类会做哪些实现,甚至不知道被代理类,是不是存在,因此我们持有一个接口,新的实现只需要在使用的时候通过java的反射机制,进行动态的创建被代理类,然后注入进来。
拦截器就是基于这种模式产生的,
首先,拦截器将所需要做的额外操作,全部集成在Interceptor接口下面,这个类提供了多种操作,如before、after等。个性化的拦截器通过实现该接口进行具体操作。
其次,拦截器的被拦截类为source,这些不规定具体体系,通过反射和动态代理等手段获取。
再次,拦截器实现结合操作在Invocation,通过对目标类,方法及参数的分解,来达到实现的目的。例如:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
interceptor.before();
result = method.invoke(joinPoint,args);
interceptor.after();
return result;
}
最后,我们通过调用代理类获取被增强后类的实例,然后进行方法调用。
整个的架构大概如下:
这就是aop实现的基础——拦截器原理。
但这还不是aop,我们说aop是实现非侵入式框架的主要手段,那么如果需要继承实现,就不能叫做非侵入式了。所以,在具体实践中,上述流程是在aop框架内,自我完成的,而具体需要什么拦截器的处理通知如何做,这个我们要自定义上传。
因此我们再进一步实现我们的aop:
首先,我们创建一个数据源的读取,也就是我们获取哪些是interceptor,哪些是obj,也就是被代理的类或者方法。最简单的方法,我们用xml配置,给定一个标签标记interceptor,一些标签标记固定用法,一些标签来写入source类和需要的方法。
其次,我们读这些配置,然后将方法放在invocation的一个map中,key为interceptor的名字,value是方法的map(key=固定标签名,value为方法名),代理类中传入被代理类的名称,然后通过反射获取类后获取。
最后根据不同的配置信息动态的获取处理。
修改后如下:
Invocation:
MyProxy:
、
当然,还可以增加更多更优秀的一些方式,使这些代码有更大的适用性。基本上,aop是根据拦截器的原理,通过代理模式和反射原理来实现无侵入增强实现的。现在我们可以用这样的思想,来思考spring的代码实现。Spring的aop有多种实现方式,我们所说的注解式,配置文件式是根据它的sourse获取方式。而source一旦被获取,同样是根据标签来解析到不同的处理类中。
Spring采用的的代理模式有很多,有基于静态代理的,有基于jdk动态代理的,也有基于cglib,这个在源码包中都能看到。用户可以根据自己的需求来动态配置。同时aop原作者也增加了一些装饰器,进行额外的操作。同时使用适配器进行新旧代码的迭代。
如下是spring的一种实现:
Spring框架会将aspect的类名称极其对应的方法名称读取,然后存入一个实体中,后续并且将这个类加入pointcut代理实现中。
在spring中,aop有如下几个比较重要的概念:
1、 JionPoint:连接点,也就是将要增加操作的方法。
2、 PointCut:切点,是一组连接点,原则上切点持有了连接点所在的类。
3、 Advice :通知,也就是上图中拦截器具体的方法,在aop中实现特定连接点上的增强处理,如上面提到的before方法。
4、 Advisor:通知者,持有一组advice,通知者允许使用者不通过拦截器来实现不同类型通知的支持。
5、 Aspect:切面,定义了一组通知的类,具体实现为拦截器的实现。
spring aop包的结构: