Java 动态代理
什么是动态代理:
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
代理类在程序运行期间,创建的代理对象称之为动态代理对象。这种情况下,创建的代理对象,并不是事先在Java代码中定义好的。而是在运行期间,根据我们在动态代理对象中的“指示”,动态生成的。也就是说,你想获取哪个对象的代理,动态代理就会为你动态的生成这个对象的代理对象。动态代理可以对被代理对象的方法进行功能增强。有了动态代理的技术,那么就可以在不修改方法源码的情况下,增强被代理对象的方法的功能,在方法执行前后做任何你想做的事情。
java 中主要有两种方式实现动态代理 : *一是JDK动态代理,二是Cglib动态代理*
1.jdk动态代理
jdk动态代理主要用的JDK自带的java.lang.reflect包下面的两个类,一个是接口:InvocationHandler,另一个类Proxy。
要求:使用jdk动态代理的方式去要求被代理类必须实现一或者多个接口
实现步骤 :
- 创建两个接口Log,UserService接口
Log 接口用于记录方法调用日志
package com.example.demo.JDKproxy;
/**
* @author gino
* 2021-04-30
*/
public interface Log {
public boolean savelog();
}
UserService 提供CRUD方法
package com.example.demo.JDKproxy;
/**
* @author gino
* 2021-04-29
*/
public interface UserService {
public void delete( long id );
public void update();
}
- 创建实现类UserServiceImpl
package com.example.demo.JDKproxy;
/**
* @author gino
* 2021-04-29
*/
public class UserServiceImpl implements UserService,Log{
@Override
public void delete(long id) {
System.out.println("delete user"+id);
}
@Override
public void update() {
System.out.println("update user");
}
@Override
public boolean savelog() {
System.out.println("save Log");
return false;
}
}
- 创建方法拦截器 实现InvocationHandler 接口,实现invoke方法
package com.example.demo.JDKproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author gino
* 2021-04-30
*/
public class ProxyHandler implements InvocationHandler {
/**
* 被代理的对象
*/
private Object taget;
/**
* 通过构造方法将被代理代理对象设置进来
* @param taget
*/
public ProxyHandler(Object taget) {
this.taget = taget;
}
/***
*
* @param proxy 代理对象
* @param method 方法对象
* @param args 方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用前增强
System.out.println("method invoke before doing "+method.getName());
//调用被代理类的方法
Object returnValue=method.invoke(taget,args);
//调用后增强
System.out.println("method invoke after doing "+method.getName());
return returnValue;
}
}
- 测试代码以及结果
package com.example.demo.JDKproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @author gino
* 2021-04-29
*/
public class main {
public static void main(String[] args) {
//jdk动态代理类的必须实现一个或者多个接口
// 1. 创建被代理的对象,UserService接口的实现类
UserServiceImpl userServiceImpl = new UserServiceImpl();
//创建代理对象,并告知代理对象代理的是谁
InvocationHandler handler=new ProxyHandler(userServiceImpl);
//获取被代理对象的类加载器和类实现的接口
ClassLoader loader = userServiceImpl.getClass().getClassLoader();
Class[] interfaces = userServiceImpl.getClass().getInterfaces();
//通过Proxy 对象获取代理对象
UserService porxy = (UserService) Proxy.newProxyInstance(loader, interfaces, handler);
porxy.delete(99);
Log porxy2 = (Log) Proxy.newProxyInstance(loader, interfaces, handler);
porxy2.savelog();
}
}
运行结果如下
源码分析:
为什么jdk动态代理的被代理类必须实现一个或者多个接口?
从测试用例中可以看出, jdk动态代理的是通过 proxy.newProxyInstance()方法返回被代理的对象, 然后再去操作放回的对象就可以实现方法的前置和后置增强
而proxy.newProxyInstance()方法中传入了三个参数 第一个是被代理类的类加载器, 第二个是被代理类的实现的所有接口,第三个是方法的拦截器接口实现类 进入源码可以看到如下代码。
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
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);
}
//设置代理对象构造方法的参数 ,参数来自InvocationHandler接口的构造方法 与上面ProxyHandler 类中要通过构造方法将被代理的类设置进去相呼应
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//通过构造方法直接返回被代理的类
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException 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);
}
}
其中就有对实现接口的判断Class<?> cl = getProxyClass0(loader, intfs); 进入getProxyClass0,会发现原来实现接口还有最大值65535,而且每一次调用方法都会先从缓存里面拿代理对象
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);
}
再点进去就会发现 Objects.requireNonNull(parameter);里面有非空判断
至此代码层次的设计便要求被代理类要实现一个或者多个接口, 为什么要加这段代码呢
我们通过反编译被生成的**$Proxy0.class**文件发现 生成的代理类本身就继承了Proxy 类实现了被代理类实现的接口,因为java是单继承的 ,如果被代理是继承父类而不是实现接口 那便违反了java的单继承规则
public final class $Proxy0 extends Proxy implements Interface {
public $Proxy0(InvocationHandler paramInvocationHandler) {\
super(paramInvocationHandler);
}
的代理类本身就继承了Proxy 类实现了被代理类实现的接口,因为java是单继承的 ,如果被代理是继承父类而不是实现接口 那便违反了java的单继承规则`
public final class $Proxy0 extends Proxy implements Interface {
public $Proxy0(InvocationHandler paramInvocationHandler) {\
super(paramInvocationHandler);
}