프 록 시 모드 의 자바 동적 프 록 시 실현 방법

오늘 우연 한 기회 에 저 는 갑자기 JDK 의 동적 대 리 를 보고 싶 었 습 니 다.예전 에 도 조금 알 고 있 었 고 간단하게 테스트 해서 사용 하고 싶 었 기 때 문 입 니 다.사용 하 자마자 몇 개의 인터페이스 와 클래스 를 적 었 습 니 다.인터페이스 클래스:UserService.java

package com.yixi.proxy;
public interface UserService {
    public int save() ;
    public void update(int id);
}
실현 클래스:UserService Impl.java

package com.yixi.proxy;
public class UserServiceImpl implements UserService {
    @Override
    public int save() {
        System.out.println("user save....");
        return 1;
    }
    @Override
    public void update(int id) {
        System.out.println("update a user " + id);
    }
}
그리고 성급 하 게 자신 이 원 하 는 Invocation Handler 를 적 었 습 니 다.이 기능 은 간단 한 것 은 방법 이 실 행 된 시작 시간 과 종료 시간 을 기록 하 는 것 입 니 다.TimeInvocation Handler.java

package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimeInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("startTime : " +System.currentTimeMillis());
        Object obj = method.invoke(proxy, args);
        System.out.println("endTime : " +System.currentTimeMillis());
        return obj;
    }
}
의 모든 준비 작업 을 기록 하 는 것 입 니 다.다 됐 으 니까 테스트 시작 해 야 지!Test.java

package com.yixi.proxy;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) { 9         TimeInvocationHandler timeHandler = new TimeInvocationHandler();
        UserService u =  (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
        u.update(2);
        u.save();
    }
}
는 즐겁게 Run 을 했 지만,당신 의 체면 을 세 워 주지 않 았 습 니 다.결 과 는 화면 에 가득 찬 이상 이 었 습 니 다.

startTime : 1352877835040
startTime : 1352877835040
startTime : 1352877835040
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
    at $Proxy0.update(Unknown Source)
    at com.yixi.proxy.Test.main(Test.java:11)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12)
    ... 2 more
com.yixi.proxy.TimeInvocation Handler.invoke(TimeInvocation Handler.java:12)는 TimeInvocation Handle 의 12 줄 에 있 는 문 제 를 명확 하 게 알려 주 었 습 니 다.즉,

public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("startTime : " +System.currentTimeMillis());
        Object obj = method.invoke(proxy, args);
        System.out.println("endTime : " +System.currentTimeMillis());
        return obj;
    }
방법 으로 볼 때 틀린 것 이 없 었 습 니 다!invoke()라 는 방법 에 method.invoke(Object,Object[])가 원 하 는 모든 인 자 를 제공 한 것 같 기 때문에 우 리 는 당연히 그것 을 사용 해 야 합 니 다.그렇게 생각한다 면 JDK 의 함정 에 빠 질 것 입 니 다.먼저 정확 한 쓰 기 를 보 세 요.어떤 학생 들 이 뒤의 것 을 볼 기분 이 나 지 않도록 정확 한 해법 을 주 십시오.TimeInvocation Handler.java

package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimeInvocationHandler implements InvocationHandler {
    private Object o;
    public TimeInvocationHandler(Object o){
        this.o = o;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("startTime : " +System.currentTimeMillis());
        Object obj = method.invoke(o, args);
        System.out.println("endTime : " +System.currentTimeMillis());
        return obj;
    }
}
를 수정 하고 Test.java

package com.yixi.proxy;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) {
        TimeInvocationHandler timeHandler = new TimeInvocationHandler(new UserServiceImpl());
        UserService u =  (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
        u.update(2);
        u.save();
    }
}
를 수정 하 는 것 은 현재 정확 한 출력 결과 입 니 다.

startTime : 1352879531334
update a user 2
endTime : 1352879531334
startTime : 1352879531334
user save....
endTime : 1352879531335
코드 가 적 으 려 면 익명 류 를 직접 쓸 수 있 습 니 다.package com.yixi.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class Test {    public static void main(String[] args) {        final UserServiceImpl usi = new UserServiceImpl();        UserService u =  (UserService) Proxy.newProxyInstance(                usi.getClass().getClassLoader(),                usi.getClass().getInterfaces(),                new InvocationHandler() {                    @Override                    public Object invoke(Object proxy, Method method, Object[] args)                            throws Throwable {                        System.out.println("startTime : " +System.currentTimeMillis());                        Object obj = method.invoke(usi, args);                        System.out.println("endTime : " +System.currentTimeMillis());                        return obj;                    }                });        u.update(2);        u.save();    }}method.invoke(target,args);첫 번 째 인 자 는 대상 에 들 어 가 는 것 입 니 다.그러면 invocation Handler 의 Invoke 방법 은 Object proxy 인 자 를 왜 원 하 십 니까?그냥 내 려 다 봐!가장 중요 한 invoke 라 는 방법 에 대해 서 는 JDK 가 어떻게 말 하 는 지 살 펴 보 겠 습 니 다.

