저번 포스팅에서는 클라이언트가 전달하는 값을 주입받을때 데이터 한개 한개를 받을 수 있는 여러가지 방법에 대해서 알아보았다. 그러한 파라미터 데이터는 객체로써 받을 수도 있다!

 

 

 

 

1. Map으로 주입받기

 

클라이언트가 전달하는 모든 파라미터 데이터를 한번에 Map을 이용하여 전달 받을 수 있다.. 단, 동일명으로 전달 되는 2개 이상의 파라미터가 있다면 하나만 담기게 된다. 동일 명으로 전달 되는 파라미터가 2개이상이라면 List를 통해서 받아야한다.... 복잡하다. 

 

 

 

 

2. @ModelAttribute로 주입받기

 

Spring MVC에서 제공하는 @ModelAttribute에 대해서 알아보자. 

@ModelAttribute를 이용하면 파라미터를 객체로 주입받을 수 있다. 전달되는 파라미터의 이름과 동일한 프로퍼티에 자동으로 주입이 된다. 이렇게 받아온 객체를 커맨드 객체(Command Object)라고 부른다. 또한, @ModelAttribute는 생략이 가능하다.

 

 

 

BoardVO라는 POJO클래스가 위와 같이 정의 되어있다고 가정하면, @ModelAttribute BoardVo board 의 형태로 파라미터를 받아올 수 있다(@ModelAttribute는 생략이 가능하다.) 전달되는 파라미터의 이름과 같은 프로퍼티에 자동으로 주입하기 때문에 setter를 이용해 자동으로 index, title, content값이 들어갔겠고, 위와같이 출력하면 Request한 값이 정상적으로 출력되는 것을 볼 수 있었다. 

 

 

 

 

3. 커맨트객체 이용하기

 

2번과 같이 Request영역에서 요청한 데이터를 이용하여 빈을 주입하는 작업을 하였다. 우리는 빈을 주입만 하였지 Request영역으로 다시 저장하기 위하여 따로 저장하는 작업을 하지는 않았다. View단에서는 어떻게 이 빈 객체의 데이터를 이용할 수 있을까??

 

<h3> index : ${boardVO.b_index} </h3>
<h3> title : ${boardVO.b_title} </h3>
<h3> content : ${boardVO.b_content} </h3>

 

View에서는 위와같이 사용할 수 있다. JSP EL코드( ${} )를 사용하고, 빈 클래스 맨 앞 글자를 소문자로 바꾸고 프로퍼티 이름을 써주면 View에서 가져다가 쓸 수 있다.

 

View에서 가져갈 때 이름을 붙여줄 수도 있다.

public String test1(@ModelAttribute("testData") BoardVO board)

이 처럼 이름을 정해주면 ${testData.b_index}의 형태로 사용할 수 있겠다

 

 

 

 

 

 

 

 

 

 

 

Contorller에서 Request를 받는방식에는 @RequesetMapping, @PostMapping, @GetMapping이 있다.

 

 

 

@RequestMapping의 사용형태

ex)

value는 Request해주는 요청값이고(form태그의 action="test1"이렇게 받았다면 위의 RequestMapping이 받는다.) method는 요청시 Request방식이 GET인지 POST인지에 따라서 설정할 수있다.

 

 

 

@PostMapping의 사용형태

ex)

전송방식이 애초에 post형식으로 받겠다고 PostMapping을 쓴거니까 method는 따로 필요가 없다. value도 생략.

 

 

 

@GetMapping의 사용형태

ex)

전송방식이 애초에 get형식으로 받겠다고 GetMapping을 쓴거니까 method는 따로 필요가 없다. value도 생략.

 

 

위와같은 어노테이션을 사용하여 Request를 받긴하는데.. 어떻게, 어떤 Parameter를 사용하여 Request를 처리할 수 있는지를 알아보자. 

 

 

 

HttpServletRequest

 

Spring MVC는 필요한 객체나 데이터를 주입받아서 사용하게 된다. Servlet / JSP에서 파라메터를 추출할 때 HttpServletRequest객체를 통하여 Spring MVC에서 이 객체를 주입받아 사용할 수 있다. 파라메터 추출 뿐만 아니라 HttpServletRequest객체가 필요할 경우 사용하면 된다. (지금 생각나는건 session을 사용할 때.. 정도..) 

 

 

이런식으로 파라메터로 받아서 JSP Servlet에서 사용했던것처럼 request.getParameter()하여 추출하면 된다.

 

 

WebRequest

 

Servlet에서 사용했던 HttpServletRequest클래스를 확장한 클래스이다. HttpServletRequest와 거의 비슷하지만 기능이 몇개 더 있는 형태이다. 비슷하기 때문에 예제는 생략한다.

 

 

 

