AOP的实现原理(俩种代理模式)

Posted 风show

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AOP的实现原理(俩种代理模式)相关的知识,希望对你有一定的参考价值。

        AOP切面编程,主要是基于代理模式实现的,使用的代理主要有俩种,jdk代理(必须是针对接口的,至于为什么后面会讲到)和(Apache)cglib代理。

为什么要使用代理模式,我感觉主要把是为了使用生成的代理类拦截器默认的方法来实现个性化的方法来实现简单业务代码的入侵。

        代理模式的主要实现方式都是,实现代理方式默认的代理器 ,在拦截器中使用各自的代理类生成方式,生成一个本地的代理类,通过动态加载代理来实现方法的调用和回调代理器中默认执行的方法(在这个方法里实现个性化的业务代码入侵)

下面针对JDK的代理模式和Cglib的代理模式进行分别阐述和基本实现。


JDK代理模式:


    主要实现的类是 java.lang.reflect.InvocationHandler 这里的实现类随便定义(JDKIntercationHander),

    1、在设置起构造方法的时候将需要代理的类传入,方便之后方法的执行和代理方法的创建(代理类会生成一个本地代的.class文件)

    

public JdkIntercationHander(Object target) {

      super();

      this.target = target;

  }

    2、需要重写方法invoke(),这是代理器InvocationHandler 唯一的一个实现类,生成的代理类会回调这个方法。


public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("------------------before------------------");

//这里就是代理类实际调用方法的地方,在这段代码前后可以随便加入业务代码

Obejct result = method.invoke(target,arg)

System.out.println("------------------after------------------");

}

    3、在这里实现一个生成代理类的方法 JDK实现代理类使用的是java.lang.reflect.Proxy;


public Object getProxy(){

//Thread.currentThread().getContextClassLoader()  当前的上下文类加载器

//target.getClass().getInterfaces()   当前类的所有实现类(这就是为啥JDK必须是基于接口的)

//this 当前的代理器,用于后面生成代理类的回调,会通过类发射机制加载出类,然后调用起invoke()

        return Proxy.newProxyInstance(

Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(),this);

}

    4、调用的时候需要这样调用


@Test

  public void testProxy() throws Throwable {

      // 实例化目标对象

      UserService userService = new UserServiceImpl();

      // 实例化InvocationHandler ,传入需要代理的类

      JDKIntercationHander invocationHandler = new MyInvocationHandler(userService);

       // 根据目标对象生成代理对象 ,这是会生成一个.class文件 可以自己查看

      UserService proxy = (UserService) invocationHandler.getProxy();

      // 调用代理对象的方法

        proxy.add();

  }


Cglib代理模式:


    cglib实现的方式和JDK的大致相同只是实现的代理器不一样,生成代理类的方式也不一样。

    cglib 只要实现的类是org.springframework.cglib.proxy.MethodInterceptor,回调的函数数intercept()

    1、首先定义一个自己的实现类CGLIBProxy实现MethodInterceptor,

定义构造方法和JDK的相似


public  CGLIBProxy(Object target) throws ClassNotFoundException{

      this.target = target;

    }


    2、重写intercept()

    

    @Override

    public Object intercept(Object object, Method method, Object[] args,     MethodProxy methodProxy) throws Throwable      

{

    //balabala  业务代码

    method.invoke(object,args);

    //balabala  业务代码

}


    3、在这里实现一个生成代理类的方法 JDK实现代理类使用的是org.springframework.cglib.proxy.Enhancer;

    

public <T> T getProxy(){

      //cglib创建java代理的方法

       //this.target为代理目标,callback为回调函数(和JDK不用的是 这里只需要传入需要代理的反射类即可,不用传入类的实现类),但是回调函数的实现机制还是一样的。

      return (T) new Enhancer().create(this.target.getClass(),this);

  }

    4、调用的时候也和JDK的方式是一样的

   

public static void main(String[] args) throws ClassNotFoundException {

    Music music = new Music();

    CGLIBProxy cglibProxy = new CGLIBProxy(this);

    return ((Music)cglibProxy.getProxy()).sing();

}



    看到这里相信大家对AOP的俩种代理模式已经有个大致的了解,可能大家有比较感兴趣了,那么是怎么生成代理类的,每次调用的时候都生成一个代理类那是不是很恶心,不要担心,下面我们在深入的了解下代理类的生成,同样我们看看俩种代理模式生成代理类的差异。)

    JDK生成代理类(基于jdk1.8的,大家可以对照源码看,缓存有点复杂,设计到三级缓存各种锁,这里就不细说了)


