스스로 Spring AOP (二) JDK 대리 실현 AOP
지난 글 은 스스로 Spring AOP (1) 환경 구축 과 지식 준 비 를 실현 했다. 나 는 프로젝트 를 구축 하고 예 를 들 었 다. 그 다음 에 나 는 이 사례 중의 AOP 기능 을 실현 할 것 이다.
Spring 에서 대상 이 인 터 페 이 스 를 실현 하면 기본적으로 JDK 의 동적 에이전트 로 AOP 를 실현 하고 대상 이 인 터 페 이 스 를 실현 하지 못 하면 CGLib (Code Generation Library) 방식 을 사용 해 야 합 니 다. 다음은 JDK 의 동적 에이전트 로 구현 하 겠 습 니 다.
준비 작업
AOP 기능 을 실현 하기 전에 테스트 할 관련 종 류 를 준비 하 세 요.
대상 관련 클래스 와 인 터 페 이 스 를 준비 하 세 요.
UserDao 인터페이스
package edu.jyu.dao;
public interface UserDao {
public void add(String user);
public String getUser(String id);
}
UserDaoImpl 클래스, UserDao 인터페이스 구현
package edu.jyu.dao;
public class UserDaoImpl implements UserDao {
@Override
public void add(String user) {
System.out.println("add " + user);
}
@Override
public String getUser(String id) {
System.out.println("getUser " + id);
return id + ":Jason";
}
}
사전 통지
아니면 사전 알림 을 예 로 들 면 스프링 을 모방 해서 사전 알림 인터페이스
MethodBeforeAdvice
를 만 들 겠 습 니 다. 사전 알림 을 사용자 정의 하려 면 반드시 이 를 실현 해 야 합 니 다. 그 안에 before
방법 이 있 습 니 다. 바로 목표 방법 이 실행 되 기 전에 실 행 된 것 입 니 다.package edu.jyu.aop;
import java.lang.reflect.Method;
/**
*
*
* @author Jason
*/
public interface MethodBeforeAdvice {
/**
*
* @param method
*
* @param args
*
* @param target
*
*/
void before(Method method, Object[] args, Object target);
}
그리고 사전 알림 클래스
MyBeforeAdvice
를 사용자 정의 하여 MethodBeforeAdvice
인 터 페 이 스 를 실현 합 니 다.package edu.jyu.advice;
import java.lang.reflect.Method;
import edu.jyu.aop.MethodBeforeAdvice;
/**
*
*
* @author Jason
*/
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) {
System.out.println(" ");
}
}
ProxyFactoryBean
지난 장 에서 제 가 사용 한 그 예 를 기억 하 시 나 요? 프 록 시 대상 을 만 들 때 지정 해 야 할
class
종 류 를 설정 합 니 다.id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
그래서 저도
org.springframework.aop.framework.ProxyFactoryBean
류 를 정의 해서 전문 적 으로 생 성 된 대리 대상 을 정 의 했 습 니 다. 아직 방법 이 없습니다.package edu.jyu.aop;
/**
*
* @author Jason
*/
public class ProxyFactoryBean {
}
배치 하 다.
AOP 와 관련 된 클래스 와 인터페이스 가 준비 되 어 있 습 니 다. 이제
ProxyFactoryBean
파일 에 생 성 프 록 시 대상 을 설정 해 야 합 니 다.
<beans>
<bean name="userDao" class="edu.jyu.dao.UserDaoImpl">bean>
<bean name="beforeAdvice" class="edu.jyu.advice.MyBeforeAdvice">bean>
<bean name="userDaoProxy" class="edu.jyu.aop.ProxyFactoryBean">
<property name="target" ref="userDao" />
<property name="proxyInterface" value="edu.jyu.dao.UserDao" />
<property name="interceptor" ref="beforeAdvice" />
bean>
beans>
이 프로필 은 이전 장의 예 와 비슷 합 니 다. 비교적 많은 차이 점 은 프 록 시 생 성 대상 을 설정 하 는 것 입 니 다. 설정 이 목표 알림 에 들 어가 면 Spring 의
applicationContext.xml
은 name
이 고 제 것 은 interceptorNames
입 니 다. 간단 하기 위해 서 저 는 하나의 목표 가 이미지 에 대한 통 지 를 강화 할 수 있 도록 하려 고 합 니 다.그러면 이 프로필
interceptor
파일 에 따라 다음 과 같이 수정 해 야 합 니 다.package edu.jyu.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
*
* @author Jason
*/
public class ProxyFactoryBean {
//
private Object target;
//
private Object interceptor;
//
private String proxyInterface;
// setter
public void setTarget(Object target) {
this.target = target;
}
public void setInterceptor(Object interceptor) {
this.interceptor = interceptor;
}
public void setProxyInterface(String proxyInterface) {
this.proxyInterface = proxyInterface;
}
}
개발 기능
현재 대상 을 만 드 는 클래스
ProxyFactoryBean
는 수정 이 필요 합 니 다. 대상 을 만 들 때 두 가지 상황 을 얻 을 수 있 기 때 문 입 니 다. 하 나 는 일반 대상 을 만 드 는 것 이 고 다른 하 나 는 프 록 시 대상 을 만 드 는 것 입 니 다.우 리 는 그것 의 edu.jyu.core.ClassPathXmlApplicationContext
방법 만 수정 해 야 한다./**
* bean bean
*
* @param bean
* @return
*/
private Object createBeanByConfig(Bean bean) {
// bean
Class clazz = null;
Object beanObj = null;
try {
clazz = Class.forName(bean.getClassName());
// bean
beanObj = clazz.newInstance();
// bean property
List properties = bean.getProperties();
// bean property , value ref bean
for (Property prop : properties) {
Map params = new HashMap<>();
if (prop.getValue() != null) {
params.put(prop.getName(), prop.getValue());
// value bean
BeanUtils.populate(beanObj, params);
} else if (prop.getRef() != null) {
Object ref = context.get(prop.getRef());
//
if (ref == null) {
ref = createBeanByConfig(config.get(prop.getRef()));
}
params.put(prop.getName(), ref);
// ref bean
BeanUtils.populate(beanObj, params);
}
}
//
if (clazz.equals(ProxyFactoryBean.class)) {
ProxyFactoryBean factoryBean = (ProxyFactoryBean) beanObj;
//
beanObj = factoryBean.createProxy();
}
} catch (Exception e1) {
e1.printStackTrace();
throw new RuntimeException(" " + bean.getClassName() + " ");
}
return beanObj;
}
코드 도 고치 지 않 고 다음 몇 마디 코드 만 새로 추 가 했 습 니 다.
//
if (clazz.equals(ProxyFactoryBean.class)) {
ProxyFactoryBean factoryBean = (ProxyFactoryBean) beanObj;
//
beanObj = factoryBean.createProxy();
}
만 든 대상 의 클래스 가
createBeanByConfig
인지 판단 하 는 것 입 니 다. 그렇다면 이 대상 을 ProxyFactoryBean
유형 대상 으로 강하 게 전환 한 다음 ProxyFactoryBean
방법 으로 프 록 시 대상 을 만 들 고 이 프 록 시 대상 을 최종 결과 createProxy()
로 합 니 다.이때
beanObj
아직 ProxyFactoryBean
방법 이 없 기 때문에 지금 이 방법 을 만 들 고 완성 해 야 할 기능 을 실현 합 니 다.코드 는 다음 과 같다./**
*
*
* @return
*/
public Object createProxy() {
// proxyInterface, CGLib
if (proxyInterface == null || proxyInterface.trim().length() == 0)
return createCGLibProxy();
// JDK
return createJDKProxy();
}
이 방법 은 매우 간단 하 다. 지정
createProxy()
에 따라 어떤 동적 프 록 시 방식 으로 프 록 시 대상 을 생 성 하 는 지 판단 하 는 것 이다. 인터페이스 가 없 으 면 CGLib 를 사용 하고 있 으 면 JDK 의 프 록 시 를 사용한다.그럼 현재 CGLib 로 프 록 시 대상 을 만 드 는
proxyInterface
방법 과 JDK 로 프 록 시 대상 을 만 드 는 createCGLibProxy()
방법 이 없습니다. 우 리 는 먼저 이 두 가지 방법 을 만 들 고 createJDKProxy()
방법 을 다음 장 에 남 겨 두 었 다가 실현 해 야 합 니 다. 지금 먼저 createCGLibProxy()
방법 을 실현 해 보 겠 습 니 다./**
* JDK
*
* @return
*/
private Object createJDKProxy() {
Class> clazz = null;
try {
clazz = Class.forName(proxyInterface);//
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(proxyInterface + " , ");
}
// JDK
Object proxyInstance = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// interceptor ,
// interceptor
if (interceptor instanceof MethodBeforeAdvice) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) interceptor;
//
advice.before(method, args, target);
//
result = method.invoke(target, args);
}
return result;
}
});
return proxyInstance;
}
JDK 를 사용 하여 프 록 시 대상 을 만 드 는 방법 도 어렵 지 않 습 니 다. 주로
createJDKProxy()
에 관심 을 가지 고 있 습 니 다. 저 는 먼저 InvocationHandler
이 어떤 알림 유형 에 속 하 는 지 판단 해 야 합 니 다. 서로 다른 알림 유형 은 목표 방법의 집행 순서 가 다 르 기 때 문 입 니 다. 예 를 들 어 사전 통 지 는 목표 방법 전에 실 행 됩 니 다. 나중에 통 지 는 목표 방법 후에 실 행 됩 니 다. 현재 의 실현 은 문제 가 있 습 니 다.예 를 들 어 내 가 사후 통 지 를 새로 추가 했다 면 나 는 분기 판단 interceptor
이 사후 통지 인지 아 닌 지 를 추가 해 야 한다.이 문 제 는 다음 장 에서 내 가 해결 할 것 이다.제 가
interceptor
전체 코드 를 다시 붙 여 드릴 게 요.package edu.jyu.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
*
* @author Jason
*/
public class ProxyFactoryBean {
//
private Object target;
//
private Object interceptor;
//
private String proxyInterface;
// setter
public void setTarget(Object target) {
this.target = target;
}
public void setInterceptor(Object interceptor) {
this.interceptor = interceptor;
}
public void setProxyInterface(String proxyInterface) {
this.proxyInterface = proxyInterface;
}
/**
*
*
* @return
*/
public Object createProxy() {
// proxyInterface, CGLib
if (proxyInterface == null || proxyInterface.trim().length() == 0)
return createCGLibProxy();
// JDK
return createJDKProxy();
}
/**
* JDK
*
* @return
*/
private Object createJDKProxy() {
Class> clazz = null;
try {
clazz = Class.forName(proxyInterface);//
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(proxyInterface + " , ");
}
// JDK
Object proxyInstance = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// interceptor ,
// interceptor
if (interceptor instanceof MethodBeforeAdvice) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) interceptor;
//
advice.before(method, args, target);
//
result = method.invoke(target, args);
}
return result;
}
});
return proxyInstance;
}
/**
* CGLib
*
* @return
*/
private Object createCGLibProxy() {
return null;
}
}
테스트
여기까지 만 해도 JDK 방식 으로 AOP 를 실현 하면 됩 니 다. 이제 테스트 클래스 테스트 를 작성 할 수 있 습 니 다.
package edu.jyu.aop;
import org.junit.Test;
import edu.jyu.core.BeanFactory;
import edu.jyu.core.ClassPathXmlApplicationContext;
import edu.jyu.dao.UserDao;
public class TestProxy {
@Test
public void testJDKProxy(){
BeanFactory ac = new ClassPathXmlApplicationContext("/applicationContext.xml");
UserDao userDao = (UserDao) ac.getBean("userDaoProxy");
System.out.println(userDao.getClass());
userDao.add("Jason");
String user = userDao.getUser("132");
System.out.println(user);
}
}
출력 결과
class com.sun.proxy.$Proxy4
add Jason
getUser 132
132:Jason
첫 번 째 줄 의 출력 은 JDK 가 생 성 한 프 록 시 대상 을 확인 한 다음 에 실행
ProxyFactoryBean
하 는 userDao
방법 과 add
방법 전에 도 사전 알림 방법 을 실 행 했 고 마지막 getUser
의 결과 도 틀 리 지 않 았 다.현재 전체 프로젝트 가 절반 을 완 성 했 고 나머지 절반 은 CGLib 방식 으로 프 록 시 대상 을 만 들 고 구 조 를 최적화 하 는 것 이다.
JSpring AOP 프로젝트 가 Github 에 올 라 왔 습 니 다.https://github.com/HuangFromJYU/JSpring-AOP
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
thymeleaf로 HTML 페이지를 동적으로 만듭니다 (spring + gradle)지난번에는 에서 화면에 HTML을 표시했습니다. 이번에는 화면을 동적으로 움직여보고 싶기 때문에 입력한 문자를 화면에 표시시키고 싶습니다. 초보자의 비망록이므로 이상한 점 등 있으면 지적 받을 수 있으면 기쁩니다! ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.