@PathVariable

 

요청주소 /값1/값2/값3 형태로 사용된다. 예를 들어, http://test.com/sub1/sub2/sub3 이러한 요청주소가 있으면 /이하는 하위 폴더를 의미하는 것으로 sub1아래의 sub2아래의 sub3폴더를 의미하는것이지만, @PathVariable 을 사용하면 /이하를 값으로서 인식하게 된다. 

 

<a href="test/100/200/300" /> 이렇게 요청했다고 하면, 

 

이렇게 사용할 수 있다. /test/{data1}/{data2}/{data3} 이렇게 받아오면 @PathVariable로써 같은 변수의 이름으로 바인딩할 수 있겠다. 만약 추출하는 데이터값을 정수형으로 받고 싶다면 @PathVariable int data1 과 같은 형태로 받아주면 된다 자동으로 형변환을 해준다!

 

 

 

@RequestParam

 

RequestParam은 value속성에 가져올 데이터의 이름을 적어주고 뒤에 그 데이터를 담을 변수를 선언해주는 형태로 사용한다. 주의할점은 넘어오는 데이터는 @RequestParam으로 받지 않아도 아무 문제 없다. (예를들어 Request 데이터가 data3까지 있을 때)

그런데.. 있지 않는 요청값을 @RequestParam으로 받으면 오류가 난다. (예를들어 Request 데이터가 data1밖에 없을 때)
오류 안나게 하려면.. @RequestParam (required=false) String data2 해주면 아무것도 넘어오지 않으면 null값이 들어가게 된다. (defaultValue="default")하면 아무것도 넘어오지 않으면 default라는 문자열이 주입된다!!!

 

 

 


RESTful API 같은경우에는 보통 @PathVariable 을 주로 사용하고, 파라메터 양식으로 넘어올때는 @RequestParam을 사용하면 된다.

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

Redirect와 Forward  (0) 2020.04.24
Form 커스텀 태그  (0) 2020.04.24
ViewResolver 에게 전달하기  (0) 2020.04.24
객체로 파라미터 주입받기, Command Object 이용하기  (0) 2020.04.24
Spring MVC xml설정하기  (0) 2020.04.23

Spring MVC를 사용하기 위해서는 몇가지 설정을 진행해야 한다.

그 전에, 이해를 돕기 위해서 Spring MVC의 동작과정을 간단하게 살펴보도록 하자.

 

 

 

먼저 클라이언트로부터 Request가 들어오면 Dispatcher Servlet이라는 놈이 맨처음 받는다. Dispatcher Servlet은 Handler Mapping이 그 Request를 받아들여 적절한 Contorller를 찾아준다. Contorller에서는 개발자가 만들어놓은 어떤 작업을 실행할 것이다. 그 후 Contorller에서는 어떠한 View를 사용하겠다는 내용을(ModelAndView객체) Dispatcher Servlet에게 반환(return)해준다. 그러면 Dispatcher Servlet은 클라이언트에게 반환할 정보를 View Resolver에게 전달한다. View Resolver는 View객체를 만들어서 다시 Dispatcher Servlet에게 전달한다. 그러면 Dispatcher Servlet은 Model 데이터들을  JSP페이지에 셋팅하여 View를 사용자에게 Response해준다.

 

위의 그림을 기반으로 MVC의 동작과정을 글로 설명한 것이다.

 

 

우리는 xml파일을 이용하여 Dispatcher Servlet을 지정하고 Dispatcher Servlet이 동작할 때, 적용될 init-param으로서 servlet-context.xml을 정의할 수 있다. 여기에서 View Resolver를 설정해줄 수 있다.

 

 

먼저, web.xml파일을 설정해보자.

web.xml 파일은 웹 어플리케이션의 환경설정을 위한 xml형식의 파일이다.

web.xml에는 ServletContext의 초기 파라미터, Session의 유효시간 설정, Servlet/JSP에 대한 정의, Servlet/JSP 매핑, Mime Type 매핑, Welcome File list, Error Pages 처리, 리스너/필터 설정보안들의 설정을 할 수 있다.


출처: https://zipoo80.tistory.com/entry/WEBXML의-역할 [아자아자 화이팅~!]

 

 

web.xml

 

appServlet이라는 이름으로 servlet을 정의해주고 그 서블릿의 클래스를 DispatcherServlet으로 등록해줌으로써 appServlet은 DispatcherServlet이 된다. 

servlet-mapping을 url-pattern / 로 지정해줌으로서 웹 어플리케이션이 실행될 때, url에 아무요청이 없다면 appServlet이 실행된다는것을 의미한다. init-param을 지정해줌으로써 SpringMVC로 만든 웹 어플리케이션에 대한 설정을 할 수 있는 servlet-context.xml파일을 설정해준다.

 

 