java.lang.reflect.Proxy

 //这个方法返回的是一个Object(包装了下getProxyClass)   getProxyClass返回的是一个Class

    //权限控制

   // @CallerSensitive  这玩意是1.8才加的注解,使用是使用JDK的安全管理器,对方法的调用方做一些权限控制

    @CallerSensitive

    public static Object newProxyInstance(ClassLoader loader,

                                          Class<?>[] interfaces,

                                          InvocationHandler h)

        throws IllegalArgumentException

    {

      //判断了一波传入的代理类不为null,要不后面回调啥

        Objects.requireNonNull(h);

//返回一个要克隆对象的副本,克隆的类型依赖被克隆对象,换句话说:克隆后的对象类型与被克隆对象的类型相同。

        final Class<?>[] intfs = interfaces.clone();

        //这里就是系统的安全管理器,可以自己定义一波试试,这里不主要讲(因为我也不会)

        final SecurityManager sm = System.getSecurityManager();

        //这里咱们没有单独定义管理器就不看了

        if (sm != null) {

            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);

        }


        //这里就是生成代理类的关键代码,查找或生成指定的代理类。下一段对吗里进行了详细解释

        Class<?> cl = getProxyClass0(loader, intfs);


        //这里就是代理类调用传入的代理器之后调用默认函数的地方

        try {

            if (sm != null) {

                checkNewProxyPermission(Reflection.getCallerClass(), cl);

            }

//获取默认的构造方法

            final Constructor<?> cons = cl.getConstructor(constructorParams);

            final InvocationHandler ih = h;

            if (!Modifier.isPublic(cl.getModifiers())) {//如果不是PUBLIC修饰的

                   .doPrivileged(new PrivilegedAction<Void>() {                //AccessController.doPrivileged意思是这个是特别的,不用做权限检查. 

                    public Void run() {

                        cons.setAccessible(true);//授权,获取private修饰的属性值的时候需要先授权

                        return null;

                    }

                });

            }

            //调用构造函数构造代理类实例,入参数为‘调用处理程序’的实例,看到这里应该就明白jdk怎么实现动态代理的吧!

            return cons.newInstance(new Object[]{h});

        } catch (IllegalAccessException|InsßßtantiationException e) {

            throw new InternalError(e.toString(), e);

        } catch (InvocationTargetException e) {

            Throwable t = e.getCause();

            if (t instanceof RuntimeException) {

                throw (RuntimeException) t;

            } else {

                throw new InternalError(t.toString(), t);

            }

        } catch (NoSuchMethodException e) {

            throw new InternalError(e.toString(), e);

        }

    }



    private static Class<?> getProxyClass0(ClassLoader loader,

                                           Class<?>... interfaces) {

        //这里判断了一波实现类的个数,超过65535个就不处理了                   

        if (interfaces.length > 65535) {

            throw new IllegalArgumentException("interface limit exceeded");

        }

        //这里会先根据传入的加载器和接口去缓存里面取已经生成的代理类,如果没有才会去重新生成。解释了一波性能的问题

        //那么从缓存是怎么取的 缓存又是什么类型的(大部分都是HashMap)

        //jdk1.8后收敛到这里 生成代理类字节码过程: ProxyClassFactory中了

        return proxyClassCache.get(loader, interfaces);

    }


        缓存这里就不进行详细解释了,比较复杂,直接说获取不到缓存回去生成代理类的过程



 private static final class ProxyClassFactory

        implements BiFunction<ClassLoader, Class<?>[], Class<?>>

    {

        //所有代理类都前缀名字

        private static final String proxyClassNamePrefix = "$Proxy";


        //用于生成唯一代理类名称的下一个数字  AtomicLong 院子操作Long 线程安全

        private static final AtomicLong nextUniqueNumber = new AtomicLong();


        @Override

        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

//生成一个结果所有实现类的HashMap  这可能就是限制实现类个数的问题,太多了可能hash冲突严重,造成性能问题

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);

            for (Class<?> intf : interfaces) {

                //验证intf接口类Class对象是否为给定的classloder解析的,每次都要传入一个classLoder

                Class<?> interfaceClass = null;

                try {

                //指定类加载器获取接口类class对象,(这玩意加载出来的不是实现类,是接口)

                    interfaceClass = Class.forName(intf.getName(), false, loader);

                } catch (ClassNotFoundException e) {

                }

                //验证是不是同一接口类对象

                if (interfaceClass != intf) {

                    throw new IllegalArgumentException(

                        intf + " is not visible from class loader");

                }

                //验证加载出来的类是不是接口  isInterface() 是接口返回true

                if (!interfaceClass.isInterface()) {

                    throw new IllegalArgumentException(

                        interfaceClass.getName() + " is not an interface");

                }

               //验证接口是不是副本(第二次调用的时候就不走了,或者说接口实现接口的情况也不管)

                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {

                    throw new IllegalArgumentException(

                        "repeated interface: " + interfaceClass.getName());

                }

            }

