BACK END/Spring

1일(08.17) Spring Framework - AOP

라미보 2022. 8. 17. 22:40

 

 

💻  AOP(Aspect Oriented Programming)
관점 지향 프로그래밍
문제를 핵심사항과 공통사항을 기준으로 프로그래밍하므로써 공통 모듈을 여러코드에 쉽게 적응할 수 있도록 한다

 

공통관심 사항 구현한 코드를 핵심 로직을 구현한 코드 안에 삽입
- 핵심 로직을 구현한 코드에서 공통 기능을 직접적으로 호출하지 않는다.
- 핵심 로직을 구현한 코드를 컴파일하거나, 컴파일된 클래스를 로딩,

  로딩된 클래스 객체를 생성할 때 AOP가 적용되어 핵심 로직 구현 코드 안에 공통 기능이 삽입된다.

- 공통 기능이 변경되더라도 핵심 로직을 구현한 코드를 변경할 필요가 없다.

 (공통 기능 코드를 변경한 뒤 핵심 로직 구현 코드에 적용)

 

-AOP에서는 핵심기능과 공통기능을 분리시켜 핵심 로직에 영향을 끼치지 않게 곹오기능을 끼워 넣는 개발형태이다

 이렇게 개발함에 따라 무분별하게 중복되는 코드를 한 곳에 모아 중복되는 코드를 제거 할 수 있게되고

 공통기능을 한 곳에 보관함으로써 공통기능 하나의 수정으로 모든 핵심기능들의 공통기능을 수정 할 수 있어 효율적인 유   지보수가 가능하며 재활용성이 극대화 된다.

 

공통기능(Aspect)
-핵심기능 전에 수행할것.


📌 AOP 용어


Aspect : 공통기능, 여러객체에 공통으로 적용되는 공통 관심 사항 (로그인, 로그아웃)
Advice :  Aspect가 수행해야 할 어떤 목록, Aspect를 어떤 사항에 적용시킬 것인지 정의

              각각의 pointcut에 삽입되어 동작할 수 있는 코드

Joinpoint : Advice를 적용할 수 있는 모든 요소들을 의미

                메소드 호출, 필드 값 수정, 예외 발생 등이 해당된다.

                각각의 구체적인 적용 지점을 Pointcut이라고 한다.

Pointcut : Jointpoint를 구성하는 각각의 구체적인 적용 지점(메서드 실행, 예외처리, 속성 변경)

                AspectJ Pointcut Expression을 사용하여 Pointcut을 표현한다.

 

 

 

 

 

🔴 AOP 설정

 

- spring에서는 jar파일을 사용하지 않는다. 대신에 아래의 <dependencies><dependency>를 사용한다.

 

 

[형식]
<dependencies>
    <dependency></dependency>
</dependencies>

 

* pom.xml 에 아래의 코드를 작성해줍니다.

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.6.11</version>
</dependency>

 

메이븐 중앙저장소에서 aop할때 필요한 파일을 가져와서 프로젝트에 넣어서 실행된다.
(mvnrepository)
https://mvnrepository.com/
https://mvnrepository.com/artifact/org.aspectj/aspectjweaver

 

 

 

 

 

📂example1

 

 

 

💾 Board.java

package example1;

public class Board {
	
	public void board() {
		//생성자 아니다. 클래스와 이름이 다르다
		
		String msg ="게시물 등록";
		
		
		System.out.println(msg+"을 하기 위한 로그인 수행");//공통기능(Aspect)
		System.out.println(msg+"하기"); //핵심기능
		System.out.println(msg+"을 DB에 저장");//공통기능
		System.out.println(msg+"을 하기 위한 로그아웃 수행");//공통기능
	}

}

 

 

💾 Order.java

package example1;

public class Order {

	public void order() {
		
		String msg ="상품 주문";
		
		System.out.println(msg +"을 하기 위한 로그인 수행");//공통기능
		System.out.println(msg +"하기");//핵심기능
		System.out.println(msg +"을 DB에 저장");//공통기능
		System.out.println(msg +"을 하기 위한 로그아웃 수행");//공통기능
	}
}
package example1;

public class AOPMain {

	public static void main(String[] args) {
		
		Board b = new Board();
		Order o = new Order();
		
		b.board();
		System.out.println();
		o.order();

	}

}

 

 

 

 

 

 

 

📂 example2

 

package example2;

public class Board {

	
	public void board() {
		//생성자가 아니다. 클래스와 이름이 다르다.
		
		String msg="게시물 등록";
		
		Login.login(msg);
		
		System.out.println(msg+"하기"); //핵심기능
		
		Logout.logout(msg);
	}
}

 

 