servlet-context.xml

 

servelt-context.xml은 위와 같이 설정하였다.

<annotation-driven/>을 설정해줌으로써 Spring MVC에서 정상적으로 어노테이션을 사용할 수 있도록 해준다. 

ViewResolver를 설정한 모습을 볼 수 있는데, /WEB-INF/views/ 경로 아래에 있는 .jsp (모든 jsp파일)을 찾아서 뷰객체를 반환하겠다고 설정을 해준것이다.

인터셉터도 설정해주었는데 이것은 나중에 자세하게 설명하도록 하겠다.

 

 

 

MyBatis란?

 

SpringFramework 에서 제공하는 JDBC라이브러리를 보다 쉽게 작업할 수 있도록 만든 라이브러리이다.

Spring JDBC에서 썼던 Mapper의 역할을 확장시켜 쿼리문 작성을 모두 Mapper인터페이스에서 할 수 있도록 지원한다.

항상 반복적으로 만들어야하는 쿼리문 코드를 최대한 줄일 수 있는 라이브러리이다!

 

우선 MyBatis를 사용하기 위해서는 라이브러리를 추가해주어야 한다.

 

 

 

mvnrepository에 MyBatis라고 검색하면 이렇게 두가지가 나온다.

이 두개 모두 추가를 해주어야 한다. 

 

 

 

다음과 같이 추가해주었다. 버전관리를 위해서 프로퍼티를 지정해서 버전을 따로 빼주었다.

 

 

 

Java Configuration 파일이다. MyBatis도 DataSource를 이용하기 때문에 BasicDataSource클래스를 만들어서 커넥션 풀을 설정해준다.

 

Spring JDBC에서는 JdbcTemplate라는 것을 만들어서 그 탬플릿의 update메소드, query메소드를 이용하여 DAO클래스에서 쿼리문을 작성하여 사용하였다.

MyBatis를 이용하려면 위와 같이 SqlSessionFactory와 Mapper가 필요하다. SqlSessionFactory는 Jdbc를 처리하는 객체로서 위와 같이 dataSource를 매개변수로 받아서 setDataSource하여 셋팅을 해준다. 

중요한것은 mapper인데 개발자가 만들어 놓은 쿼리문을 관리하는 것이다.

포스팅 처음에 MyBatis는 Spring JDBC의 mapper를 확장시켜 mapper 인터페이스를 이용한다고 하였다. 위의 코드에서 MapperFactoryBean<MapperInterface>형의 빈을 만들었다. MapperInterface는 사용자가 만든 인터페이스이다. 

 

setSqlSessionFactory()하여 위에서 만든 SqlSessionFactory를 셋팅해주고 factoryBean을 반환해준다.

사용자는 빈으로 등록한 testMapper를 getBean하여 불러오고 그 빈객체를 이용하여 쿼리문을 불러올 수 있다.

 

또, mapper는 여러개 있어도 된다. 위의 예제는 한개만 만들었는데 interface이름만 다르게 해서 여러개 만들어도 상관없다.

 

 

여기까지 설정하면 이제 MapperInterface에 사용자가 원하는 쿼리문을 작성해주면 되겠다.

 

 


MapperInterface에 어노테이션을 이용하여 쿼리문을 작성해주면 된다.

@Select("select * from test") List<JdbcVO> select(); 라고 선언해주면 사용자는 select()메소드를 호출함으로서 어노테이션에 선언되어 있는 쿼리문을 실행할 수 있다.

 

위에 @Results, @Result는 왜 주석처리를 했을까???

 

생략해도 되기 때문.

 

주석처리된 구문의 의미는 '데이터베이스의 data1 칼럼에는 지정된 빈의 data1 프로퍼티에 넣겠다', '데이터베이스의 data2 칼럼에는 지정된 빈의 data2 프로퍼티에 넣겠다' 의 의미가 된다.

 

그런데, Mybatis에서는 프로퍼티의 이름과 칼럼의 이름이 같으면 알아서 그냥 주입을 해준다. POJO클래스를 만들 때, 칼럼의 이름과 프로퍼티의 이름을 맞추어 만들어 주면 @Results 을 사용하는 일을 줄일 수 있겠다.

 

 

@Insert("insert into test values(#{data1}, #{data2})") 를 보면, values가 #{data1}와 같이 되어있다.

Spring JDBC에서는 Java에서 사용했던 방식과 똑같이 ?을 넣어 쿼리문을 선언해주고, update()메소드 매개변수에 순서를 맞추어 줌으로써 ?에 값이 1:1대응으로 삽입이 되게 만들어 주었다.

