Java 반사 및 동적 에이전트

8851 단어 java 반사
반사와 동적 에이전트는 일정한 관련성을 가지지만 단순히 동적 에이전트는 반사 메커니즘으로 이루어진 것으로 전면적이고 정확하지 않다. 동적 에이전트는 일종의 기능 행위이고 그 실현 방법은 매우 많다.이 말을 어떻게 이해해야 할지 다음 문장을 보십시오.

반사


반사 메커니즘은 자바 언어가 제공하는 기초 기능으로 프로그램이 실행할 때 자성(introspect, 공식 용어)을 할 수 있는 능력을 부여한다.반사를 통해 우리는 클래스나 대상을 직접 조작할 수 있다. 예를 들어 특정한 대상의 클래스 정의를 얻고, 클래스 성명의 속성과 방법을 얻고, 호출 방법이나 구조 대상을 사용하며, 심지어 실행할 때 클래스 정의를 수정할 수 있다.

1. 클래스 획득(Class) 대상


클래스 객체를 가져오는 방법은 다음과 같습니다.
  • forName()-> 예: Class.forName(“PeopleImpl”)
  • getClass()-> 예: new PeopleImpl().getClass()
  • .class 직접 획득 -> 예: PeopleImpl.class
  • 2. 클래스의 상용 방법

  • getName(): 클래스 전체 방법 가져오기;
  • getSuperclass(): 클래스의 부모 클래스 가져오기;
  • newInstance(): 인스턴스 객체 만들기;
  • getFields(): 현재 클래스와 부모 클래스의public 수식의 모든 속성을 가져옵니다.
  • getDeclaredFields(): 현재 클래스(부모 클래스 포함하지 않음)의 성명에 대한 모든 속성을 가져옵니다.
  • getMethod(): 현재 클래스와 부모 클래스의public 수식의 모든 방법을 가져옵니다.
  • getDeclaredMethods(): 현재 클래스(부모 클래스 포함하지 않음)의 성명을 가져오는 모든 방법;
  • 추가 방법:http://icdn.apigo.cn/blog/class-all-method.png

    3. 유형 방법 호출


    클래스의 방법을 반사하려면 관건적인 방법인'invoke()'를 통해 이루어져야 하며 방법 호출도 세 가지로 나뉜다.
  • 정적 (static) 방법 호출
  • 일반 방법 호출
  • 사유 방법 호출
  • 다음은 각종 호출의 실현 코드, 각종 호출의 공공 코드 부분을 각각 보여 준다.
    //  
    interface People {
        int parentAge = 18;
        public void sayHi(String name);
    }
    class PeopleImpl implements People {
        private String privSex = " ";
        public String race = " ";
        @Override
        public void sayHi(String name) {
            System.out.println("hello," + name);
        }
        private void prvSayHi() {
            System.out.println("prvSayHi~");
        }
        public static void getSex() {
            System.out.println("18 ");
        }
    }

    3.1 정적 메서드 호출

    //  ( )
    public static void main(String[] args) {
        Class myClass = Class.forName("example.PeopleImpl");
        //  (static) 
        Method getSex = myClass.getMethod("getSex");
        getSex.invoke(myClass);
    }
    정적 방법의 호출은 비교적 간단하다. getMethod(xx)를 사용하여 대응하는 방법을 얻고 invoke(xx)를 직접 사용하면 된다.

    3.2 일반 메서드 호출


    일반적인 비정상적인 방법이 호출되려면 먼저 클래스 예시를 가져와야 합니다. 'newInstance ()' 방법으로 가져오면 핵심 코드는 다음과 같습니다.
    Class myClass = Class.forName("example.PeopleImpl");
    Object object = myClass.newInstance();
    Method method = myClass.getMethod("sayHi",String.class);
    method.invoke(object," ");
    
    getMethod 획득 방법, 전달할 매개 변수의 종류를 설명할 수 있습니다.

    3.3 사유 방법을 사용한다


    개인 메서드를 호출하려면 "getDeclaredMethod(xx)"를 사용하여 이 클래스의 모든 메서드를 가져와야 합니다. 코드는 다음과 같습니다.
    Class myClass = Class.forName("example.PeopleImpl");
    Object object = myClass.newInstance();
    Method privSayHi = myClass.getDeclaredMethod("privSayHi");
    privSayHi.setAccessible(true); //  
    privSayHi.invoke(object);
    'getDeclaredMethod(xx)'를 제외하고 개인 방법을 호출하는 관건은 setAccessible(true) 속성을 설정하고 접근 제한을 수정하는 것이다. 이렇게 설정하면 호출할 수 있다.

    4. 총결산


    1. 반사에서 핵심적인 방법은 newInstance()에서 클래스 실례를 가져오고 getMethod(..)획득 방법, invoke(..) 사용setAccessible을 통해 개인 변수/메소드의 액세스 제한을 수정하는 메소드 호출
    2. 속성/방법을 얻을 때'Declared'가 있는지 없는지의 차이점은 Declared가 수식한 방법이나 속성이 있으면 본 클래스의 모든 방법이나 속성(private에서public까지)을 얻을 수 있으나 부류에 대한 정보를 얻을 수 없다는 것이다.Declared 수식 방법이나 속성이 아닌 경우,public 수식 방법이나 속성만 얻을 수 있고, getMethod (...) 와 같은 부모 클래스의 정보를 얻을 수 있습니다.getDeclaredMethod(...)

    2. 동적 에이전트


    동적 에이전트는 실행할 때 동적 구축 에이전트, 동적 처리 에이전트 방법이 호출되는 메커니즘으로 많은 장면이 유사한 메커니즘을 이용하여 이루어진다. 예를 들어 RPC 호출을 포장하고 절단면을 위한 프로그래밍(AOP)을 한다.
    동적 에이전트를 실현하는 방식이 매우 많다. 예를 들어 JDK 자체가 제공하는 동적 에이전트는 위에서 언급한 반사 메커니즘을 주로 이용했다.전설에서 더 높은 성능을 가진 바이트 코드 조작 메커니즘을 이용하여 ASM, cglib(ASM 기반) 등과 같은 다른 실현 방식도 있다.
    동적 에이전트가 해결한 문제?
    우선, 그것은 대리 메커니즘이다.디자인 모델 중의 에이전트 모델을 잘 알면 에이전트는 호출 목표에 대한 포장으로 볼 수 있다. 그러면 우리가 목표 코드에 대한 호출은 직접적으로 발생하는 것이 아니라 에이전트를 통해 이루어진다.에이전트를 통해 호출자와 실현자 간의 결합을 해소할 수 있다.예를 들어 RPC 호출을 하면 에이전트를 통해 더욱 우호적인 인터페이스를 제공할 수 있다.에이전트를 통해 전체적인 차단기를 만들 수도 있다.

    1. JDK Proxy 동적 에이전트


    JDK Proxy는 InvocationHandler 인터페이스를 구현하는 것으로 코드는 다음과 같습니다.
    interface Animal {
        void eat();
    }
    class Dog implements Animal {
        @Override
        public void eat() {
            System.out.println("The dog is eating");
        }
    }
    class Cat implements Animal {
        @Override
        public void eat() {
            System.out.println("The cat is eating");
        }
    }
    
    // JDK  
    class AnimalProxy implements InvocationHandler {
        private Object target; //  
        public Object getInstance(Object target) {
            this.target = target;
            //  
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(" ");
            Object result = method.invoke(target, args); //  
            System.out.println(" ");
            return result;
        }
    }
    
    public static void main(String[] args) {
        // JDK  
        AnimalProxy proxy = new AnimalProxy();
        Animal dogProxy = (Animal) proxy.getInstance(new Dog());
        dogProxy.eat();
    }
    위 코드와 같이 우리는 동적 에이전트를 통해 모든 요청 전과 후에 간단한 정보를 출력할 수 있습니다.
    주의: JDK Proxy는 인터페이스의 클래스만 프록시할 수 있습니다. (extends 계승 클래스라도 프록시할 수 없습니다.)
    JDK Proxy는 왜 인터페이스의 클래스만 프록시할 수 있습니까?
    이 질문은 동적 프록시 구현 방법인 newProxyInstance 소스에서 시작합니다.
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
    //  
    다음 두 가지 소스 매개변수 설명을 참조하십시오.
    * @param   loader the class loader to define the proxy class
    * @param   interfaces the list of interfaces for the proxy class to implement
    
  • loader: 클래스 마운트기, 즉 target.getClass().getClassLoader()
  • interfaces: 인터페이스 에이전트의 인터페이스 구현 목록
  • 그래서 이 문제의 근원은 JDK Proxy의 원본 코드 디자인에 있다.동적 에이전트를 고집하는 경우 비인터페이스 구현 클래스가 오류를 보고합니다.
    Exception in thread “main” java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to xxx

    2, Cglib 동적 에이전트


    JDK 동적 에이전트 메커니즘은 인터페이스의 클래스만 에이전트할 수 있고 Cglib는 클래스를 대상으로 에이전트를 실현한다. 그의 원리는 지정된 목표 클래스에 하위 클래스를 생성하고 그 중에서 덮어쓰는 방법으로 강화를 실현하는 것이다. 그러나 계승을 사용하기 때문에final 수식의 클래스에 대한 에이전트를 할 수 없다.
    Cglib은 Maven을 통해 버전 참조를 직접 수행할 수 있습니다. Maven 버전 주소:https://mvnrepository.com/artifact/cglib/cglib
    본 논문은 최신 버전 3.2.9의 Cglib를 사용했습니다.pom.xml에서 다음 참조를 추가합니다.
    
        cglib
        cglib
        3.2.9
    
    Cglib 코드는 다음과 같이 구현됩니다.
    class Panda {
        public void eat() {
            System.out.println("The panda is eating");
        }
    }
    class CglibProxy implements MethodInterceptor {
        private Object target; //  
        public Object getInstance(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            //  
            enhancer.setSuperclass(this.target.getClass());
            //  
            enhancer.setCallback(this);
            //  
            return enhancer.create();
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println(" ");
            Object result = methodProxy.invokeSuper(o, objects); //  
            System.out.println(" ");
            return result;
        }
    }
    
    public static void main(String[] args) {
        // CGLIB  
        CglibProxy proxy = new CglibProxy();
        Panda panda = (Panda)proxy.getInstance(new Panda());
        panda.eat();
    }
    cglib의 호출은 MethodInterceptor 인터페이스를 실현하는intercept 방법을 통해 invokeSuper를 호출하여 동적 에이전트를 할 수 있으며 일반 클래스에 직접 동적 에이전트를 할 수 있다.

    3. JDK Proxy VS Cglib


    JDK Proxy의 장점:
  • 의존 관계를 최소화하고 의존을 줄이는 것은 개발과 유지보수를 간소화하고 JDK 자체의 지원을 통해 더욱 신뢰할 수 있음을 의미한다.
  • JDK 버전을 부드럽게 업그레이드하고 바이트 코드 라이브러리는 새 버전에서 사용할 수 있도록 업데이트해야 한다.
  • Cglib 프레임워크의 이점:
  • 일반 클래스를 호출할 수 있으며 인터페이스를 실현할 필요가 없다.
  • 고성능;
  • 결론: 주의해야 할 것은 우리가 모델을 선택할 때 성능이 유일한 고려 요소가 아니라 신뢰성, 유지보수성, 프로그래밍 작업량 등이 흔히 더욱 주요한 고려 요소이다. 표준 라이브러리와 반사 프로그래밍의 문턱이 훨씬 낮고 코드량도 더욱 제어할 수 있기 때문이다. 만약에 우리가 서로 다른 소스 개발 프로젝트가 동적 에이전트 개발에 투입된 것을 비교하면 이 점을 볼 수 있다.
    이 문서의 모든 예제 코드:https://github.com/vipstone/java-core-example.git
    저는 위챗 공식계정이 하나 있는데 자바 기술과 관련된 건제품들을 자주 공유합니다.만약 당신이 나의 공유를 좋아한다면 위챗으로'java 단장'또는'javatuanzhang'팔로우를 검색할 수 있습니다.

    4. 참고 문서


    Java Core Technology 36 강의:http://t.cn/EwUJvWA
    Java 반사 및 동적 프록시:https://www.cnblogs.com/hanganglin/p/4485999.html

    좋은 웹페이지 즐겨찾기