AOP는 무엇입니까?
AOP (Aspect-Oriented Programming, Aspect-Oriented Programming)는 OOP (Object-Oriented Programming)의 보충 및 개선이라고 할 수 있습니다. OOP는 캡슐화, 상속 및 다형성과 같은 개념을 소개하여 대중 행동 모음을 시뮬레이션하기 위해 객체 계층을 설정합니다. 흩어진 물체에 대중의 행동을 도입해야 할 때 OOP는 무력한 것 같습니다. 즉, OOP를 사용하면 관계를 위에서 아래로 정의 할 수 있지만 왼쪽에서 오른쪽으로 관계를 정의하는 데 적합하지 않습니다. 예를 들어 로깅 기능. 로그 코드는 종종 산란 된 객체의 핵심 기능과 관련이없이 모든 객체 수준에 걸쳐 수평으로 흩어져 있습니다. 보안, 예외 처리 및 투명성과 같은 다른 유형의 코드에 대해서도 마찬가지입니다. 모든 곳에서 흩어져있는 이러한 종류의 관련없는 코드를 크로스 절단 코드라고합니다. OOP 설계에서는 많은 코드 복제를 유발하며 각 모듈의 재사용에는 도움이되지 않습니다.
반대로 AOP 기술은 "크로스 컷팅"이라는 기술을 사용하여 캡슐화 된 객체의 내부를 해부하고 여러 클래스에 영향을 미치는 일반적인 동작을 재사용 가능한 모듈에 영향을 미치는 일반적인 동작을 캡슐화하고, 소위 "측면", 즉 소위 "측면", 간단히 말하면 논리 또는 책임을 촉진하는 것이 아니라 시스템을 촉진하는데, 이는 시스템을 촉진합니다. 모듈 사이의 커플 링 및 향후 작동 가능성 및 유지 관리 촉진. AOP는 수평 관계를 나타냅니다. "객체"가 중공 실린더 인 경우 물체의 특성과 동작을 캡슐화합니다. 그런 다음 측면 지향 프로그래밍 방법은 날카로운 칼날과 같 으며이 중공 실린더를 자르고 내부 정보를 얻습니다. 컷 아웃 섹션은 소위 "얼굴"입니다. 그런 다음 흔적을 남기지 않고 영리한 손 으로이 컷 아웃 섹션을 복원했습니다.
"크로스 컷팅"기술을 사용하여 AOP는 소프트웨어 시스템을 핵심 관심사와 교차 절단 문제의 두 부분으로 나눕니다. 비즈니스 처리의 주요 프로세스는 핵심 초점이며, 이와 관련하여 거의 관련이없는 부분은 단면 초점입니다. 교차 절단 문제의 특징 중 하나는 종종 여러 핵심 문제에서 발생하며 기본적으로 모든 곳에서 유사하다는 것입니다. 예를 들어, 허가 인증, 로깅 및 거래 처리. AOP의 역할은 시스템에서 다양한 문제를 분리하고 핵심 문제를 교차 절단 문제와 분리하는 것입니다. Avanade의 수석 솔루션 설계자 인 Adam Magee는 AOP의 핵심 아이디어는“응용 프로그램의 비즈니스 논리를이를 지원하는 일반적인 서비스와 분리하는 것”이라고 말했습니다.
AOP를 구현하는 기술은 주로 두 가지 범주로 나뉩니다. 하나는 동적 프록시 기술을 사용하고 메시지를 가로 채는 방법을 사용하여 원래 객체 동작의 실행을 대체하기 위해 메시지를 장식하는 방법을 사용하는 것입니다. 다른 하나는 정적 직조 메소드를 사용하여 특정 구문을 소개하여 "면"을 생성하여 컴파일러가 컴파일하는 동안 "면"과 관련된 코드를 직조 할 수 있도록하는 것입니다.
AOP 사용 시나리오
AOP는 다음 시나리오에서 사용할 수있는 교차 절단 문제를 캡슐화하는 데 사용됩니다.
인증 권한
캐싱 캐싱
컨텐츠 전달 컨텐츠
오류 처리
게으른 적재
디버깅
로깅, 추적, 프로파일 링 및 모니터링
성능 최적화
지속성 지속성
리소스 풀링
동기화
업무
AOP 관련 개념
측면 : 여러 객체를 더 크로스 컷 할 수있는 초점의 모듈성. 거래 관리는 J2EE 애플리케이션에서 문제를 교차 절단하는 좋은 예입니다. 측면은 Spring의 고문 또는 인터셉터를 사용하여 구현됩니다.
JoinPoint : 메소드 호출 또는 특정 예외와 같은 프로그램 실행 중 명확한 점.
조언 : 특정 연결 지점에서 AOP 프레임 워크에서 수행하는 작업. 다양한 유형의 알림에는 "주변", "이전"및 "던져"알림이 포함됩니다. 알림 유형은 아래에서 설명합니다. 스프링을 포함한 많은 AOP 프레임 워크는 인터셉터 체인 "라운드"연결 지점을 유지하기 위해 인터셉터를 알림 모델로 사용합니다. 봄에는 4 가지 조언이 정의됩니다 : Beforeadvice, AfterAdvice, ThrowAdvice 및 DynamicinTroductionAdvice
PointCut : 알림이 트리거 될 연결 지점 모음을 지정합니다. AOP 프레임 워크를 사용하면 개발자가 진입 점을 지정할 수 있어야합니다. 예를 들어 일반 표현식을 사용합니다. Spring은 MethodMatcher와 ClassFilter를 결합하는 데 사용되는 PointCut 인터페이스를 정의하며 이름을 통해 명확하게 이해할 수 있습니다. MethodMatcher는 대상 클래스의 메소드를 사용 하여이 알림을 적용 할 수 있는지 확인하는 데 사용되며 ClassFilter는 PointCut이 대상 클래스에 적용되어야하는지 확인하는 데 사용됩니다.
소개 : 알림을받은 클래스에 메소드 또는 필드를 추가합니다. Spring을 사용하면 새로운 인터페이스를 알리는 모든 객체에 도입 할 수 있습니다. 예를 들어, 객체가 ismodified 인터페이스를 구현할 수있는 소개를 사용하여 캐싱을 단순화 할 수 있습니다. 봄에 소개를 사용하려면 DelegatingIntroductioninterceptor를 사용하여 알림을 구현하고 DefaultIntructionAdvisor를 사용하여 인터페이스를 구성하여 조언 및 프록시 클래스를 구현할 수 있습니다.
대상 객체 : 연결 지점을 포함하는 객체. 알림 또는 프록시 객체라고도합니다. 포조
AOP 프록시 : 알림이 포함 된 AOP 프레임 워크에서 생성 된 개체. 봄에 AOP 프록시는 JDK 다이내믹 프록시 또는 CGLIB 프록시 일 수 있습니다.
직조 : 알림 객체를 만들려면 조립하십시오. 컴파일 타임 (예 : AnalpectJ Compiler 사용) 또는 런타임에 수행 할 수 있습니다. Spring은 다른 순수한 Java AOP 프레임 워크와 마찬가지로 런타임에 직조를 완료합니다.
스프링 AOP 구성 요소
다음 클래스 다이어그램에는 봄의 주요 AOP 구성 요소가 나와 있습니다.
Spring AOP를 사용하는 방법
Spring AOP는 구성 파일 또는 프로그래밍 방식에 사용할 수 있습니다.
구성은 XML 파일을 통해 수행 할 수 있으며 약 네 가지 방법이 있습니다.
1. proxyfactoryBean, 명시 적으로 설정된 고문, 조언, 대상 등을 구성하십시오.
2. 자동 xycreator를 구성하십시오. 이런 식으로, 정의 된 콩은 여전히 이전과 같이 사용되지만 컨테이너에서 얻는 것은 실제로 프록시 객체입니다.
3. <AOP : config>를 통해 구성하십시오
4. <AOP : SASTERJ-AUTOPROXY>를 통해 구성하고 AspectJ 주석을 사용하여 알림 및 진입 점을 식별합니다.
또한 근접 Factory를 사용하여 스프링 AOP를 프로그래밍 방식으로 사용할 수도 있습니다. 근위로 제공되는 메소드를 통해 대상 객체, 어드바이저 및 기타 관련 구성을 설정하고 최종적으로 getProxy () 메소드를 통해 프록시 객체를 얻을 수 있습니다.
사용의 구체적인 예는 Google 일 수 있습니다. 여기서 생략되었습니다
스프링 AOP 프록시 객체 생성
Spring은 프록시 객체를 생성하는 두 가지 방법 인 JDKPROXY 및 CGLIB를 제공합니다. 특정 생성 방법은 Advissupport 객체의 구성에 기초하여 AopProxyFactory에 의해 결정됩니다. 기본 정책은 대상 클래스가 인터페이스 인 경우 JDK 동적 프록시 기술을 사용하는 것입니다. 그렇지 않으면 cglib를 사용하여 프록시를 생성합니다. Spring이 JDK를 사용하여 프록시 객체를 생성하는 방법을 연구합시다. 특정 생성 코드는 jdkdynamicaopproxy 클래스에 배치되며 관련 코드가 직접 추가됩니다.
/** * <ol> * <li> 프록시 클래스에서 구현할 인터페이스를 가져옵니다. 조언 된 객체의 구성 외에도 SpringProxy가 추가되고 권고됩니다 (Opaque = False) * <li> 위에서 얻은 인터페이스에 동등한 인터페이스를 정의하는 인터페이스가 있는지 확인하십시오. (logger.isdebugenabled ()) {logger.debug ( "JDK 동적 프록시 생성 : 대상 소스는" +this.advised.getTargetSource ()); } class [] proxiedInterfaces = aopproxyutils.completeproxiedinterfaces (this.advised); findDefinedEqualSandhashCodemEthods (proxiedinterfaces); return proxy.newproxyInstance (클래스 로더, 프록시 링크페이스, this); } 그러면 이것은 실제로 매우 분명합니다. 나는 이미 의견을 명확하게 작성했으며 다시 반복하지 않을 것입니다.
다음 질문은 프록시 객체가 생성된다는 것입니다. 절단 표면은 어떻게 직조됩니까?
invocationHandler는 JDK Dynamic Proxy의 핵심이며 생성 된 프록시 객체의 메소드 호출은 invocationHandler.inVoke () 메소드에 위임됩니다. jdkdynamicaopproxy의 서명을 통해이 클래스가 실제로 호출 핸들러를 구현한다는 것을 알 수 있습니다. 이 클래스에서 구현 된 invoke () 메소드를 분석하여 Spring AOP가 섹션에 어떻게 직조되는지 살펴 보겠습니다.
publicobject invoke (오브젝트 프록시, 메소드 메소드, 개체 [] args) throwsTrowable {methodInvocation invocation = null; Object OldProxy = null; 부울 setProxyContext = false; TargetSource TargetSource = this.advised.targetSource; 클래스 targetclass = null; 객체 대상 = null; try {// eqauls () 메소드, 대상 객체는이 메소드를 구현하지 않습니다. } // hashcode () 메소드, 대상 객체는이 메소드를 구현하지 않습니다. } // 상위 인터페이스 또는 상위 인터페이스에 정의 된 메소드가 호출을 직접 반영하며 (! this.advised.opaque && method.getDeclaringClass () IsInterface () && method.getDeclaringClass (). isassignablefrom (world.class)) {// service recocations on procoy fig with with with with with with with with with on the assignablefrom (). aoputils.invokejoinpointusingReflection (this.advised, method, args); } 개체 retval = null; if (this.advised.exposeproxy) {// Ifnecessary를 사용할 수 있습니다. OldProxy = AOPCONTEXT.SETCURRENTPROXY (프록시); setProxyContext = true; } // 대상 객체의 클래스 대상을 가져옵니다. target = targetSource.getTarget (); if (target! = null) {targetclass = target.getClass (); } //이 메소드에 적용 할 수있는 인터셉터 목록을 가져옵니다. 목록 체인 = this.advised.getInterceptorsandDynamicinInterceptionAdVice (method, targetClass); //이 메소드 (인터셉터)에 적용 할 수있는 알림이 없으면이 직접 반사 통화 메소드 .invoke (target, args) if (chain.isempty ()) {retval = aoputils.invokejoinpointusingReflection (target, method, args); } else {// methodInvocation revoction을 생성 = newReflectiveMethodInvocation (프록시, 대상, 메소드, args, targetclass, 체인); retval = invocation.proceed (); } // 필요한 경우 마사지 리턴 값. if (retval! = null && retval == target && method.getReturntype (). isinstance (proxy) &&! rawtargetAccess.class.isassignablefrom (method.getDeclaringClass ())) {// 특수 사례 : "this"및 메소드의 반환 유형은 유형을 대적합니다. 대상이 설정되어 있으면 도움이되지 않습니다. retval = 프록시; } return retval; } lANDE {if (target! = null &&! targetSource.isstatic ()) {//에서 TargetSource에서 나왔어야합니다. TargetSource.releaseTarget (대상); } if (setProxyContext) {// 오래된 프록시를 복원합니다. AOPCONTEXT.SETCURRENTPROXY (OldProxy); }}} 주요 프로세스는 다음과 같이 간단히 설명 할 수 있습니다.이 방법에 적용될 수있는 알림 체인 (인터셉터 체인). 있는 경우 알림을 적용하고 JoinPoint를 실행하십시오. 없는 경우 JoinPoint를 직접 반영하십시오. 여기서 핵심은 알림 체인을 얻는 방법과 실행 방법입니다. 하나씩 분석 해 봅시다.
우선 위의 코드에서 알림 체인이 권고 된 interceptorsanddynamicininterceptionAdvice () 메소드를 통해 얻어진다는 것을 알 수 있습니다. 이 방법의 구현을 살펴 보겠습니다.
public list <bood> getInterceptorsandDynamicinInterceptionAdVice (메소드 메소드, 클래스 targetclass) {MethodCacheKeyCacheKey = new MethodCacheKey (메소드); List <bood> 캐시드 = this.methodcache.get (Cachekey); if (cached == null) {cached = this.advisorChainFactory.getInterceptorsandDynamicinInterceptionAdvice (this, method, target class); this.methodcache.put (캐시 키, 캐시); } returnCached; } 실제 획득 작업은 실제로 AdvisorchainFactory에 의해 수행된다는 것을 알 수 있습니다. getInterceptorsandDynamicinInterceptionAdvice () 메소드 및 획득 된 결과가 캐시됩니다.
아래 에서이 방법의 구현을 분석 해 봅시다.
/*** 제공된 구성 인스턴스 구성에서 Advisor 목록을 가져 와서이 어드바이저를 통과하십시오. 소개 Aadvisor 인 경우 * 대상 클래스 TargetClass 에이 어드바이저를 적용 할 수 있는지 여부를 결정하십시오. PointCutadvisor 인 경우이 어드바이저를 대상 방법에 적용 할 수 있는지 * 결정하십시오. 조건을 충족하는 고문은 AdvisorAdaptor를 통해 인터셉터 목록으로 변환됩니다. */ publiclist getInterceptorsandDynamicininterceptionAdvice (조언, 메소드 메드, 클래스 targetclass) {// 이것은 약간 까다 롭습니다 ... 먼저 소개를 처리해야하지만 궁극적 인 목록에서 순서를 보존해야합니다. interceptorList를 목록 = new ArrayList (config.getAdvisors (). length); // 소개 Aadvisor Boolean hasintroductions = HasmatchingIntroductions (config, targetclass); // 실제로, Advisor를 MethodInterceptor AdvisorAdapterRegistry Registry로 변환하기 위해 일련의 AdvisOradapters가 여기에 등록됩니다. Advisor [] Advisors = config.getAdvisors (); for (int i = 0; i <advisors.length; i ++) {Advisor Advisor = Advisors [i]; if (Advisor instanceof pointcutadvisor) {// 조건부 추가. PointCutadvisor PointCutadvisor = (PointCutadvisor) 고문; if (config.isprefiltered () || pointCutadvisor.getPointCut (). getClassFilter (). matches (targetclass)) {// todo :이 두 방법의 위치는이 장소에서 변환 할 수 있습니다. // 현재 고문의 포인트 컷이 현재 메소드 메소드 메소드 MATCHER MM = POINTCUTADVISOR.getPointCut (). getMethodMatcher (); if (methodmatchers.matches (mm, method, targetclass, hasintroductions))) {if (mm.isruntime ()) {// getInterceptors () 메소드에서 newObject 인스턴스를 만듭니다. for (intj = 0; j <interceptors.length; j ++) {interceptorlist.add (new interceptoranddynamicmethodmatcher (interceptors [j], mm)); }} else {interceptorList.Addall (Arrays.AsList (interceptors)); }}}} else if (Advisor instanceof instanceof instanceof instorctionAdvisor) {소개 advisor IA = (소개 advisor) 고문; if (config.ispRefiltered () || ia.getClassFilter (). matches (targetClass)) {interceptor [] interceptor = registry.getInterceptors (Advisor); interceptorList.Addall (Arrays.Aslist (인터셉터)); }} else {interceptor [] interceptor = registry.getInterceptor (Advisor); interceptorList.Addall (Arrays.Aslist (인터셉터)); }} interceptorList를 반환합니다. } 이 방법이 실행 된 후 연결 지점에 적용 할 수 있거나 대상 클래스에 적용 할 수 있도록 조언에 구성된 모든 고문이 메소드 인턴 셉터로 변환됩니다.
다음으로, 얻은 인터셉터 체인의 작동 방식을 살펴 보겠습니다.
if (chain.isempty ()) {retval = aoputils.invokejoinpointusingReflection (target, method, args); } else {// methodInvocation revoction을 생성 = newReflectiveMethodInvocation (프록시, 대상, 메소드, args, targetclass, 체인); retval = invocation.proceed (); } 이 코드에서 얻은 인터셉터 체인이 비어 있으면 대상 방법이 직접 반사되는 것을 알 수 있습니다. 그렇지 않으면 MethodInvocation이 생성되고 프로세스 방법이 호출되며 인터셉터 체인의 실행이 트리거됩니다. 특정 코드를 살펴 보겠습니다
Public Object Crade ()는 던질 수있는 {// 우리는 -1의 인덱스로 시작하고 일찍 증가합니다. if (this.currentInterceptorIndex == this.InterceptorsandDynamicMethodMatchers.size () -1) {// 인터셉터가 완료된 경우 joinpoint return retokJoinPoint (); } 객체 interceptorororInterinceptionAdVice = this.InterceptorsandDynamicMethodMatchers.get (++ this.CurrentInterceptorIndex); // if (interceptorororInceptionAdvice interceptoranddynamicmethodmatcher의 interceptororInceptionAdVice 인스턴스) {// 동적 메소드 매칭을 평가하는 경우 : 정적 부분이 이미 평가되고 일치하는 것으로 나타났습니다. interceptoranddynamicmethodmatcher dm = (interceptoranddynamicmethodmatcher) interceptororinceptionAdvice; // 동적 일치 : 런타임 매개 변수가 일치 조건을 충족하는지 여부 (dm.methodmatcher.matches (this.method, this.targetclass, this.arguments)) {// 현재 intercetpor returndm.interceptor.invoke (this); } else {// 동적 일치가 실패하면 현재 intercetpor를 건너 뛰고 다음 인터셉터 리턴 프로 시저 ()를 호출하십시오. }} else {// 인터셉터이기 때문에 우리는 그것을 호출합니다. // 현재 intercetpor return을 실행합니다 ((Methodinterceptor) interceptororinceptionAdvice) .invoke (this); }}코드도 비교적 간단하므로 여기서는 자세히 설명하지 않습니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.