本篇博客参考《架构探险--从零开始写java web框架》4.3章节
1代理接口:
package smart.myaop.framework; public interface Proxy { /** * 执行链式调用 */ Object doProxy(ProxyChain proxyChain) throws Throwable; }
2代理链(责任链模式,同一个对象可以被多个Proxy层层代理):
package smart.myaop.framework; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * 代理链 */ public class ProxyChain { private final Class<?> targetClass; //目标类 private final Object targetObject; //目标对象 private final Method targetMethod; //目标方法 private final MethodProxy methodProxy; //方法代理,cglib提供的方法代理对象 private final Object[] methodParams; //方法参数 private List<Proxy> proxyList = new ArrayList<>(); //代理列表 private int proxyIndex = 0; //代理索引 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 Class<?> getTargetClass() { return targetClass; } public Method getTargetMethod() { return targetMethod; } public Object[] getMethodParams() { return methodParams; } /** * 在Proxy接口的实现中提供相应横切逻辑并调用doProxyChain方法 * methodProxy的invokeSuper方法执行目标对象的业务逻辑 * @return * @throws Throwable */ 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; } }
3创建代理对象的工具类:
package smart.myaop.framework; 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; /** * 代理管理器,输入目标类和一组proxy接口实现,创建一个代理对象并输出 * 由切面类来调用ProxyManager创建代理链,切面类在目标方法调用前后进行增强 * * 在框架里使用ProxyManager创建代理对象并放入ioc容器,然后将代理对象注入到其它对象中 */ public class ProxyManager { 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 { return new ProxyChain(targetClass, targetObject, targetMethod, methodProxy, methodParams, proxyList); } }); } }
4Proxy接口的抽象实现,模板方法模式:
package smart.myaop.framework; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Method; /** * 切面代理 * 该抽象类提供模板方法,由其子类扩展相应的抽象方法 */ public abstract class AspectProxy implements Proxy { private static final Logger logger = LoggerFactory.getLogger(AspectProxy.class); @Override public Object doProxy(ProxyChain proxyChain) throws Throwable { Object result = null; Class<?> cls = proxyChain.getTargetClass(); Method method = proxyChain.getTargetMethod(); Object[] params = proxyChain.getMethodParams(); /** * 从proxyChain中获取目标类,目标方法和目标参数,通过try ... catch ...finally代码块调用代理框架 */ begin(); try { if(intercept(cls, method, params)) { before(cls, method, params); result = proxyChain.doProxyChain(); after(cls, method, result); } } catch (Exception e) { logger.error("proxy failure", e); error(cls, method, params, e); } finally { end(); } return result; } /** * 下面几个都是钩子方法,可在子类中有选择性的实现,可以有选择性的实现,所以不定义成抽象方法 */ public boolean intercept(Class<?> cls, Method method, Object[] params) { return true; } public void before(Class<?> cls, Method method, Object[] params) {} public void after(Class<?> cls, Method method, Object result) {} public void begin() {} public void end() {} public void error(Class<?> cls, Method method, Object[] params, Exception e) {} }
5举例某个具体的Proxy实现:
package smart.myaop; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import smart.myaop.framework.AspectProxy; import java.lang.reflect.Method; /** * 该示例类继承AspectProxy类,指定拦截Controller所有方法,并在方法前后加日志并记录执行时间 */ @Aspect(Controller.class) public class ControllerAspect extends AspectProxy { private static final Logger logger = LoggerFactory.getLogger(ControllerAspect.class); private long begin; @Override public void before(Class<?> cls, Method method, Object[] params) { logger.debug("----------begin----------"); logger.debug(String.format("class: %s"), cls.getName()); logger.debug(String.format("method: %s"), method.getName()); begin = System.currentTimeMillis(); } @Override public void after(Class<?> cls, Method method, Object result) { logger.debug(String.format("time: %dms"), System.currentTimeMillis() - begin); logger.debug("----------end----------"); } }
6自定义注解@Aspect,作为代理标记:
package smart.myaop; import java.lang.annotation.*; /** * 切面注解 */ @Target(ElementType.TYPE) //只能用在类上 @Retention(RetentionPolicy.RUNTIME) public @interface Aspect { /** * 注解,注解类,用来定义注解 * @return */ Class<? extends Annotation> value(); }
7初始化框架并创建代理对象放入ioc容器:
package smart.myaop; import smart.myaop.framework.AspectProxy; import smart.myaop.framework.Proxy; import smart.myaop.framework.ProxyManager; import java.lang.annotation.Annotation; import java.util.*; /** * 初始化时获取所有目标类和其被拦截的切面类实例,获取AspectProxy抽象类的所有子类和@Aspect注解的所有类 * 调用ProxyManager#createProxy方法创建代理对象放入ioc容器 * AopHelper在框架初始化时调用,要在初始化bean之后,ioc容器初始化之前调用,这样ioc容器做属性注入时候才能拿到相应的代理对象 */ public class AopHelper { private static Set<Class<?>> allClassSet = new HashSet<>(); //项目启动时把指定路径下的所有class文件加载为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); //todo 放入targetClass为键,proxy为值放入ioc容器,在ioc容器做属性注入的时候通过class对象拿到的就是代理对象了 } } catch (Exception e) { System.out.println("aop failure"); e.printStackTrace(); } } /** * 目标类与代理对象列表之间的映射关系,如一个业务类被多个@Aspect注解修饰的AspectProxy子类代理,这里得到这样的1对n映射关系 * @param proxyMap * @return * @throws Exception */ private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception { Map<Class<?>, List<Proxy>> targetMap = new HashMap<>(); 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<>(); proxyList.add(proxy); targetMap.put(targetClass, proxyList); } } } return targetMap; } /** * 给createTargetMap方法用 * 代理类(切面类)与目标类集合之间的一对多映射关系 * 在全部class对象集合中搜索满足1是AspectProxy子类,2被@Aspect注解,这样的类(代理类),根据@Aspect注解指定的注解属性去获取该注解对应的目标类集合 * 然后建立代理类与目标类集合之间的映射关系,据此分析出目标类与代理对象列表之间的映射关系 * @return * @throws Exception */ private static Map<Class<?>, Set<Class<?>>> createProxyMap() throws Exception { Map<Class<?>, Set<Class<?>>> proxyMap = new HashMap<>(); Set<Class<?>> proxyClassSet = new HashSet<>(); for (Class<?> aClass : allClassSet) { if(AspectProxy.class.isAssignableFrom(aClass) && !AspectProxy.class.equals(aClass)) { proxyClassSet.add(aClass); //获取AspectProxy子类class对象集合 } } for (Class<?> aClass : proxyClassSet) { if(aClass.isAnnotationPresent(Aspect.class)) { Aspect aspect = aClass.getAnnotation(Aspect.class); Set<Class<?>> targetClassSet = createTargetClassSet(aspect); proxyMap.put(aClass, targetClassSet); } } return proxyMap; } /** * 给createProxyMap方法用 * 获取被指定aspect注解的所有类对象 * @param aspect * @return * @throws Exception */ private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception { Set<Class<?>> targetClassSet = new HashSet<>(); Class<? extends Annotation> annotation = aspect.value(); if(annotation != null && annotation.equals((Aspect.class))) { for (Class<?> aClass : allClassSet) { if(aClass.isAnnotationPresent(annotation)) { targetClassSet.add(aClass); //这里从所有的class集合中挑出被annotation类型注解的class对象集合 } } } return targetClassSet; } }
注意:代码从1写到7,从7到1理解有助于了解整体工作流程,整个用了责任链模式、模板方法模式,CGLIB动态代理。