SPRING 기술 내막: SPRING 구조 와 디자인 원리 (2 판) - 필기 (6) AOP 차단기 호출 의 실현
/** * Implementation of <code>InvocationHandler.invoke</code>. * <p>Callers will see exactly the exception thrown by the target, * unless a hook method throws an exception. */
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 {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
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) {
// 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.
/*targetSource.getTarget() ,target , * , HotSwappableTargetSource ProxyFactoryBean target 。 */
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// Massage return value if necessary.
if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
6.2 Cglib2aopProxy Cglib2aopProxy 의 intercept 의 리 셋: 내부 클래스 Dynamic Advised Interceptor 의 intercept 방법 으로 리 셋 되 며, CglibMethodInvocation 을 사용 하여 차단기 체인 호출 을 완료 합 니 다.
/** * General purpose AOP callback. Used when the target is dynamic or when the * proxy is not frozen. */
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
private AdvisedSupport advised;
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}
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 <code>null</code>. 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<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying. AOP , target
retVal = methodProxy.invoke(target, args);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
@Override
public boolean equals(Object other) {
return (this == other ||
(other instanceof DynamicAdvisedInterceptor &&
this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
}
/** * CGLIB uses this to drive proxy creation. */
@Override
public int hashCode() {
return this.advised.hashCode();
}
protected Object getTarget() throws Exception {
return this.advised.getTargetSource().getTarget();
}
protected void releaseTarget(Object target) throws Exception {
this.advised.getTargetSource().releaseTarget(target);
}
}
6.3 목표 대상 방법의 호출 JdkDynamicAopProxy 목표 대상 방법의 호출 은 AopUtils 의 invoke Joinpoint Using Reflection 방법 을 통 해 이 루어 집 니 다.
/** * Invoke the given target via reflection, as part of an AOP method invocation. * @param target the target object * @param method the method to invoke * @param args the arguments for the method * @return the invocation result, if any * @throws Throwable if thrown by the target method * @throws org.springframework.aop.AopInvocationException in case of a reflection error */
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
}
6.4 차단기 체인 의 호출
JdkDynamicAopProxy 든 Cglib2aopProxy 든 결국은 ReflectiveMethodInvocation 의 proced 방법 으로 차단기 체인 을 호출 하 는 것 입 니 다. advised (class: Advised Support) 는 차단기 체인 을 가지 고 있 습 니 다. getInterceptors AndDynamicInterception Advice 방법의 실현 은 기본 적 인 Advised Support 에서 이 루어 집 니 다.
/** * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects * for the given method, based on this configuration. * @param method the proxied method * @param targetClass the target class * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers) */
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
공장 advisor Chain Factory (impl: Default Advisor Chain Factory) 는 차단기 체인 을 가 져 오 는 과정 을 마 쳤 습 니 다. 그 중에서 Advisor Adapter Registry 는 Proxy Factory Bean 이 XML 파일 에서 얻 은 차단기 의 등록 을 마 쳤 습 니 다. 주로 이전에 설 치 된 List 집합 에 넣 었 습 니 다.list 의 차단 기 는 JdkDynamicaopProxy 의 invoke 나 Cglib2aopProxy 의 intercep 대상 에 의 해 차단 기 를 사용 합 니 다.Default Advisor Chain Factory 의 getInterceptors AndDynamic Interception Advice 방법:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class targetClass) {
// This is somewhat tricky... we have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
사실 advisor 는 advisor Support 에서 받 고 초기 화 된 것 이 라 고 알 렸 습 니 다. 그리고 하위 클래스 Proxy Factory Bean 에서 의 실현 은 다음 과 같 습 니 다.
/** * Create the advisor (interceptor) chain. Aadvisors that are sourced * from a BeanFactory will be refreshed each time a new prototype instance * is added. Interceptors added programmatically through the factory API * are unaffected by such changes. */
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (this.advisorChainInitialized) {
return;
}
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
}
// Globals can't be last unless we specified a targetSource using the property...
if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
throw new AopConfigException("Target required after globals");
}
// Materialize interceptor chain from bean names.
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
}
if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
// If we get here, we need to add a named interceptor.
// We must check if it's a singleton or prototype.
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
// Add the real Advisor/Advice to the chain.
advice = this.beanFactory.getBean(name);
}
else {
// It's a prototype Advice or Advisor: replace with a prototype.
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
}
this.advisorChainInitialized = true;
}
XML 에 설 정 된 advisor 는 IOC 용기 (DefaultListableBeanFactory) 의 getbean 을 통 해 얻 을 수 있 습 니 다.
6.7 advice 알림 의 실현 위 Default Advisor Chain Factory 의 getInterceptors AndDynamic Interception Advice 가 차단기 체인 을 받 았 을 때 등록 기 (Advisor Adapter Registry registry = GlobalAdvisor Adapter Registry. getInstance () 를 사 용 했 습 니 다. 이 registry 는 AOP 의 주요 핵심 실현 을 많이 포함 하고 있 습 니 다. 이것 은 단일 입 니 다.
/** * Singleton to publish a shared DefaultAdvisorAdapterRegistry instance. * * @author Rod Johnson * @author Juergen Hoeller * @see DefaultAdvisorAdapterRegistry */
public abstract class GlobalAdvisorAdapterRegistry {
/** * Keep track of a single instance so we can return it to classes that request it. */
private static final AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();
/** * Return the singleton DefaultAdvisorAdapterRegistry instance. */
public static AdvisorAdapterRegistry getInstance() {
return instance;
}
}
Default Advisor Adapter Registry 에 adapters 구성원 변 수 를 설정 하 였 습 니 다. 일련의 Advisor Adapter 어댑터 를 놓 았 습 니 다. spring 의 advice 는 어댑터 에 대응 합 니 다. 이것 은 어댑터 의 역할 입 니 다. 하 나 는 adapter 를 호출 하 는 support 방법 입 니 다. 이 방법 을 통 해 얻 은 advice 가 어떤 유형의 advice 에 속 하 는 지 판단 하 는 것 입 니 다.따라서 서로 다른 advice 유형 에 따라 서로 다른 advice interceptor 를 등록 합 니 다. 즉, 앞에서 본 어떤 차단기 입 니까?둘째, 이러한 advice interceptor 는 모두 spring AOP 프레임 워 크 를 디자인 한 것 으로 서로 다른 advice 기능 을 실현 하기 위해 서 비 스 를 제공 하 는 것 이다. 이런 advice interceptor 가 있 으 면 spring 이 제공 하 는 다양한 advice 를 편리 하 게 사용 하여 AOP 응용 을 디자인 할 수 있다. 즉, 이러한 advice interceptor 는 최종 적 으로 advice 알림 이 AOP Proxy 대리 대상 에서 의 짜 임 기능 을 실현 했다.advisorAdaptor 인터페이스 에서 클래스 의 디자인 차원 과 관계: DefaultAdvisor Adapter Registry 코드:
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);
/** * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters. */
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
// adapter support , advice advice , advice
// adviceinterceptor
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
this.adapters.add(adapter);
}
}
MethodBefore AdviceAdapter 를 예 로 들 면 원본 코드 는 다음 과 같 습 니 다.
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
코드 에서 볼 수 있 듯 이 슈퍼 ports Advice 방법 은 Advice 가 MethodBefore Advice 유형 인지 아 닌 지 를 판단 하 는 것 입 니 다. 만약 에 위의 Default Advisor Adapter Registry 의 getInterceptors 방법 으로 getInterceptor 방법 을 호출 하여 MethodBefore Advice Interceptor 를 되 돌려 줍 니 다. 이것 이 바로 차단기 체인 에 놓 인 interceptor 입 니 다. 그 코드 는 다음 과 같 습 니 다.
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
/** * Create a new MethodBeforeAdviceInterceptor for the given advice. * @param advice the MethodBeforeAdvice to wrap */
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
// invoke ,
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}
MethodBefore Advice Interceptor 는 advice 를 봉 인 했 습 니 다. methodbeforeadviceinterceptor 가 디자인 한 invoke 리 셋 방법 에서 먼저 advice 를 촉발 한 before 리 셋 을 볼 수 있 습 니 다. 그 다음 에 methodInvocation 의 process 방법 호출 입 니 다. 여 기 를 보면 앞에서 reflectmethodInvocation 의 process 분석 과 연결 되 어 있 습 니 다. 기억 해 보 세 요.AOPCroxy 프 록 시 대상 이 ReflectiveMethodInvocation 을 실행 하 는 process 방법 에서 차단 기 를 얻 은 후에 차단기 invoke 방법 에 대한 호출 을 시작 합 니 다. AOP 의 설정 규칙 에 따라 reflectiveMethodInvocation 이 실행 하 는 차단기 invoke 방법 은 서로 다른 advice 유형 에 따라 spring 이 서로 다른 advice 에 대한 차단기 패 키 징 을 촉발 합 니 다. 예 를 들 어 MethodBeforAdvice 등 입 니 다.최종 적 으로 MethodBeforAdvice Interceptor 방법 을 촉발 합 니 다.MethodBeforAdvice Interceptor 방법 에 서 는 advice 의 before 방법 을 먼저 호출 합 니 다. 이것 이 MethodBeforAdvice 에 필요 한 대상 의 증강 효과 입 니 다. 방법 전에 알림 강 화 를 완료 합 니 다.After Returning AdviceAdapter, Throws AdviceAdapter 는 같은 원리 로 진행 된다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Spring AOP 단위 테스트를 수행하는 방법Spring AOP로 만든 업무 횡단적인 처리를 가진 클래스를 단독으로 테스트하고 싶다. Spring AOP는 DI를 기반으로 성립되어 있어 컴퍼넌트를 사용하는 측이 proxy(컨테이너에 등록한 Bean 그 자체가 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.