MyBatist도 마찬가지로 #{data1}와 같이 선언하면, void insert(JdbcVO bean); 에서 bean변수의 프로퍼티를 알아서 찾아주어서 data1은 data1에 data2는 data2에 자동으로 주입해준다.

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

Spring Socket에 대해 알아보자! - 1. WebSocket  (0) 2020.06.18
Spring WebSocket에 대한 내용 정리  (0) 2020.06.11
Spring JDBC  (0) 2020.04.20
@AspectJ 어노테이션  (0) 2020.04.17
execution 명시자  (0) 2020.04.17
더보기

JDBC(Java Database Connectivity)는 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API이다. JDBC는 데이터베이스에서 자료를 쿼리하거나 업데이트하는 방법을 제공한다.

 

출처 : 위키백과, JDBC, https://ko.wikipedia.org/wiki/JDBC

 

Spring에서 JDBC를 이용하여 데이터베이스에 접근해볼것이다.

JDBC를 이용하기 위해서는 몇가지 라이브러리가 필요하다. 

 

 

mvnrepository에서 Spring JDBC를 검색하여 Maven코드를 추가해주었다. Spring버전과 일치하는 라이브러리를 사용하는것이 좋다. Spring버전과 일치하는 5.2.5 버전을 사용하였다.

 

 

 

Java에서 jdbc를 사용하는것과 비슷하게 Mysql connector가 필요하다. 

자바에서는 직접 커넥터를 다운로드한다음 프로젝트 빌드에 라이브러리로써 추가해주었던 기억이 난다.

Spring에서 우리는 Maven을 사용하기 때문에 아주 간편하게 의존성만 추가해주면 커넥터를 사용할 수 있다.

 

 

 

DBCP를 사용할 것이다.

 

DBCP란?

데이터베이스와 연결하는 커넥션을 미리 설정해두고 풀에 저장해두었다가 필요할때 꺼내쓰고 사용 후에는 다시 풀에 반환하는 기법을 말한다.

 

 

https://leminity.tistory.com/20#rp

 

jdbc, dbcp 차이?

오늘 일과중에 예전에 학원에서 같이 교육받았던 동생한테서 연락이 와서 이런 저런 얘기를 나누다가 요즘은 뭐 배우고 있냐고 물었더니, JDBC랑 DBCP를 배웠는데 갑자기 DBCP가 더 빠르다고 말한다. 왠지 어감이..

leminity.tistory.com

JDBC와 DBCP의 프로세서에 대해 너무 쉽게 잘 표현해주셔서 이 블로그를 참고하여 이해하였다...

커넥터를 손쉽게 관리하기 위하여 DBCP를 사용한다고 보면 되겠다.

 

 

 

pom.xml에 다음과 같이 dependency를 추가해주었다. version관리를 위해 property로 따로 버전을 빼주었다.

 

 

이제 데이터베이스와의 연동을 위한 코드를 작성할 것이다.

그 전에 미리 데이터베이스와 테이블을 만들어 놓겠다. 만들어준 테이블의 필드 속성에 맞추어서 자바코드를 작성할 예정이다.

데이터베이스는 MySQL을 사용하였고, MySQL Workbench를 이용하여 쿼리문을 작성하였다. 

사용할 필드는 data1(int형), data2(String형). 

 

 

그리고 위의 데이터 타입과 똑같은 데이터를 자바에서도 활용하기 위해서 VO클래스도 만들어주었고, 빈으로 등록했다.

 

 

 

 

 

 

 

 

 

1. 자바 설정파일

 

 

POJO클래스와 DAO클래스를 @Component를 사용하여 빈으로 등록할것이기 때문에 @ComponentScan을 이용하여 다음과 같이 해당 패키지를 스캔하도록 등록하였다.

 

먼저, BasicDataSource클래스의 source메소드를 한개 만들었다.

이 클래스는 위에서 추가해준 라이브러리의 DBCP패키지에 있는 클래스로, 커넥션풀을 관리할 수 있다. 위와같이 setter를 이용하여 Driver와 Url, Username, Password를 설정할 수 있다. 셋팅해준 속성을 리턴해준다.

 

둘째로, JdbcTemplate클래스의 db메소드를 만들었다.

이 클래스는 위에서 추가해준 라이브러리의 jdbc패키지에 있는 클래스이다. 위에서 만든 source메소드를 매개변수로 받아준다. 위와같이 BasicDataSource source로 매개변수를 받으면 빈으로 등록되어 있던 source메소드가 자동으로 매개변수로 들어가게 된다. 그 후, JdbcTemplate 클래스 변수를 만들어서 리턴해준다. 

