实现简单AOP

本文介绍了AOP的概念,并通过Java动态代理JDK和Cglib实现AOP的原理。重点讲解了JDK动态代理需要目标类实现接口的原因,以及Cglib代理对无接口类的处理方式。通过源码分析,帮助读者深入理解AOP的实现细节。
摘要由CSDN通过智能技术生成

AOP都是使用设计模式的代理模式来实现的

jdk的是通过动态代理 且目标类必须有接口

为啥要有接口 建议可以看看 这篇 https://blog.csdn.net/zhangerqing/article/details/42504281 笔者下面也有JDK1.7的源码分析,该博客是分析JDK1.8的 出入不大 基本一样 

现在先来实现JDK 的动态代理

首先是需要被增强的目标类与目标类的接口

public interface SourceAble {
    void method();
}
public class Source implements SourceAble {
    @Override
    public void method() {
        System.out.println("this is source method");
    }
}

实现自己的 InvocationHandler  这里实现切面的增强 这里前后都做了增强 在Spring就是@Around 切面

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

    private Object target; //们既然要做代理,我们必须知道我们是给谁做代理,这里的obj就是被代理者。

    MyInvocationHandler() {
        super();
    }

    public MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target,args);
        atfer();
        return result;
    }

    private void atfer() {
        System.out.println("after proxy!");
    }
    private void before() {
        System.out.println("before proxy!");
    }
}

这样就完成切面的操作了 是不是很简单

然后调用jdk反射包的代理类来实现,下面是测试类

public class Test {
    public static void main(String[] args) {
      
        System.out.println("-----------动态代理--------------");
       //动态代理测试
        SourceAble sourceAble1 = new Source();//被代理的对象
        InvocationHandler invocationHandler = new MyInvocationHandler(sourceAble1);//
        //代理对象
        SourceAble dynamicProxy = (SourceAble) java.lang.reflect.Proxy.newProxyInstance(sourceAble1.getClass().getClassLoader(),sourceAble1.getClass().getInterfaces(),invocationHandler);
        dynamicProxy.method();
    }

}

执行动态代理类的方法

接下来我们也来简单用cglib来实现AOP

首先导入cglib包

目标类User

public class User {
    private String name;
    public void playName(String name){
      System.out.println(" 这是 palyName 方法 参数被偷换啦!参数是:"+name);
    }
    public void save(){
        System.out.println("this is save 方法 :";
    }
}

Cglib代理 这里是实现MethodInterceptor来做到切面的增强

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor{

    public Object getProxy(Class<?> clazz) {//这里需要知道代理的对象 传入被代理对象的class对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();//返回代理对象
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy proxy) throws Throwable {

        // 拦截父类所有方法的调用
        System.out.println(method.getName()+":方法执行前......");
        //通过代理类调用父类中的方法
        Object result = proxy.invokeSuper(o,new Object[]{"ssss"});//这里可以拦截所有的父类方法参数
        System.out.println(method.getName()+"方法执行结果:"+result);//这里可以拦截所有父类方法的返回值
        System.out.println(method.getName()+":方法执行后");
        return result;
    }
}
然后是测试类
 
public class Test {

    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        User user = (User) proxy.getProxy(new User().getClass());
        user.playName("kaizen");//明明是kaizen 输出是SSSS 代理模式坑爹啊
        user.save("kaizen");
    }


}

测试结果 这里注意一下参数会拦截所有的方法!


这里还能修改参数,执行结果,不过使用asm好像会导致性能问题,由于笔者能力有限,也说不出个所以然~

最后对JDK动态代理为什么要实现接口,可以看看笔者写的是否对不对,如果有错麻烦指导一下,本人查看的是JDK1.7

为啥要有接口  

首先看 newProxyInstance这个方法里 

 @CallerSensitive
    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);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);//这边就是生成代理对象的class

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);//这里获得的是代理对象放置InvocationHandler的构造函数
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                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());
        }
    }

这里还能补充一个知识点:

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。


重点应该在

Class<?> cl = getProxyClass0(loader, intfs);//这边就是生成代理对象的class
 private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

proxyClassCache是个缓存

先从缓存里查一下,如果存在,直接返回代理对象的class,不存在就新创建代理对象的class。具体的代码

 
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
 
WeakCache(BiFunction<K, P, ?> subKeyFactory,
                BiFunction<K, P, V> valueFactory)
 public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }
 

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
apply()是Proxy类的内部类ProxyClassFactory实现其接口的一个方法
R apply(T t, U u);
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

    Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
    for (Class<?> intf : interfaces) {
        /*
         * Verify that the class loader resolves the name of this
         * interface to the same Class object.
         */
        Class<?> interfaceClass = null;
        try {
            interfaceClass = Class.forName(intf.getName(), false, loader);
        } catch (ClassNotFoundException e) {
        }
        if (interfaceClass != intf) {
            throw new IllegalArgumentException(
                intf + " is not visible from class loader");
        }
 
Class.forName (class)这就是前面缓存里得到的东西
既然是生成类,那就要有对应的class字节码,我们继续往下看:


[java]  view plain  copy
  1. /* 
  2.  * Generate the specified proxy class. 
  3.  */  
  4.    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  
  5.    proxyName, interfaces, accessFlags);  
  6.     try {  
  7.           return defineClass0(loader, proxyName,  
  8.           proxyClassFile, 0, proxyClassFile.length);  
这段代码就是利用ProxyGenerator为我们生成了最终代理类的字节码文件,即getProxyClass0()方法的最终返回值。

下面的函数大概




这段代码就是利用ProxyGenerator为我们生成了最终代理类的字节码文件,即getProxyClass0()方法的最终返回值。

final Constructor<?> cons = cl.getConstructor(constructorParams);//这里获得的是代理对象放置InvocationHandler的构造函数

对应反编译后把InvocationHandler 对象构造进去

  1. public $Proxy0(InvocationHandler paramInvocationHandler)  
  2.     throws   
  3.   {  
  4.     super(paramInvocationHandler);  
  5.   }  

 return newInstance(cons, ih);//构造函数生成代理类 源码在下面

获得了接口的构造函数 等价于 new Interface(InvocationHandler myInvocationHandler);
private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
    try {
        return cons.newInstance(new Object[] {h} );
    } catch (IllegalAccessException | InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString());
        }
    }
}

