什么是代理?
一种常用的设计模式
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。
代理分类
1.静态代理
JAVA静态代理是指由程序员创建或工具生成的代理类,这个类在编译期就已经是确定了的,存在的。
①定义统一接口
public interface Person {
//接口中定义取钱的方法,目标类和代理类都实现该接口
void DrawMoney();
}
②定义目标类
public class TargetPerson implements Person{
@Override
public void DrawMoney() {
System.out.println("阿狸取到了1个亿");
}
}
③定义代理类
//代理类需要实现和实现类相同的接口,具备共同的功能,这样才能做代理
public class ProxyPerson implements Person{
//定义私有化目标对象,注意不提供get set方法
private Person target;
//对外提供唯一的构造函数初始化目标对象
public ProxyPerson(Person person){
this.target = person;
}
//看似是代理人取钱,实际上还是取得目标对象的钱
//只是取钱之前之后可以另外做一些事情 相当于方法增强了,但是也没有影响到目标代码
@Override
public void DrawMoney() {
System.out.println("代理人办理取款手续···");
this.target.DrawMoney();
System.out.println("代理人取钱完毕后兑换成了美元···");
}
}
④测试
public class Main {
public static void main(String[] args) {
//创建目标对象
Person target = new TargetPerson();
//根据目标代理创建目标对象的代理
Person proxy = new ProxyPerson(target);
//代理帮忙取钱
proxy.DrawMoney();
}
}
静态代理实现总结
1.创建接口|抽象类,定义行为规范
2.创建目标类,实现接口|抽象类,重写行为
3.创建代理类:(包含)
3.1 实现和目标同同样的接口|抽象类
3.2 定义真实角色对象并且私有化
3.3 定义带参构造函数,为自定义的真实角色对象进行初始化
3.4 重写行为,调用真实角色对象的对应方法
静态代理存在的问题
代理类依赖于真实类,因为代理类最根本的业务功能是需要通过调用真实类来实现的。那么如果事先不知道真实类,该如何使用代理模式呢?
一个真实类必须对应一个代理类,即当有多个真实类RealA、RealB、RealC…的时候,就需要多个代理类ProxyA、ProxyB、ProxyC…。这样的话如果大量使用静态代理,容易导致类的急剧膨胀。
2.动态代理 (分为 JDK动态代理 和 Cglib动态代理 )
什么是JAVA动态代理?
JAVA动态代理与静态代理相对,静态代理是在编译期就已经确定代理类和真实类的关系(代理类里面私有化了该真实类),并且生成相应类的代理类的。而动态代理是在运行期利用JVM的反射机制生成代理类,这里是直接生成类的字节码,然后通过类加载器载入JAVA虚拟机执行。现在主流的JAVA动态代理技术的实现有两种:一种是JDK自带的,就是我们所说的JDK动态代理,另一种是开源社区的一个开源项目CGLIB
什么是jdk动态代理
JDK动态代理的实现是在运行时,根据一组接口定义,使用Proxy、InvocationHandler等工具类去生成一个代理类和代理类实例。
JDK动态代理的类关系模型和静态代理看起来差不多。也是需要一个或一组接口来定义行为规范。需要一个代理类来实现接口。区别是没有真实类,因为动态代理就是要解决在不知道真实类的情况下依然能够使用代理模式的问题。
主要方法
建代理对象的时候,和静态代理一样,要知道目标对象是谁(ClassLoader),和目标对象实现同样的接口(interfaces),由代理类执行方法的时候要怎么处理(创建一个实现接口InvocationHandler的类,它必须实现invoke方法)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
//创建目标对象
Person target = new TargetPerson();
//jdk创建动态代理
Person proxy = (Person) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk代理人办理取款手续···");
Object result = method.invoke(target,args);
System.out.println("jdk代理人取钱完毕后兑换成了美元···");
return result;
}
}
);
//代理对象去取钱
proxy.DrawMoney();
}
}
关于
public Object invoke(Object proxy, Method method, Object[] args)
参数解析:
第一个参数obj一般是指代理类,method是被代理的方法,如上例中的DrawMoney(),args为该方法的参数数组。
有很多问题不是很了解
1.最后调用的这个DrawMoney()方法是怎么和invoke()方法联系上的?
2.invoke()方法怎么知道我想代理的是这个DrawMoney()方法而不是其他的
3.传入的proxy,method具体是什么并没有指定,它怎么知道的。
4.invoke()方法中的第一个参数是Proxy的实例,但是有什么用呢?或者说,程序内是怎样显示出作用的?
jdk动态代理怎么实现的?(jdk8)
以Proxy.newProxyInstance()
方法为切入点来剖析代理类的生成及代理方法的调用。
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
///如果h为空直接抛出空指针异常,之后所有的单纯的判断null并抛异常,都是此方法
Objects.requireNonNull(h);
// 拷贝类实现的所有接口
final Class>[] intfs = interfaces.clone();
// 获取当前系统安全接口
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflection.getCallerClass返回调用该方法的方法的调用类;
//loader:接口的类加载器
// 进行包访问权限、类加载器权限等检查
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
*查找或生成指定的代理类
*/
Class> cl = getProxyClass0(loader, intfs);
/*
* 用指定的调用处理程序调用它的构造函数
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取代理类的构造函数对象。
//constructorParams是类常量,作为代理类构造函数的参数类型,常量在源码上面定义如下:
//private static final Class>[] constructorParams = { InvocationHandler.class };
final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
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);
}
}
newProxyInstance()
方法帮我们执行了生成代理类----获取构造器----生成代理对象这三步;
生成代理类:Class> cl = getProxyClass0(loader, intfs);
获取构造器: final Constructor> cons = cl.getConstructor(constructorParams);
生成代理对象: cons.newInstance(new Object[]{h});
Proxy.getProxyClass0()如何生成代理类?
private static Class> getProxyClass0(ClassLoader loader,
Class>... interfaces) {
//接口数不得超过65535个,这么大,足够使用的了
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//如果缓存中有代理类了直接返回,否则将由代理类工厂ProxyClassFactory创建代理类
return proxyClassCache.get(loader, interfaces);
}
如果缓存中没有代理类,Proxy中的ProxyClassFactory如何创建代理类?
从get()方法追踪进去看看
// key:类加载器;parameter:接口数组
public V get(K key, P parameter) {
// 检查指定类型的对象引用不为空null。当参数为null时,抛出空指针异常。
Objects.requireNonNull(parameter);
// 清除已经被GC回收的弱引用
expungeStaleEntries();
// 将ClassLoader包装成CacheKey, 作为一级缓存的key
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 获取得到二级缓存
ConcurrentMap
get方法中Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
subKeyFactory调用apply,具体实现在ProxyClassFactory中完成。
ProxyClassFactory.apply()实现代理类创建
private static final class ProxyClassFactory
implements BiFunction[], Class>>
{
//统一代理类的前缀名都以$Proxy
private static final String proxyClassNamePrefix = "$Proxy";
// 使用唯一的编号给作为代理类名的一部分,如$Proxy0,$Proxy1等
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class> intf : interfaces) {
//验证指定的类加载器(loader)加载接口所得到的Class对象(interfaceClass)是否与intf对象相同
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对象是不是接口
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; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
//验证所有非公共的接口在同一个包内;公共的就无需处理
for (Class> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
// 截取完整包名
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
/*如果都是public接口,那么生成的代理类就在com.sun.proxy包下如果报java.io.FileNotFoundException: com\sun\proxy\$Proxy0.class
(系统找不到指定的路径。)的错误,就先在你项目中创建com.sun.proxy路径*/
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
//nextUniqueNumber 是一个原子类,确保多线程安全,防止类名重复,类似于:$Proxy0,$Proxy1
long num = nextUniqueNumber.getAndIncrement();
// 代理类的完全限定名,如com.sun.proxy.$Proxy0.calss
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成类字节码的方法(重点)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
代理类创建真正在ProxyGenerator.generateProxyClass()方法中
public static byte[] generateProxyClass(final String var0, Class>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
// 真正生成字节码的方法
final byte[] var4 = var3.generateClassFile();
// 如果saveGeneratedFiles为true 则生成字节码文件,所以在开始我们要设置这个参数
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
最终代理类生成的最终方法是ProxyGenerator.generateClassFile()
使用这个方法生成的字节码是个什么样子?
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
//创建目标对象
Person target = new TargetPerson();
//jdk创建动态代理
Person proxy = (Person) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk代理人办理取款手续···");
Object result = method.invoke(target,args);
System.out.println("jdk代理人取钱完毕后兑换成了美元···");
return result;
}
}
);
//代理对象去取钱
proxy.DrawMoney();
//将生成的字节码保存到本地
createProxyClassFile();
}
private static void createProxyClassFile(){
String name = "ProxyPerson";
byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Person.class});
FileOutputStream out =null;
try {
out = new FileOutputStream(name+".class");
//只是看看文件的路径在哪里
System.out.println((new File("hello")).getAbsolutePath());//getAbsolutePath() 方法返回文件的绝对路径
out.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
if(null!=out) try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public final class ProxyPerson extends Proxy implements Person {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public ProxyPerson(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void DrawMoney() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("agent.Person").getMethod("DrawMoney");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
1.它继承自Proxy并实现了我们定义的Person接口
2.动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法
3.调用我们定义的InvocationHandlerImpl的 invoke()方法
什么是cglib动态代理
JDK动态代理必须提供一些接口才能代理,在一些不能提供的接口环境下,只能采取其他第三方技术,比如CGLIB动态代理,他的优势在于不需要提供接口,只要一个非抽象类就能实现动态代理。
导入 cglib 依赖
cglib
cglib-nodep
假设我们有一个没有实现任何接口的类HelloConcrete
public class HelloConcrete {
public String sayHello(String str) {
return "HelloConcrete: " + str;
}
}
通过CGLIB代理实现如下:
1.首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
2.然后在需要使用HelloConcrete的时候,通过CGLIB动态代理获取代理对象。
// CGLIB动态代理
// 1. 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
class MyMethodInterceptor implements MethodInterceptor{
...
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
logger.info("You said: " + Arrays.toString(args));
return proxy.invokeSuper(obj, args);
}
}
// 2. 然后在需要使用HelloConcrete的时候,通过CGLIB动态代理获取代理对象。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloConcrete.class);
enhancer.setCallback(new MyMethodInterceptor());
HelloConcrete hello = (HelloConcrete)enhancer.create();
System.out.println(hello.sayHello("I love you!"));
运行上述代码输出结果:
You said: [I love you!]
HelloConcrete: I love you!
上述代码中,我们通过CGLIB的Enhancer
来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()
方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()
方法,在intercept()
方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;通过调用MethodProxy.invokeSuper()
方法,我们将调用转发给原始对象,具体到本例,就是HelloConcrete
的具体方法。CGLIG中MethodInterceptor
的作用跟JDK代理中的InvocationHandler
很类似,都是方法调用的中转站。CGLIB代理也会进行代理,如hashCode()、equals()、toString()等
CGLIB动态生成的类型;父类是
HelloConcrete
,印证了CGLIB是通过继承实现代理既然是继承就不得不考虑final的问题。我们知道final类型不能有子类,所以CGLIB不能代理final类型的类;final方法是不能重载的,所以也不能通过CGLIB代理,遇到这种情况不会抛异常,而是会跳过final方法只代理其他方法
-。-更深入学不动也记不住了
参考文章
https://www.javazhiyin.com/44776.html
https://blog.csdn.net/yhl_jxy/article/details/80586785