Spring 은 개인 적 인 방법 을 실현 하 는 몇 가지 문제(친 측 통용 해결 방안)
필드 설명:
예 를 들 어 XXXService 에는 두 개의 함수 a,함수 b 가 있다.
그리고 실현 류 XXXServiceImpl 에서 함수 a,함수 b 를 실 현 했 고 개인 적 인 방법 함수 c 와 함수 d 도 포함 했다.
XXXTTestController 를 써 서 XXXTServiceImpl 의 함수 c 를 호출 하려 고 합 니 다.
몇 가지 문제 에 직면 하 다.
1.인 터 페 이 스 를 주입 하면 실현 류 의 개인 류 를 호출 할 수 없습니다.
2.구현 클래스 를 주입 하려 면 구현 클래스 의 개인 적 인 방법 을 공유 로 바 꾸 고@EnableaspectJAutoProxy(proxy TargetClass=true)를 설정 하여 CGLIB 프 록 시 방식 을 사용 해 야 합 니 다.
단순히 테스트 를 위해 인터페이스 에서 실현 류 의 사유 방법 을 정의 하거나 테스트 를 위해 사유 방법 을 임시로 공유 방법 으로 바 꾸 는 것 은 적절 하지 않다.
해결 방안:
CGLIB 를 통 해 구현 클래스 의 하위 클래스 를 주입 할 수 있 으 며,Gradle 프로젝트 라면 Aspect 플러그 인 을 사용 할 수 있 으 며,절단면 코드 를 컴 파일 러 에서 구현 클래스 에 주입 하 는 유형 은 구현 클래스 로 만 든 다음 반사 설정 을 통 해 접근 가능 한 개인 방법 으로 호출 할 수 있 습 니 다.
방안 은 BeanUtils.findDeclared Method 반사 방법 을 사용 합 니 다.
반사 호출 코드:
BeanInvokeUtil
public class BeanInvokeUtil {
public class InvokeParams {
// ( )
private String methodName;
//
private Class<?>[] paramTypes;
//
private Object object;
// ( null, paramTypes )
private Object[] args;
public InvokeParams() {
super();
}
public InvokeParams(Object object, String methodName, Class<?>[] paramTypes, Object[] args) {
this.methodName = methodName;
this.paramTypes = paramTypes;
this.object = object;
this.args = args;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Class<?>[] getParamTypes() {
return paramTypes;
}
public void setParamTypes(Class<?>[] paramTypes) {
this.paramTypes = paramTypes;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
}
public static Object invokePrivateMethod(InvokeParams invokeParams) throws InvocationTargetException, IllegalAccessException {
//
checkParams(invokeParams);
//
return doInvoke(invokeParams);
}
private static Object doInvoke(InvokeParams invokeParams) throws InvocationTargetException, IllegalAccessException {
Object object = invokeParams.getObject();
String methodName = invokeParams.getMethodName();
Class<?>[] paramTypes = invokeParams.getParamTypes();
Object[] args = invokeParams.getArgs();
Method method;
if (paramTypes == null) {
method = BeanUtils.findDeclaredMethod(object.getClass(), methodName);
} else {
method = BeanUtils.findDeclaredMethod(object.getClass(), methodName, paramTypes);
}
method.setAccessible(true);
if (args == null) {
return method.invoke(object);
}
return method.invoke(object, args);
}
private static void checkParams(InvokeParams invokeParams) {
Object object = invokeParams.getObject();
if (object == null) {
throw new IllegalArgumentException("object can not be null");
}
String methodName = invokeParams.getMethodName();
if (StringUtils.isEmpty(methodName)) {
throw new IllegalArgumentException("methodName can not be empty");
}
//
Class<?>[] paramTypes = invokeParams.getParamTypes();
Object[] args = invokeParams.getArgs();
boolean illegal = true;
if (paramTypes == null && args != null) {
illegal = false;
}
if (args == null && paramTypes != null) {
illegal = false;
}
if (paramTypes != null && args != null && paramTypes.length != args.length) {
illegal = false;
}
if (!illegal) {
throw new IllegalArgumentException("paramTypes length != args length");
}
}
}
사용 방법:사용 시 CGLIB 방식 으로 구현 클래스 를 주입 하거나 절단면 코드 컴 파 일 러 를 구현 클래스 에 짜 서 Bean 을 주입 합 니 다.
@Autowired private XXXService xxxService;
그 다음 에 호출 대상 을 입력 하고 호출 할 개인 적 인 방법,매개 변수 유형 배열 과 매개 변수 배열 을 입력 합 니 다.
BeanInvokeUtil.invokePrivateMethod(new BeanInvokeUtil()
.new InvokeParams(xxxService, "somePrivateMethod", null, null));
이때 주입 한 xxxService 의 유형 은 xxxServiceImpl 입 니 다.반환 값 이 필요 하 다 면 이 호출 방법의 반환 값 을 가 져 올 수 있 습 니 다.
프로젝트 2:jdk 와 cglib 도 구 를 사용 하여 실제 대상 을 가 져 옵 니 다.
테스트 클래스
public class Test {
@Autowired
ServiceImpl serviceImpl;
@Test
public void test() throws Exception {
Class<?> clazz = Class.forName("ServiceImpl");
Method method = clazz.getDeclaredMethod("MethodA");
method.setAccessible(true);
Object target = ReflectionUtil.getTarget(serviceImpl);
// , serviceImpl, spring ,
// serviceImpl , ,
method.invoke(target);
}
}
spring 프 록 시 대상 의 실제 인 스 턴 스 를 가 져 오 는 도구 류 는 두 가지 동적 프 록 시 상황 에 적 용 됩 니 다:jdk 와 cglib
public class ReflectionUtil {
/**
* spring
* @param proxy
* @return
* @throws Exception
*/
public static Object getTarget(Object proxy) throws Exception {
if(!AopUtils.isAopProxy(proxy)) {
return proxy;//
}
if(AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy);
} else { //cglib
return getCglibProxyTargetObject(proxy);
}
}
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
return target;
}
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(proxy);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
return target;
}
}
그리고 더 좋 은 오픈 소스 도구 인 PowerMock 도 있 습 니 다https://github.com/powermock/powermock관심 있 는 학생 은 연구 해 보 세 요.이상 은 Spring 실현 류 사유 방법 테스트 유 니 버 설 방안 의 상세 한 내용 입 니 다.Spring 류 사유 방법 에 관 한 자 료 는 다른 관련 글 에 주목 하 시기 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[MeU] Hashtag 기능 개발➡️ 기존 Tag 테이블에 존재하지 않는 해시태그라면 Tag , tagPostMapping 테이블에 모두 추가 ➡️ 기존에 존재하는 해시태그라면, tagPostMapping 테이블에만 추가 이후에 개발할 태그 기반 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.