因为不知道如何反编译 还是直接复制博主的文章过来了

(反编译方法 可跳过)

  1. import java.io.FileOutputStream;  
  2. import java.io.IOException;  
  3. import sun.misc.ProxyGenerator;  
  4.   
  5. public class DynamicProxyTest {  
  6.   
  7.     public static void main(String[] args) {  
  8.         UserService userService = new UserServiceImpl();  
  9.         MyInvocationHandler invocationHandler = new MyInvocationHandler(  
  10.                 userService);  
  11.   
  12.         UserService proxy = (UserService) invocationHandler.getProxy();  
  13.         proxy.add();  
  14.           
  15.         String path = "C:/$Proxy0.class";  
  16.         byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",  
  17.                 UserServiceImpl.class.getInterfaces());  
  18.         FileOutputStream out = null;  
  19.   
  20.         try {  
  21.             out = new FileOutputStream(path);  
  22.             out.write(classFile);  
  23.             out.flush();  
  24.         } catch (Exception e) {  
  25.             e.printStackTrace();  
  26.         } finally {  
  27.             try {  
  28.                 out.close();  
  29.             } catch (IOException e) {  
  30.                 e.printStackTrace();  
  31.             }  
  32.         }  
  33.     }  
  34. }  


上面测试方法里的proxy.method(),此处的method()方法,就已经不是原始的Source

里的method()方法了,而是新生成的代理类的method()方法。

类似于本来是SourceAble  source = new Source();调用source.method();

换成了SourceAble  proxy = new Proxy();调用proxy.method();

我们将生成的$Proxy0.class文件用jd-gui打开,我去掉了一些代码,add()方法如下:

[java]  view plain  copy
  1. public final void add()  
  2.     throws   
  3.   {  
  4.     try  
  5.     {  
  6.       this.h.invoke(this, m3, null);  
  7.       return;  
  8.     }  
  9.     catch (Error|RuntimeException localError)  
  10.     {  
  11.       throw localError;  
  12.     }  
  13.     catch (Throwable localThrowable)  
  14.     {  
  15.       throw new UndeclaredThrowableException(localThrowable);  
  16.     }  
  17.   }  

核心就在于this.h.invoke(this. m3, null);此处的h是啥呢?我们看看这个类的类名:

public final class $Proxy0 extends Proxy implements UserService

不难发现,新生成的这个类,继承了Proxy类实现了SourceAble这个method方法,而这个SourceAble就是我们指定的接口,所以,这里我们基本可以断定,JDK的动态代理,生成的新代理类就是继承了Proxy基类,实现了传入的接口的类。那这个h到底是啥呢?我们再看看这个新代理类,看看构造函数:

[java]  view plain  copy
  1. public $Proxy0(InvocationHandler paramInvocationHandler)  
  2.     throws   
  3.   {  
  4.     super(paramInvocationHandler);  
  5.   }  

构造函数里传入了一个InvocationHandler类型的参数,看到这里,我们就应该想到之前的一行代码:

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

这是newInstance方法的最后一句,传入的h,就是这里用到的h,也就是我们最初自己定义的MyInvocationHandler类的实例。所以,我们发现,其实最后调用的method()方法,其实调用的是MyInvocationHandler的invoke()方法。我们再来看一下这个方法,找一下m3的含义,继续看代理类的源码:

[java]  view plain  copy
  1. static  
  2.   {  
  3.     try  
  4.     {  
  5.       m1 = Class.forName("java.lang.Object").getMethod("equals"new Class[] { Class.forName("java.lang.Object") });  
  6.       m2 = Class.forName("java.lang.Object").getMethod("toString"new Class[0]);  
  7.       m3 = Class.forName("dynamic.proxy.SourceAble").getMethod("method"new Class[0]);  
  8.       m0 = Class.forName("java.lang.Object").getMethod("hashCode"new Class[0]);  
  9.       return;  
  10.     }  

惊喜的发现,原来这个m3,就是原 接口 method()方法,看到这里,还有什么不明白的呢?我们继续,看看原始MyInvocationHandler里的invoke()方法:


m3 =  Class.forName("dynamic.proxy.SourceAble").getMethod("method"new Class[0]);  


因为作者的逻辑是 生成的新代理类就是继承了Proxy基类,实现了传入的接口的类

复习一下实现的思路

1. 通过getProxyClass0()生成代理类。

2. 通过Proxy.newProxyInstance()生成代理类的实例对象,创建对象时传入InvocationHandler(h)类型的实例。

3. 调用新实例的方法,即此例中的add(),即原InvocationHandler类中的invoke()方法。


SourceAble  source = new Source();调用source.method();

换成了SourceAble  proxy = new Proxy();调用proxy.method();


文章参考自 https://blog.csdn.net/zhangerqing/article/details/42504281


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值