자바 디자인 모드 - 프 록 시 모드 [프 록 시 패턴] (동적 에이전트)

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 가 제공 하 는 동적 프 록 시 는 인 터 페 이 스 를 기반 으로 합 니 다.

좋은 웹페이지 즐겨찾기