invoke
Object invoke(Object proxy,
              Method method,
              Object[] args)
              throws Throwable 。 , 。

proxy -
method - Method 。Method , 。
args - , , null。 ( java.lang.Integer java.lang.Boolean) 。
proxy-그 위 에서 호출 하 는 방법의 프 록 시 인 스 턴 스?이 말 이 무슨 뜻 이 죠?대리method 는 대리 방법 입 니까?그럼 제 가 프 록 시 를 수행 하 는 method 는 Object obj=method.invoke(proxy,args)가 아 닙 니 다.그렇습니까?당시 에 나 도 모퉁이 를 돌 지 않 았 다.토론 군 에 가서 구 글 에 가도 영감 을 찾 지 못 했다.생각해 보 니 이것 이 소스 코드 를 보 는 것 이 좋 겠 다.아마도 뭔 가 를 볼 수 있 을 것 이다!Proxy 류 의 소스 코드 를 열 어 보 니 어떤 구조 방법 이 있 습 니까?

protected InvocationHandler h;

protected Proxy(InvocationHandler h) {
    this.h = h;
    }

Invocation Handler 를 Proxy 의 구조 방법의 매개 변수 로...그럼 Invocation Handler 는 무엇 을 해 야 합 니까?Invocation Handler 의 invoke()방법 과 무슨 연관 이 있 습 니까?제 가 제일 먼저 생각 한 것 은 Proxy 내부 에서 다음 문 구 를 호출 하 는 것 입 니 다.

h.invoke(this,methodName,args);
invoke 방법 을 사용 해 야 해당 하 는 method 방법 을 실행 할 수 있 기 때 문 입 니 다.이것 부터 살 펴 보 겠 습 니 다.

여기 서 약간 느낌 이 있 는 것 같 습 니 다.u.update(2)시 à Proxy 는 handler.invoke(proxy Class,update,2)à 즉 proxy Class.update(2)를 호출 합 니 다.u.save();시 à Proxy 는 handler.invoke(proxyClass,save,null)à 를 호출 합 니 다.즉,proxyClass.save()를 호출 합 니 다.
Test.java 가 이렇게 바 뀌 었 을 때:

public class Test {
    public static void main(String[] args) {
        final UserServiceImpl usi = new UserServiceImpl();
        UserService u =  (UserService) Proxy.newProxyInstance(
                usi.getClass().getClassLoader(),
                usi.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        return null;
                    }
                });
        u.update(2);
        u.save();
    }
}
이때 익명 류 의 방법 이 null 로 되 돌아 오 는 것 을 주의 하 십시오.실행 해 보면

Exception in thread "main" java.lang.NullPointerException
    at $Proxy0.save(Unknown Source)
    at com.yixi.proxy.Test.main(Test.java:17)
17 줄 에 빈 포인터 가 있 습 니 다.바로 여기 u.save()방법 이 null 인 요소 가 있 습 니 다.u 가 비어 있 습 니까?아 닙 니 다.u 가 null 이 라면 u.update(2)는 거기에 빈 포인터 이상 을 알 립 니 다.17 줄 의 주석 을 떨 어 뜨 린 후에 이상 이 없 으 면 u.update()가 정상적으로 실 행 될 수 있 습 니 다.그럼 이 건 도대체 왜 일 까?사실은 이것 이 바로 invoke 방법 이 null 로 돌아 가 는 이유 입 니 다.UserService 류 의 두 가지 방법 을 주의 하 십시오.

public interface UserService {
    public int save() ;
    public void update(int id);
}
Save()방법 은 int 형 으로 돌아 가 고 update 방법 은 void 형 으로 돌아 갑 니 다.위의 추측 에 따 르 면 handler.invoke()는 proxyClass.update(2)를 실현 하 는 것 으로 추정 된다.의,invoke 방법 중의 return 방법 은 해당 하 는 프 록 시 방법의 반환 값 입 니 다.따라서 invoke 방법 이 null 로 돌아 갈 때 프 록 시 update 방법 은 반환 값 이 null 인 것 을 받 았 습 니 다.원래 void 로 돌아 가기 때문에 이상 을 보고 하지 않 았 습 니 다.프 록 시 save 는 int 형의 수 치 를 되 돌려 야 합 니 다.JVM 은 null 을 int 형 으로 바 꿀 수 없어 서 이상 을 신 고 했 습 니 다.이렇게 해석 하면 설명 이 통 하고 앞의 추측 도 상대 적 으로 증명 할 수 있 습 니 다.InvocationHandler 에서 invoke 방법 에서 첫 번 째 매개 변수 proxy 는 프 록 시 클래스 가 자신의 InvocationHandler 대상 에 게 호출 방법 을 인용 할 때 프 록 시 대상 proxyClass 의 인용 을 전송 하여 proxyClass 가 완성 해 야 할 업 무 를 완성 할 수 있 도록 하 는 것 같 습 니 다.
문채 가 안 돼!능력 에 한계 가 있다!지적 해 주 셨 으 면 좋 겠 는데...

좋은 웹페이지 즐겨찾기