• 自定义Aspect风格的AOP框架


    本篇博客参考《架构探险--从零开始写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动态代理。书中叫Proxy和ProxyChain叫做代理和代理链,改叫增强和增强链更容易理解,每一个Proxy就是对目标类的方法的一次功能增强。

    使用举例:

    可以提供一个@Login注解,可以用在方法或类上,然后实现一个AuthzAnnotationAspect继承AspectProxy,用@Aspect(Controller.class)注解,在before方法中判断目标方法或目标类是否被@Login注解了,如果是,使用Shiro的代码判断用户是否登录:
    PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
    if(principals == null || principals.isEmpty()) {
        throw new Exception(“当前用户未登录!”);
    }
    
  • 相关阅读:
    可以多个广告浮动的页面
    获取内容并截取长度用...替换
    用UIManager使Swing界面转换风格。
    Java FileInputStream
    win7 部署tomcat总结
    观察者模式
    Java RandomAccessFile
    Java 客户端界面功能:停止当前操作
    多线程的开启与管理
    log4j内容记录
  • 原文地址:https://www.cnblogs.com/kibana/p/11762432.html
Copyright © 2020-2023  润新知