Spring framework core 정리 - Kim-Taesu/study GitHub Wiki
- IoC는 의존성 주입(DI, Depedency Injection)으로 잘 알려져 있다.
- DI는 아래 상황에서 객체의 의존성을 정의한다.
- 생성자의 아규먼트
- 팩토리 메서드의 아규먼트
- 팩토리 메서드로부터 생성되거나 반환된 객체 인스턴스의 프로퍼티에 의해
- 컨테이너는 빈을 생성할 때 의존성을 주입한다.
- 기본적으로 Bean이 직접 클래스를 생성하거나 Service Locator 패턴으로 의존성을 주입하는데 IoC는 반대되는 개념이다.
-
org.springframework.beans
,org.springframework.context
패키지는 스프링 프레임워크의 IoC 컨테이너의 기본이다. -
BeanFacotry
인터페이스는 모든 유형의 객체를 관리할 수 있는 매커니즘을 제공한다. -
ApplicationContext
는BeanFacotry
의 서브 인터페이스다.- Spring AOP를 더 쉽게 사용할 수 있게 해준다.
- 메시지 Resource 처리 (국제화에 사용)
- Event 발행
- 웹 애플리케이션에서 사용하기 위한 WebApplicationContext와 같은 애플리케이션 계층을 제공
-
ApplicationContext
는BeanFacotry
의 상위 집합이다.
-
org.springframework.context.ApplicationContext
인터페이스는 Spring IoC 컨테이너를 의미하고, Bean의 인스턴스화, 구성, Bean 조립을 담당. - 컨테이너는 메타데이터를 읽어서 인스턴스화, 구성, 조립할 개체에 대한 지침을 얻는다.
- 메타데이터는
XML
,Java 어노테이션
,Java 코드
로 표시된다. - 메타데이터로 애플리케이션을 구성하는 객체 간의 상호 의존성을 표현할 수 있다.
- 메타데이터는
- 과거 직관적인 XML 형태로 메타데이터를 관리했지만 요즘은 Java 기반으로 메타데이터 설정을 관리한다.
- Spring 설정은 적어도 1개 이상의 Bean 정의로 구성된다.
- XML 기반 설정은 요소 내부에 요소로 구성
- Java 구성은
@Configuration
클래스 내에서@Bean
주석이 달린 메소드를 사용
- 도메인 객체를 만들고 로드하는 것은 DAO 및 비즈니스 로직에서 해결하고 컨테이너에서는 도매인 객체를 구성/생성하지 않는다.
- AspectJ 를 사용하여 IoC 컨테이너가 관리하지 않는 외부 객체를 구성할 수 있다.
- Spring IoC 컨테이너는 최소 1개 이상의 빈을 관리한다.
- IoC 컨테이너가 메타데이터를 읽어서 Bean을 생성한다.
- 컨테이너 내부에서는 Bean 정의를
BeanDefinition
으로 표시한다.-
BeanDefinition
은 아래 메타 데이터를 포함한다. - 패키지 이름 : Bean의 실제 구현 클래스
- 동작 구성 요소 : Bean이 커넽이너에서 어떻게 동작해야하는지 (scope, lifecycle callback)
- 해당 Bean이 작업을 수행하는데 필요한 다른 Bean에 대한 참조
- 새로 생성된 객체에서 설정한 기타 구성 설정
-
- 모든 Bean에는 하나 이사의 식별자가 있다.
- 컨테이너 내부에서 Bean의 식별자는 고유한 값이어야 한다.
-
id
,name
속성으로 Bean의 식별자를 지정할 수 있다. - 네이밍 컨벤션 : 소문자로 시작하고 카멜 케이스
- Componenet Scan을 통해 식별자를 따로 지정해주지 않은 Bean 들은 클래스 이름의 제일 앞 문자를 소문자로 바꾸고 사용한다.
- 두 개 이상의 문자가 있고 첫 번째와 두 번째 문자가 모두 대문자인 특수한 경우에는 원래 대소문자가 유지된다.
- 컨테이너는 생성할 Bean의 구성 메타데이터를 사용하여 실제 객체를 생성한다.
- Bean의 클래스는 2가지 방법으로 지정할 수 있다.
-
컨테이너 자체가 생성자를 호출하여 빈을 직접 생상하는 경우
-
자바의
new SomeClass()
와 동일하다.<bean id="exampleBean" class="examples.ExampleBean"/>
-
-
컨테이너가 클래스의 static 팩토리 메서드를 호출하여 빈을 만드는 경우
-
static 팩토리 메서드에서 실제 클래스를 지정한다.
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
public class ClientService { private static ClientService clientService = new ClientService(); private ClientService() {} public static ClientService createInstance() { return clientService; } }
public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private static AccountService accountService = new AccountServiceImpl(); public ClientService createClientServiceInstance() { return clientService; } public AccountService createAccountServiceInstance() { return accountService; } }
-
-
- 일반적인 애플리케이션은 단일 객체로 구성되지 않는다.
-
생성자 아규먼트
,팩토리 메서드에 대한 아규먼트
,팩토리 메서드에서 반환된 후 객체 인스턴스에 설정된 프로퍼티
를 통해 객체의 의존성이 설정되는 프로세스이다. - 객체는 의존성을 검색하지 않으며 의존성의 위치 또는 클래스를 알지 못한다.
- 특히 인터페이스나 추상 클래스에 의존성이 있는 경우, 단위 테스트에서 stub/mock 으로 구현하여 사용할 수 있다.
- DI는 크게 2가지로 나뉜다.
생성자 기반 의존성 주입
setter 기반 의존성 주입
-
의존성을 나타내는 여러 아규먼트가 있는 생성자를 호출하는 컨테이너에 의해 수행된다.
-
Bean을 구성하기 위해 특정 아규먼트를 가진 static 팩토리 메서드를 호출하는 것과 거의 동일하다.
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on a MovieFinder private final MovieFinder movieFinder; // a constructor so that the Spring container can inject a MovieFinder public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually uses the injected MovieFinder is omitted... }
-
@ConstructorProperties
를 사용하여 생성자 프로퍼티를 지정할 수 있다.public class ExampleBean { // Fields omitted @ConstructorProperties({"years", "ultimateAnswer"}) public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } }
-
빈 생성자 또는 빈 static 팩토리 메서드를 호출 한 후, Bean의 컨테이너 호출 setter 메서드에 의해 수행된다.
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on the MovieFinder private MovieFinder movieFinder; // a setter method so that the Spring container can inject a MovieFinder public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually uses the injected MovieFinder is omitted... }
- 생성자 기반, setter 기반 DI를 혼합해서 사용할 수 있다.
- 따라서 필수 의존성에는 생성자를 사용하고, 선택적 의존성에는 setter 메서드 또는 설정 메서드를 사용하는 것이 좋다.
- setter 메서드에
@Required
를 사용하면 필수 의존성으로 만들수 있지만 유효성 검사가 포함된 생성자 기반 DI를 사용하는 것이 좋다.
- 일반적으로 생성자 주입을 권장
- 구성 요소를
final
로 사용할 수 있다. - null 체크 가능
- 생성자 아규먼트가 너무 많다면 리팩토링 대상이다.
- 구성 요소를
- setter 기반 주입은 default 값을 할당할 수 있는 선택적인 의존성에만 사용해야 한다.
- setter 기반 주입의 이점의 해당 객체를 재구성하거나 다시 주입할 수 있다.
- Bean을 생성할 때 생성될 객체의 scope를 제어할 수 있다.
- 총 6가지 Scope를 지원하며, 4개는 웹용 ApplicationContext를 사용하는 경우에만 사용할 수 있다.
Scope | 설명 |
---|---|
singleton | (Default) 각 Spring IoC 컨테이너마다 단 1개의 객체 인스턴스를 정의 |
prototype | n개 이상의 객체 인스턴스를 정의할 수 있음 |
request | 단일 HTTP 요청 생명주기로 scope를 지정 각 HTTP request 마다 고유한 객체 인스턴스를 가지고 있다. 웹용 ApplicationContext를 사용하는 경우에만 사용할 수 있다. |
session | HTTP 세션 생명주기로 scope를 지정 웹용 ApplicationContext를 사용하는 경우에만 사용할 수 있다. |
application | ServletContext의 생명주기로 scope를 지정 웹용 ApplicationContext를 사용하는 경우에만 사용할 수 있다. |
websocket | WebSocket의 생명주기로 scope를 지정 웹용 ApplicationContext를 사용하는 경우에만 사용할 수 있다. |
- 스프링 프레임워크는 Bean을 커스터마이징 할수 있도록 여러 인터페이스를 제공한다.
-
InitializingBean
과DisposableBean
인터페이스를 구현하여 bean의 lifecycle 를 관리할 수 있다.-
InitializingBean::afterPropertiesSet
으로 Bean 초기화 시 액션을 걸 수 있다.<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean { public void init() { // do some initialization work } }
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean { @Override public void afterPropertiesSet() { // do some initialization work } }
-
DisposableBean::destroy
으로 Bean 초기화 시 액션을 걸 수 있다.<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean { public void cleanup() { // do some destruction work (like releasing pooled connections) } }
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean { @Override public void destroy() { // do some destruction work (like releasing pooled connections) } }
-
-
@PostConstruct
과@PreDestroy
어노테이션을 사용하여 lifecycle Callback을 설정할 수 있다. - 내부적으로 스프링 프레임워크는
BeanPostProcessor
의 구현체를 사용하여 lifecycle callback을 수행한다.- 커스터마이징이 필요하면
BeanPostProcessor
를 구현해서 사용하면 된다.
- 커스터마이징이 필요하면
- spring 2.5 부터, bean의 lifecycle callback을 정의하는 방법은 3가지가 있다.
- The
InitializingBean
andDisposableBean
callback interfaces - Custom
init()
anddestroy()
methods - The
@PostConstruct
and@PreDestroy
annotations.
- The
- 여러 매커니즘을 동시에 설정한 경우 아래 순서대로 실행된다.
- The
@PostConstruct
and@PreDestroy
annotations. - The
InitializingBean
andDisposableBean
callback interfaces - Custom
init()
anddestroy()
methods
- The
- 여러 매커니즘 구성에 대해 만약 동일한 이름의 메서드가 존재한다면 1번 만 실행된다.
-
ApplicationContext
는org.springframework.context.ApplicationContextAware
인터페이스를 구현한 객체 인스턴스를 생성하는데, 해당 인스턴스는 특정ApplicationContext
를 참조하는 메서드와 함께 제공된다.public interface ApplicationContextAware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }
-
위 인터페이스의
setApplicationContext
메서드를 통해ApplicationContext
에 접근할 수 있다.-
ex) 다른 Bean 검색 용도
public class ExampleClass implements ApplicationContextAware{ private SomeClass someClass; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // logic ... this.someClass = applicationContext.getBeansOfType(SomeClass.class) } }
-
-
@Autowired
어노테이션을 사용해서ApplicationContext
에 대한 참조를 얻을 수 있다.- 필드, 생성자 또는 메서드에
@Autowired
어노테이션이 있는 경우ApplicationContext
가 자동으로 제공된다.
- 필드, 생성자 또는 메서드에
-
ApplicationContext
는org.springframework.beans.factory.BeanNameAware
인터페이스를 구현한 클래스를 생성하는데, 해당 클래스는 특정 name을 가지는 Bean을 참조하는 메서드와 함께 제공된다public interface BeanNameAware { void setBeanName(String name) throws BeansException; }
- 일반적으로 개발자는
ApplicationContext
구현한 클래스의 서브클래스를 구현할 필요가 없습니다. - 대신
integretion interface
를 통해 Spring IoC 컨테이너를 확장할 수 있습니다.
-
BeanPostProcessor
인터페이스는 컨테이너의 초기화 로직, 의존성 해결 로직 등을 제공하는 callback 메서드를 정의한다.-
ApplicationContext
는 자동으로BeanPostProcessor
인터페이스 구현한 클래스를 감지한다.
-
- Spring 컨테이너 초기화, 설정 또는 Bean의 초기화 이후 사용자 커스텀 로직을 실행하려면
BeanPostProcessor
를 구현하면 된다. - 여러개의
BeanPostProcessor
인스턴스를 구성할 수 있고order
프로퍼티를 설정하여 순서를 지정할 수 있다.-
order
프로퍼티는BeanPostProcessor
가Ordered
인퍼페이스를 구현해야 한다.
-
-
BeanPostProcessor
인스턴스는 Bean 인스턴스에서 작동한다.- Spring IoC 컨테이너가 Bean 인스턴스를 만들고 이후에
BeanPostProcessor
인스턴스가 작업을 수행한다.
- Spring IoC 컨테이너가 Bean 인스턴스를 만들고 이후에
-
BeanPostProcessor
의 scope는 컨테이너 단위이다. -
BeanPostProcessor
에는 2가지 callback 메서드가 존재한다.-
postProcessBeforeInitialization
: Bean 초기화 전에 실행 -
postProcessAfterInitialization
: Bean 초기화 전에 실행public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor { // simply return the instantiated bean as-is public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; // we could potentially return any object reference here... } public Object postProcessAfterInitialization(Object bean, String beanName) { System.out.println("Bean '" + beanName + "' created : " + bean.toString()); return bean; } }
-
-
Environment
인터페이스는 애플리케이션 확녕에서 properties와 profiles를 모델링한다. -
profile
: 지정된 profile이 활성 상태인 경우에만 Bean이 컨테이너에 등록 -
property
:
- Spring의
Environment
에 property 값을 제공한다.
# app.properties
testbean.name=myTestBean
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
-
ApplicationContext
인터페이스는 국제화 기능을 제공하는MessageSource
인터페이스를 사용할 수 있다.-
HierarchicalMessageSource
인터페이스도 제공하여 계층적인 메시지를 제공할 수 있다.
-
-
String getMessage(String code, Object[] args, String default, Locale loc)
-
MessageSource
로부터 메시지를 가져오는 기본적인 메서드이다. - 특정 Locale의 메시지가 없는 경우 default 메시지가 사용된다.
-
-
String getMessage(String code, Object[] args, Locale loc)
- 위 메소드와 동일한 기능이나 출력할 메시지가 없는 경우
NoSuchMessageException
예외를 발생시킨다.
- 위 메소드와 동일한 기능이나 출력할 메시지가 없는 경우
-
ApplicationContext
가 로딩될 때 context 내부에서 자동으로MessageSource
Bean을 찾는다.- 반드시 bean 이름은
messageSource
로 해야 한다.
- 반드시 bean 이름은
- Spring은 3가지
MessageSource
구현체를 제공한다. (모두HierarchicalMessageSource
를 구현한다.)ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
-
StaticMessageSource
: 거의 사용되지 않음
- MessageSource bean 설정
-
format.properties
,exceptions.properties
,windows.properties
파일이 classpath 경로에 있어야 한다.
-
<beans>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>
- properties 예시
# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.
- 메시지 출력 예시
public static void main(String[] args) {
// 모든 ApplicationContext의 구현체는 MessageSource의 구현체이므로 바로 캐스팅이 가능하다.
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
System.out.println(message); // output: Alligators rock!
}
- Java 표준인
java.net.URL
클래스는 모든 low-level resource에 접근하기에 충분하지 않다.- classpath 또는 servletContext에서 상대경로로 resource를 가져와야 하는 경우 접근하기 위한 표준화된 URL 구현체가 없다.
-
Resource
인터페이스는org.springframework.core.io
패키지 하위에 위치한다.
public interface Resource extends InputStreamSource {
boolean exists();
boolean isReadable();
boolean isOpen();
boolean isFile();
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
ReadableByteChannel readableChannel() throws IOException;
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
-
Resource
인터페이스는InputStreamSource
인터페이스를 상속한다.
public interface InputStreamSource {
// resource를 읽을 수 있는 InputStream을 반환한다.
// stream의 닫는 책임도 맡고있다.
InputStream getInputStream() throws IOException;
}
-
WriteableResource
인터페이스를 상속하여 쓰기도 가능하다.
-
UrlResource
-
java.net.URL
를 래핑하여 URL resource에 접근할 수 있다.- files
- https target
- ftp target
- ...
- URL은 일반적으로
String
타입으로 표시하며 resource에 따라 prefix를 설정한다.-
file:
: filesystem path인 경우 -
https:
: HTTPS protocol을 통해 resource에 접근하는 경우 -
ftp:
: FTP를 통해 resource에 접근하는 경우
-
-
-
ClassPathResource
- classpath에 위치하는 resource에 대해 접근한다.
-
java.io.File
로 classpath의 resource에 접근할 수 있다. - classpath 경로에 없을 경우를 대비해
java.net.URL
도 지원한다.
-
FileSystemResource
-
java.nio.file.Path
,java.nio.file.Files
를 지원하여 filesystem의 resource에 접근할 수 있다.
-
PathResource
-
ServletContextResource
- 웹 애플리케이션 Root dir의 resource에 접근할 수 있다.
-
java.io.File
을 지원하여 filesystem의 resource에 접근할 수 있다.
InputStreamResource
ByteArrayResource
- Validation은 웹 계층에만 묶여있지 않아야 하고, 모든 사용 가능한 validator를 연결할 수 있어야 한다.
- Spring Validation은 모든 계층에서 사용 가능한 Validator를 지원한다.
- Data biding은 input 값이 동적으로 domain 객체에 바인딩되도록 한다.
- Validator와 Data binding은 주로 웹 계층에서 사용하지만 다른 계층에서도 사용이 가능하다.
- Data binder와 BeanWrapper 모두
PropertyEditorSupport
을 구현하여 property 값을 파싱하고 포맷팅한다.\
- Validator 인터페이스를 구현해서 유효성 검증을 하면 된다.
- 이 때 검증 실패 시
Errors
객체에 실패 결과를 기입한다.
- 이 때 검증 실패 시
public class Person {
private String name;
private int age;
// the usual getters and setters...
}
public class PersonValidator implements Validator {
/**
* This Validator validates only Person instances
*/
public boolean supports(Class clazz) {
return Person.class.equals(clazz); // 해당 Validator가 주어진 클래스에 대해 유효성 검사를 할 수 있는지 여부
}
// 실질적인 검증 로직
public void validate(Object obj, Errors e) {
// name 프로퍼티 값이 null 이거나 빈 문자열인 경우 검증 실패
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
Person p = (Person) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}
- 특정 Validator에서 다른 Validator를 가져와 사용할 수 있다.
public class CustomerValidator implements Validator {
private final Validator addressValidator;
public CustomerValidator(Validator addressValidator) {
if (addressValidator == null) {
throw new IllegalArgumentException("The supplied [Validator] is " +
"required and must not be null.");
}
if (!addressValidator.supports(Address.class)) {
throw new IllegalArgumentException("The supplied [Validator] must " +
"support the validation of [Address] instances.");
}
this.addressValidator = addressValidator;
}
/**
* This Validator validates Customer instances, and any subclasses of Customer too
*/
public boolean supports(Class clazz) {
return Customer.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
Customer customer = (Customer) target;
try {
errors.pushNestedPath("address");
ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
} finally {
errors.popNestedPath();
}
}
}
- 검증 에러 표시할 때 기본적으로
DefaultMessageCondesResolver
를 사용한다.-
reject
,rejectXXX
메소드에서 설정한 에러 코드와 함께 에러 메시지를 등록 -
rejectValue("age", "too.darn.old")
- 코드 값 :
too.darn.old
- Spring은 추가로
too.darn.old.age
와too.darn.old.age.int
를 등록한다.
- 코드 값 :
-
- 프로그램 구조에 대한 다른 사고 방식을 제공하여 OOP를 보완
- AOP의 모듈화 단위는
aspect
이다. - IoC 컨테이너가 AOP에 의존하지 않는다. (꼭 사용하지 않아도 된다)
- Spring의 AOP는 스키마 기반 접근 방식 또는 @Aspectj 주석 방식으로 사용할 수 있다.
- Aspect : 여러 클래스를 crosscutting 하는 관심사(concern)의 모듈화
- EX) 트랜잭션 관리, 로깅
- Spring AOP에서는
@Aspect
주석을 사용하거나 스키마 선언을 사용하여 구현한다.
- Join Point : 프로그램 실행 중의 특정 지점
- Spring AOP에서 Join Point는 항상 메소드 실행을 나타낸다.
- Advice : 특정 Join Point에서 Aspect에 의해 실행되는 액션
-
around
,before
,after
와 같은 다양한 유형의 advice가 있다. - Spring을 포함한 많은 AOP 프레임워크는 advice를 인터셉터로 모델링하고 Join Point 주변에 intercepter 체인을 유지한다.
-
- Pointcut : Join Point와 비슷한 용어
- Advice는 Pointcut 표현식과 연관되며 Pointcut에 일치하는 모든 Join Point에서 실행된다.
- EX) 특정 이름의 메소드 실행
- Pointcut 표현식과 일치하는 Join point 구조는 AOP의 핵심 개념이다.
- Spring은 기본적으로 AspectJ pointcut 표현식을 사용한다.
- Advice는 Pointcut 표현식과 연관되며 Pointcut에 일치하는 모든 Join Point에서 실행된다.
- Target object : 1개 이상의 advice가 적용된 aspect object
- Spring AOP는 런타임 프록시를 사용해서 구현하기 때문에 항상 프록시 객체이다.
- AOP proxy : aspect를 구현하기 위해 AOP 프레임워크에 의해 생성된 객체
- Spring Framework에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시이다.
- JDK Dynamic Proxy : Java의 리플렉션 패키지에 존재하는 Proxy라는 클래스를 통해 생성된 Proxy 객체
- target object가 하나 이상의 인터페이스를 구현하고 있는 클래스라면 JDK DYnamic Proxy 방식으로 생성
- target object가 인터페이스를 구현하지 않은 클래스라면 CGLIB 방식으로 AOP 프록시를 생성
- 추가 설명 링크
- Spring Framework에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시이다.
- before advice : join point 이전에 실행되지만 Join Point로 진행하는 실행 흐름을 막을수는 없다.
- 예외를 던지면 막을 수 있다.
- after advice : Join point가 정상적으로 완료된 후 실행할 Advice
- 메서드가 예외 없이 반환되어야 한다.
- after throwing advice : 메서드가 예외를 throw 하여 종료되는 경우 실행할 Advice
- after advice : Join Point 종료 시 실행될 Advice
- 정상 종료 / 예외 발생 모두 포함
- around advice : 메서드 호출과 같은 Joint point를 둘러싸는 advice
- 메서드 호출 전후에 사용자 코드를 실행할 수 있다.
- 예외를 throw 하여 join point의 실행을 막을 수 있습니다.
- execution : 특정 메서드와 일치하는지에 대한 표현식
- Spring AOP로 작업할 때 사용하는 기본 포인트컷 지정자
// 모든 public 메소드를 대상
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
// set으로 시작하는 모든 메서드
@Pointcut("execution(* set*(..))")
...
// AccountService 인터페이스 하위 메서드 대상
@Pointcut("execution(* com.xyz.service.AccountService.*(..))")
...
// service 패키지 하위 모든 메서드 대상
@Pointcut("execution(* com.xyz.service.*.*(..))")
...
// service 패키지 또는 service의 하위 패키지
@Pointcut("execution(* com.xyz.service..*.*(..))")
...
// service 패키지 하위
@Pointcut("within(com.xyz.service.*)")
...
// service 패키지 또는 service 하위 패키지
@Pointcut("within(com.xyz.service..*)")
...
- within : 특정 유형과 일치하는지에 대한 표현식
// trading 모듈 내부 메소드를 대상
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {}
- this : Bean의(Spring AOP Proxy) 특정 유형의 인스턴스와 일치하는지에 대한 표현식
// AccountService 인터페이스인 경우
// this instanceof AccountService 인 경우
@Pointcut("this(com.xyz.service.AccountService)")
...
- target : target object가 특정 인스턴스와 일치하는지에 대한 표현식
// AccountService 인터페이스를 구현한 object 대상
// this AccountSereviceImpl instanceof AccountService 인 경우
@Pointcut("target(com.xyz.service.AccountService)")
- args : 인스턴스의 아규먼트가 특정 타입과 일치하는지에 대한 표현식
// 메서드 아규먼트가 1개이고 타입이 Serializable인 경우
@Pointcut("args(java.io.Serializable)")
- @target : 실행 객체의 클래스가 특정 타입과 일치하는지에 대한 표현식
// target object가 @Transactional 어노테이션이 있을 때
@Pointcut("@target(org.springframework.transaction.annotation.Transactional)")
- @args : 실제 전달된 아규먼트의 타입이 특정 타입과 일치하는지에 대한 표현식
// 메서드 아규먼트가 1개이고 타입이 Classified인 경우
@Pointcut("@args(com.xyz.security.Classified)")
- @within : 특정 어노테이션이 존재하는지에 대한 표현식
// target에 @Transactional 어노테이션이 있을 때
@Pointcut("@within(org.springframework.transaction.annotation.Transactional)")
- @annotaion : join point에 특정 어노테이션이 있는지에 대한 표현식
// 실행 메서드 중 @Transactional 어노테이션이 있을 때
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
// 모든 public 메소드를 대상
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
// trading 모듈 내부 메소드를 대상
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {}
// 모든 public 메소드 + trading 모듈 내부 메소드 대상
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}