이제 저 db변수를 사용함으로써 우리는 데이터베이스 커넥션 풀을 얻을 수 있게 되었고, 연결된 커넥션을 활용하여 데이터를 삽입, 삭제, 수정, 조회할 수 있게 된다.

 

 

 

 

 

 

 

 

 

 

2. DAO클래스 (insert, delete, update)

 

 

JdbcDAO라는 이름으로 클래스를 만든다. 우선 insert, update, delete 부터 살펴보자.

설정파일에서 커넥션풀을 함께 가진 JdbcTemplate db를 빈으로 등록해주었었다.

@Autowired하여 타입으로써 해당 빈을 가져와 사용한다.

 

insert, update 메소드는 VO(생각해보니 여기서는 데이터베이스에 접근하는 객체여서 DTO가 더 어울리는것 같다...)를 매개변수로 받아서 입력한 데이터를 데이터베이스에 insert한다.

update메소드를 사용하여 쿼리문을 삽입해준다. 이는 insert, update, delete에 공통적으로 사용한다.

쿼리문에서 ?는 사용자가 직접 넣어주어야한다. update메소드의 매개변수가 ?에 1:1대응으로 삽입된다.

insert문의 첫번째 ?는 bean.getData1(), 두번째 ?는 bean.getData2()가 들어간다.

 

자바 jdbc에서는 insert문을 넣어줄 때, PreparedStatement를 사용해서 인덱스에 대응되는 값을 한개씩 넣어주는 작업을 거쳐야 했다... 그런데 Spring Jdbc에서는 이 작업을 생략해도 알아서 1:1대응으로 삽입시켜준다.

 

delete 메소드는 int형 변수를 매개변수로 받았다. 입력하는 값이 data1의 값과 일치하면 삭제할 수 있도록 하려고 만들어 보았다.

 

 

 

main 클래스이다.

JdbcDAO 클래스를 getBean하여 가져온다. VO는 객체를 만들어서 필요한 것에 넣어주었다.

delete는 data1이 1이면 삭제할 수 있도록 1을 매개변수로 넣었다.

모두 성공적으로 동작함을 확인하였다.

 

 

 

 

 

 

 

 

 

3. select

 

select 를 따로 뺀이유는 Mapper클래스를 사용하기 때문이다.

데이터베이스 테이블의 Row가 한개라면 그냥 가져와도 상관이 없겠지만, 만약 Row가 여러개이고 그 Row의 다양한 type의 데이터가 들어가 있다면 한번에 가져오는데에 무리가 있을 것이다.

Java Jdbc를 사용하여 db를 연결할때 우리는 List형을 사용하여 Row를 담아서 사용하였다. Spring도 다를건 없다.

 

select문을 사용하여 데이터를 가지고 올때 어떤 칼럼의 값을 어느 bean에 주입할 것인지 결정해 주어야 하는데 이 역할을 하는 클래스를 Mapper클래스라고 부른다.

 

 

 

MapperClass를 다음과 같이 정의하였다. MapperClass는 SpringFramework에서 제공하는 RowMapper<T>인터페이스를 구현하여 작성한다. mapRow라는 메소드를 오버라이딩하여 작성한다. VO 객체를 만들어서 그 객체의 setter를 이용하여 데이터를 담아주었다.

Java Jdbc에서 우리는 Select문을 이용할 때, ResultSet을 이용하여 해당 Row에 데이터를 담았던것을 기억한다.

코드만 살짝 다를 뿐. 전체적 개념은 비슷하다는 것을 알 수 있었다.

매번 ResultSet을 이용하여 Row를 담는 작업을 줄여서 MapperClass에 담아 놓고 이 MapperClass를 활용해서 Select하는 것이다..

 

 

 

select부분을 추가하였다.

아까 말한것 처럼 Row가 여러개면 List의 형태로 담아서 활용한다. JdbcVO를 제네릭으로 받는 List형태의 select메소드를 만들었다. select는 다른 작업과 다르게 update()가 아닌 query()를 사용한다.

위에서 만들었던 MapperClass를 빈으로 등록하여 그 빈객체를 query메소드의 매개변수로 받는다. 이렇게 하면 MapperClass에서 했던 mapRow의 작업을 해당 select문에서 적용할 수 있는 것이다.

 

 

 

반환형이 List<JdbcVO> 이었으니까 위와 같이 하면 모든 Row의 값을 select 할 수 있을 것이다.

 

 

 

 

 

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

Spring WebSocket에 대한 내용 정리  (0) 2020.06.11
MyBatis  (0) 2020.04.21
@AspectJ 어노테이션  (0) 2020.04.17
execution 명시자  (0) 2020.04.17
AOP  (0) 2020.04.17

지금까지 Advisor를 사용할때는, Advisor로 호출할 클래스를 bean으로 등록하고 그 Advisor를 등록하여 사용하는 방법을 사용하였다.

