Jdk 동적 프록시를 사용하여 클래스를 프록시하려면 프록시된 클래스가 하나 이상의 인터페이스를 구현해야 하며 인터페이스의 메서드만 프록시할 수 있습니다.
jdk에서 동적 프록시 구현은 일반적으로 세 단계로 나뉩니다.
1. 인터페이스와 구현 클래스를 작성합니다.
2. InvocationHandler 인터페이스를 구현하는 프로세서를 작성하십시오. 이 인터페이스에는 메소드가 하나만 있으며 해당 서명은 public Object Invoke(Object Proxy, Method method, Object[] args)입니다.
throws Throwable; 동적 가로채기를 수행하기 위해 메서드 호출 전후에 프로세서의 구현 메서드에 고유한 코드를 추가할 수 있습니다. 프록시에 의해 생성된 동적 프록시 클래스는 우리가 프록시하는 실제 클래스가 아니므로 실제로 프록시하려는 클래스(즉, 1단계 구현 클래스의 클래스).
3. java.lang.reflect.Proxy 클래스의 newProxyInstance 메소드를 사용하여 동적 프록시 클래스를 생성하십시오. 프록시 메서드에 대한 모든 호출의 경우 생성된 동적 프록시 클래스의 메서드를 직접 호출할 수 있지만 먼저 강제 형식 변환을 수행하고 호출하려는 메서드의 인터페이스로 변환해야 합니다.
JDK 원칙 분석:
Proxy의 소스코드를 분석하면 동적 Proxy 클래스의 상세한 생성을 확인할 수 있습니다. newProxyInstance 메소드는 먼저 동적 프록시 클래스의 Class 인스턴스를 생성한 다음 매개변수 유형이 InvocationHandler인 생성자를 호출하여 동적 프록시 클래스를 생성하고 반환합니다.
동적 프록시 클래스의 클래스 인스턴스를 생성하는 방법 ProxyGenerator 클래스는 동적 프록시 클래스의 클래스 바이트 스트림을 생성하고 이를 메서드 영역에 로드하는 데 사용됩니다.
클래스 바이트 스트림 생성 과정을 분석해 보면 Proxy를 상위 클래스로 사용하여 프록시할 인터페이스의 모든 메서드를 구현하는 것을 볼 수 있습니다. 각 메서드의 구현 본문은 주로 프로세서의 Invoke 메서드를 호출합니다.
클래스 바이트 스트림 생성 프로세스의 주요 코드는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다 .
개인 바이트[] generateClassFile()
{
addProxyMethod(hashCodeMethod, java/lang/Object);
addProxyMethod(equalsMethod, java/lang/Object);
addProxyMethod(toStringMethod, java/lang/Object);
for(int i = 0; i < 인터페이스.길이; i++)
{
메소드 amethod[] = 인터페이스[i].getMethods();
for(int k = 0; k < amethod.length; k++)
addProxyMethod(amethod[k], 인터페이스[i]);
}
목록 목록;
for(Iterator iterator = ProxyMethods.values().iterator(); iterator.hasNext(); checkReturnTypes(list))
목록 = (목록)iterator.next();
노력하다
{
메소드.추가(generateConstructor());
for(Iterator iterator1 = ProxyMethods.values().iterator(); iterator1.hasNext();)
{
목록 list1 = (목록)iterator1.next();
반복자 iterator2 = list1.iterator();
동안(iterator2.hasNext())
{
ProxyMethod Proxymethod = (ProxyMethod)iterator2.next();
fields.add(new FieldInfo(proxymethod.methodFieldName, "Ljava/lang/reflect/Method;", 10));
method.add(<SPAN style="COLOR: red">proxymethod.generateMethod()</SPAN><SPAN style="COLOR: #000000">);</SPAN>
다음과 같이 코드 코드를 복사합니다 .
}
}
메소드.추가(generateStaticInitializer());
}
catch(IOException io예외)
{
throw new InternalError("예기치 않은 I/O 예외");
}
if(methods.size() > 65535)
throw new IllegalArgumentException("메서드 제한 초과");
if(fields.size() > 65535)
throw new IllegalArgumentException("필드 제한이 초과되었습니다.");
cp.getClass(dotToSlash(클래스이름));
cp.getClass("java/lang/reflect/Proxy");
for(int j = 0; j < 인터페이스.길이; j++)
cp.getClass(dotToSlash(인터페이스[j].getName()));
cp.setReadOnly();
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
DataOutputStream dataoutputstream = new DataOutputStream(bytearrayoutputstream);
노력하다
{
dataoutputstream.writeInt(-889275714);
dataoutputstream.writeShort(0);
dataoutputstream.writeShort(49);
cp.write(데이터출력스트림);
dataoutputstream.writeShort(49);
dataoutputstream.writeShort(cp.getClass(dotToSlash(className)));
dataoutputstream.writeShort(cp.getClass("java/lang/reflect/Proxy"));
dataoutputstream.writeShort(인터페이스.길이);
for(int l = 0; l < 인터페이스.길이; l++)
dataoutputstream.writeShort(cp.getClass(dotToSlash(interfaces[l].getName())));
dataoutputstream.writeShort(fields.size());
FieldInfo fieldinfo;
다음과 같이 코드 코드를 복사합니다 .
//속성 추가
for(Iterator iterator3 = fields.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
fieldinfo = (FieldInfo)iterator3.next();
//메소드 추가
dataoutputstream.writeShort(methods.size());
MethodInfo 메소드정보;
for(Iterator iterator4 =methods.iterator(); iterator4.hasNext(); methodinfo.write(dataoutputstream))
methodinfo = (MethodInfo)iterator4.next();
dataoutputstream.writeShort(0);
}
catch(IOException ioException1)
{
throw new InternalError("예기치 않은 I/O 예외");
}
return bytearrayoutputstream.toByteArray();
}
참고: 코드의 빨간색 부분인 Proxymethod.generateMethod()는 각 메서드에 대한 메서드 본문을 생성합니다. 소스 코드를 보면 모두 InvocationHandler 인터페이스 구현 프로세서의 호출 메서드를 호출하고 있음을 알 수 있습니다.