위의 글 에서 우 리 는 모든 인 텐 시파 이 어 를 얻 고 일치 하 는 인 텐 시파 이 어 를 얻 는 것 을 분 석 했 습 니 다. 본 고 에서 Spring AOP 의 또 다른 핵심 논리 인 프 록 시 생 성 을 분석 하 겠 습 니 다.이 부분의 논리 적 입 구 는 wrapIfNecessary () 방법 에서 인 텐 시파 이 어 를 가 져 온 createProxy () 입 니 다.
protected Object createProxy(
Class> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
//
proxyFactory.copyFrom(this);
// bean targetClass if (!shouldProxyTargetClass(beanClass, beanName)) {
// Must allow for introductions; can't just set interfaces to
// the target's interfaces only.
Class>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
for (Class> targetInterface : targetInterfaces) {
// proxyFactory.addInterface(targetInterface);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
// proxyFactory.addAdvisor(advisor);
}
// proxyFactory.setTargetSource(targetSource);
// customizeProxyFactory(proxyFactory);
// ,
// false( , )
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(this.proxyClassLoader);
}
프 록 시 클래스 의 생 성 및 처리 에 대해 Spring 은 프 록 시 팩 토리 에 의뢰 하 였 으 며, 위의 함수 에 서 는 프 록 시 팩 토리 의 초기 화 작업 을 수행 하 였 으 며, 실제 프 록 시 생 성 을 위 한 준 비 를 하 였 으 며, 초기 화 는 다음 과 같은 내용 을 포함 합 니 다.
현재 속성 가 져 오기;
프 록 시 인터페이스 추가;
Advisor 를 밀봉 하고 Proxy Factory 에 가입 합 니 다.
대리 할 클래스 설정 하기;
대리 공장 에 대해 맞 춤 형 처 리 를 하여 자 류 를 실현 하도록 한다.
프 록 시 가 져 오기;
그 중에서 Advisor 를 봉인 하고 프 록 시 팩 토리 에 가입 하 며 에이 전 트 를 만 드 는 것 은 상대 적 으로 번 거 로 운 과정 입 니 다. 프 록 시 팩 토리 가 제공 하 는 addAdvisor 방법 을 통 해 인 텐 시파 이 어 를 프 록 시 공장 에 직접 설치 할 수 있 지만 인 텐 시파 이 어 를 인 텐 시파 이 어 로 밀봉 하 는 것 은 일정한 논리 가 필요 합 니 다.
Spring 에 서 는 과도 한 차단기, 인 텐 시파 이 어, 인 텐 시파 이 어 등 방식 으로 논 리 를 강화 하기 때문에 인 텐 시파 이 어 를 Advisor 로 패키지 하여 프 록 시 생 성 을 진행 할 필요 가 있 습 니 다. 강 화 된 패 키 징 과정 을 마 쳤 습 니 다. 그 다음은 가장 중요 한 단계 인 프 록 시 생 성 과 획득 입 니 다.
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
1. 프 록 시 생 성
protectedfinalsynchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
//return getAopProxyFactory().createAopProxy(this);
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null) {
thrownew AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
returnnew JdkDynamicAopProxy(config);
}
return CglibProxyFactory.createCglibProxy(config);
}
else {
returnnew JdkDynamicAopProxy(config);
}
}
여기 서 프 록 시 생 성 이 완료 되 었 습 니 다. 우리 가 이전에 Spring 의 소스 코드 를 읽 었 든 안 읽 었 든 간 에 Spring 의 프 록 시 에서 JDK Proxy 의 실현 Cglib Proxy 의 실현 에 대해 어느 정도 들 어 본 적 이 있 을 것 입 니 다.Spring 은 만약 에 골 랐 다 면?이제 우 리 는 소스 코드 의 측면 에서 스프링 이 어떻게 대리 방식 을 선택 하 는 지 분석 해 보 자. 상기 코드 에서 의 판단 조건 을 보면 세 가지 측면 이 Spring 의 판단 에 영향 을 미 친 다 는 것 을 알 수 있다.
optimize: CGLIB 를 통 해 만 든 에이전트 가 급진 적 인 최적화 전략 을 사용 하 는 지 제어 하 는 데 사 용 됩 니 다.AOP 에이전트 가 최 적 화 를 어떻게 처리 하 는 지 완전히 알 지 않 으 면 사용자 가 이 설정 을 사용 하 는 것 을 추천 하지 않 습 니 다.현재 이 속성 은 CGLIB 에이전트 에 만 사 용 됩 니 다. JDK 동적 에이전트 (기본 에이전트) 에 대해 서 는 유효 하지 않 습 니 다.
proxy TargetClass: 이 속성 이 true 일 때 대상 클래스 자체 가 대상 클래스 의 인터페이스 가 아 닌 프 록 시 되 고 CGLIB 방식 으로 프 록 시 를 만 듭 니 다. xml 파일 설정 방식 은 다음 과 같 습 니 다.
hasNoUser Supplied ProxyInterfaces: 프 록 시 인터페이스 가 존재 하 는 지 여부 입 니 다.
다음은 JDK 와 Cglib 방식 에 대한 정리 입 니 다.
대상 이 인 터 페 이 스 를 실현 하면 기본 적 인 상황 에서 JDK 의 동적 에이전트 로 AOP 를 실현 합 니 다.
대상 이 인 터 페 이 스 를 실현 하면 CGLIB 를 강제로 사용 하여 AOP 를 실현 할 수 있다.
대상 이 인 터 페 이 스 를 실현 하지 못 하면 CGLIB 방식 으로 AOP 를 실현 해 야 하 며 Spring 은 자동 으로 전환 된다.
어떻게 CGLIB 를 강제로 사용 하여 AOP 를 실현 합 니까?
CGLIB 라 이브 러 리 추가, SpringHOME/cglib/*.jar
Spring 프로필 에 추가
JDK 동적 에이전트 와 CGLIB 바이트 코드 생 성의 차이 점 은?
JDK 동적 에이 전 트 는 인 터 페 이 스 를 실현 한 클래스 에 만 에이 전 트 를 생 성 할 수 있 을 뿐 클래스 에 대한 에이 전 트 는 할 수 없다.
CGLIB 는 클래스 에 대한 에이전트 로 지 정 된 클래스 에 하위 클래스 를 생 성하 고 덮어 쓰 는 방법 입 니 다. 계승 이기 때문에 이 클래스 나 방법 은 final 이 라 고 밝 히 지 않 는 것 이 좋 습 니 다.
2. 에이전트 가 져 오기 어떤 프 록 시 방식 을 사용 하면 프 록 시 생 성 을 할 수 있 는 지 확 정 했 습 니 다. Spring 에 서 는 주로 두 가지 방식 으로 프 록 시 생 성 을 실 현 했 습 니 다. JDK 동적 프 록 시, cglib. 우 리 는 분석 합 니 다. 2.1 JDK 동적 에이전트 방식 여 기 는 JdkDynamicAopProxy 의 getProxy () 로 직접 위치 합 니 다.
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
JDK 동적 에이전트 의 사용 관건 은 사용자 정의 InvocationHandler 를 만 드 는 것 입 니 다. InvocationHandler 에는 덮어 써 야 할 함수 getProxy 가 포함 되 어 있 습 니 다. 여기 서 JdkDynamicAopProxy 는 InvocationHandler 를 계승 한 것 입 니 다. 그래서 위의 방법 은 바로 이 조작 을 완성 한 것 입 니 다. 그리고 우 리 는 JdkDynamicAopProxy 에 반드시 invoke 함수 가 있 을 것 이 라 고 추측 할 수 있 습 니 다.그리고 JdkDynamicaopProxy 는 AOP 의 핵심 논 리 를 그 안에 쓰 고 찾 아 보면 반드시 이런 함수 가 있 을 것 입 니 다.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class> targetClass = null;
Object target = null;
try {
// equals if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
// hash if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
}
if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
// , if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
//
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
//
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// ReflectiveMethodInvocation, proceed
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//
retVal = invocation.proceed();
}
//
Class> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
}
elseif (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
thrownew AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource. targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy. AopContext.setCurrentProxy(oldProxy);
}
}
}
위의 invoke () 함수 의 가장 중요 한 작업 은 바로 차단기 체인 을 만 들 고 ReflectiveMethodInvocation 류 를 사용 하여 체인 의 패 키 징 을 하 는 것 입 니 다. ReflectiveMethodInvocation 류 의 proced 방법 에서 차단기 의 하나씩 호출 을 실현 하면 우 리 는 이어서 탐구 합 니 다.proceed 방법 에서 선행 강화 가 목표 방법 전에 호출 되 고 나중에 목표 방법 후에 호출 되 는 논 리 를 어떻게 실현 합 니까?
ReflectiveMethodInvocation 의 주요 직책 은 체인 호출 카운터 를 유지 하고 현재 호출 체인 의 위 치 를 기록 하여 체인 이 질서 있 게 진행 할 수 있 도록 하 는 것 이다.사실 이 방법 에서 우리 가 구상 한 각종 증강 순 서 는 없 지만 세심 한 독자 들 은 이 부분 이 사실은 각 증강 기 에 의뢰 하여 실현 되 었 다 는 것 을 알 수 있다. 앞에서 말 한 바 와 같다. 2.2 Cglib 방식 CGLIB 대 리 를 완성 한 종 류 는 CglibAopProxy 류 에 의뢰 하여 이 루어 진 것 입 니 다. 끝까지 알 아 보 겠 습 니 다.앞의 분석 에 따 르 면 우 리 는 CglibAopProxy 의 입구 도 getProxy () 에 있 을 것 이 라 고 쉽게 판단 할 수 있다.
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
}
try {
Class> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class>[] additionalInterfaces = rootClass.getInterfaces();
for (Class> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Class validateClassIfNecessary(proxySuperClass);
// CGLIB Enhancer
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new MemorySafeUndeclaredThrowableStrategy(UndeclaredThrowableException.class));
enhancer.setInterceptDuringConstruction(false);
//
Callback[] callbacks = getCallbacks(rootClass);
Class>[] types = new Class>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
enhancer.setCallbacks(callbacks);
// Object proxy;
if (this.constructorArgs != null) {
proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
}
else {
proxy = enhancer.create();
}
return proxy;
}
catch (CodeGenerationException ex) {
catch 。。。
}
}
위의 함수 에서 Enhancer 를 완전 하 게 만 드 는 과정 입 니 다. Enhancer 의 문 서 를 참고 할 수 있 습 니 다. 여기 서 가장 중요 한 것 은 getCallbacks () 방법 으로 차단기 체인 을 설정 하 는 것 입 니 다.
private Callback[] getCallbacks(Class> rootClass) throws Exception {
// expose-proxy boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
// DynamicAdvisedInterceptor
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
// Choose a "straight to target" interceptor. (used for calls that are
// unadvised but can return this). May be required to expose the proxy. Callback targetInterceptor;
if (exposeProxy) {
targetInterceptor = isStatic ?
new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
}
else {
targetInterceptor = isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
}
// callback
Callback targetDispatcher = isStatic ?
new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
Callback[] mainCallbacks = new Callback[] {
aopInterceptor, // for normal advice
targetInterceptor, // invoke target without considering advice, if optimizednew SerializableNoOp(), // no override for methods mapped to this
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};
Callback[] callbacks;
// If the target is a static one and the advice chain is frozen,
// then we can make some optimisations by sending the AOP calls
// direct to the target using the fixed chain for that method.if (isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap(methods.length);
// TODO: small memory optimisation here (can skip creation for methods with no advice)for (int x = 0; x < methods.length; x++) {
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
this.fixedInterceptorMap.put(methods[x].toString(), x);
}
// Now copy both the callbacks from mainCallbacks
// and fixedCallbacks into the callbacks array.
callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
this.fixedInterceptorOffset = mainCallbacks.length;
}
else {
callbacks = mainCallbacks;
}
return callbacks;
}
getCallback () 에서 Spring 은 많은 상황 을 고려 했 고 디 테 일 한 부분 이 많 았 습 니 다. 그러나 우 리 는 소스 코드 를 읽 을 필요 도 없고 모든 디 테 일 을 이해 할 정력 도 없 었 습 니 다. 중요 한 것 은 중심 을 잡 으 면 됩 니 다.여기 서 가장 많이 사용 되 는 것 만 이해 해 야 합 니 다. 예 를 들 어 advised 속성 을 Dynamic Advised Interceptor 에 밀봉 하고 콜 백 스에 가입 하 는 목적 은 무엇 입 니까?CGLIB 에서 방법 에 대한 차단 은 사용자 정의 차단기 (MethodInterceptor 인 터 페 이 스 를 실현 하 는 클래스) 를 Callback 에 추가 하고 프 록 시 를 호출 할 때 차단기 의 intercept () 방법 을 직접 활성화 시 키 는 것 으로 이 루어 졌 으 며, getCallback () 방법 에 서 는 이러한 기능 이 구현 되 었 습 니 다. Dynamic Advised Interceptor 는 MethodInterceptor 에서 계승 하여 Callback 에 가입 한 후,프 록 시 를 다시 호출 할 때 intercept () 방법 을 직접 호출 합 니 다. 이 를 통 해 CGLIB 방식 으로 이 루어 진 프 록 시 에 대해 핵심 논 리 는 Dynamic Advised Interceptor 의 intercept () 방법 에 있 을 것 으로 추정 합 니 다.
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
//
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
//
retVal = methodProxy.invoke(target, args);
}
else {
//
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy. AopContext.setCurrentProxy(oldProxy);
}
}
}
여기 서 의 실현 은 JDK 동적 에이전트 방식 실현 에이전트 의 invoke 방법 과 크게 다 릅 니 다. 모두 먼저 차단기 체인 을 구성 한 다음 에 이 체인 을 밀봉 하여 직렬 호출 을 하 는 것 입 니 다. 다른 것 은 JDK 동적 에이전트 방식 에서 Reflective MethodInvocation 을 직접 구성 하 는 것 이 고 cglib 에 서 는 Cglib MethodInvocation 을 사용 합 니 다. 이것 은 Reflective MethodInvocation 을 계승 하 는 것 입 니 다.그러나 프로 세 스 () 방법 은 다시 쓰 지 않 았 다.
3. 총화 본 고 는 Spring AOP 실현 원리 에서 대리 대상 의 생 성 과정 을 분석 하 는 데 중심 을 두 었 다. bean 의 초기 화 과정 에서 Spring 의 백 엔 드 프로 세 서 를 실행 할 것 이다. 여기 서 이 bean 이 강화 되 어야 하 는 지 여 부 를 판단 하고 필요 하 다 면 Aspect 에서 정의 한 강화 정보 에 따라 지정 bean 을 강화 하 는 것 이다. 즉, 대리 대상 을 만 드 는 것 이다.프 록 시 대상 을 만 드 는 방법 은 두 가지 가 있 습 니 다. 하 나 는 JDK 동적 프 록 시 를 통 해, 다른 하 나 는 cglib 를 통 해 이 루어 집 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다: