이번 게시물에서는 Spring의 AOP(Aspect-Oriented 프로그래밍)의 내부 메커니즘에 대해 설명하겠습니다. AOP가 종종 "마법"의 형태로 간주되는 로깅과 같은 기능을 달성하는 방법을 이해하는 데 중점을 둘 것입니다. 핵심 Java 구현을 살펴봄으로써 우리는 이것이 진정한 마술이 아닌 Java의 반사, 프록시 패턴 및 주석에 관한 모든 것을 알게 될 것입니다.
전제 조건
- Java 핵심 프록시 API
- 리플렉션 API
- 주석 API
이들은 모두 java.lang.reflect,java.lang.annotation 및 javassist.util.proxy 패키지의 일부입니다.
핵심 메커니즘
Spring AOP의 핵심에는 프록시 객체, 메소드 인터셉터 및 리플렉션 개념이 있습니다. 이 패턴의 핵심 플레이어는 MethodHandler(또는 호출 핸들러)입니다. 이 핸들러는 메소드 호출을 가로채서 프록시 객체의 동작을 제어합니다. 프록시에서 메서드가 호출되면 핸들러를 통해 전달되며, 여기서 주석은 리플렉션을 통해 검사될 수 있습니다. 적용된 주석을 기반으로 예외 전, 후 또는 발생 시 필요한 로직(예: 로깅)을 실행할 수 있습니다.
무너뜨리기
- 프록시 개체: 실제 비즈니스 개체를 대신하는 동적으로 생성된 개체로, 메서드 핸들러를 통해 메서드 호출을 라우팅합니다.
- 호출 핸들러: 여기에서 가로채기 마법이 발생합니다. 리플렉션을 사용하면 핸들러는 대상 메서드에 있는 주석을 검사하고 이에 따라 동작을 변경할 수 있습니다.
- 사용자 정의 주석: 로깅, 보안 검사 또는 트랜잭션 관리와 같은 추가 기능을 트리거하는 마커 역할을 하는 사용자 정의 주석을 정의할 수 있습니다.
예: 특정 메소드 실행 전후에 로깅을 추가한다고 가정해 보겠습니다. 모든 곳에서 로깅을 하드 코딩하는 대신 @BeforeMethod 및 @AfterMethod를 사용하여 메소드에 주석을 달 수 있습니다. 우리 핸들러는 이 주석에 대한 메소드를 검사하고 적절한 로깅 로직을 동적으로 추가합니다.
다음은 우리 예에서 컨트롤러와 서비스가 어떻게 보이는지 클래스입니다.
WorkerController.java
package edu.pk.poc.aop.controller; import edu.pk.poc.aop.annotation.AfterMethod; import edu.pk.poc.aop.annotation.All; import edu.pk.poc.aop.annotation.BeforeMethod; import edu.pk.poc.aop.helper.ProxyFactory; import edu.pk.poc.aop.service.Worker; import edu.pk.poc.aop.service.WorkerService; import edu.pk.poc.aop.service.WorkerServiceImpl; public class WorkerController { WorkerService workerService = ProxyFactory.createProxy(WorkerServiceImpl.class); /** * This Method 1s annotated with @BeforeMethod and @AfterMethod, So the log statements * will be generated before and after method call. */ @BeforeMethod @AfterMethod public void engageFullTimeWorker() throws Exception { Worker fullTimeWorker = new Worker(); fullTimeWorker.setName("FullTime-Worker"); fullTimeWorker.setPartTime(false); fullTimeWorker.setDuration(9); workerService.doWork(fullTimeWorker); } /** * This Method is annotated with @All, So the log statements will be generated before and after method call * along with exception if raised. */ @All public void engagePartTimeWorker() throws Exception { Worker partTimeWorker = new Worker(); partTimeWorker.setName("PartTime-Worker"); partTimeWorker.setPartTime(true); partTimeWorker.setDuration(4); workerService.doWork(partTimeWorker); } }
WorkerServiceImpl.java
package edu.pk.poc.aop.service; import edu.pk.poc.aop.annotation.AfterMethod; public class WorkerServiceImpl implements WorkerService { /** * Here this method is annotated with only @AfterMethod, So only log statement * will be generated after method call */ @AfterMethod @Override public void doWork(Worker worker) throws Exception { if (worker.isPartTime()) { throw new Exception("Part time workers are not permitted to work."); } System.out.print("A full time worker is working for " + worker.getDuration() + " hours :: "); for (int i = 1; i <p><strong>Main.java 테스트 클래스</strong><br> </p> <pre class="brush:php;toolbar:false">package edu.pk.poc.aop.test; import edu.pk.poc.aop.controller.WorkerController; import edu.pk.poc.aop.helper.ProxyFactory; import edu.pk.util.Logger; public class Main { public static void main(String[] args) { WorkerController controller = ProxyFactory.createProxy(WorkerController.class); Logger logger = new Logger(); try { System.out.println("Testing @BeforeMethod and @AfterMethod"); System.out.println("-----------------------------------------"); controller.engageFullTimeWorker(); System.out.println("Testing @All"); System.out.println("-----------------------------------------"); controller.engagePartTimeWorker(); } catch (Exception e) { logger.error("Exception caught in Main class"); } } }
출력
Testing @BeforeMethod and @AfterMethod ----------------------------------------- >>> Entering into edu.pk.poc.aop.controller.WorkerController.engageFullTimeWorker() A full time worker is working for 9 hours :: * * * * * * * * >>> Exiting from edu.pk.poc.aop.service.WorkerServiceImpl.doWork() >>> Exiting from edu.pk.poc.aop.controller.WorkerController.engageFullTimeWorker() Testing @All ----------------------------------------- >>> Entering into edu.pk.poc.aop.controller.WorkerController.engagePartTimeWorker() >>> Exception in edu.pk.poc.aop.controller.WorkerController.engagePartTimeWorker() Exception caught in Main class
작동 방식
프록시 객체에서 메서드가 호출되면 리플렉션을 사용하여 대상 메서드의 모든 주석을 검사하는 핸들러가 호출을 가로챕니다. 이러한 주석을 기반으로 핸들러는 메소드 시작/종료를 기록할지, 예외를 기록할지, 아니면 로깅을 모두 건너뛸지를 결정합니다.
시각화하는 방법은 다음과 같습니다.
- 실행 전: 로그 메소드를 입력합니다.
- 실행 후: 메소드 종료 또는 성공을 기록합니다.
- 모두: 메소드 항목, 메소드 항목 및 예외 발생 시 로그를 기록합니다. 이러한 동적 동작은 Spring AOP가 일부 마술을 사용하기보다는 핵심 Java API를 활용한다는 것을 보여줍니다.
주석 정의
package edu.pk.poc.aop.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AfterMethod { }
package edu.pk.poc.aop.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface BeforeMethod { }
package edu.pk.poc.aop.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface All { }
프록시 팩토리 정의
package edu.pk.poc.aop.helper; /** * The {@code ProxyFactory} class is responsible for creating proxy objects using the Javassist library. * It allows for dynamic generation of proxies for classes or interfaces, with support for method interception. */ public class ProxyFactory { /** * A Javassist ProxyFactory instance used to generate proxy classes. */ private static final javassist.util.proxy.ProxyFactory factory = new javassist.util.proxy.ProxyFactory(); /** * Creates a proxy object for the given class or interface. * If the class is an interface, the proxy implements the interface. * If it's a concrete class, the proxy extends the class. * * @param <t> the type of the class or interface for which the proxy is to be created * @param klass the {@code Class} object representing the class or interface to proxy * @return a proxy instance of the specified class or interface, or {@code null} if proxy creation fails */ public static <t> T createProxy(Class<t> klass) { if (klass.isInterface()) factory.setInterfaces(new Class[]{klass}); else factory.setSuperclass(klass); try { return (T) factory.create(new Class>[0], new Object[0], new AOPLoggingMethodHandler()); } catch (Exception e) { System.err.println(e.getMessage()); } return null; } } </t></t></t>
MethodHandler 정의
package edu.pk.poc.aop.helper; import edu.pk.poc.aop.annotation.AfterMethod; import edu.pk.poc.aop.annotation.All; import edu.pk.poc.aop.annotation.BeforeMethod; import edu.pk.poc.aop.annotation.OnException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import edu.pk.util.Logger; import javassist.util.proxy.MethodHandler; public class AOPLoggingMethodHandler implements MethodHandler { private static final Logger logger = new Logger(); public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { if (proceed != null) { // Concrete Method Object result = null; String className = resolveClassName(self); try { if (isAnnotationPresent(thisMethod, BeforeMethod.class) || isAnnotationPresent(thisMethod, All.class)) { logger.info(">>> Entering into " + className + "." + thisMethod.getName() + "()"); } result = proceed.invoke(self, args); if (isAnnotationPresent(thisMethod, AfterMethod.class) || isAnnotationPresent(thisMethod, All.class)) { logger.info(">>> Exiting from " + className + "." + thisMethod.getName() + "()"); } } catch (Throwable t) { if (isAnnotationPresent(thisMethod, OnException.class) || isAnnotationPresent(thisMethod, All.class)) { logger.error(">>> Exception in " + className + "." + thisMethod.getName() + "()"); } throw t; } return result; } throw new RuntimeException("Method is Abstract"); } private boolean isAnnotationPresent(Method method, Class klass) { Annotation[] declaredAnnotationsByType = method.getAnnotationsByType(klass); return declaredAnnotationsByType != null && declaredAnnotationsByType.length > 0; } private String resolveClassName(Object self) { String className = self.getClass().getName(); if (className.contains("_$$")) { className = className.substring(0, className.indexOf("_$$")); } return className; } }
결론
Spring AOP는 다양한 문제를 해결하는 강력한 도구이지만 혁신적인 작업을 수행하지는 않습니다. 이는 언어 자체에서 사용할 수 있는 리플렉션 및 프록시와 같은 핵심 Java 개념을 기반으로 구축되었습니다. 이를 이해하면 Spring이 개발자 편의를 위해 이러한 하위 수준 메커니즘을 어떻게 단순화하는지 더 잘 이해할 수 있습니다.
위 내용은 Spring AOP의 내부 작동 방식 공개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

이 기사에서는 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 및 Gradle을 사용하여 접근 방식과 최적화 전략을 비교합니다.

이 기사에서는 Maven 및 Gradle과 같은 도구를 사용하여 적절한 버전 및 종속성 관리로 사용자 정의 Java 라이브러리 (JAR Files)를 작성하고 사용하는 것에 대해 설명합니다.

이 기사는 카페인 및 구아바 캐시를 사용하여 자바에서 다단계 캐싱을 구현하여 응용 프로그램 성능을 향상시키는 것에 대해 설명합니다. 구성 및 퇴거 정책 관리 Best Pra와 함께 설정, 통합 및 성능 이점을 다룹니다.

이 기사는 캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA를 사용하는 것에 대해 설명합니다. 잠재적 인 함정을 강조하면서 성능을 최적화하기위한 설정, 엔티티 매핑 및 모범 사례를 다룹니다. [159 문자]

Java의 클래스 로딩에는 부트 스트랩, 확장 및 응용 프로그램 클래스 로더가있는 계층 적 시스템을 사용하여 클래스로드, 링크 및 초기화 클래스가 포함됩니다. 학부모 위임 모델은 핵심 클래스가 먼저로드되어 사용자 정의 클래스 LOA에 영향을 미치도록합니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

SublimeText3 Linux 새 버전
SublimeText3 Linux 최신 버전

Dreamweaver Mac版
시각적 웹 개발 도구

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

mPDF
mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.

VSCode Windows 64비트 다운로드
Microsoft에서 출시한 강력한 무료 IDE 편집기
