JAVA 동적 프록시
프록시 모드
에이전트 패턴은 일반적으로 사용되는 Java 디자인 패턴으로 에이전트 클래스와 위임 클래스가 동일한 인터페이스를 갖는 것이 특징입니다. 에이전트 클래스는 주로 위임 클래스에 대한 메시지 전처리, 메시지 필터링, 메시지 전달을 담당합니다. , 이후에 메시지를 처리합니다. 일반적으로 프록시 클래스와 대리자 클래스 사이에는 연결이 있습니다. 프록시 클래스의 개체는 대리자 클래스의 개체와 실제로 연결되지 않지만 관련 메서드를 호출합니다. 대리자 클래스의 객체에 대한 특정 서비스를 제공합니다.
에이전트의 생성시기에 따라 에이전트 클래스는 두 가지 유형으로 나눌 수 있습니다.
정적 프록시: 프로그래머가 생성하거나 특정 도구에 의해 자동으로 생성된 후 컴파일됩니다. 프로그램이 실행되기 전에 프록시 클래스의 .class 파일이 이미 존재합니다.
동적 프록시: 프로그램이 실행될 때 반사 메커니즘을 사용하여 동적으로 생성됩니다.
먼저 정적 프록시를 살펴보십시오.
1.Count.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.dao;
/**
* 계정 인터페이스 정의
*
* @author 관리자
*
*/
공개 인터페이스 개수 {
// 계정 메소드 보기
공개 무효 쿼리카운트();
//계정 메소드 수정
공개 무효 업데이트카운트();
}
2. CountImpl.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.dao.impl;
import net.battier.dao.Count;
/**
* 위임 클래스(비즈니스 로직 포함)
*
* @author 관리자
*
*/
공개 클래스 CountImpl은 Count {를 구현합니다.
@보수
공개 무효 쿼리카운트() {
System.out.println("계정 방법 보기...");
}
@보수
공개 무효 업데이트카운트() {
System.out.println("계정 방법 수정...");
}
}
CountProxy.java
패키지 net.battier.dao.impl;
import net.battier.dao.Count;
/**
* 이것은 프록시 클래스입니다(향상된 CountImpl 구현 클래스).
*
* @author 관리자
*
*/
공개 클래스 CountProxy는 Count {를 구현합니다.
개인 CountImpl countImpl;
/**
* 기본 생성자를 재정의
*
* @param countImpl
*/
공개 CountProxy(CountImpl countImpl) {
this.countImpl = countImpl;
}
@보수
공개 무효 쿼리카운트() {
System.out.println("트랜잭션 처리 전");
// 대리자 클래스의 메서드를 호출합니다.
countImpl.queryCount();
System.out.println("트랜잭션 처리 후");
}
@보수
공개 무효 업데이트카운트() {
System.out.println("트랜잭션 처리 전");
// 대리자 클래스의 메서드를 호출합니다.
countImpl.updateCount();
System.out.println("트랜잭션 처리 후");
}
}
3. TestCount.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.test;
import net.battier.dao.impl.CountImpl;
수입 net.battier.dao.impl.CountProxy;
/**
*테스트 횟수 클래스
*
* @author 관리자
*
*/
공개 클래스 TestCount {
공개 정적 무효 메인(String[] args) {
CountImpl countImpl = new CountImpl();
CountProxy countProxy = new CountProxy(countImpl);
countProxy.updateCount();
countProxy.queryCount();
}
}
코드를 관찰하면 각 프록시 클래스는 하나의 인터페이스만 제공할 수 있으므로 프로그램 개발 중에 필연적으로 너무 많은 프록시가 생성됩니다. 또한 코드를 제외하면 모든 프록시 작업이 동일합니다. 이때 반복해야 합니다. 이 문제를 해결하는 가장 좋은 방법은 프록시 클래스를 사용하여 모든 프록시 기능을 완료하는 것입니다. 이 경우 이를 완료하려면 동적 프록시를 사용해야 합니다.
동적 프록시를 살펴보겠습니다.
JDK 동적 프록시에는 클래스와 인터페이스가 포함되어 있습니다.
InvocationHandler 인터페이스:
공개 인터페이스 InvocationHandler {
공개 객체 호출(객체 프록시, 메소드 메소드, 객체[] args)는 Throwable을 발생시킵니다.
}
매개변수 설명:
개체 프록시: 프록시되는 개체를 나타냅니다.
메소드 메소드: 호출할 메소드
Object[] args: 메소드 호출 시 필요한 매개변수
InvocationHandler 인터페이스의 하위 클래스를 ProxySubject를 대체하는 프록시의 최종 작업 클래스로 생각할 수 있습니다.
프록시 클래스:
Proxy 클래스는 프록시 작업을 전문으로 하는 클래스입니다. 이 클래스는 하나 이상의 인터페이스에 대한 구현 클래스를 동적으로 생성하는 데 사용할 수 있습니다.
공용 정적 개체 newProxyInstance(ClassLoader 로더, 클래스<?>[] 인터페이스,
호출 핸들러 h)
IllegalArgumentException이 발생합니다.
매개변수 설명:
ClassLoader 로더: 클래스 로더
클래스<?>[] 인터페이스: 모든 인터페이스를 가져옵니다.
InvocationHandler h: InvocationHandler 인터페이스의 하위 클래스 인스턴스를 가져옵니다.
추신: 클래스 로더
ClassLoader 클래스의 인스턴스는 Proxy 클래스의 newProxyInstance() 메소드에 필요합니다. ClassLoader는 실제로 클래스 로더에 해당합니다. Java에는 세 가지 주요 클래스 로더가 있습니다.
Booststrap ClassLoader: 이 로더는 C++로 작성되었으며 일반 개발에서는 볼 수 없습니다.
Extension ClassLoader: 일반적으로 jre/lib/ext 디렉토리의 클래스에 해당하는 확장 클래스를 로드하는 데 사용됩니다.
AppClassLoader: (기본값) 가장 일반적으로 사용되는 로더인 classpath에 지정된 클래스를 로드합니다.
동적 프록시
정적 프록시 클래스와 달리 동적 프록시 클래스는 프로그래머가 소스 코드를 수동으로 작성할 필요 없이 프로그램이 실행될 때 Java 리플렉션 메커니즘에 의해 동적으로 생성됩니다. 동적 프록시 클래스는 프로그래밍 작업을 단순화할 뿐만 아니라 소프트웨어 시스템의 확장성을 향상시킵니다. Java 리플렉션 메커니즘은 모든 유형의 동적 프록시 클래스를 생성할 수 있기 때문입니다. java.lang.reflect 패키지의 Proxy 클래스 및 InvocationHandler 인터페이스는 동적 프록시 클래스를 생성하는 기능을 제공합니다.
동적 프록시 예:
1.BookFacade.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.dao;
공개 인터페이스 BookFacade {
공공 무효 addBook();
}
2.BookFacadeImpl.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.dao.impl;
수입 net.battier.dao.BookFacade;
공개 클래스 BookFacadeImpl은 BookFacade를 구현합니다.
@보수
공공 무효 addBook() {
System.out.println("책 추가 방법...");
}
}
BookFacadeProxy.java
패키지 net.battier.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK 동적 프록시 프록시 클래스
*
* @작가 학생
*
*/
공개 클래스 BookFacadeProxy는 InvocationHandler를 구현합니다.
개인 개체 대상;
/**
* 위임 개체를 바인딩하고 프록시 클래스를 반환합니다.
* @param 타겟
* @반품
*/
공공 객체 바인드(객체 대상) {
this.target = 대상;
//프록시 객체를 가져옵니다.
반환 Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); //인터페이스를 바인딩하려면 (이것은 결함이며, cglib는 이 결함을 보완합니다)
}
@보수
/**
* 통화 방법
*/
공용 객체 호출(객체 프록시, 메소드 메소드, Object[] args)
Throwable {를 던집니다.
개체 결과=null;
System.out.println("사건이 시작됩니다");
//실행 방법
결과=method.invoke(target, args);
System.out.println("모든 일의 끝");
결과 반환;
}
}
3. TestProxy.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.test;
수입 net.battier.dao.BookFacade;
수입 net.battier.dao.impl.BookFacadeImpl;
net.battier.proxy.BookFacadeProxy를 가져옵니다.
공개 클래스 TestProxy {
공개 정적 무효 메인(String[] args) {
BookFacadeProxy 프록시 = 새로운 BookFacadeProxy();
BookFacade bookProxy = (BookFacade) Proxy.bind(new BookFacadeImpl());
bookProxy.addBook();
}
}
그러나 JDK의 동적 프록시는 인터페이스 구현에 의존합니다. 일부 클래스가 인터페이스를 구현하지 않으면 JDK 프록시를 사용할 수 없으므로 cglib 동적 프록시를 사용해야 합니다.
Cglib 동적 프록시
JDK의 동적 프록시 메커니즘은 인터페이스를 구현하는 클래스만 프록시할 수 있습니다. 인터페이스를 구현할 수 없는 클래스는 JDK의 동적 프록시를 구현할 수 없습니다. 그 원칙은 지정된 대상 클래스에 대한 하위 클래스를 생성하고 향상을 달성하는 것입니다. , 그러나 상속이 사용되므로 최종 수정된 클래스를 프록시할 수 없습니다.
예
1.BookFacadeCglib.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.dao;
공용 인터페이스 BookFacade {
공공 무효 addBook();
}
2.BookCadeImpl1.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.dao.impl;
/**
* 인터페이스를 구현하지 않는 구현 클래스입니다.
*
* @작가 학생
*
*/
공개 클래스 BookFacadeImpl1 {
공공 무효 addBook() {
System.out.println("책을 추가하는 일반적인 방법...");
}
}
3.BookFacadeProxy.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
수입 net.sf.cglib.proxy.MethodProxy;
/**
* cglib 동적 프록시 사용
*
* @작가 학생
*
*/
공개 클래스 BookFacadeCglib는 MethodInterceptor를 구현합니다.
개인 개체 대상;
/**
* 프록시 객체 생성
*
* @param 타겟
* @반품
*/
공개 객체 getInstance(객체 대상) {
this.target = 대상;
Enhancer Enhancer = new Enhancer();
Enhancer.setSuperclass(this.target.getClass());
// 콜백 메소드
Enhancer.setCallback(this);
//프록시 객체 생성
return Enhancer.create();
}
@보수
// 콜백 메소드
공용 객체 인터셉트(객체 obj, 메소드 메소드, Object[] args,
MethodProxy 프록시)가 Throwable {를 발생시킵니다.
System.out.println("사건이 시작됩니다");
proxy.invokeSuper(obj, args);
System.out.println("모든 일의 끝");
null을 반환;
}
}
4. TestCglib.java
다음과 같이 코드 코드를 복사합니다 .
패키지 net.battier.test;
수입 net.battier.dao.impl.BookFacadeImpl1;
수입 net.battier.proxy.BookFacadeCglib;
공개 클래스 TestCglib {
공개 정적 무효 메인(String[] args) {
BookFacadeCglib cglib=새로운 BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
bookCglib.addBook();
}
}