• Spring之AOP一


    面向切片式编程不仅在Java中存在,在其他语言也是存在,例如asp.net的管道模型中,可以利用aop来进行自定义一些操作,比如权限认证、日志等。今天主要是引入AOP,具体它涉及到的专有名词先不做解释。

    一、横切纵切

    首先要知道什么是横切什么是纵切(吐槽大会的观众们这也是知识点),下面的两张图是从网上找的,是讨论刨妇产是横切好还是纵切好的问题,知道什么是横切什么是纵切之后,我们也可以用莲藕来做比喻,横切切出来是圆的,纵切就不是了。

    二、AOP

    对于一些系统特别时生产环境的问题有些是在UAT或者ST环境是发现不了的,但又不可能用真时的正式环境来进行测试,我们做项目一般都会增加日志输出,这样未来可以通过日志来定位发现问题,那就需要在每个方法中增加日志信息,并需要进行捕获异常,如果按照一般的做法那就需要在每个方法中增加日志信息,并在方法中增加try catch来进行捕获日常,这样就会很麻烦,哪天我又不想增加日志信息、捕获异常了那还需要修改代码,而且影响的范围也是特别的大,这样就体现不了低耦合的特点。那该如何是好呢?于是乎AOP来了。AOP技术利用一种称为“横切”的技术,解剖封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,这样就能减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。红字部分基本把AOP的作用解释清楚了,它会将多个类的公共行为封装到一个可重用模块,然后将这个模糊注入里面,这里有个切的过程,不是内部操作,内部操作那还是复用了,是外部来进行操作。

    三、代码中实现AOP

     还是在上面博客代码的基础上,新建了一个ServiceImplA类,它实现了IService接口,我想实现这样一个功能,就是想在调用service方法前后增加日志打印或为service方法增加try catch,那该怎么做呢?

    package Cuiyw.Spring.IService;
    
    public interface IService {
    
        public void service(String name) throws Exception;
    }
    View Code
    package Cuiyw.Spring.Service;
    
    import Cuiyw.Spring.IService.IService;
    
    public class ServiceImplA implements  IService{
    
        public void service(String name) {
            // TODO Auto-generated method stub
            System.out.println("ServiceImplA name"+name);
        }
    
    }
    View Code

    1.在每处调用的地方增加日志和try catch

    这种也是一种方法,但缺点是很明显的,就是每处都要更改,量也会很大,显然不可取。这里是每个点都要加,一个方法可能被调用多处,那就要写多次。

    2.代理模式

    代理模式又分为动态代理模式和静态代理模式。

    (1)静态代理

    静态代理关键是在代理对象和目标对象实现共同的接口,并且代理对象持有目标对象的引用。这里我创建了类ProxyServiceA,它实现IService接口,同时将实现IService接口的对象作为一个属性。

    package Cuiyw.Spring.Service;
    
    import Cuiyw.Spring.IService.IService;
    
    public class ProxyServiceA implements  IService{
    
        public ProxyServiceA(IService service) {
            super();
            this.service = service;
        }
        private IService service;
        public void service(String name) throws Exception {
            // TODO Auto-generated method stub
            System.out.println("log start");
            try{
                service.service(name);
            }
            catch(Exception e)
            {
                 throw e;
            }
            System.out.println("log end");
        }
    
    }
    View Code

    上面的代码确实能为实现IService接口的对象增加调用前后的方法,并且增加了try catch。但是问题又来了。项目中接口可不止一个,可能会有很多,而且每个接口中的方法也会有好多,如果这样一个一个的增加不也是个问题。to be or not to be,that is a question.虽然比第一种方式,这种只需在每个方法中设置一次。

    (2)动态代理

    静态代理虽然也能解决上面增加日志和try catch来捕获异常,但是还是很麻烦,每个方法都要设置,于是动态代理来解决这个问题了。动态代理实现主要是实现InvocationHandler,并且将目标对象注入到代理对象中,利用反射机制来执行目标对象的方法。

    package Cuiyw.Spring.Service;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class DynaProxyServiceA implements InvocationHandler {
    
        private Object target;//目标对象
        public Object bind(Object object){
            this.target = object;
            return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            
            System.out.println("log start");
            try{
                 result = method.invoke(this.target, args);
            }
            catch(Exception e)
            {
                 throw e;
            }
            System.out.println("log end");
            return result;
        }
    
    }
    View Code
               IService service = (IService) new DynaProxyServiceA().bind(new ServiceImplA());
               service.service("cuiyw");
    View Code

     (3)2的升华

    上面通过动态代理可以实现在方法前后打印日志以及捕获异常,但是打印日志也可能有几种日志啊,现在是之间输出到操作台,那可能会是输入到文件或数据库,那肿么办呢?这里打印日志的操作我们也可以进一步的抽象,还是先创建一个接口ILog,然后创建一个类ConsoleLog实现ILog这个接口。

    package Cuiyw.Spring.Service;
    
    import java.lang.reflect.Method;
    
    public interface ILog {
        public void start(Method method) throws Exception;
        public void end(Method method) throws Exception;
    }
    View Code
    package Cuiyw.Spring.Service;
    
    import java.lang.reflect.Method;
    import java.util.Date;
    
    public class ConsoleLog implements ILog{
    
    
        public void start(Method method) throws Exception {
            // TODO Auto-generated method stub
            System.out.println(new Date()+" method name:"+method.getName() + " start...");
    
        }
    
        public void end(Method method) throws Exception {
            // TODO Auto-generated method stub
            System.out.println(new Date()+" method name:"+ method.getName() + " end...");
        }
    
    }
    View Code

    然后将打印对象也在动态代理中抽象。

    package Cuiyw.Spring.Service;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class DynaProxyServiceA implements InvocationHandler {
    
        //调用对象
        private Object proxy;
        //目标对象
        private Object target;
        
        public Object bind(Object object,Object proxy){
            this.target = object;
            this.proxy=proxy;
            return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            
            //反射得到操作者的实例
            Class<? extends Object> clazz = this.proxy.getClass();
            //反射得到操作者的Start方法
            Method start =clazz.getDeclaredMethod("start",new Class[]{Method.class});
            //反射执行start方法
            start.invoke(this.proxy, new Object[]{method});
            //执行要处理对象的原本方法
            result=method.invoke(this.target, args);
            //反射得到操作者的end方法
            Method end = clazz.getDeclaredMethod("end", new Class[]{Method.class});
            //反射执行end方法
            end.invoke(this.proxy, new Object[]{method});
            return result;
        }
    
    }
    View Code
               IService service = (IService) new DynaProxyServiceA().bind(new ServiceImplA(),new ConsoleLog());
               service.service("cuiyw");
    View Code

    上面是把start、end都在动态代理中执行了,其实我们可以判断哪个方法有被实现,然后再进行执行。

    四、总结感悟

    上面标题是代码中实现AOP,虽并未提到AOP但是,如果把上面的能理解明白了,那AOP就很好理解了,也不用理解什么切点、连接点等这些名字就能用。

    最后聊几句大家可能都会遇到的问题,上周项目组开会说项目组有重组的可能,哎,感觉公司还真是够冷漠无情的,有利用价值就利用,没利用价值就被干掉,不管年龄大小,自身本来就没安全感,遇到这事还真是觉得对未来更加恐惧,害怕以后也会遇到这样的情况,这也让我更加下定决心要尽快把java学好。留给中国队的时间不多了。

  • 相关阅读:
    一步一步做高性能服务器(C++) -- Day 3
    一步一步做高性能服务器(C++) -- Day 2
    一步一步做高性能服务器(C++) -- Day 1
    一步一步做高性能服务器(C++) -- Day 0
    HDU ACMSTEPS 1.3.4
    铭记
    子窗口与主窗口分开处理
    autoit小贴士
    autoit使用WMIC获取硬件信息
    Windows WMIC命令使用详解
  • 原文地址:https://www.cnblogs.com/5ishare/p/8093965.html
Copyright © 2020-2023  润新知