@AspectJ를 사용하면 Advisor역할을 할 bean을 어노테이션이 붙어있는 클래스 자체에서 설정할 수 있다.

 

 

@AspectJ를 사용하기 위해서는 xml파일에 네임스페이스를 추가해주어야한다.

 

 

<aop:aspectj-autoproxy /> 를 추가해주면 advisor 클래스에 설정되어 있는 어노테이션을 분석하여 셋팅을 할 수 있게 해준다.

 

 

 

BeanTest1이라는 클래스를 만들고 @Component 를 써주어 빈으로 등록해주었다. 

AdvisorClass 라는 클래스를 만들고 @Component 를 써주어 빈으로 등록해주었다. Advisor역할을 할 빈을 여기에 설정할 예정이다. 컴포넌트 어노테이션을 써주었기 때문에 컴포넌트 스캔을 해주어야 한다.

 

 

이렇게 지정해주고 Advisor클래스에 @AspectJ 어노테이션을 이용해서 설정을 해주겠다.

 

 

 

이런식으로 @AspectJ 를 붙여주고 그 클래스안의 메소드(advice)에 어노테이션을 붙여서 선언해준다.

각각 @Before, @After, @Around, @AfterReturning, @AfterThrowing 이며 속성에는 execution명시자를 넣어서 관심사를 설정해주었다. xml파일에서 설정해준것과 형식은 유사했다.

 

 

 

xml 에서 말고 당연히 java configuration에서도 할 수 있을것이다.

 

 

<aop:aspectj-autoproxy /> xml에서 이렇게 했다면 java에서는 @EnableAspectJAutoProxy 해주면 된다.

 

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

MyBatis  (0) 2020.04.21
Spring JDBC  (0) 2020.04.20
execution 명시자  (0) 2020.04.17
AOP  (0) 2020.04.17
@Component 어노테이션  (0) 2020.04.15

 

execution 명시자는 포인트컷을 명시할 때 어드바이스들이 동작할 수 있도록 해주는 표현식 문법이다.

 

 

출처 : [토비의 스프링 3.1] 6장_AOP_포인트컷 표현식 문법

 

execution 명시자는 위의 그림과 같이 접근제한자, 리턴타입, 클래스이름, 메소드이름, 매개변수 의 순으로 이루어져있다.

접근제한자는 public만 지원하기 때문에 생략한다. 클래스이름은 패키지까지 적어주어야하는데 이 역시도 *(Asterisk) 이면 생략이 가능하다.

패턴에 *이나 ..을 사용하여 줄일 수 있는데 *과 ..의 의미는 다음과 같다.

 

 

*(Asterisk) : 하나의 모든 것을 의미한다.

.. : 개수 상관 없이 모든 것을 의미한다.

 

 

 

어떻게 쓰이는지 xml Configuration 파일을 살펴보자. 

 

 

 

여러가지 상황에 대해서 설정을 해보았다. 

 

expression="execution(void com.study.spring.beans.TestBean2.method1())"

-> 매개변수에 아무것도 설정되어 있지 않기 때문에 매개변수가 없는 void형의 method1 메소드에만 포인트컷이 적용된다.

 

 

expression="execution(void com.study.spring.beans.TestBean2.method1(int))"

-> 매개변수에 int 한개가 들어가 있다. 같은 이름의 메소드 method1이 매개변수만 다르게 여러개가 있으면 그 중에서 매개변수를 int로 받는 advice만 적용된다.

 

 

expression="execution(void com.study.spring.beans.TestBean2.method1(int, int))"

-> 매개변수에 int 두개가 들어가 있다. 매개변수의 개수까지 철저하게 지키기 때문에 위에처럼 int 한개만 받는 advice는 적용이 안되고 두개가 들어가 있어야 적용된다.

 

 

expression="execution(void com.study.spring.beans.TestBean2.method1(*))"

-> *는 모든 타입을 받지만 갯수는 한개! 예를들어 매개변수가 java.lang.String (String은 자바에서 클래스이기 때문에 이렇게 입력해주어야 한다.) 이면 적용이되지만 int, double 이렇게 두개면 적용이 안된다.

 

 

expression="execution(void com.study.spring.beans.TestBean2.method1(..))"

-> ..는 타입, 갯수 상관없이 모든 매개변수를 받는다. method1이라는 메소드에 매개변수의 타입이 여러 형태이고 갯수가 여러개여도 상관이 없다.

 

 

expression="execution(void com.study.spring.beans.TestBean2.*(..))"

-> 메소드부분이 *처리가 되어 있다. com.study.spring.beans.TestBean2 라는 클래스에 있는 모든 메소드에 어떤 매개변수가 들어가도 다 적용할 수 있다는 뜻이 되겠다.

 

 

