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字节码,我们继续往下看:
- /*
- * Generate the specified proxy class.
- */
- byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
- proxyName, interfaces, accessFlags);
- try {
- return defineClass0(loader, proxyName,
- proxyClassFile, 0, proxyClassFile.length);
这段代码就是利用ProxyGenerator为我们生成了最终代理类的字节码文件,即getProxyClass0()方法的最终返回值。
final Constructor<?> cons = cl.getConstructor(constructorParams);//这里获得的是代理对象放置InvocationHandler的构造函数
对应反编译后把InvocationHandler 对象构造进去
- public $Proxy0(InvocationHandler paramInvocationHandler)
- throws
- {
- super(paramInvocationHandler);
- }
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());
}
}
}
因为不知道如何反编译 还是直接复制博主的文章过来了
(反编译方法 可跳过)
- import java.io.FileOutputStream;
- import java.io.IOException;
- import sun.misc.ProxyGenerator;
- public class DynamicProxyTest {
- public static void main(String[] args) {
- UserService userService = new UserServiceImpl();
- MyInvocationHandler invocationHandler = new MyInvocationHandler(
- userService);
- UserService proxy = (UserService) invocationHandler.getProxy();
- proxy.add();
- String path = "C:/$Proxy0.class";
- byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",
- UserServiceImpl.class.getInterfaces());
- FileOutputStream out = null;
- try {
- out = new FileOutputStream(path);
- out.write(classFile);
- out.flush();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- out.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
上面测试方法里的proxy.method(),此处的method()方法,就已经不是原始的Source
里的method()方法了,而是新生成的代理类的method()方法。
类似于本来是SourceAble source = new Source();调用source.method();
换成了SourceAble proxy = new Proxy();调用proxy.method();
我们将生成的$Proxy0.class文件用jd-gui打开,我去掉了一些代码,add()方法如下:
- public final void add()
- throws
- {
- try
- {
- this.h.invoke(this, m3, null);
- return;
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
核心就在于this.h.invoke(this. m3, null);此处的h是啥呢?我们看看这个类的类名:
public final class $Proxy0 extends Proxy implements UserService
不难发现,新生成的这个类,继承了Proxy类实现了SourceAble这个method方法,而这个SourceAble就是我们指定的接口,所以,这里我们基本可以断定,JDK的动态代理,生成的新代理类就是继承了Proxy基类,实现了传入的接口的类。那这个h到底是啥呢?我们再看看这个新代理类,看看构造函数:
- public $Proxy0(InvocationHandler paramInvocationHandler)
- throws
- {
- super(paramInvocationHandler);
- }
构造函数里传入了一个InvocationHandler类型的参数,看到这里,我们就应该想到之前的一行代码:
return cons.newInstance(new Object[]{h});
这是newInstance方法的最后一句,传入的h,就是这里用到的h,也就是我们最初自己定义的MyInvocationHandler类的实例。所以,我们发现,其实最后调用的method()方法,其实调用的是MyInvocationHandler的invoke()方法。我们再来看一下这个方法,找一下m3的含义,继续看代理类的源码:
- static
- {
- try
- {
- m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
- m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
- m3 = Class.forName("dynamic.proxy.SourceAble").getMethod("method", new Class[0]);
- m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
- return;
- }
惊喜的发现,原来这个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