美文网首页
Java高级主题(六)下——Ioc+AOP原理

Java高级主题(六)下——Ioc+AOP原理

作者: 路远处幽 | 来源:发表于2019-12-20 13:45 被阅读0次

    三、AOP实现

    前面多次谈到AOP,以及我们的Bean是通过原始对象+代理对象,这里来看看AOP部分的实现:
    AOP说到底主要目的不是产生代理对象,而是要通过代理对象执行方法,并对方法进行有效的拦截!
    简单起见,将拦截分为置前,置后,以及出现异常时的拦截。
    而拦截又是何时产生的?
    还是为了简单实现,后面都只使用CGLibProxy,有关CGLib的代理我在上一篇有介绍,这里也就不累赘了。

    关于拦截器的产生,我之前的实现方式是给要拦截的方法添加注解,给出拦截Id,然后提供一套方法,给指定Id号的方法创建拦截器,但是,在知道Spring的处理后,这种方式很快被否定了!在工程中,往往很多需要拦截的方法是不允许侵入式修改的,又或者是被打成了jar包,那么就更不可能对其添加注解,所以给出新的解决思路:
    由用户自己写一个方法,然后给这个方法添加注解,使其和要拦截的方法产生对映射关系,这样我们实际执行的拦截器方法完全是由用户提供,并不会干预源代码!
    前面说过只是处理置前,置后,以及出现异常时的拦截,所以会给出三种不同的注解,用于区分!
    由于是要使用注解,那么就要用到包扫描【Java】包、jar包的扫描
    包扫描就需要对类进行区分,只处理带有标识的类,所以还缺少一个对类的注解:

    @Aspect

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Aspect {
    }
    

    这个注解只是为了表明这个类存放着用户编写的拦截器方法!

    主要的是下面三个注解:
    @Before

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Before {
        Class<?> klass();
         String method();
    }
    

    置前拦截方法的注解,klass表明对哪个类进行置前拦截,method表明对哪个方法进行拦截,但发现仅仅通过这好像不能找到具体的方法,但仔细想一想,置前拦截是对要拦截的方法参数进行判断,用户在编写拦截时必然知道拦截的方法是什么,参数个数和类型当然也知道,那我们只要让用户写的方法的参数和要拦截的方法参数保持一致就行了,如果不一致,就异常处理!这样就能通过用户编写的方法,知道被拦截的方法参数,进而定位到具体要拦截的方法!

    @After

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface After {
        Class<?> klass();
        String method();
        Class<?>[] parameterTypes() default {};
    }
    

    置后拦截方法的注解,同置前拦截一样,klass表明对哪个类进行置前拦截,method表明对哪个方法进行拦截。由于之后拦截是对方法执行结果的操作,用户写的方法的参数有且只有一个,且参数类型要与原方法的返回值类型匹配,这样,我们就不能像处理@Before时一样,必须申明被拦截的方法的参数类型,只有这样才能定位到具体的被拦截方法!

    @Throwable

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Throwable {
        Class<?> klass();
        String method();
        Class<?>[] parameterTypes();
    }
    

    出现异常时拦截的注解,和@After一样,它只是处理异常,那么用户所提供的方法的参数有且只有一个,类型是执行被拦截方法产生的异常类型,它也必须传递被拦截方法的参数,使其定位到具体的被拦截方法!

    其实在Spring里面使用了更为犀利的手段,它并没有使用Class<?> 只是使用了一个字符串就解决了类、方法、参数的定位,只不过它就需要对字符串解析,利用了正则表达式,虽然比我的方法繁琐,但面向用户的使用是十分友好的!
    AOP框图

    image.png
    看起来我把它做的很复杂,事实上这里用到的全是接口,是非常灵活的,如果说不想使用这套方式,那么可以自己实现Advice接口;如果说拦截器链用的时list存储,以后想更换为链表也是可以的;拦截器的产生不想使用上面说的注解方式,那么自己去实现IntercepterFactory接口!

    AopFactory

    public class AopFactory {
        private Advice advice;
        
        public AopFactory() {
        }
        
        public AopFactory setAdvice(Advice advice) {
            this.advice = advice;
            return this;
        }
        
        public <E> E creatCGLibProxy(Class<?> klass) throws Exception {
            return creatCGLibProxy(klass, klass.newInstance());
        }
        
        public <E> E creatCGLibProxy(Object object) {
            return creatCGLibProxy(object.getClass(), object);
        }
        
        @SuppressWarnings("unchecked")
        public <E> E creatCGLibProxy(Class<?> klass, Object object) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(klass);
            enhancer.setCallback(new MethodInterceptor() {    
                @Override
                public Object intercept(Object proxyObject, Method method, 
                        Object[] args, MethodProxy methodProxy) throws Throwable {
                    return doInvoke(object, method, args);
                }
            });
            
            return (E) enhancer.create();
        }
        
        private Object doInvoke(Object object, Method method, 
                Object[] args)
                 throws Throwable{
            Object result = null;
            // AdviceAdapter是Advice的适配,什么都不做
            advice = advice == null ? new AdviceAdapter() : advice;
            if (!advice.dealBefore(method, args)) {
                return result;
            }
            try {
                result = method.invoke(object, args);
                result = advice.dealAfter(method, result);
            } catch (Throwable e) {
                advice.delaThrowable(method, e);
                throw e;
            }
            
            return result;
        }
    
    }
    

    AopFactory只负责产生代理对象,而代理的拦截就下发给Advice

    Advice

    public interface Advice {
        boolean dealBefore(Method method, Object[] args);
        Object dealAfter(Method method, Object result);
        void delaThrowable(Method method, Throwable e);
    }
    

    暂时只处理置前、置后、以及异常,以后需要再添加,而不是修改!这就是使用接口的好处!

    IntercepterLink

    public interface IntercepterLink {
        boolean add(IntercepterMethod tagMethod);
        public boolean doBefore(Object[] args);
        Object doAfter(Object result);
        void doThrowable(Throwable e);
    }
    

    拦截器链,一个方法可以由多个拦截器,拦截器链是拦截器方法的真正执行者!提供了添加拦截器,处理置前、置后、异常,也可以给个remove,这里就不写了。

    IntercepterFactory

    public interface IntercepterFactory {
        void addBeforeIntercepter(Method tagMethod, IntercepterMethod imd);
        void addAfterIntercepter(Method tagMethod, IntercepterMethod imd);
        void addThrowableIntercepter(Method tagMethod, IntercepterMethod imd);
        
        IntercepterLink getBeforeIntercepterLink(Method tagMethod);
        IntercepterLink getAfterIntercepterLink(Method tagMethod);
        IntercepterLink getThrowableIntercepterLink(Method tagMethod);
    }
    

    拦截器链的创建者和拥有着,其中的IntercepterMethod就是拦截器接口:

    IntercepterMethod

    public interface IntercepterMethod {
        Object getIntercepterObject();
        Method getIntercepterMethod();
    }
    

    我们的拦截器的执行是通过反射机制,那么就必须知道方法和对象,至于参实是通过CGLib代理机制传递过来的,就不用考虑!

    准备工作完成,接下来就是真真的处理部分:
    IntercepterFactory在上面说是拦截器的创建者和持有者,所以我把它的是实现类进行了分级:

    IntercepterLoader

    public class IntercepterLoader implements IntercepterFactory {
        // 可以看到每个方法都有自己的置前、置后、异常拦截器链
        private static  final Map<Method, IntercepterLink> beforeMap;
        private static  final Map<Method, IntercepterLink> afterMap;
        private static  final Map<Method, IntercepterLink> exceptionMap;
    
        static {
            beforeMap = new HashMap<>();
            afterMap = new HashMap<>();
            exceptionMap = new HashMap<>();
        }
        
        public IntercepterLoader() {
        }
        
        @Override
        public IntercepterLink getBeforeIntercepterLink(Method tagMethod) {
            return beforeMap.get(tagMethod);
        }
    
        @Override
        public IntercepterLink getAfterIntercepterLink(Method tagMethod) {
            return afterMap.get(tagMethod);
        }
    
        @Override
        public IntercepterLink getThrowableIntercepterLink(Method tagMethod) {
            return exceptionMap.get(tagMethod);
        }
    
        private void add(Map<Method, IntercepterLink> map, 
                Method tagMethod, IntercepterMethod imd) {
            IntercepterLink link = map.get(tagMethod);
            // 防止多线程的访问而创建不同的拦截器链
            if (link == null) {
                synchronized (map) {
                    if (link == null) {
                        // IntercepterNodeList是我这套机制默认的IntercepterLink实现类
                        link = new IntercepterNodeList(imd);
                    }
                }
                // 该方法还未创建拦截器链
                map.put(tagMethod, link);
            } else {
                // 方法相同,则在拦截器链上追加
                link.add(imd);
            }
        }
        
        @Override
        public void addBeforeIntercepter(Method tagMethod, IntercepterMethod imd) {
            add(beforeMap, tagMethod, imd);
        }
        
        @Override
        public void addAfterIntercepter(Method tagMethod, IntercepterMethod imd) {
            add(afterMap, tagMethod, imd);
        }
    
        @Override
        public void addThrowableIntercepter(Method tagMethod, IntercepterMethod imd) {
            add(exceptionMap, tagMethod, imd);
        }
    
    }
    

    真正意义上的拦截器持有者,它要完成的功能非常简单!

    我们是要通过注解的方式产生拦截器,所以就有更高级来处理:

    IntercepterLoaderFactory

    /**
    *  使用包扫描,找到带有@Before、@After、@Throwable的方法,将其添加至拦截器map中
    */
    public class IntercepterLoaderFactory extends IntercepterLoader {
        public IntercepterLoaderFactory() {
        }
        
        public IntercepterLoaderFactory parseMethodForPackage(String packageName) {
            new PackageScanner() {
                @Override
                public void dealClass(Class<?> klass) {
                    // 判断类是否满足我们定义的@Aspect
                    if (!klass.isAnnotationPresent(Aspect.class)) return;
                    try {
                        // 产生方法执行的对象
                        Object object = klass.newInstance();
                        Method[] methods = klass.getDeclaredMethods();
                        // 遍历所有方法,处理带有注解的方法
                        for (Method method : methods) {
                            if (method.isAnnotationPresent(Before.class)) {
                                parseBeforeIntercepter(klass, object, method, method.getAnnotation(Before.class));
                            } else if (method.isAnnotationPresent(After.class)) {
                                parseAfterIntercepter(klass, object, method, method.getAnnotation(After.class));
                            } else if (method.isAnnotationPresent(Throwable.class)) {
                                parseExceptionIntercepter(klass, object, method, method.getAnnotation(Throwable.class));
                            }
                        }
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }.scanPackage(packageName);
            return this;
        }
        
        /**
        *    处理带@Before注解的方法
        */
        private void parseBeforeIntercepter(Class<?> klass, Object object, Method method, Before before) {
            if (!method.getReturnType().equals(boolean.class)) {
                try {
                    throw new ReturnTypeNotMatch(method + "返回值类型必须是boolean!");
                } catch (ReturnTypeNotMatch e) {
                    e.printStackTrace();
                }
            }
            // 从@Before注解中获取被拦截方法的信息
            Class<?> targetClass = before.klass();
            String targetMethodName = before.method();
            Class<?>[] methodTypes = method.getParameterTypes();
    
            try {
                Method targetMethod = targetClass.getDeclaredMethod(targetMethodName, methodTypes);
                // 父类的方法调用,其中的IntercepterMethodDefination是IntercepterMethod的实现类
                addBeforeIntercepter(targetMethod, new IntercepterMethodDefination(object, method));
            } catch (NoSuchMethodException e) {
                try {
                    throw new IntercepterMethodParaNotMatch(method + "参数不匹配!");
                } catch (IntercepterMethodParaNotMatch e1) {
                    e1.printStackTrace();
                }
            }
        }
    
        /**
        *    处理带@After注解的方法
        */
        private void parseAfterIntercepter(Class<?> klass, Object object, Method method, After after) {
            //    从@After注解中获取被拦截方法的信息
            Class<?> targetClass = after.klass();
            String targetMethodName = after.method();
            Class<?>[] targetMethodPara = after.parameterTypes();
            try {
                // 通过上述参数得到被拦截方法,如果得不到,异常处理
                Method targetMethod = targetClass.getDeclaredMethod(targetMethodName, targetMethodPara);
                Class<?> targetMethodReturnType = targetMethod.getReturnType();
                Class<?> methodReturnType = method.getReturnType();
                Class<?>[] methodParameters = method.getParameterTypes();
                //    判断是否满足置后拦截方法的条件:
                //    置后拦截的方法返回值类型必须和被拦截方法相同
                //    置后拦截的方法的参数有且只有一个,且是被拦截的方法返回值类型
                if (methodParameters.length != 1
                        || !targetMethodReturnType.equals(methodReturnType)
                        || !methodReturnType.equals(methodParameters[0])) {
                    try {
                        throw new IntercepterMethodParaNotMatch("拦截器方法:" + method + 
                                " 与被拦截方法" + targetMethod + "不匹配!");
                    } catch (IntercepterMethodParaNotMatch e) {
                        e.printStackTrace();
                    }
                }
                
                addAfterIntercepter(targetMethod, new IntercepterMethodDefination(object, method));
            } catch (NoSuchMethodException | SecurityException e) {
                try {
                    throw new IntercepterMethodParaNotMatch("被拦截方法[" + targetMethodName + "]不存在!");
                } catch (IntercepterMethodParaNotMatch e1) {
                    e1.printStackTrace();
                }
            }
        }
    
        private void parseExceptionIntercepter(Class<?> klass, Object object, Method method, Throwable throwable) {
                //    从@Throwable 注解中获取被拦截方法的信息
                Class<?> targetClass = throwable.klass();
                String targetMethodName = throwable.method();
                Class<?>[] targetMethodPara = throwable.parameterTypes();
                try {
                    // 通过上述参数得到被拦截方法,如果得不到,异常处理
                    Method targetMethod = targetClass.getDeclaredMethod(targetMethodName, targetMethodPara);
                    Class<?>[] methodParameters = method.getParameterTypes();
                    //    判断是否满足异常拦截方法的条件:
                    //    异常拦截的方法的参数有且只有一个,且是java.lang.Throwable
                    if (methodParameters.length != 1
                            || methodParameters[0].equals(java.lang.Throwable.class)) {
                        try {
                            throw new IntercepterMethodParaNotMatch("拦截器方法:" + method + 
                                    " 与被拦截方法" + targetMethod + "不匹配!");
                        } catch (IntercepterMethodParaNotMatch e) {
                            e.printStackTrace();
                        }
                    }
                    addAfterIntercepter(targetMethod, new IntercepterMethodDefination(object, method));
                } catch (NoSuchMethodException | SecurityException e) {
                    try {
                        throw new IntercepterMethodParaNotMatch("被拦截方法[" + targetMethodName + "]不存在!");
                    } catch (IntercepterMethodParaNotMatch e1) {
                        e1.printStackTrace();
                    }
                }
        }
        
    }
    

    通过这套机制,我们就能通过注解+包扫描,十分方便地给指定方法添加拦截了!

    IntercepterMethodDefination

    public class IntercepterMethodDefination implements IntercepterMethod {
        private Object intercepterObject;
        private Method intercepterMethod;
        
        protected IntercepterMethodDefination() {
        }
    
        protected IntercepterMethodDefination(Object intercepterObject, Method intercepterMethod) {
            this.intercepterObject = intercepterObject;
            this.intercepterMethod = intercepterMethod;
        }
    
        @Override
        public Object getIntercepterObject() {
            return intercepterObject;
        }
    
        @Override
        public Method getIntercepterMethod() {
            return intercepterMethod;
        }
    
    }
    

    拦截器方法执行所需的封装

    拦截器我们也有了,就剩下拦截器链了:
    我的拦截器链使用了链表,为了能够方法地链式调用,也就是设计模式之一的职责链模式,当然也可以使用List,只不过使用链表相比于List,在处理时都需要遍历,没有什么差别,但是链表比List占的空间小,List在内部是数组,且数组大小是大于有效元素个数的!

    IntercepterNodeList

    public class IntercepterNodeList implements IntercepterLink {
        private IntercepterMethod imd; // 拦截器
        private IntercepterNodeList next; // 下一结点
        private IntercepterNodeList last; // 尾结点
        
        protected IntercepterNodeList() {
            this(null);
        }
        
        protected IntercepterNodeList(IntercepterMethod imd) {
            this.imd = imd;
            this.next = null;
            this.last = this;
        }
        
        /**
        *    尾插法追加结点
        */
        @Override
        public boolean add(IntercepterMethod imd) {
            if (next == null) {
                next = new IntercepterNodeList(imd); 
                last = next;
            } else {
                last = last.next = new IntercepterNodeList(imd);
                last.next = null;
            }
            return true;
        }
        /**
        *    链式调用处理置前拦截
        */
        @Override
        public boolean doBefore(Object[] args) {
            boolean isContinue = this.innerInvoke(imd.getIntercepterObject(), imd.getIntercepterMethod(), args);
            if (this.next != null && isContinue) {
                isContinue = this.next.doBefore(args);
            }
            return true;
        }
        
        @SuppressWarnings("unchecked")
        private <T> T innerInvoke(Object object, Method method, Object[] args) {
            T result = null;
            try {
                result = (T) method.invoke(object, args);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
        
        /**
        *    链式调用处理置后拦截
        */
        @Override
        public Object doAfter(Object result) {
            result = innerInvoke(imd.getIntercepterObject(), imd.getIntercepterMethod(), new Object[] {result});
            if (this.next != null) {
                result = this.next.doAfter(result);
            }
            return result;
        }
        
        /**
        *    链式调用处理异常拦截
        */
        @Override
        public void doThrowable(Throwable e) {
            innerInvoke(imd.getIntercepterObject(), imd.getIntercepterMethod(), new Object[] {e});
            if (this.next != null) {
                this.next.doThrowable(e);
            }
        }
        
    }
    

    AOP到这里就已经结束了,来看看它的使用吧:
    被拦截类及其方法:

    public class Test {
        public Test() {
        }
        
        public String fun(int arg) {
            System.out.println("Test的fun方法执行 arg = " + arg);
            return "fun";
        }
    }
    

    拦截器所在类:

    @Aspect
    public class Action {
        public Action() {
        }
        
        @Before(klass=Test.class, method="fun")
        public boolean beforeFun(int arg) {
            System.out.println("置前拦截beforeFun:arg = " + arg);
            return true;
        }
        
        @Before(klass=Test.class, method="fun")
        public boolean beforeFunOther(int arg) {
            System.out.println("置前拦截beforeFunOther:arg = " + arg);
            return true;
        }
        
        @After(klass=Test.class, method="fun", parameterTypes= {int.class})
        public String AfterFun(String arg) {
            System.out.println("置后拦截:arg = " + arg);
            
            return "AfterFun";
        }
    }
    

    主函数:

    public static void main(String[] args) throws Exception {
            IntercepterLoaderFactory intercepterLoaderFactory = 
                    new IntercepterLoaderFactory().parseMethodForPackage("com.zc.action");
            
            AopFactory aopFactory = new AopFactory();
            aopFactory.setAdvice(new AdviceHander()
                    .setIntercepterFactory(intercepterLoaderFactory));
            
            Test testProxy = aopFactory.creatCGLibProxy(Test.class);
            System.out.println(testProxy.fun(10));
    }
    

    执行结果:


    image.png

    这样的用法是有些恶心了,但是,别忘了,AOP配合IOC才是使用的精华:
    注解方式的注入:

    @Component
    public class StudentA {
        @Value(value="我是A")
        String name;
        @Autowired
        private StudentB B;
        
        public String fun(int arg) {
            System.out.println("StudentA的fun方法执行 arg = " + arg);
            return "fun";
        }
        
        @Override
        public String toString() {
            return "A:" + name + "->" +  B;
        }
        
    }
    
    @Component
    public class StudentB {
        @Value(value="我是B")
        private String name;
        @Autowired
        private StudentC C;
        
        public StudentB() {
        }
    
        @Override
        public String toString() {
            return "B:" + name + "->" + C;
        }
        
    }
    
    @Component
    public class StudentC {
        @Value(value="我是C")
        private String name;
        @Autowired
        private StudentD D;
        
        @Autowired
        private StudentA A;
        
        public StudentC() {
        }
    
        @Override
        public String toString() {
            return "C:" + name + "->" + D;
        }
        
    }
    

    Xml方式的注入:

    <SimpleSpring>
        <bean class="com.zc.ioc.demo.StudentD">
            <property name="name" value="我是D"></property>
        </bean>
    </SimpleSpring>
    

    拦截器:

    @Aspect
    public class Action {
        public Action() {
        }
        
        @Before(klass=StudentA.class, method="fun")
        public boolean beforeFun(int arg) {
            System.out.println("置前拦截beforeFun:arg = " + arg);
            return true;
        }
        
        @Before(klass=StudentA.class, method="fun")
        public boolean beforeFunOther(int arg) {
            System.out.println("置前拦截beforeFunOther:arg = " + arg);
            return true;
        }
        
        @After(klass=StudentA.class, method="fun", parameterTypes= {int.class})
        public String AfterFun(String arg) {
            System.out.println("置后拦截:arg = " + arg);
            return "AfterFun";
        }
        
    }
    

    主函数:

    public static void main(String[] args) throws Exception {
            new IntercepterLoaderFactory().parseMethodForPackage("com.zc.action");
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/test_simple_spring.xml");
            StudentD studentD = applicationContext.getBean(StudentD.class);
            System.out.println(studentD);
            
            applicationContext = new AnnotationConfigApplicationContext("com.zc.model");
            StudentA studentA = applicationContext.getBean(StudentA.class);
            studentA.fun(10);
            System.out.println(studentA);
    }
    

    执行结果:

    image.png
    Spring的IOC和AOP就是先到这里了,有兴趣的可以交流一下
    感谢您的阅读(_)

    参考:
    模拟Sping,实现其IOC和AOP核心(一)
    用 JDK 动态代理 API 写的简单的 AOP 程序

    相关文章

      网友评论

          本文标题:Java高级主题(六)下——Ioc+AOP原理

          本文链接:https://www.haomeiwen.com/subject/hrvdsctx.html