expression="execution(void *.*(..))"

-> 클래스부분까지 *처리가 되어 있다. 반환형이 void인 모든 패키지의 모든 클래스의 모든 메소드에 어떤 매개변수가 들어가도 다 적용을 할 수 있다는 뜻이다.

 

 

expression="execution(* *.*(..))"  /  expression="execution(* *(..))" 
-> 반환타입까지 *처리가 되어 있다. 모든 반환타입, 모든 패키지, 모든 클래스, 모든 메소드, 모든 매개변수에 다 적용된다. 클래스는 *이면 생략할 수 있기 때문에 후자의 형태로 적어도 상관이 없다.

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

Spring JDBC  (0) 2020.04.20
@AspectJ 어노테이션  (0) 2020.04.17
AOP  (0) 2020.04.17
@Component 어노테이션  (0) 2020.04.15
JSR-250 어노테이션  (0) 2020.04.11

AOP이란?  Aspect Oriented Programming : 관점 지향 프로그래밍 

 

하나의 프로그램을 관점(관심사)라는 논리적인 단위로 분리하여 관리하는 개념이다. 여기에서 관심사란 메소드 호출이나 특정주소를 요청하는 등의 행위들을 말한다. 로깅, 감사, 선언적 트렌젝션, 보안, 캐싱 등 다양한 곳에서 사용이 된다. 

AOP는 원래 실행되는 코드는 건드리지 않고 AOP를 사용함으로써 모듈을 삽입,호출하는 것에 의의가 있다.

 

 

 

AOP 동작과정

 

왼쪽의 메소드 호출이라고 되어있는 부분이 바로 관심사 부분이 되겠다. 메소드 호출하는 행위자체를 관심사로 등록했다면 호출된 메소드의 전, 후로 모듈을 삽입하여 사용하는 것이 기본적인 AOP의 개념이자 동작과정이다.

 

조금 더 쉽게 생각해보면 특정 메소드를 호출하게 되면 그 호출을 가로채서 다른 메소드를 먼저 호출하거나, 아니면 호출된 메소드 이후에 호출하거나 .. 섞어서 사용할 수 있겠다.

 

웹 프로젝트에서는 특정 웹 페이지를 요청하였을 때, 로그인 여부를 검사하게 하는 것처럼 요청이 발생할 때마다 공통적으로 처리해야할 것이 있을 경우에 사용할 수 있을 것이다!

 

 

 

AOP에서 사용되는 여러가지 용어에 대해서 알아보자. 위의 AOP 동작과정 그림기반으로 용어를 생각하면 이해가 빠르다.

 

- Join Point : 모듈이 삽입되어 동작하는 실제 위치이다. 관심사가 메소드 호출이라면 위의 그림에서 왼쪽 메소드 호출 부분이 Join Point가 될 것이다.

 

- Point Cut : 다양한 Join Point중에서 어떤 것을 사용할 것인지 선택하는 것이다. Advice를 적용할 조인 포인트를 선별하는 작업 또는 그 기능을 위한 모듈을 말한다. 즉, 메소드를 선정하는 기능을 가진것을 의미한다.

 

- Advice : 타겟에게 제공할 부가기능을 담은 모듈을 말한다. 다시말해, Join Point가 벌어질 때 동작할 코드라고 할 수 있겠다.

 

- Advisor : Point Cut과 Advice를 가지고 있는 오브젝트이다. 어떤 부가기능(Advice)를 어디에(Point Cut) 전달할 것인가를 알고 있는 모듈이라고 할 수 있다.

 

- Weaving : Advice를 핵심 로직 코드에 적용하는것을 말한다. 메소드를 호출하는 사건이 벌어졌을 때 메소드 전,후에 코드를 삽입해서 하나의 동작으로 만드는 행위를 말한다.

 

- Aspect : 한개 또는 그 이상의 Point Cut과 Advice조합으로 만들어진 오브젝트이다. Advisor는 아주 단순한 Aspect라고 볼 수 있다.

 

 

 

 

실제로 동작하는 메소드(메소드의 호출이 관심사이며 Point Cut으로 인해 Advice가 동작한다는 가정)인 Advice의 종류에 대해서 알아보자.

 

- before : 타겟 메소드 호출 전에 동작하는 Advice이다.

 

- after : 예외발생 여부와 관계 없이 타겟 메소드의 동작이 완료되면 동작하는 Advice이다.

 

- around : 타겟 메소드 호출 전, 후에 동작하는 Advice이다. 

 

- after-returning : 예외 없이 호출된 메소드의 동작이 완료되면 동작하는 Advice이다. 예외가 없어야 동작한다.

 

