zoukankan      html  css  js  c++  java
  • Java_AOP原理

    AOP :

    面向切面编程

    在程序设计中,我们需要满足低耦合高内聚,所以编程需满足六大原则,一个法则.

    AOP面向切面编程正是为了满足这些原则的一种编程思想.

    一.装饰者模式:

    当我们需要给对象增加功能时,为了满足单一职责原则,可利用装饰者模式编程,创建一个类用来装饰原来的类,这个类写需要在原来的功能上增加的功能.

    比如:一个类里面有一个增加图书的功能,

    @Service
    public class BookSericeImpl implements BookSerice {
        @Override
        public void addOne(BokBean bokBean) {
            System.out.println("执行逻辑:插入一本书");
        }
        @Override
        public void deletOne(Long bookId) {
            System.out.println("执行逻辑:删除一本书");
        }
    }

    我们需要在这个基础上新增打印日志的功能,

    public class BooklogServiceImpl implements BookSerice {
        private BookSerice bookSerice;
        public BooklogServiceImpl(BookSerice bookSerice) {
            this.bookSerice = bookSerice;
        }
        @Override
        public void addOne(BokBean bokBean) {
    
            System.out.println("准备新增一本书");
            this.bookSerice.addOne(bokBean);
            System.out.println("新增一本书完成");
        }
        @Override
        public void deletOne(Long bookId) {
    
            System.out.println("准备删除一本书");
            this.bookSerice.deletOne(323L);
            System.out.println("删除一本书完成");
        }
    }

    下面我们调用这个增强过后的的对象

       public void test1(){
    
            //Aop :面向切面编程
    
            //使用装饰者模式设计对象
            BookSerice bookSerice = new BookSericeImpl();
            //把原来功能的对象通过构造方传给新增功能的类,并把新增功能类的对象赋给原来对象
            //这里新增功能类和原来的类都是实现了同一个接口.
            bookSerice = new BooklogServiceImpl(bookSerice);
            //调用新增功能类的方法,在这个方法里让构造方法传过去的对象调用原来的功能
            bookSerice.addOne(new BokBean());
        }

    这样我们就在不改变原来代码的基础上新增了功能,并且也满足单一职责的原则,降低了代码的耦合性.

    但是如果接口里面有很多方法,如果每个方法都需要增加日志功能,这样就会出现很多重复代码,并且装饰者模式不能同时为多个没有关系的类同时增强

    所以java引入动态代理技术来增加功能.

    二.动态代理

    在java里动态代理有两个实现方式:

    ①针对有接口的类的代理,使用jdk中反射包下的动态代理

    ②针对没有接口的类的代理,使用第三方的jar包Enhancer

    如果一个类既没有接口,又是final,那么不能进行增强

    1.第一种实现: reflect包下的Proxy

    基于接口的动态代理,使用java内部反射包增强

    这种方式创建对象是目标对象的兄弟对象.

    同样上面是实现了接口的两个功能的类:

    @Service
    public class BookSericeImpl implements BookSerice {
        @Override
        public void addOne(BokBean bokBean) {
            System.out.println("执行逻辑:插入一本书");
        }
        @Override
        public void deletOne(Long bookId) {
            System.out.println("执行逻辑:删除一本书");
        }
    }
    调用通过对象调用上面两个方法:
    public void test2(){
    
        //创建需要代理的对象
        BookSerice bookSerice = new BookSericeImpl();
        //根据对象的类获取类加载器
        ClassLoader classLoader = bookSerice.getClass().getClassLoader();
        //获取被代理对象说实现的所有接口
        Class<?>[] interfaces = bookSerice.getClass().getInterfaces();
        //新建代理对象,里面参数需要(类加载器,一个对象所实现的接口,InvocationHandler接口类的对象)
        bookSerice = (BookSerice) Proxy.newProxyInstance(classLoader, interfaces, new LogHandler(bookSerice)); 
        bookSerice.addOne(new BokBean());
        bookSerice.deletOne(232L);
    }

    在创建代理对象的时候需要一个InvocationHandler接口类的对象,下面创建一个该类的实现类

    public class LogHandler implements InvocationHandler {
    
        //通过构造方法接受一个没有被代理的原来的对象
        //通过下面的方法名的反射找到这个对象对应方法
        private Object target;
        public LogHandler(Object target) {
            this.target = target;
        }
        //当代理对象调用原方法的时候,就会调用这个invoke方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String classname = target.getClass().getName();
            String methodName = method.getName();
            System.out.println(classname+"."+methodName+"方法开始执行");
            //这里实际是Method类通过方法名反射调用了原方法(addone)
            Object value = method.invoke(target, args);
            System.out.println(classname+"."+methodName+"方法执行完毕");
            return value;
    
        }
    }

    这样实现了动态代理之后,不管实现的接口里有多少个方法,你只需调用该方法,就会增强该方法,而不需要针对每个方法写一遍增强功能,

    并且这个增强类LogHandler类和原来的实现类BookSericeImpl类并没有耦合性,这就是说不管你是什么接口类的实现类,只需要对该类的对象进行代理即可,就能对该类的方法添加上这个新增的功能

    总的来说,这种动态代理实现方式就是利用反射技术,找到调用的方法名,针对这个方法进行增强.

    如果当不需要对某方法增加功能时,就不用不带.

    2.第二种实现:cglib包下的Enhancer类

    基于类的动态代理,使用cglib框架.
    这种方式创建的代理对象是目标对象的子类对象

     第二种方式是利用第三方jar包来实现,下载CGLIB包:

    利用jar包中的Enhancer类创建增强对象.

    创建增强对象需要根据原对象的类名创建类增强器,还需要根据原对象的类型创建子类代理对象

    属性通过增强对象set方法赋值,上一种方式是通过调用方法Proxy.newProxyInstance传参.

      public void test3(){
    
            //创建需要代理增强的对象
            BookSerice bookSerice = new BookSericeImpl();
            Enhancer enhancer = new Enhancer();
            //用增强器对象创建类增强器
            enhancer.setClassLoader(bookSerice.getClass().getClassLoader());
    
            //因为创建的代理对象是目标对象的子类,所以这里填的就是目标对象的类
            enhancer.setSuperclass(bookSerice.getClass());
    
            //创建代理对象,这里需要的参数是Callback接口的对象,所以需要创建一个接口的实现类.
            enhancer.setCallback(new TimeMethodInterceptor(bookSerice));
            //把代理对象赋给原对象
            bookSerice = (BookSerice) enhancer.create();
            bookSerice.addOne(new BokBean());
            bookSerice.deletOne(1l);
            
        }

    创建Callback接口的实现类,也就是功能增强部分,

    这一部分跟第一种方式的实现是一样的,都是通过反射在添加功能过程中调用原方法.

    //Callback接口没有实现方法,所以这里实现的是他的子接口
    public class TimeMethodInterceptor implements MethodInterceptor {
    
        private  Object target;
    
        public TimeMethodInterceptor(Object target) {
            this.target = target;
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            //记录当前系统时间
            //这个时间是从1970年1月1日 0点0分到现在的毫秒数
            long start = System.currentTimeMillis();
            Object value = method.invoke(target, objects);
            long time = System.currentTimeMillis() - start;
            System.out.println("当前时长"+time+"毫秒");
            return null;
        }
    }

    总结:

    两种方法的区别:

    第一种是用jdk内部方法创建代理对象,由于创建过程中需要一个对象的接口,所以只能针对有接口类的对象进行代理.

    第二种是利用第三方jar包中的增强器(Enhancer)创建代理对象,通过set方法给需要的属性赋值.由于没有接口实现,所以创建的是对象的子类代理对象.

  • 相关阅读:
    ios UIWebView截获html并修改便签内容(转载)
    IOS获取系统时间 NSDate
    ios 把毫秒值转换成日期 NSDate
    iOS  如何判断当前网络连接状态  网络是否正常  网络是否可用
    IOS开发 xcode报错之has been modified since the precompiled header was built
    iOS系统下 的手机屏幕尺寸 分辨率 及系统版本 总结
    iOS 切图使用 分辨率 使用 相关总结
    整合最优雅SSM框架:SpringMVC + Spring + MyBatis 基础
    Java面试之PO,VO,TO,QO,BO
    Notes模板说明
  • 原文地址:https://www.cnblogs.com/zhouchangyang/p/10920202.html
Copyright © 2011-2022 走看看