자바 디자인 모드 - 프 록 시 모드 [프 록 시 패턴] (동적 에이전트)
47759 단어 디자인 모드
프 록 시 모드 에 대해 서 는 정적 대 리 를 소개 하 는 편 을 썼 습 니 다.이 글 을 보기 전에 정적 대리 라 는 글 을 먼저 보 는 것 을 권장 합 니 다.
자바 디자인 모드 - 프 록 시 모드 [프 록 시 패턴] (정적 에이전트)
정적 에이전트
대리 에 대해 우 리 는 두 가지 개념 을 알 아야 한다. 대리 대상, 목표 대상 이다.말 그대로 대리 대상 은 증강 후의 대상 을 말한다.목표 대상 은 강화 가 필요 한 대상 을 말한다.
우리 가 정적 대 리 를 실현 하 는 방식 은 두 가지 가 있 는데 그것 이 바로 계승, 집합 이다.
1. 상속
계승 은 대리 대상 이 목표 대상 을 계승 해 야 하 는 것 이다. 그러면 대리 대상 은 목표 대상 의 모든 방법 을 가지 게 되 고 목표 대상 에서 강화 해 야 하 는 방법 만 다시 쓰 면 정적 대 리 를 실현 할 수 있다.
//
public class UserDao {
public void proxy(){ };
}
//
public class UserDaoProxy extends UserDao {
@Override
public void proxy() {
System.out.println("UserDaoProxy proxy()");
super.proxy();
}
}
만약 에 우리 가 서로 다른 업무 가 필요 하 다 면 대리 대상 이 필요 하 다. 예 를 들 어 Order Dao Proxy, PayDao Proxy 등 은 모든 업무 유형 에 하나의 유형 을 정의 하고 이 UserDao 를 계승 해 야 하 는 지 생각해 보 자.이렇게 하면 대리 류 가 매우 많다.
단점: 대리 류 가 너무 많 고 복잡 하 며 폭발 이 일어 날 수 있다.
2. 집합
취 합 된 실현 방향 은 목표 대상 과 대리 대상 이 같은 인 터 페 이 스 를 실현 하고 대리 대상 에 목표 대상 을 포함 해 야 한 다 는 것 이다.
이 곳 의 코드 는 자바 디자인 모델 인 프 록 시 모드 [Proxy Pattern] (정적 에이전트) 라 는 글 의 디 스 플레이 코드 를 참조 할 수 있 습 니 다.
취 합 모드 의 정적 에이전트 단점: 클래스 폭발 도 발생 할 수 있 습 니 다. 계승 보다 조금 적 을 뿐 입 니 다.
요약: 불확실 한 상황 에서 정적 대 리 를 사용 하지 않도록 하 세 요.코드 를 쓰 면 클래스 가 생기 고, 클래스 가 생기 면 폭발 하기 때문이다.
3. 동적 에이전트
정적 대리 가 실현 하 는 두 가지 방식 중 하 나 는 공통 적 인 단점 이 있다. 바로 업무 류 가 증가 함 에 따라 많은 대리 류 가 발생 하 는데 우 리 는 이 를 폭발 이 라 고 부른다.그러면 어떻게 이런 폭발 문 제 를 해결 합 니까?
우 리 는 동적 대 리 를 사용 할 수 있다.그럼 동적 대리 가 뭐 죠?말 그대로 목표 대상 의 동태 에 따라 우리 가 필요 로 하 는 대리 류 를 만 드 는 것 이다.
동적 에이 전 트 는 프로그램 이 실 행 될 때 반사, 동적 생 성에 필요 한 에이전트 클래스 입 니 다.
1. 대상 은 어떻게 만 듭 니까?
아 날로 그 동적 에이전트 에 앞서 자바 에서 자바 대상 이 어떻게 만 들 어 졌 는 지 생각해 보 겠 습 니 다.
잘 모 르 겠 으 면 먼저 가서 자바 대상 이 어떻게 만 들 어 졌 는 지 볼 수 있 습 니 다.
간단하게 요약 하면 자바 원본 파일 을 만 들 고 자바 의 원본 파일 을 'class 파일' 로 컴 파일 해 야 클래스 로 더 에 불 러 올 수 있 습 니 다. 마지막 으로 자바 대상 을 만 들 수 있 습 니 다.
그럼 동적 대리 가 실현 하 는 사고방식 은 무엇 입 니까?우 리 는 인 터 페 이 스 를 통 해 클래스 파일 (자바 소스 파일) 을 반사 적 으로 생 성 한 다음 에 제3자 의 컴 파일 기술 을 호출 하여 이 생 성 된 클래스 파일 을 class 파일 로 동적 컴 파일 한 다음 에 UrlclassLoader (이 동적 으로 생 성 된 class 가 프로젝트 에 없 기 때문에 UrlclassLoader 를 사용 해 야 합 니 다) 를 이용 하여 이 동적 컴 파일 된 클래스 를 jvm 에 불 러 올 수 있 습 니 다.마지막 으로 반 사 를 통 해 이 종 류 를 예화 하 다.
이것 이 바로 동적 대리 의 실현 사고 입 니 다. 쓸데없는 말 은 하지 않 겠 습 니 다 ~
2. 어떻게 동적 대 리 를 모 의 합 니까?
프 록 시 클래스 를 만 드 는 ProxyUtil 클래스 를 수 동 으로 모 의 하면 들 어 오 는 대상 에 따라 대상 의 프 록 시 클래스 를 동적 으로 생 성 할 수 있 습 니 다.
public class ProxyUtil {
/**
* targetInf :
* CustomInvocationHandler : InvocationHandler
**/
public static Object newInstance(Class targetInf, CustomInvocationHandler h){
//
Object proxy = null;
//
Method methods[] =targetInf.getDeclaredMethods();
//
String line="
";
//
String tab ="\t";
//
String infName = targetInf.getSimpleName();
//
String content ="";
//
// package
String packageContent = "package com.scorpios;"+line;
// import
String importContent = "import "+targetInf.getName()+";"+line
+"import com.scorpios.dao.CustomInvocationHandler;"+line
+"import java.lang.Exception;"
+"import java.lang.reflect.Method;"+line;
//
String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
//
String filedContent = tab +"private CustomInvocationHandler h;"+line;
//
String constructorContent = tab+"public $Proxy (CustomInvocationHandler h){" +line
+tab+tab+"this.h =h;"
+line+tab+"}"+line;
//
String methodContent = "";
//
for (Method method : methods) {
//
String returnTypeName = method.getReturnType().getSimpleName();
//
String methodName = method.getName();
// , :Sting.class String.class
Class args[] = method.getParameterTypes();
// ,String p0,Sting p1
String argsContent = "";
//
String paramsContent="";
//
int flag = 0;
for (Class arg : args) {
// String
String temp = arg.getSimpleName();
// String p0,Sting p1
argsContent+=temp+" p"+flag+",";
// p0,p1
paramsContent+="p"+flag+",";
flag++;
}
// ‘,’
if (argsContent.length()>0){
argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1);
}
//
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
+tab+tab+"return ("+returnTypeName+")h.invoke(method);"+line;
methodContent+=tab+"}"+line;
}
//
content = packageContent+importContent+clazzFirstLineContent+
filedContent+constructorContent+methodContent+"}";
// , package
File file =new File("c:\\com\\scorpios\\$Proxy.java");
try {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
// , .java .class
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
// URLClassLoader ,
URL[] urls = new URL[]{new URL("file:c:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazz = urlClassLoader.loadClass("com.scorpos.$Proxy");
// ,
Constructor constructor = clazz.getConstructor(CustomInvocationHandler.class);
proxy = constructor.newInstance(h);
}catch (Exception e){
e.printStackTrace();
}
return proxy;
}
}
프 록 시 유 틸 로 테스트 해 보 겠 습 니 다. 프 록 시 대상 을 만 드 세 요.
public interface UserDao {
public String proxy() throws Exception;
}
public interface CustomInvocationHandler {
public Object invoke(Method method);
}
public class TestCustomHandler implements CustomInvocationHandler {
//
Object target;
public TestCustomHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Method method) {
try {
System.out.println("----------------");
//
return method.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class Test {
public static void main(String[] args) {
//
UserDao proxy = (UserDao ) ProxyUtil.newInstance(UserDao.class,new TestCustomHandler(new UserDaoImpl()));
try {
proxy.proxy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
프 록 시 유 틸 리 티 를 사용 하여 생 성 된 클래스 입 니 다. 프 록 시 클래스 를 동적 으로 생 성 한 것 이 아 닙 니까?
package com.scorpios;
import com.scorpios.dao.UserDao;
import com.scorpios.dao.CustomInvocationHandler;
import java.lang.Exception;
import java.lang.reflect.Method;
public class $Proxy implements UserDao{
private CustomInvocationHandler h;
public $Proxy (CustomInvocationHandler h){
this.h = h;
}
public String proxy()throws Exception {
Method method = Class.forName("com.scorpios.dao.UserDao").getDeclaredMethod("proxy");
return (String)h.invoke(method);
}
}
위 는 수 동 으로 모 의 한 동적 프 록 시 를 만 드 는 과정 입 니 다. 이 과정 에서 우 리 는 수 동 으로 만 드 는 방식 이 다음 과 같은 단점 이 있 음 을 알 수 있 습 니 다. 먼저 파일 생 성, 동적 컴 파일 파일 파일 class, URLClassLoader 가 필요 합 니 다.
3. JDK 동적 에이전트
인 터 페 이 스 를 통 해 바이트 코드 를 반사 한 후 바이트 코드 를 class 로 변환 합 니 다.
public class Test {
public static void main(String[] args) {
// , class
byte[] bytes = ProxyGenerator.generateProxyClass("",new Class[]{UserDao.class});
try {
FileOutputStream fileOutputStream = new FileOutputStream("c:\\com\\scorpios\\$Proxy18.class");
fileOutputStream.write(bytes);
fileOutputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
// Jdk
/**
*
*
* InvocationHandler ,
**/
UserDao jdkproxy = (UserDao) Proxy.newProxyInstance(Test.class.getClassLoader(),
new Class[]{UserDao.class},new TestCustomHandler(new UserDaoImpl()));
try {
jdkproxy.proxy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
JDK 동적 에이전트 에서 생 성 된 바이트 파일 입 니 다.
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.scorpios.dao.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy18 extends Proxy implements UserDao {
private static Method m3;
public $Proxy18 (InvocationHandler var1) throws {
super(var1);
}
public final String proxy() throws Exception {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (Exception | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m3 = Class.forName("com.scorpios.dao.UserDao").getMethod("proxy");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
위 는 JDK 가 제공 하 는 방식 으로 동적 으로 생 성 된 프 록 시 대상 클래스 입 니 다.생 성 된 코드 에서 알 수 있 듯 이 프 록 시 클래스 를 계 승 했 기 때문에 JDK 의 동적 에이 전 트 는 인터페이스 만 전달 할 수 있 을 뿐 구체 적 인 실현 클래스 는 전달 할 수 없다. 자바 의 단일 계승 때문이다.
소결
본 고 는 주로 동적 대 리 를 소개 하고 반사 체 제 를 사용 하여 수 동 으로 동적 대리 ProxyUtil 을 실현 했다. 이것 은 우리 가 JDK 바 텀 의 동적 대리 체 제 를 이해 하 는 데 좋 은 도움 이 된다.
마지막 으로 JDK 가 제공 하 는 동적 프 록 시 를 사용 하여 바이트 코드 파일 을 생 성 했 는데 프 록 시 클래스 가 프 록 시 클래스 를 계승 하 는 것 을 발 견 했 습 니 다. 그래서 우 리 는 JDK 가 제공 하 는 동적 프 록 시 는 인 터 페 이 스 를 기반 으로 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
디자인 모델 의 공장 모델, 단일 모델자바 는 23 가지 디자인 모델 (프로 그래 밍 사상/프로 그래 밍 방식) 이 있 습 니 다. 공장 모드 하나의 공장 류 를 만들어 같은 인 터 페 이 스 를 실현 한 일부 종 류 를 인 스 턴 스 로 만 드 는 것...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.