zoukankan      html  css  js  c++  java
  • Spring代理模式及AOP基本术语

    一、代理模式:

    静态代理、动态代理

    动态代理和静态代理区别??

    解析:静态代理需要手工编写代理类,代理类引用被代理对象。

            动态代理是在内存中构建的,不需要手动编写代理类

    代理的目的:是为了在原有的方法上进行增强。

    动态代理的两种方式:JDK动态代理与CGLIB代理
    默认情况下,Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的代理使用CGLIB来实现。

    二、静态代理

    ①定义接口 Subject并添加方法:

    public interface Subject {
    public void request();
    }

    ② 定义接口的实现类 RealSubject并实现该接口,重写方法--被代理对象

    public class RealSubject implements Subject{
     
        public void request() {
            System.out.println("真实主题内容");
         
        }

    ③定义接口的实现类ProxySubject并实现该接口重写方法。自定义属性RealSubject,调用request方法,在这里进行增强

    public class ProxySubject implements Subject{
     
    private RealSubject real;
         
    public void request() {
        System.out.println("===before===");
        //调用request()
        real.request();
        System.out.println("===after===");
             
        }
     
     
        public RealSubject getReal() {
            return real;
        }
        public void setReal(RealSubject real) {
            this.real = real;
        }

    ④测试类: 

    分别创建出被代理对象和代理对象,执行方法

    public class ProxyTest {
    //静态代理
       @Test
       public void test01(){
        //准备一个真实主题,被代理对象
        RealSubject real=new RealSubject();
        //创建一个代理对象
        ProxySubject proxy=new ProxySubject();
         
        proxy.setReal(real);
        proxy.request();
    }

     执行效果:

    可以看出静态代理类有一个缺点:当如果接口加一个方法,所有的实现类和代理类里都需要做个实现。这就增加了代码的复杂度。动态代理就可以避免这个缺点。 

    三、动态代理

    1.1、JDK动态代理:

    本质:在内存中构建接口实现类

    特点:被代理对象必须有接口 

     JDK提供了java.lang.reflect.Proxy类来实现动态代理的,可通过它的newProxyInstance来获得代理实现类。同时对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。可以对实际的实现进行一些特殊的处理,像Spring AOP中的各种advice。下面来看看如何使用。

    定义接口IUserDao:

    public interface IUserDao {
     
    public String add();
     
    public String edit();
    }

    定义接口实现类,实现某接口,并重写该方法:

    public String add() {
        System.out.println("==add===");
        return "我是add";
         
    }
     
    public String edit() {
        System.out.println("===edit===");
        return "edit";
         
    }

    测试类:

    实现效果:

    可以由源码可见如何生成代理类的:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException {
            if (h == null) {
                throw new NullPointerException();
            }
     
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
            // 这里是生成class的地方
            Class<?> cl = getProxyClass0(loader, intfs);
            // 使用我们实现的InvocationHandler作为参数调用构造方法来获得代理类的实例
            try {
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                    return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        public Object run() {
                            return newInstance(cons, ih);
                        }
                    });
                } else {
                    return newInstance(cons, ih);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString());
            }
        }
    其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,这里重点是看getProxyClass0这个方法的实现:
        private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { 
            // 代理的接口数量不能超过65535
            if (interfaces.length > 65535) { 
                throw new IllegalArgumentException("interface limit exceeded"); 
            } 
            // JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理 
            return proxyClassCache.get(loader, interfaces); 
        } 

    可以看到,动态生成的代理类有如下特性:
       1) 继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
       2) 提供了一个使用InvocationHandler作为参数的构造方法。
       3) 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
       4) 重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。

    ——————————————————————————————————————————————————————————-————————————————————

    1.2、cglib动态代理:

    JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要cglib了。cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用  

     创建被代理的类:

    public class UserService {
    public void delete(){
        System.out.println("delete ok!!!");
    }
    }

    测试类:

    该类实现了创建子类的方法与代理的方法。SuperClass方法通过入参即父类的字节码,通过扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类实例。proxy.invoke(obj, args)通过代理类调用父类中的方法 !

     

    实现效果:

     

    ———————————————————————————————————————————————————————————————————————————————

    四、AOP基本术语:

    AOP Aspect Oriented Programming 面向切面编程

    在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    面向对象编程是从【静态角度】考虑程序的结构,而面向切面编程是从【动态角度】考虑程序运行过程。
    AOP底层,就是采用【动态代理】模式实现的。采用了两种代理:JDK动态代理和CGLIB动态代理。

    基本术语(一些名词): 
    (1)切面(Aspect)
    切面泛指[*交叉业务逻辑*]。事务处理和日志处理可以理解为切面。常用的切面有通知(Advice)与顾问(Advisor)。实际就是对主业务逻辑的一种增强。

    (2)织入(Weaving)
    织入是指将切面代码插入到目标对象的过程。代理的invoke方法完成的工作,可以称为织入。

    (3) 连接点(JoinPoint) 
    连接点是指可以被切面织入的方法。通常业务接口的方法均为连接点

    (4)切入点(PointCut)
    切入点指切面具体织入的方法
    注意:被标记为final的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

    (5)目标对象(Target)
    目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。

    (6)通知(Advice) 
    通知是切面的一种实现,可以完成简单的织入功能。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是执行之后执行等。切入点定义切入的位置,通知定义切入的时间。

    (7)顾问(Advisor)
    顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。

  • 相关阅读:
    ceph集群jewel版本 rbd 块map 报错-故障排查
    基本的Ceph性能测试工具和方法
    dd命令的高级应用
    Ceph recover的速度控制
    Linux mount命令
    Centos7.2:搭建Ceph管理系统Inscope
    rpm --import /etc/pki/rpm-gpg/RPM* 有什么用?
    dd命令的解释
    Playbooks 中的错误处理
    Ansible之Playbooks的when语句
  • 原文地址:https://www.cnblogs.com/Smile-123/p/6008548.html
Copyright © 2011-2022 走看看