从零开始写javaweb框架 (3)
这一节来讲一下如何在框架中加入AOP。首先讲一下AOP技术的优势,AOP技术很好的解决了代码之间的耦合性,简直就是责任单一制的代表之作,它将如性能监控、日记记录、权限控制等功能从核心业务逻辑代码中抽离出来,通过切面添加到核心代码中。
再来谈一谈AOP的整个流程,理清思路便于后面对于代码的理解。
-
在框架初始化时将Bean容器中拥有代理类的对象替换为代理对象
-
代理类必须有Aspect注解,并且注解中带有参数。参数表示所代理的注解类的对象。
-
将一个类所有的代理类保存在一个集合中
-
创建AOP代理链,在处理类之前经过代理链之后再处理原本的方法
不知道这么说起来好不好理解,下面就放上代码我们再来按顺序慢慢梳理一下。
-
获取代理类和目标类集合之间的映射关系
private static void addAspectProxy(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{
//获取所有代理类
Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
for(Class<?> proxyClass:proxyClassSet){
//注解指定的类型
if(proxyClass.isAnnotationPresent(Aspect.class)){
//获取proxyClass的Aspect的标签
Aspect aspect = proxyClass.getAnnotation(Aspect.class);
Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
proxyMap.put(proxyClass, targetClassSet);
}
}
}
- 获取目标类和代理列表之间的映射关系
//目标类和代理列表之间的映射关系
private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{
Map<Class<?>,List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();
for(Map.Entry<Class<?>, Set<Class<?>>> proxyEntry:proxyMap.entrySet()){
//代理类
Class<?> proxyClass = proxyEntry.getKey();
Set<Class<?>> targetClassSet = proxyEntry.getValue();
for(Class<?> targetClass : targetClassSet){
Proxy proxy = (Proxy) proxyClass.newInstance();
if(targetMap.containsKey(targetClass)){
targetMap.get(targetClass).add(proxy);
}else{
List<Proxy> proxyList = new ArrayList<Proxy>();
proxyList.add(proxy);
targetMap.put(targetClass, proxyList);
}
}
}
return targetMap;
}
这两段代码都十分的简单,也就是叙述。主要就是要理清楚在代理类上有Aspect的注解,而注解中有目标对象的信息,如下所示:
@Aspect(Controller.class)
- 创建AOP的代链,这个部分有几个设计模式,在看懂代码的情况可以更加深入的思考一下我们平时在什么情况下可以使用。
public static <T> T createProxy(final Class<?> targetClass,final List<Proxy> proxyList){
//为目标类创建子类
return (T)Enhancer.create(targetClass, new MethodInterceptor() {
@Override
public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams,
MethodProxy methodProxy) throws Throwable {
//AOP链
return new ProxyChain(targetClass, targetObject, targetMethod,
methodProxy, methodParams, proxyList).doProxyChain();
}
});
}
这里使用了CGLib进行代理类的创建。而代理类都要实现代理接口Proxy并重写doProxy方法,在这里我们创建一个Proxy的子类--AspecyProxy类,在该类中使用了钩子方法用于调用子类的方法。
//出现一系列类似于钩子方法,子类进行选择性的实现
//钩子方法:由子类的一个方法返回值决定公共部分的执行结果
private static final Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class);
@Override
public final Object doProxy(ProxyChain proxyChain) throws Throwable {
Object result =null;
Class<?> cls = proxyChain.getTargetClass();
Method method = proxyChain.getTargetMethod();
Object[] params = proxyChain.getMethodParams();
begin();
try{
if(intercept(cls,method,params)){
before(cls,method,params);
result = proxyChain.doProxyChain();//实现AOP链中的下一个代理方法
after(cls,method,params,result);
}
}catch(Exception e){
LOGGER.error("proxy failure",e);
error(cls,method,params,e);
}finally{
end();
}
return result;
}
在这个方法里面的begin(),before(cls,method,params),after(cls,method,params,result),error(cls,method,params,e),end()实际上都是一些空方法,当子类继承时就可以选择性的实现,而不强制性实现。这些方法就叫做钩子方法。
这样一个简单的AOP框架就写完了,下一节将会把数据库事务加入到AOP框架中,实现事务和核心代码的分离。