본문 바로가기
JAVA/Spring

Spring 예제 - 어노테이션 , @Aspect

by 설총이 2018. 9. 10.

- Aop (관점지향 프로그래밍)


1. 공통관심사항 - aop

:: (여러객체가 사용하거나, 거쳐가는 기능인지)

공통기능으로 어플리케이션 전반에 걸쳐 필요한 기능을 



2. 핵심관심사항 - 원래쓰던 객체처럼 사용

:: (이 객체가 핵심의 기능인지)



기존의 공통부분의 코드를 클래스화해서 메서드 호출하는것까지는 성공했지만,

그 호출하는 기능까지는 분리시키질 못했다.그렇기때문에 쓸때마다 공통클래스를 호출하는데

만약 그 공통부분의 클래스를 여러곳에서 호출했는데

여러곳중 하나가 기능을 추가해서 매개변수가 하나라도 늘어났다면

모든 클래스에서 에러가 나고 수정을 해야한다.

그렇기때문에 '횡단관점의 분리'라는 AOP로는 그 호출까지도 완전히 분리시키는 작업을한다.



- Aop 주요 용어


Advice :: 언제 공통기능을 핵심 로직에 적용할지 정의

JoinPoint :: Advice를 적용 가능한 지점 == 메서드 호출, 필드 값 변경

Pointcut :: Joinpoint의 부분집합. 실제로 Advice가 적용되는 Joinpoint

Weaving :: Advice를 핵심 로직 code에 적용하는 것을 weaving이라 한다.

Target object :: 하나 또는 그 이상의 Aspect에 의해 advice 되는 객체 -> 핵심 로직을 구현하는 클래스


Aspect :: 여러객체에 공통으로 적용되는 공통 관심 사항을 Aspect라 한다.


Around :: 메서드 실행의 전과 후에 모두 작업을 할 수 있고

언제, 어떻게, 상황에 따라 결정하기 위해서 메서드는 실제로 실행할 모든 것을 획득한다.





- excution(수식어패턴? 리턴타입패턴 클래스이름패턴?이름패턴(파라미터패턴))


수식어패턴 :: 생략가능, (ex:public,protected)

리턴타입패턴 :: 리턴타입명시

클래스이름패턴 :: 생략가능, 클래스이름명시

이름패턴 :: 메서드 이름 명시

파라미터패턴 :: 매칭될 파라미터에 대해서 명시


각 패턴은 *을 이용해 모든 값을 표현 가능


..을 이용하면 0개이상이라는 의미를 표현




[applicationContext.xml]



1
2
3
4
5
6
7
8
9
10
    <!-- aop 사용시작 -->
    <aop:aspectj-autoproxy />
    <!-- target이 잡히지 않을때 사용하는방법. -->
    <aop:config proxy-target-class="true" />
    
 
    <!-- 어노테이션 사용하겠다는의미 -->
    <context:annotation-config />
    <!-- spring.anno패키지에 있는 모든 클래스 스캔 -->
    <context:component-scan    base-package="spring.aop" />
cs



[GreetingText.java]



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class GreetingTest {
 
    public static void main(String[] args) {
        AbstractApplicationContext context = 
                new GenericXmlApplicationContext("applicationContext2.xml");
        GreetingService bean = (GreetingService) context.getBean("greeting");
        bean.sayHello("홍길동");
 
        try {
            bean.sayGoodbye("홍길동2");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
 
    }
}
 
cs



AbstractApplicationContext에 사용하려는 'Spring Bean Configuration File'로 생성한 xml 파일을 담는다.


이때, xml파일로 저장한 태그들 정보 로딩과, @로 지정한 모든 정보들을 담아 가지고 있는다.


밑에 저장한 greeting 클래스를 getBean해서 빈생성한 후 .sayHello("홍길동"); 실행







그러나 xml파일에는 'greeting'이라는것이 없다. 그것을 xml에서 설정하지않고

어노테이션으로 자바 클래스에서 설정했기때문이다.




[GreetingServiceImple.java]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//greeting이라는 이름으로 사용할 bean 생성
@Component("greeting")
public class GreetingServiceImpl implements GreetingService {
 
    private String greeting;
 
    //기본형데이터타입이나 String을 넣을 Value로 값 저장.
    @Value("scott")
    public void setGreeting(String greeting) {
        this.greeting = greeting;
    }
 
    public void sayHello(String name) {
        System.out.println("sayHello : " + greeting + ":" + name);
    }
 
    public void sayGoodbye(String name) throws Exception {
        System.out.println("sayGoodbye : " + greeting + ":" + name);
        System.out.println(":: 예외 발생 :: 강제예외");
        throw new Exception();
    }
}
cs



String 매개변수 하나를 받은 sayHello메서드가 실행되어야한다......................... 하지만.

public이라는 클래스의 메서드 실행이므로

밑에 LogAspect에 담은 표현식에따라 어노테이션이 실행된다.



@Component 라는 annotation(어노테이션)으로 xml에서 <bean>태그로 잡아주던 빈 컨테이너를

자바에서 바로바로 할 수 있게 해준다.


---------------------------------------------------------------------------------------------------


 -- Component 설명글 -- 알면 지나가세요


★★★ @Component ★★★ 


일반 java Class파일은@Component 적어주면 된다.

빈 객체의 이름은 자동적으로 클래스명에서 첫문자만 소문자이고 나머진 같다

ex)Foo -> foo