// 代理类默认的包

            String proxyPkg = null;    

             //定义代理类的修饰符:public和final类型  

            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;


            /*

             * Record the package of a non-public proxy interface so that the

             * proxy class will be defined in the same package.  Verify that

             * all non-public proxy interfaces are in the same package.

             *记录一个非公共代理接口的包,以便在同一个包中定义代理类。验证所有非公共代理接口都在同一个包中。

             */

            for (Class<?> intf : interfaces) {

                int flags = intf.getModifiers();

                if (!Modifier.isPublic(flags)) {//不是PUBLIC修饰的类

                //是FINAL 修饰的静态类

                    accessFlags = Modifier.FINAL;

                    String name = intf.getName();

                    int n = name.lastIndexOf('.');//截取.class签名的类名,用于之后的类记载

                    //截取当前实现类的包名,如果没有定义包名这使用当前类的包

                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));

                    if (proxyPkg == null) {

                        proxyPkg = pkg;

                    } else if (!pkg.equals(proxyPkg)) {

                        throw new IllegalArgumentException(//interfaces含有来自不同包的非公共接口,抛错

                            "non-public interfaces from different packages");

                    }

                }

            }


            if (proxyPkg == null) {

                // if no non-public proxy interfaces, use com.sun.proxy package

                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";

            }


            //540行定义了生成代理类的随机数,是一个原子操作的Long

            long num = nextUniqueNumber.getAndIncrement();

            //代理类的名规则  包名+默认的类前缀+Long

            String proxyName = proxyPkg + proxyClassNamePrefix + num;


            //这里就是使用字节码来构建之前定义好的代理类,这里直接吧字节码的过程写到此类里

            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

                proxyName, interfaces, accessFlags);

            try {

            //记载字节码class对象

                return defineClass0(loader, proxyName,

                                    proxyClassFile, 0, proxyClassFile.length);

            } catch (ClassFormatError e) {

                /*

                 * A ClassFormatError here means that (barring bugs in the

                 * proxy class generation code) there was some other

                 * invalid aspect of the arguments supplied to the proxy

                 * class creation (such as virtual machine limitations

                 * exceeded).

                 */

                throw new IllegalArgumentException(e.toString());

            }

        }

    }

    

      

    Cglib生成代理类(org.springframework.cglib.proxy),这块没咋看懂简单讲讲吧

       我们调用的时候是 return (T) new Enhancer().create(this.target.getClass(),this);那我们就从这里入手


//这就没啥东西,进入下一个方法的分析setSuperclass()

public static Object create(Class type, Callback callback) {

      Enhancer e = new Enhancer();

      e.setSuperclass(type);

      e.setCallback(callback);

        return e.create();

    }


//这块能解释cglib为啥不用基于接口

    public void setSuperclass(Class superclass) {

        if (superclass != null && superclass.isInterface()) {//对进入的类进行判断,如果是一个接口的话,标记为接口

        this.setInterfaces(new Class[]{superclass});

        } else if (superclass != null && superclass.equals(Object.class)) {//如果是一个Object的话,它就是最高等级的父类,不需要有父类,设置superclass 属性为null

            this.superclass = null;

        }   else {

            this.superclass = superclass;

        }

}

    

 

