从零开始写javaweb框架笔记20-使框架具备AOP特性-开发AOP框架
来源:互联网 发布:剑网三御姐捏脸数据 编辑:程序博客网 时间:2024/06/16 14:43
借鉴Spring AOP的风格,写一个基于切面注解的AOP框架,需了解前面的动态代理技术
一:定义切面注解
在框架中添加一个名为Aspect的注解,代码如下:
package org.smart4j.framework.annotation;import java.lang.annotation.*;/** * Created by jack on 2017/6/27. * 切面注解 */@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface Aspect { /** * 注解 * @return */ Class<? extends Annotation> value();}
通过
@Target(ElementType.TYPE)来设置该注解只能应用在类上。该注解中包含了一个名为value的属性,它是一个注解类,用了定义Controller这类注解。在使用切面之前,我们需要先搭建一个代理框架。
二:搭建代理框架
在框架中添加一个名为Proxy的接口,代码如下:
package org.smart4j.framework.proxy;/** * Created by jack on 2017/6/28. * 代理接口 */public interface Proxy { Object doProxy(ProxyChain proxyChain) throws Throwable;}
这个Proxy接口中包括了一个doRroxy方法,传入一个ProxyChain,用于执行“链式代理操作”。所谓链式代理,也就是说,可将多个代理通过一条链子串起来,一个个地去执行,执行顺序取决于添加到链式上的先后顺序。
下面是ProxyChain的代码:
package org.smart4j.framework.proxy;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List;/** * Created by jack on 2017/6/28. * 代理链 */public class ProxyChain { private final Class<?> targetClass; private final Object targetObject; private final Method targetMethod; private final MethodProxy methodProxy; private final Object[] methodParams; private List<Proxy> proxyList = new ArrayList<Proxy>(); private int proxyIndex = 0; /** * 构造函数 * @param targetClass * @param targetObject * @param targetMethod * @param methodProxy * @param methodParams * @param proxyList */ public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) { this.targetClass = targetClass; this.targetObject = targetObject; this.targetMethod = targetMethod; this.methodProxy = methodProxy; this.methodParams = methodParams; this.proxyList = proxyList; } public Object[] getMethodParams() { return methodParams; } public Class<?> getTargetClass() { return targetClass; } public Method getTargetMethod() { return targetMethod; } public Object doProxyChain()throws Throwable{ Object methodResult; if (proxyIndex < proxyList.size()){ methodResult = proxyList.get(proxyIndex++).doProxy(this); }else { methodResult = methodProxy.invokeSuper(targetObject,methodParams); } return methodResult; }}
在ProxyChain类中,我们定义了一系列的成员变量,包括targetClass(目标类),targetObject(目标对象),targetMethod(目标方法),methodProxy(方法代理),methodParams(方法参数),此外还包括了proxyList(代理列表),proxyIndex(代理索引),这些成员变量在构造中进行初始化,并提供了几个重要的获值方法。
需要注意的是MethodProxy这个类,它是CGLib开源项目为我们提供的一个方法代理对象在doProxyChain方法中被使用。
需要解释的是doProxyChain方法,在该方法中,我们通过proxyIndex来充当代理对象的计数器,若未达到proxyList的上限,则从proxyList中取出相应的Proxy对象,并调用其doProxy方法。在Proxy接口的实现中会提供相应的横切逻辑,并调用doProxyChain方法,随后将再次调用当前ProxyChain对象的doProxyChain方法,直到proxyIndex达到proxyList的上限为止,最后调用methodProxy的invokeSuper方法,执行目标对象的业务逻辑。
我们必须在pom.xml文件中添加CGLib的Maven依赖:
<!--添加cglib类库,实现动态代理--> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
现在我们需要些一个类,让它提供一个创建代理对象的方法,输入一个目标类和一组Proxy接口实现,输出一个代理对象,将该类命名为ProxyManager,让它来创建所有的代理对象,代码如下:
package org.smart4j.framework.proxy;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;import java.util.List;/** * Created by jack on 2017/6/28. * 代理管理器 */public class ProxyManager { public static <T> T createProxy(final Class<T> targetClass, final List<Proxy> proxyList){ return (T) Enhancer.create(targetClass, new MethodInterceptor() { public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable { return new ProxyChain(targetClass,targetObject,targetMethod,methodProxy,methodParams,proxyList).doProxyChain(); } }); }}
使用CGLib提供的Enhance的create方法来创建代理对象,将intercept的参数传入ProxyChain的构造器中即可。
谁来调用ProxyManager呢,当然是切面类了,因为在切面类中,需要一个目标方法被调用前后增加相应的逻辑。我们有必要写一个抽象类,让它提供一个模板方法,并在该抽象类的具体实现中扩展相应的抽象方法。我们不妨将该抽象类命名为AspectProxy,代码如下:
package org.smart4j.framework.proxy;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.lang.reflect.Method;/** * Created by jack on 2017/6/28. * 切面代理 */public class AspectProxy implements Proxy{ private static final Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class); public 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(); after(cls,method,params); }else { result = proxyChain.doProxyChain(); } }catch (Exception e){ LOGGER.error("proxy failure ",e); error(cls,method,params); throw e; }finally { end(); } return result; } public boolean intercept(Class<?> cls,Method method,Object [] params) throws Throwable{ return true; } public void before(Class<?> cls,Method method,Object [] params) throws Throwable{ } public void after(Class<?> cls,Method method,Object [] params) throws Throwable{ } public void error(Class<?> cls,Method method,Object [] params) throws Throwable{ } public void begin(){} public void end(){}}
需要注意的是AspectProxy类中的doProxy方法,我们从proxyChain参数中获取了目标类,目标方法与目标参数,随后通过一个try。。。catch。。。finally代码块来实现调用框架,从框架中抽象出一系列的“钩子方法”,这些抽象方法可在AspectProxy的子类中选择性地进行实现,就像下面这样:
package org.smart4j.framework.aspect;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.smart4j.framework.annotation.Aspect;import org.smart4j.framework.annotation.Controller;import org.smart4j.framework.proxy.AspectProxy;import java.lang.reflect.Method;/** * Created by jack on 2017/7/17. */@Aspect(Controller.class)public class ControllerAspect extends AspectProxy{ private static final Logger LOG = LoggerFactory.getLogger(ControllerAspect.class); private long begin; @Override public void before(Class<?> cls, Method method, Object[] params) throws Throwable { LOG.debug("------------begin--------------"); LOG.debug(String.format("class: %s",cls.getName())); LOG.debug(String.format("method: %s",method.getName())); begin = System.currentTimeMillis(); } @Override public void after(Class<?> cls, Method method, Object[] params) throws Throwable { LOG.debug(String.format("time: %s",System.currentTimeMillis()-begin)); LOG.debug("------------end--------------"); }}
我们只需实现before与after方法,就可以在目标方法执行前后添加其他需要执行的代码了。
那么这样就结束了吗?no,我们还需要在整个框架里使用ProxyManager来创建代理对象,并将该代理对象放入框架底层的Bean Map中,随后才能通过IOC将代理的对象注入到其他对象中。
三:加载AOP框架
按照之前的方式,为了加载AOP框架,我们需要编写一个名为AopHelper的类,然后将其添加到HelperLoader类中。在AopHelper中我们需要获取所有的目标类及其被拦截的切面类实例,并通过ProxyManager的createProxy方法来创建代理对象,最后将其放入Bean Map中。
首先需要为BeanHelper类添加一个setBean方法,用于将Bean实例放入Bean Map中,代码如下:
package org.smart4j.framework.helper;import org.smart4j.framework.util.ReflectionUtil;import java.util.HashMap;import java.util.Map;import java.util.Set;/** * Created by jack on 2017/5/23. * bean 助手类 */public class BeanHelper { /** * 定义bean映射,(用于存放bean类与bean实例的映射关系) */ private static final Map<Class<?>,Object> BEAN_MAP = new HashMap<Class<?>, Object>(); /** * 静态代码块 */ static { Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet(); for (Class<?> beanClass : beanClassSet) { Object object = ReflectionUtil.newInstance(beanClass); BEAN_MAP.put(beanClass,object); } } /** * 获取Bean映射 */ public static Map<Class<?>,Object> getBeanMap(){ return BEAN_MAP; } /** * 获取Bean实例 */ public static <T> T getBean(Class<?> cls){ if (!BEAN_MAP.containsKey(cls)){ throw new RuntimeException("can not get bean by class:"+cls); } return (T) BEAN_MAP.get(cls); } /** * 设置Bean实例 * @param cls * @param obj */ public static void setBean(Class<?> cls,Object obj){ BEAN_MAP.put(cls,obj); }}
然后,由于我们需要扩展AspectProxy抽象类的所有具体类,此外,还需要获取带有Aspect注解的所有类,因此需要在ClassHelper中添加以下两个方法:
/** * 获取应用包名下某父类(或接口)的所有子类(或实现类) * @param superClass * @return */ public static Set<Class<?>> getClassSetBySupper(Class<?> superClass){ Set<Class<?>> classSet = new HashSet<Class<?>>(); for (Class<?> cls : CLASS_SET){ if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)){ classSet.add(cls); } } return classSet; } /** * 获取应用包名带有某注解的所有类 * @param annotationClass * @return */ public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){ Set<Class<?>> classSet = new HashSet<Class<?>>(); for (Class<?> cls : CLASS_SET){ if (cls.isAnnotationPresent(annotationClass)){ classSet.add(cls); } } return classSet; }
完整代码如下:
package org.smart4j.framework.helper;import org.smart4j.framework.annotation.Controller;import org.smart4j.framework.annotation.Service;import org.smart4j.framework.util.ClassUtil;import java.lang.annotation.Annotation;import java.util.HashSet;import java.util.Set;/** * Created by jack on 2017/5/22. * 类操作助手类 */public class ClassHelper { /** * 定义类集合,用于存放所加载的类 */ private static final Set<Class<?>> CLASS_SET; static { String basePackage = ConfigHelper.getAppBasePackage(); CLASS_SET = ClassUtil.getClassSet(basePackage); } /** * 获取应用包下的所有类 */ public static Set<Class<?>> getClassSet() { return CLASS_SET; } /** * 获取应用包名下所有Service类 */ public static Set<Class<?>> getServiceClassSet() { Set<Class<?>> classSet = new HashSet<Class<?>>(); for (Class<?> cls : CLASS_SET) { if (cls.isAnnotationPresent(Service.class)) { classSet.add(cls); } } return classSet; } /** * 获取应用包名下所有Controller类 */ public static Set<Class<?>> getControllerClassSet() { Set<Class<?>> classSet = new HashSet<Class<?>>(); for (Class<?> cls : CLASS_SET) { if (cls.isAnnotationPresent(Controller.class)) { classSet.add(cls); } } return classSet; } /** * 获取应用包名下所有Bean类(包括Service,Controller) */ public static Set<Class<?>> getBeanClassSet() { Set<Class<?>> beanClassSet = new HashSet<Class<?>>(); beanClassSet.addAll(getServiceClassSet()); beanClassSet.addAll(getControllerClassSet()); return beanClassSet; } /** * 获取应用包名下某父类(或接口)的所有子类(或实现类) * @param superClass * @return */ public static Set<Class<?>> getClassSetBySupper(Class<?> superClass){ Set<Class<?>> classSet = new HashSet<Class<?>>(); for (Class<?> cls : CLASS_SET){ if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)){ classSet.add(cls); } } return classSet; } /** * 获取应用包名带有某注解的所有类 * @param annotationClass * @return */ public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){ Set<Class<?>> classSet = new HashSet<Class<?>>(); for (Class<?> cls : CLASS_SET){ if (cls.isAnnotationPresent(annotationClass)){ classSet.add(cls); } } return classSet; }}
有了以上两个工具方法以后,我们就可以在AopHelper类中编写一个带有Aspect注解的所有类,把封装成一个方法:
private static Set<Class<?>> createTargetClassSet(Aspect aspect)throws Exception{ Set<Class<?>> targetClassSet = new HashSet<Class<?>>(); Class<? extends Annotation> annotation = aspect.value(); if (annotation != null && annotation.equals(Aspect.class)){ targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation)); } return targetClassSet; }
获取Aspect注解中设置的注解类,若该注解类不是Aspect类,则可调用ClassHelper的getClassSetByAnnotation方法获取相关的类,并把这些类放入目标类集合中,最终返回这个集合。
紧接着我们需要获取代理类及其目标类集合之间的映射关系,一个代理类可对应一个或多个目标类。需要强调的是,这里所说的代理类指的是切面类。通过以下代码获取这个映射关系:
private static Map<Class<?>,Set<Class<?>>> createProxyMap()throws Exception{ Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>(); Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySupper(AspectProxy.class); for (Class<?> proxyClass: proxyClassSet) { if (proxyClass.isAssignableFrom(Aspect.class)){ Aspect aspect = proxyClass.getAnnotation(Aspect.class); Set<Class<?>> targetClassSet = createTargetClassSet(aspect); proxyMap.put(proxyClass,targetClassSet); } } return proxyMap; }
代理类需要扩展AspectProxy抽象类,还需要带有Aspect注解,只有满足这两个条件,才能根据Aspect注解中所定义的注解属性去获取该注解所对应的目标类集合,然后才能建立代理类与目标类集合之间的映射关系,最终返回这个映射关系
一旦获取了代理类与目标类集合之间的映射关系,就能根据这个关系分析出目标类与代理对象列表之间的映射关系,就像下面这样:
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) targetClass.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; }
最后,在AopHelper中通过一个静态块来初始化整个AOP框架,代码如下:
static { try { Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap(); Map<Class<?>,List<Proxy>> targetMap = createTargetMap(proxyMap); for (Map.Entry<Class<?>,List<Proxy>> targetEntry:targetMap.entrySet()) { Class<?> targetClass = targetEntry.getKey(); List<Proxy> proxyList = targetEntry.getValue(); Object proxy = ProxyManager.createProxy(targetClass,proxyList); BeanHelper.setBean(targetClass,proxy); } }catch (Exception e){ } }
获取代理类及其目标类集合的映射关系,进一步获取目标类与代理对象列表的映射关系,进而遍历这个映射关系,从中获取目标类与代理对象列表,调用ProxyManager.createProxy方法获取代理对象,调用BeanHelper.setBean方法,将该代理对象重新放入Bean Map中。
下面是AopHelper类的所有代码:
package org.smart4j.framework.helper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.smart4j.framework.annotation.Aspect;import org.smart4j.framework.proxy.AspectProxy;import org.smart4j.framework.proxy.Proxy;import org.smart4j.framework.proxy.ProxyManager;import java.lang.annotation.Annotation;import java.util.*;/** * Created by jack on 2017/7/17. */public final class AopHelper { private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class); static { try { Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap(); Map<Class<?>,List<Proxy>> targetMap = createTargetMap(proxyMap); for (Map.Entry<Class<?>,List<Proxy>> targetEntry:targetMap.entrySet()) { Class<?> targetClass = targetEntry.getKey(); List<Proxy> proxyList = targetEntry.getValue(); Object proxy = ProxyManager.createProxy(targetClass,proxyList); BeanHelper.setBean(targetClass,proxy); } }catch (Exception e){ LOGGER.error("load aop failure",e); } } private static Set<Class<?>> createTargetClassSet(Aspect aspect)throws Exception{ Set<Class<?>> targetClassSet = new HashSet<Class<?>>(); Class<? extends Annotation> annotation = aspect.value(); if (annotation != null && annotation.equals(Aspect.class)){ targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation)); } return targetClassSet; } private static Map<Class<?>,Set<Class<?>>> createProxyMap()throws Exception{ Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>(); Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySupper(AspectProxy.class); for (Class<?> proxyClass: proxyClassSet) { if (proxyClass.isAssignableFrom(Aspect.class)){ Aspect aspect = proxyClass.getAnnotation(Aspect.class); Set<Class<?>> targetClassSet = createTargetClassSet(aspect); proxyMap.put(proxyClass,targetClassSet); } } return proxyMap; } 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) targetClass.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; }}
别忘了将AopHelper添加到HelperLoader中进行初始化,代码如下:
package org.smart4j.framework;import org.smart4j.framework.helper.*;import org.smart4j.framework.util.ClassUtil;/** * Created by jack on 2017/5/24. * 加载相应类的,帮助类 */public final class HelperLoader { //初始化,加载类 public static void init(){ Class<?> [] classList = {ClassHelper.class, BeanHelper.class, AopHelper.class,IocHelper.class, ControllerHelper.class,}; for (Class<?> cls: classList) { ClassUtil.loadClass(cls.getName(),true); } }}
需要注意的是,AopHelper要在IocHelper之前加载,因为首先需要通过AopHelper获取代理对象,然后才能通过IocHelper进行依赖注入,到这里一个简单的AOP就开发完毕了。
github代码中的地址:https://github.com/wj903829182/smartframework
- 从零开始写javaweb框架笔记20-使框架具备AOP特性-开发AOP框架
- 从零开始写javaweb框架笔记19-使框架具备AOP特性-AOP技术简介
- 从零开始写javaweb框架笔记23-使框架具备AOP特性-实现事务控制特性
- 从零开始写javaweb框架笔记18-使框架具备AOP特性-代理技术简介
- 从零开始写javaweb框架笔记21-使框架具备AOP特性-ThreadLocal简介
- 从零开始写javaweb框架笔记22-使框架具备AOP特性-事务管理简介
- [笔记]架构探险-从零开始写JavaWeb框架-2.2. 之使框架具有aop特性-干货,让框架支持事务处理
- [笔记]架构探险-从零开始写JavaWeb框架-2.1. 之使框架具有aop特性-aop框架加载与切面运行流程分析
- 从零开始写javaweb框架笔记7-动手开发web应用
- 《从零开始写JavaWeb框架》的AOP实现和SpringAOP实现的比较
- 从零开始写javaweb框架笔记11-搭建轻量级JAVAWEB框架-搭建开发环境
- 从零开始写javaweb框架笔记13-搭建轻量级JAVAWEB框架-开发一个类加载器
- 从零开始写javaweb框架笔记13-搭建轻量级JAVAWEB框架-开发一个类加载器
- 从零开始写javaweb框架笔记2-搭建web项目框架
- spring框架开发笔记 lesson3 AOP
- 从零开始写javaweb框架笔记16-搭建轻量级JAVAWEB框架-加载Controller,初始化框架
- 从零开始写javaweb框架笔记16-搭建轻量级JAVAWEB框架-加载Controller,初始化框架
- AOP框架
- 洲阁筛学习总结
- 压缩感知整理学习
- 老服务器Subversion 升级和与 xampp 集成方法
- Android内存泄漏学习总结
- 【Linux】虚拟机免密登录
- 从零开始写javaweb框架笔记20-使框架具备AOP特性-开发AOP框架
- myBatis中删除条件的拼接问题
- PHP 运算符
- 活动安排问题的贪心
- 如何安装VisualSVN-Server-2.7.2?
- IDEA maven项目不能新建package和class的解决
- Python学习笔记整理
- Codeforces Round #420 (Div. 2) E. Okabe and El Psy Kongroo(矩阵快速幂)
- 二线制、三线制、四线制,PT100,电桥