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方法给需要的属性赋值.由于没有接口实现,所以创建的是对象的子类代理对象.

  • 相关阅读:
    百度面试题
    京东2014年招聘会成都站笔试经历
    把一个字符串的大写字母放到字符串的后面,各个字符的相对位置不变,不能申请额外的空间
    POJ 2234 Matches Game
    POJ 3903 Stock Exchange
    POJ 2853 Sequence Sum Possibilities
    POJ 3519 Minimal Backgammon
    POJ 2096 Collecting Bugs
    POJ 3071 Football
    HDU 1175 连连看
  • 原文地址:https://www.cnblogs.com/zhouchangyang/p/10920202.html
Copyright © 2011-2022 走看看