private Object createHelper() {

    this.preValidate();//基本参数的校验

    //下面这段代码是比较重要的,咱们进行拆分下

   Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);

    第一个参数:

    if(his.superclass != null){//如果父类标示不为null,这去除父类的类名,否则为null

        this.superclass.getName()

    }else{

        null

    }

    第二个参数ReflectUtils.getNames(this.interfaces),  接口名

    第三个参数: 这玩意可能是个过滤的规则,如果没有配置规则

    if( this.filter == ALL_ZERO){

        null .//如果没有配置什么规则的话 返回null

    }else{

        new WeakCacheKey(this.filter),  //从缓存里面渠道相关的规则

    }

    剩下的这下参数 大致应该是回调参数一类的

    this.callbackTypes, this.useFactory, this.interceptDuringConstruction,     this.serialVersionUID

    this.currentKey = key;//主要是为了生成这个key

    Object result = super.create(key);//这里是主要生成代理的地方

    return result;

}


//最主要的部分来了 //这先从缓存里取相关信息 取不到的话走 data = new AbstractClassGenerator.ClassLoaderData(loader);

protected Object create(Object key) {

    try {

        ClassLoader loader = this.getClassLoader();

        Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;

        AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);

        if (data == null) {

            Class var5 = AbstractClassGenerator.class;

             synchronized(AbstractClassGenerator.class) {//这边加了同步代码块,重度锁

            cache = CACHE;

            data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);//这里应该是在选择类加载器

    if (data == null) {

        Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);

          data = new AbstractClassGenerator.ClassLoaderData(loader);

        newCache.put(loader, data);

        CACHE = newCache;

    }

    .......

}



//中间一些缓存的地方就不说了 最终会调用到这里

 protected Class generate(AbstractClassGenerator.ClassLoaderData data) {

        Object save = CURRENT.get();

        CURRENT.set(this);

        try {

            ClassLoader classLoader = data.getClassLoader();

            if (classLoader == null) {

                throw new IllegalStateException("ClassLoader is null while trying to define class " + this.getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " + "Please file an issue at cglib's issue tracker.");

            } else {

                String className;

                synchronized(classLoader) {

//构建代理类的名字 

//规则如下

if (prefix == null) {

prefix = "org.springframework.cglib.empty.Object";

} else if (prefix.startsWith("java")) {

 prefix = "$" + prefix;

}

String base = prefix + "$$" + source.substring(source.lastIndexOf(46) + 1) + this.getTag() + "$$" + Integer.toHexString(STRESS_HASH_CODE ? 0 : key.hashCode());

String attempt = base;

for(int var7 = 2; names.evaluate(attempt); attempt = base + "_" + var7++) {

    ;

}

                    className = this.generateClassName(data.getUniqueNamePredicate());

                    data.reserveName(className);

                    this.setClassName(className);

                }

                Class gen;

                if (this.attemptLoad) {

                    try {

                        gen = classLoader.loadClass(this.getClassName());

                        Class var25 = gen;

                        return var25;

                    } catch (ClassNotFoundException var20) {

                        ;

                    }

                }


                byte[] b = this.strategy.generate(this);

  //加载代理类的

                className = ClassNameReader.getClassName(new ClassReader(b));

                ProtectionDomain protectionDomain = this.getProtectionDomain();

                synchronized(classLoader) {

//这里是执行默认方法,实现回调的贷方

                    if (protectionDomain == null) {

                        gen = ReflectUtils.defineClass(className, b, classLoader);

                    } else {

                        gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);

                    }

....... 

    }


        那么问题来了,大家都了解了AOP的俩种代理模式了,思考过spring的AOP是怎么实现的了吗? Main()中那样的调用方式是不是很繁琐,拦截到的方法中实现的方式我spring@After @Before 等是不是有点差别了,怎么通过注解实现这俩种方式了?

   

以上是关于AOP的实现原理(俩种代理模式)的主要内容,如果未能解决你的问题,请参考以下文章

AOP实现原理

JDK动态代理和cglib动态代理(AOP的底层实现原理)

熟知代理模式,你将明白Spring AOP原理!!!

Java JDK 动态代理(AOP)使用及实现原理分析

Spring容器AOP的理解

Java设计模式——代理模式