빈의 이름을 따로 지정해주고 싶다면, @Component(""); 로 지정하면 된다.

빈객체의 scope(getBean할대마다 새로운주소값을 가지는 객체생성)를 지정해주려면

@Component밑에 @Scope("prototype")으로 빈의 범위를 지정할 수 있다.


@Bean


새로운 빈 객체를 제공할 때 사용.. 메서드에 붙힘.


--------------------------------------------------------------------------------------------------------------------------

 



[LogAspect.java]



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Component
@Aspect
public class LogAspect {
 
    //아무런 기능도안하는 메서드를 선언해 pointCut으로 쓰기위한 표현식지정. 
    @Pointcut("execution(public * * (..))")
    public void publicMethod() {}
    
    
    //표현식에 해당되는 클래스의 '메서드'가 실행되기 '전에' 먼저실행하기위한 before
    @Before("publicMethod()")
    public void beforeLogging(JoinPoint j) {
        System.out.println("뭘까요? :  " +j.getSignature().getName());
        System.out.println(":: 메서드 호출 전 :: ");
    }
    
    //표현식에 해당되는 클래스의 '메서드'가 실행한 후에
    //리턴값이 있다면 실행해서. returning 변수에 담아서 실행
    @AfterReturning(pointcut="publicMethod()", returning="returnValue")
    public void afterLogging(Object returnValue) {
        System.out.println(":: 메서드 호출 후 :: ");
    }
    
    //표현식에 해당되는 클래스의 '메서드'가 실행될때,
    //예외처리가 발생하면 AfterThrowing이 채가서 throwing 변수에 담아 실행한다.
    @AfterThrowing(pointcut="publicMethod()", throwing="ex")
    public void throwingLogging(Exception ex) {
        System.out.println(":: 예외 발생 = " + ex.getMessage()+ " :: ");
    }
    
    
    // 표현식에 해당되는 클래스의 '메서드'가 실행되는 후에 
    // 1. 리턴했던, 2. 예외발생을했던, 3. 메서드가 문제없이 끝났던 ==> 후에 무조건 실행한다
    //같은 After형식이 있을때 순서는 After 가 먼저 실행되고 AfterReturning이 실행된다.
    @After("publicMethod()")
    public void alwaysLogging() {
        System.out.println(":: 항상 실행 ::");
    }
}

cs







[PerformanceAspect.java]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Component
@Aspect
public class PerformanceAspect {
 
    @Pointcut("execution(public * spring.aop..*.sayHello(..))")
    private void pointCut() {}
    
    //메서드 실행의 전과 후 모두 작업할수있는 공통 로직.
    //위의 표현식에 해당하는 클래스의 메서드가 실행되면 무조건 Around를 먼저 실행하고
    //원래 실행되려던 메서드는 .proceed()가 실행한다..
    @Around("pointCut()")
    public Object timeCheck(ProceedingJoinPoint joinPoint) throws Throwable{
        
        Signature s = joinPoint.getSignature();
        String methodName = s.getName();
        long startTime = System.nanoTime();
        System.out.println("[Log]METHOD before : " + methodName + " time check start");
        Object obj = null;
        
        try {
            obj = joinPoint.proceed();
        }catch(Exception e) {
            System.out.println("[Log]METHOD error : " + methodName);
        }
        
        long endTime = System.nanoTime();
        System.out.println("[Log]METHOD After : " + methodName + " time check end");
        System.out.println("[Log] " + methodName + " Procedding time is " + (endTime - startTime)+ "ns");
        return obj;
    }
}
cs





'JAVA > Spring' 카테고리의 다른 글

Spring - JdbcDaoSupport  (0) 2018.09.10
Spring을 통한 jdbc연결  (0) 2018.09.10
Spring예제 - setter방식  (2) 2018.09.06
Spring예제 - bean과 aop혼합사용  (0) 2018.09.05
Spring예제 - bean컨테이너 사용  (1) 2018.09.05