[포스코DX|Spring|7주] 30일차 수업
포스코DX X 비트교육센터 6기 - Spring
AOP
cross concern 횡단 관심을 바깥으로 들어내서 없애버리는 것.
- aoptest -> jar 파일로 만듦.
- 폴더 구조
- pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.poscodx</groupId>
<artifactId>spring-practices</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>aoptest</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<org-springframework-version>5.3.25</org-springframework-version>
</properties>
<dependencies>
<!-- spring mvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org-springframework-version}</version>
</dependency>
<!-- spring aspect -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${org-springframework-version}</version>
</dependency>
</dependencies>
<build>
<finalName>aoptest</finalName>
</build>
</project>
- applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!-- auto proxy -->
<aop:aspectj-autoproxy />
<context:annotation-config />
<context:component-scan base-package="service, aspect">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />
</context:component-scan>
</beans>
- mainApp.java
package main;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.ProductService;
import vo.ProductVo;
public class MainApp {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("config/applicationContext.xml");
ProductService ps = ac.getBean(ProductService.class);
ProductVo vo = ps.find("TV");
System.out.println(vo);
((AbstractApplicationContext)ac).close();
}
}
- productService
package service;
import org.springframework.stereotype.Service;
import vo.ProductVo;
@Service
public class ProductService {
public ProductVo find(String keyword) {
ProductVo vo = new ProductVo(keyword);
return vo;
}
}
- ProductVo.java
package vo;
public class ProductVo {
private String name;
public ProductVo(String name) {
this.name = name;
}
@Override
public String toString() {
return "ProductVo [name=" + name + "]";
}
}
- 결과
- MyAspect.java의 추가
package com.poscodx.aoptest.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAspect {
@Before("execution(public com.poscodx.aoptest.vo.ProductVo com.poscodx.aoptest.service.ProductService.find(String))") //해당 함수가 실행되기전 실행해라!!
public void adviceBefore() {
System.out.println("----before advice----");
}
//접근 지시자는 생략 가능!
@After("execution(com.poscodx.aoptest.vo.ProductVo com.poscodx.aoptest.service.ProductService.find(String))") //해당 함수가 실행되기전 실행해라!!
public void adviceAfter() {
System.out.println("----before advice----");
}
}
- Spring Exception의 catch 문을 없애기 위해 사용하는 스타일
[ 첫 번째 ]
//ProductService.java
package com.poscodx.aoptest.service;
import org.springframework.stereotype.Service;
import com.poscodx.aoptest.vo.ProductVo;
@Service
public class ProductService {
public ProductVo find(String keyword) {
ProductVo vo = new ProductVo(keyword);
if( 1 - 1 == 0) {
throw new RuntimeException("ProductService.find() Exception");
}
return vo;
}
}
- main 실행
[ 두 번째 ]
-
강력한 에러 처리!!
-
MyAspect.java
@Around("execution(* *..*.ProductService.*(..))")
public Object adviceAround(ProceedingJoinPoint pjp) throws Throwable {
/*before*/
System.out.println("----- Around(before) Advice ----");
Object result = pjp.proceed();
/* after */
System.out.println("----- Around(after) Advice ----");
return result;
}
- 강력함을 보여주는 예시
@Around("execution(* *..*.ProductService.*(..))")
public Object adviceAround(ProceedingJoinPoint pjp) throws Throwable {
/*before*/
System.out.println("----- Around(before) Advice ----");
/* Point Cut Method 실행 */
Object[] params = {"Camera"};
Object result = pjp.proceed(params);
//Object result = pjp.proceed();
/* after */
System.out.println("----- Around(after) Advice ----");
return result;
}
실행 시간 알기
- pom.xml 추가
<!-- spring aspect -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${org-springframework-version}</version>
</dependency>
- repository에 추가
public List<GuestbookVo> findAll() {
StopWatch sw = new StopWatch();
sw.start();
List<GuestbookVo> result = sqlSession.selectList("guestbook.findAll");
sw.stop();
long totalTime = sw.getTotalTimeMillis();
System.out.println("--->" + totalTime);
return result;
}
처음에는 로딩이 오래걸리지만,
캐시를 기억하고있기 때문에 두번째 이상부터는 빠른 속도로 실행됨.
실행시간 aspect 패키지로 빼기
- spring-servlet.xml 과 applicationContext.xml 에 다음 내용을 추가
<!-- auto proxy -->
<aop:aspectj-autoproxy />
- 파일 구조
- 내용 작성
package com.poscodx.mysite.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Component
@Aspect
public class MeasureExecutionTimeAspect {
@Around("execution(* *..*.repository.*.*(..))") //모든 레파지토리의 모든 메소드
public Object adviceAround(ProceedingJoinPoint pjp) throws Throwable{
//before
StopWatch sw = new StopWatch();
sw.start();
Object result = pjp.proceed();
//after
sw.stop();
long totalTime = sw.getTotalTimeMillis();
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
String taskName = className + "." + methodName;
System.out.println("[Execution Time][" + taskName + "] " + totalTime + "mills");
return result;
}
}
- sql 사용할 때, 측정
@Around("execution(* *..*.repository.*.*(..)) || execution(* *..*.service.*.*(..)) || execution(* *..*.controller.*.*(..))")
이렇게 바꿔보기도 하고~
************* 이게 운영 일임!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
에러 페이지
- web.xml
<!-- 공통 에러 페이지 -->
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/views/error/404.jsp</location>
</error-page>
- sql에서 에러 페이지를 이용하는 방법. (try - catch 안에서)
+ 404페이지 : 없는 페이지
+ 500페이지 : 예기치 못한 오류 (ex. SQL문 에러)
- spring에서는 runtime error를 사용한다. / controller에 옮길 때, runtime exception으로 전환하다. / 한곳에서 aop사용해서 처리한다.
//assets /WEB-INF/ views /error/exception.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Exeption Thrown</h1>
<p>
${errors }
</p>
</body>
</html>
spring-servlet.xml 에 추가
<context:component-scan base-package="com.poscodx.mysite.controller, com.poscodx.mysite.exception" />
package com.poscodx.mysite.exception;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handlerException(Model model, Exception e) {
//1. 로깅(Logging)
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
System.out.println(errors.toString());
//2. 사과 페이지
model.addAttribute("errors", errors.toString());
return "error/exception";
}
}
package com.poscodx.mysite.exception;
public class UserRepositoryException extends RuntimeException {
public UserRepositoryException() {
super("UserRepositoryException Thrown");
}
public UserRepositoryException(String message) {
super(message);
}
}