💾 Order.java

package example2;

public class Order {

	
	public void order() {
		String msg ="상품 주문";
		
		Login.login(msg);
		
		System.out.println(msg +"하기");//핵심기능
		
		Logout.logout(msg);
	}
}

 

 

💾 Login.java

package example2;

public class Login {
//공통기능 클래스 하나만 만든다.
	
	public static void login(String msg) {
		System.out.println(msg+"하기 전 로그인 수행");	
		
	}
}

 

 

 

💾 Logout.java

package example2;

public class Logout {

	public static void  logout(String msg) {
		System.out.println(msg+"한 후 로그아웃 수행");
	}
}

 

 

💾 AOPMain.java

package example2;


public class AOPMain {

	public static void main(String[] args) {	
		
		Board b = new Board();
		Order o = new Order();
		
		b.board();
		System.out.println();
		o.order();

	}

}

 

 

 

 

📂example3

 

 

💾 Hello.java

package example3;

public class Hello {

	
	public void hello() {
		System.out.println("안녕하세요");
	}
}

 

 

💾 Login.java

package example3;

public class Login {

	
	public void login() {
		System.out.println("로그인수행");
	}
	
}

 

 

💾 Logout.java

package example3;

public class Logout {

	
	public void logout() {
		System.out.println("로그아웃 수행");
	}
}

 

 

💾 Board.java(interface로 생성)

package example3;

public interface Board {

	
	public void board();
}

 

 

💾 BoardImpl.java

package example3;

public class BoardImpl implements Board{
	//여기가 핵심 나머지는 공통기능
	@Override
	public void board() {
	
		String msg="게시물 등록";
		System.out.println(msg+" 하기");
		
	}

}

 

 

 

💾 Order.java(interface로 생성)

package example3;

public interface Order {

	public void order();
}

 

 

💾 OrderImpl.java

package example3;

public class OrderImpl implements Order{

	@Override
	public void order() {
		
		String msg="상품 주문하기";
		System.out.println(msg+" 하기");//핵심 기능
		
	}

}

 

 

 

 

💾 AOPMain.java

package example3;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class AOPMain {

	public static void main(String[] args) {
	
		
		AbstractApplicationContext context = new GenericXmlApplicationContext("aopExam.xml");
		
		
		System.out.println("---게시물 등록 하기---");
		Board myboard = (Board)context.getBean("myboard");
		myboard.board();
		
		System.out.println();
		System.out.println("---상품 주문 하기---");
		Order myorder = (Order)context.getBean("myorder");
		myorder.order();
	}

}

 

AOPMain에 아래와 같이 출력되게끔 해보았다.


 로그인수행 
 안녕하세요 
 DB 작업 수행함 
 게시물등록하기:핵심기능 
 로그아웃수행

로그인수행 
상품하기:핵심기능 
DB 작업 수행함 
로그아웃수행

 

- GenericXmlApplicationContext 클래스는 xml파일에 정의된 설정 정보를 읽어와서 객체를 생성하고, 각각의 객체를 연결 한 뒤에 내부적으로 보관한다.

GenericXmlApplicationContext 에서 생성할 객체가 무엇인지 설정, 객체를 어떻게 연결할지, 정보는 xml파일에 설정한다.

 

 

📌 spring 컨테이너 종류

 

- BeanFactory 인터페이스 : Bean 객체를 관리, Bean 객체간의 의존관계를 설정해주는 기능을 제공

XmlBeanFactory 클래스가 있다.

 

- AbstractApplicationContext : 컨테이너 종료(close)와 같은 기능을 제공해 주는 객체.

- GenericXmlApplicationContext :  xml파일에 정의된 설정 정보를 읽어와서 객체를 생성하고, 각각의 객체를 연결 한 뒤에 내부적으로 보관한다.

  • GenericXmlApplicationContext 에서 생성할 객체가 무엇인지 설정, 객체를 어떻게 연결할지, 정보는 xml파일에 설정한다.
  • AbstractApplicationContext 객체를 상속 받아 만드는 클래스,
  • xml 파일에서 스프링 빈 설정 정보를 읽어오는 역할.
  • 객체를 생성할 때 파라미터 값으로 xml의 경로를 전달하여 설정 파일로 사용
  • 객체는 전달받은 xml파일에서 설정 정보를 읽어와서 Bean 객체를 생성하고 property 값을 설정한다.

 

 

 