- after-throwing : 타겟 메소드 동작 중 예외가 발생하면 동작하는 Advice이다.

 

 

 

지금가지 알아본 AOP의 용어들을 실제로 코드에서 어떻게 사용되는지 알아보자.

우선, AOP사용을 위한 의존성을 추가해주어야 하며 aop네임스페이스도 선언해주어야 한다.

 

 

 

mvnrepository에서 AspectJ Weaver를 검색한 후 maven코드를 복사해준다.

 

 

Version관리를 위해 버전을 xml property에 설정해주고 변수로 Version을 설정해주었다. 

 

 

xml configuration 파일에 위와 같은 네임스페이스를 추가해주었다. xmlns:aop와 xsi:schemaLocation에 aop부분을 추가한다.

 

 

AdvisorClass라는 클래스를 위와 같이 정의해준다. Advisor종류에서 살펴보았던 before, after, around를 사용해보겠다. 각각 beforeMethod, afterMethod, aroundMethod라는 이름으로 Advisor를 정의해주었다. 

 

around는 다른 2개와 조금 다르게 생겼다?

around는 ProceedingJoinPoint라는 클래스를 매개변수로 받는다. 예외발생을 하여 thorws Throwable도 해준다.

before과 after는 실행위치가 명확하다. 타겟 메소드의 전과 후.

그런데 around는 전과 후의 경계가 약간 애매하다.. 그래서 타겟 메소드(원래 호출할 메소드)의 위치를 직접 정해준다.

 

위에서 한것 처럼 pjp.proceed() 하면 원래 메소드가 호출된다. 

타겟 메소드가 반환값이 없으면 상관이 없는데 반환값이 있을수도 있겠다. Object 형 변수로 받아서 반환을 해주자.

 

 

 

xml Configuration 파일이다. 방금 설정한 Advice를 담고있는 AdvisorClass를 advisor1이라는 이름의 빈으로 등록해준다.

aop:aspect를 advisor1을 참조하게 설정함으로써 advisor1 애스펙트가 만들어졌고, point1이라는 이름으로 Point Cut한개를 만들어 주었다.

expression은 관심사를 설정한다. 위와같이 execution(* method1())로 설정하면 패키지,클래스와 상관없이 method1이라는 메소드가 실행되는 행위를 관심사로 설정하며 이를 실행하는 것을 포인트 컷으로 설정한다는 의미이다. execution명시자에 대해서는 다음 포스트에서 더 자세하게 설명하겠다.

 

AdvisorClass에서 정의한 메소드를 aop:before, aop:after, aop:around 를 사용해서 가져온다. point1이라고 설정해주었던 Point Cut을 pointcut-ref를 해서 참고(사용)한다.

 

 

 

타겟메소드는 method1()이다. bean1.method1()을 해줄 때 TestBean1이라는 이름의 빈안에 있는 method1() 메소드를 불러왔다. 

 

 

'metho1 메소드'라는 문자열을 찍어주는 method1() 메소드 앞뒤로 before, after가 실행됨을 확인할 수 있으며 method1()메소드를 감싸는 around메소드가 출력된것을 확인하였다. 함께 찍어봄으로써 before, after보다 around가 더 타겟메소드와 가깝게 실행된다는것을 알 수 있었다.

 

 

 

 

 

 

이번에는 afterReturning과 afterThorwing 을 알아보자. 

AdvisorClass에 위와 같이 메소드 두개를 추가해주었다. afterThrowingMethod는 예외가 발생할 때 동작하는 Advice이기 때문에 매개변수로 Throwable을 받고 예외문을 출력까지 해보겠다.

 

 

 

 

 

point2라는 이름의 Point Cut을 한개 더 선언했다. 이번에는 위의 예제와 구분하기 위해서 method2를 만들었고, expression="execution(* method2())"를 선언해줌으로써 관심사를 '패키지와 상관없이 method2()를 호출하는 행위' 로 설정하였다. 

aop:after-returning, aop:after-throwing을 선언했다. aop:after-throwing는 예외로 던져주었던 변수 e를 throwing="e"로 선언해주어야 한다. 

 

 

 

method2()의 반환값이 int형이기 때문에 다음과 같이 출력해주었다. method2()의 반환값이 오류가 없다면(result=100)

 

 

 

afterReturningMethod메소드가 출력되고,

오류가 있다면(result =10/0) 

 

 

예외문과 함께 afterThrowingMethod 메소드가 출력된다

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

@AspectJ 어노테이션  (0) 2020.04.17
execution 명시자  (0) 2020.04.17
@Component 어노테이션  (0) 2020.04.15
JSR-250 어노테이션  (0) 2020.04.11
Annotation을 이용한 빈 설정  (0) 2020.04.11

+ Recent posts