원본 분석 JDK 동적 에이전트의 본질
jdk 동적 에이전트
일반 코드 크로스 정의 인터페이스
public interface ProxyInterface {
void method();
}
인터페이스 구현 클래스 정의
public class ProxyInterfaceImpl implements ProxyInterface {
@Override
public void method() {
System.out.println("ProxyInterfaceImpl method");
}
}
사용자 정의handler, 사용자 정의 에이전트의 업무 논리 완성
public class ProxyHandler implements InvocationHandler {
Object proxyObject =null;
public ProxyHandler(Object object){
this.proxyObject = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(" ");
method.invoke(proxyObject,args);
System.out.println(" ");
return null;
}
public static void main(String[] args) {
ProxyInterfaceImpl proxyInterface = new ProxyInterfaceImpl();
ProxyHandler proxyHandler= new ProxyHandler(proxyInterface);
ProxyInterface proxyInterface1 =(ProxyInterface) Proxy.newProxyInstance(proxyHandler.getClass().getClassLoader(),proxyInterface.getClass().getInterfaces(),proxyHandler);
proxyInterface1.method();
}
}
핵심 코드는 모두 다음과 같은 몇 가지 일을 했다
/** Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.*/
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
문서의 뜻: 이 방법은 특정한 인터페이스(그 방법은 특정한handler에 분배되어 번역이 정확하지 않음)를 위해 프록시 실례 방법이 받아들인 파라미터를 되돌려줍니다.
프록시 클래스의class 대상 생성 논리
Class> cl = getProxyClass0(loader, intfs); //
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
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);
}
캐시를 사용했습니다. 캐시 생성은proxy에 있습니다.
/**
* a cache of proxy classes
*/
private static final WeakCache[], Class>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
프록시 클래스의 생성 논리는ProxyClassFactory라는 함수에서 다음과 같이 간소화된 코드가 있다
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction[], Class>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
}
}
}
주로 다음과 같은 작업을 수행했습니다.
다음에는 ProxyGenerator에 중점을 두겠습니다.generateProxyClass () 는 주로 다음 코드에서 이루어진다
private byte[] generateClassFile() {
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
//
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
//
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
//
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
//
Iterator var15;
try {
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
//
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
이렇게 긴 코드가 도대체 무엇을 하고 있는지 정리해 보자. 사실 자바 바이트 코드의 격식에 대해 알고 있다면 한눈에 알 수 있다. 이 코드는 바이트 코드를 조합하여 방법, 필드, 상수도 등 정보를 순서대로 격식에 따라 하나씩 조합한다.예를 들면var14.writeInt(-889275714);첫 번째 필드, 4바이트를 쓰는 거야. 제인이 정의한 마수잖아.
에이전트가 어떻게 생성되는지 알게 되면 생성된 에이전트가 어떻게 생겼는지 살펴보자
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements ProxyInterface {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(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 method() 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("com.czj.proxy.ProxyInterface").getMethod("method");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
프록시 클래스는proxy 클래스를 계승하기 때문에 jdk의 동적 프록시는 계승을 통해 실현할 수 없습니다.저희는 주로 두 가지 방법에 주목을 해요.
구조 방법은 InvocationHandler를 받아들인다. 이handler는 우리가 실례화할 때 전송된 것이고 부류인 Proxy의 구조 방법을 호출한다.우리 스스로가 이룬method, 슈퍼를 보자.h.invoke(this, m3, (Object[])null);말하자면 핸들러를 실행하는 invoke 방법이야.
여기는 틀림없이 우리가 정의한handler를 실행하고 코드를 붙인 것이다.
public class ProxyHandler implements InvocationHandler {
Object proxyObject =null;
public ProxyHandler(Object object){
this.proxyObject = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(" ");
method.invoke(proxyObject,args);
System.out.println(" ");
return null;
}
여기서 invoke 방법 중의 invoke는 반사 원리를 사용했다. 그 중에서 매개 변수 method는 인터페이스 ProxyInterface 성명 방법의 대상이고 proxyObject는 이 인터페이스의 실현 클래스이다. 반사 원리를 이해하지 못한 친구들도 여기가 우리 인터페이스의 실현 클래스 ProxyObject를 호출하는 method 방법이라는 것을 알고 있을 것이다.
자, 끝났으니 총결산을 합시다.
jdk 동적 프록시 프로세스: 프록시 클래스를 계승하여 프록시 클래스의 실현된 인터페이스를 실현하고 값을 부여하는handler를 실행합니다. 실제 실행 방법이 호출될 때 프록시 클래스는 자신이 실행하지 않고handler 실행 방법을 통해 호출합니다.handler의 호출 방법인 invoke에는 자신이 프록시 클래스에 제공할 업무 논리를 포함하는 것 외에 반사적으로 프록시 클래스를 실행하는 방법도 필요합니다.
이점:
나쁜 점:
사고: 동적 의미: 프록시 클래스의class는 프로그램이 실행될 때 동적으로 생성되며 컴파일할 때 존재하지 않습니다. 위의 프록시 클래스의class는 기본적으로 로컬 디스크에 쓰지 않습니다.
handler와 에이전트 클래스의 서비스 영역: 하나의 에이전트 클래스는 특정한 인터페이스의 실현 클래스 서비스를 제공할 수 있고, 하나의handler는 모든 에이전트가 필요한 업무 논리와 같은 클래스 서비스를 제공할 수 있다.
프록시 클래스를 생성할 때, 바이트 코드를 직접 생성하는 방식은 성능이 좋고 나쁨이나 더 좋은 방법이 있습니까?
첫 번째 블로그에서 닭고기는 대신으로 가는 길에 더욱 분발해야 한다. 다음 분석은 cglib이다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.