💾 aopExam.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">

<!-- example3.BoardImpl myboard = example3.BoardImpl() -->


	<bean class="example3.BoardImpl" id="myboard"/>
	<bean class="example3.Hello" id="myhello"/>
	<bean class="example3.Login" id="mylogin"/>
	<bean class="example3.Logout" id="mylogout"/>
	<bean class="example3.Dao" id="mydao"/>
	<bean class="example3.OrderImpl" id="myorder"/>
	
	
	<!-- 핵심기능을 실행하기 전에 실행할 것이다. -->
	
	<aop:config>
		<aop:aspect ref="mydao" order="3">
			<aop:before method="dao" pointcut="execution(* example3.BoardImpl.*())"/>
		</aop:aspect>
	</aop:config>
	
	
	<aop:config>
		<aop:aspect ref="mylogin" order="1">
			<aop:before method="login" 
					pointcut="execution(* example3.BoardImpl.*())"/>
		</aop:aspect>
	</aop:config>
	
	<aop:config>
		<aop:aspect ref="myhello" order="2">
			<aop:before method="hello" pointcut="execution(* example3.BoardImpl.*())"/>
		</aop:aspect>
	</aop:config>
	
	
	
	<aop:config>
		<aop:aspect ref="mylogout">
			<aop:after method="logout" pointcut="execution(* example3.BoardImpl.*())"/>
		</aop:aspect>
	</aop:config>
	
	
	

	
	<aop:config>
		<aop:aspect ref="mylogin">
			<aop:before method="login" pointcut="execution(* example3.OrderImpl.*())"/>
		</aop:aspect>
	</aop:config>
	
	<aop:config>
		<aop:aspect ref="mydao" order="2">
			<aop:after method="dao" pointcut="execution(* example3.OrderImpl.*())"/>
		</aop:aspect>
	</aop:config>
	
	<aop:config>
		<aop:aspect ref="mylogout" order="1">
			<aop:after method="logout" pointcut="execution(* example3.OrderImpl.*())"/>
		</aop:aspect>
	</aop:config>

 

 

📌 xml Schima를 이용한 AOP설정

 

<aop:aspect ref="mylogin" order="1"> 

: order를 지정하여 순서대로 출력하게 할 수 있다. 순서는 상대적임.

 

 

<aop:config> : aop 설정하겠다는 의미
   <aop:aspect ref="mylogout" order="1"> : aspect(공통기능)을 지정, ref속성: 공통기능을 구현하고 있는 Bean
      <aop:after method="logout" pointcut="execution(* example3.OrderImpl.*())"/>

         -<aop:pointcut>  : pointcut을 설정, expression속성: Advice를 적용할 pointcut에 대한 정보 지정
   </aop:aspect>
</aop:config>

 

 

- after: 숫자 큰것이 우선순위로 실행되고
- before: 숫자 작은것이 우선순위로 실행된다.

 

 

📌 AspectJ Pointcut Expression 예시

<aop:before method="login" pointcut="execution(* example3.BoardImpl.*())"/> 

: 메서드()괄호 안에 매개변수(파라미터가)가 없는, 아무 메서드 실행 전에 로그인 클래스 안에 로그인 메서드가 있냐

  로그인메서드 먼저 실행해라
execution(* example3.BoardImpl.*()) :  example3.BoardImpl의 어떤 메서드 상관 안한다.
mylogin 변수로 관리하는  example3.Login 클래스 안에 메서드를 실행해라

 

- execution(public void set*(..)) : 리턴타입이 void,  패키지명 없음, 메서드 이름이 set으로 시작, 파라미터 0개 이상인 메서드 호출 

 

- execution(* 패키지이름.클래스이름.*()) : 괄호앞에 있는것은 메서드 이름(이름을 *), 파라미터가 없는 메서드 호출, 앞에 있는 *은 리턴타입 아무 타입으로 리턴해라

 

- execution(*.패키지이름..*.*(..)) : 패키지의 하위 패키지에 있는 파라미터가 0개 이상인 메소드 호출


- execution(* read*(Integer), ..) :  메서드 이름이 read로 시작, 앞에 정수가 꼭 와야한다. 1개 이상 파라미터 갖는 메소드 호출  

 

- execution(* get*(*), ..) : 이름이 get으로 시작, 파라미터 1개 와야 한다

- execution(* get*(*,*), ..) : 이름이 get으로 시작, 파라미터 2개 와야 한다

 

 

 

 

 

 

📂 example4

 

 

 

💾