일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- interrupt()
- include액션태그
- 동기화
- InputDialog
- char[] String 형변환
- ThreadGroup()
- 아이디중복
- first-of-child
- 상관서브쿼리
- 리눅스셋팅
- StringReader
- Linux셋팅
- Daemon()
- interrupted()
- isinterrupted()
- sleep()메소드
- ID중복
- StringWriter
- include지시자
- 리눅스세팅
- 메모리스트림
- String char[] 형변환
- 표현 언어
- include 지시자
- first-child
- 상관 서브 쿼리
- ObjectInputStream
- Linux세팅
- 스레드그룸
- MemoryStream
- Today
- Total
다연이네
[days16] AOP와 트랜잭션 본문
AOP
AOP는 '관심사의 분리'를 추구한다. 예를 들어 나눗셈을 구현한다고 하면 '핵심 로직'은 두 개의 숫자를 나누는 것이지만, '주변 로직'은 0을 나누는 것ㅇ이 아닌지 등을 체크하는 것이다.
AOP는 과거에 개발자가 작성했던 '관심사 + 비즈니스 로직'을 분리해서 별도의 코드로 작성하도록 하고, 실행할 때 이를 결합하는 방식으로 접근한다. 실제 실행은 결합된 상태의 코드가 실행되므로 개발자들은 핵심 비즈니스 로직에만 근거하여 코드를 작성하고, 나머지는 어떤 관심사들과 결합할 것인지를 설정하는 것 만으로 모든 개발을 마칠 수 있다.
예제
1. pom.xml에 스프링 버전과 AOP 버전을 수정, 라이브러리 추가
- 스프링AOP는 AspectJ라는 라이브러리의 도움을 많이 받음
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.zerock</groupId>
<artifactId>controller</artifactId>
<name>ex04</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.8</java-version>
<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
<org.aspectj-version>1.9.0</org.aspectj-version>
<org.slf4j-version>1.7.25</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<!-- p.54 <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId>
<version>1.2.15</version> <exclusions> <exclusion> <groupId>javax.mail</groupId>
<artifactId>mail</artifactId> </exclusion> <exclusion> <groupId>javax.jms</groupId>
<artifactId>jms</artifactId> </exclusion> <exclusion> <groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId> </exclusion> <exclusion> <groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId> </exclusion> </exclusions> <scope>runtime</scope>
</dependency> -->
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<!-- <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId>
<version>2.5</version> <scope>provided</scope> </dependency> -->
<!-- p.111 -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test p.54 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- p.54 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
<scope>provided</scope>
</dependency>
<dependency><!-- log4j.xml 설정 필요(자동) -->
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- p.83 https://github.com/brettwooldridge/HiKariCP -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.2</version>
</dependency>
<!-- p 90 -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<!-- <version>3.4.6</version> -->
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<!-- <version>1.3.2</version> -->
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- p 101 -->
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
<version>1.16</version>
</dependency>
<!-- p 147 jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
<!-- p 149 -->
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- p 357 -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.12.1</version>
</dependency>
<!-- p 357 Java 인스턴스를 JSON 타입의 문자열로 변환 -->
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<!-- AOP 중요 -->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz-jobs -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${org.springframework-version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 서비스 계층 설계
SampleService 인터페이스
package org.zerock.service;
public interface SampleService {
public Integer doAdd(String str1, String str2)throws Exception;
}
SampleServiceImpl
package org.zerock.service;
import org.springframework.stereotype.Service;
@Service
public class SampleServiceImpl implements SampleService {
@Override
public Integer doAdd(String str1, String str2) throws Exception {
return Integer.parseInt(str1) + Integer.parseInt(str2);
}
}
3. Advice 작성
위의 SampleServiceImpl 코드를 보면 기존에 항상 하던 log.info()가 빠져있다. 지금까지 해왔던, 로그를 기록해 오던 일은 '반복적이면서 핵심 로직도 아니고, 필요하기는 한 기능'이기 때문에 '관심사'라고 볼 수 있다.
로그를 기록해주는 LogAdvice를 설계해보자
org.zerock.aop에 LogAdvice.java 생성
package org.zerock.aop;
import java.util.Arrays;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import lombok.extern.log4j.Log4j;
@Aspect //보조 기능을 하는 구현 클래스
@Log4j //로그 기록
@Component // 컴포넌트 스캔
public class LogAdvice {
@Before( "execution(* org.zerock.service.SampleService*.*(..))") //AspectJ 문법
public void logBefore() {
log.info("========================");
}
@Before("execution(* org.zerock.service.SampleService*.doAdd(String, String)) && args(str1, str2)")
public void logBeforeWithParam(String str1, String str2) {
log.info("str1: " + str1);
log.info("str2: " + str2);
}
//before두개면 다 실행
@AfterThrowing(pointcut = "execution(* org.zerock.service.SampleService*.*(..))", throwing="exception")
public void logException(Exception exception) {
log.info("Exception....!!!!");
log.info("exception: "+ exception);
}
@Around("execution(* org.zerock.service.SampleService*.*(..))")
public Object logTime( ProceedingJoinPoint pjp) {
long start = System.currentTimeMillis();
log.info("Target: " + pjp.getTarget());
log.info("Param: " + Arrays.toString(pjp.getArgs()));
//invoke method
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
log.info("TIME: " + (end - start));
return result;
}
}
4. AOP 설정
스프링 프로젝트에 AOP를 설정하는 것은 스프링 2버전 이후에는 간단히 자동으로 Proxy 객체를 만들어주는 설정을 추가해주면 된다.
프로젝트의 root-context.xml에서 Namespaces에 aop와 context를 추가하고 아래 코딩을 추가하자
<!-- p.455 -->
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="org.zerock.service" />
<context:component-scan base-package="org.zerock.aop" />
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
컴포넌트 스캔을 통해 패키지들을 스캔하고, 이 과정에서 SampleServiceImpl 클래스와 LogAdvice가 스프링의 빈으로 등록된다. <aop:aspectj-autoproxy>를 이용해 LogAdvice에 설정한 @들이 동작하게 된다.
-Java 설정을 이용하는 경우 p456
5. AOP 테스트
정상적인 상황이라면 SampleServiceImpl, LogAdvice는 같이 묶여서 자동으로 Proxy 객체가 생성된다.
테스트 폴더에 SampleServiceTests 추가
package org.zerock.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@Log4j
@ContextConfiguration({ "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
//Java설정의 경우
//@ContextConfiguration(classes= {RootConfig.class})
public class SampleServiceTests {
//@Setter(onMethod = @__({ @Autowired }))
@Setter(onMethod_ = @Autowired)
private SampleService service;
@Test
public void testClass() {
//보조기능 출력
log.info(service);
log.info(service.getClass().getName());
}
@Test
public void testAdd() throws Exception {
log.info(service.doAdd("123", "456"));
}
@Test
public void testAddError() throws Exception {
log.info(service.doAdd("123", "ABC"));
}
}
스프링에서 트랜잭션 관리
스프링 트랜잭션 설정은 AOP와 같이 XML을 이용해서 설정하거나 어노테이션을 이용해서 설정이 가능하다.
1. pom.xml에 라이브러리 추가 - spring-jdbc, spring-tx, mybatis, mybatis-spring, hikari 등
(위 pom.xml과 동일)
2. root-context.xml에는 Namespaces 탭에서 tx 항목 체크 아래 태그 등록
<tx:annotation-driven />
: 어노테이션 기반으로 트랜잭션을 설정할 수 있도록 함
-Java 설정을 이용한 트랜잭션 설정 p470
3. 예제 테이블 생성
create table tbl_sample1(col1 varchar2(500));
create table tbl_sample2(col2 varchar2(50));
Sample1MApper - tbl_sample1 테이블에 데이터 삽입하는 메소드
package org.zerock.mapper;
import org.apache.ibatis.annotations.Insert;
public interface Sample1Mapper {
@Insert("insert into tbl_sample1 (col1) values (#{data}) ")
public int insertCol1(String data);
}
Sample2Mapper - tbl_sample2 테이블에 데이터 삽입하는 메소드
package org.zerock.mapper;
import org.apache.ibatis.annotations.Insert;
public interface Sample2Mapper {
@Insert("insert into tbl_sample2 (col2) values (#{data}) ")
public int insertCol2(String data);
}
4. 비즈니스 계층과 트랜잭션 설정
트랜잭션은 비즈니스 계층에서 이루어지므로, org.zerock.service 계층에서 Sample1Mapper, Sample2Mapper를 사용하는 SampleTxService, SampleTxServiceImpl 클래스 설계
SampleTxService
package org.zerock.service;
public interface SampleTxService {
public void addData(String value);
}
SampleTxServiceImpl
package org.zerock.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.zerock.mapper.Sample1Mapper;
import org.zerock.mapper.Sample2Mapper;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@Service
@Log4j
public class SampleTxServiceImpl implements SampleTxService {
@Setter(onMethod_ = { @Autowired })
private Sample1Mapper mapper1;
@Setter(onMethod_ = { @Autowired })
private Sample2Mapper mapper2;
@Transactional
@Override
public void addData(String value) { //마크: 내부적으로 AOP 처리된다는 의미
log.info("mapper1....................");
mapper1.insertCol1(value);
log.info("mapper2.....................");
mapper2.insertCol2(value);
log.info("end..........................");
//insert 작업을 2번 하는 동안 @트랜잭션 걸었음
// 하나라도 잘못되면 전부 롤백되는지 확인 (O)
}
}
5. Test
package org.zerock.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@Log4j
@ContextConfiguration({ "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
//Java설정의 경우
//@ContextConfiguration(classes= {RootConfig.class})
public class SampleTxServiceTests {
@Setter(onMethod_ = { @Autowired })
private SampleTxService service;
@Test
public void testLong() {
/*
String str ="Starry\r\n" +
"Starry night\r\n" +
"Paint your palette blue and grey\r\n" +
"Look out on a summer's day";
*/
String str ="Starry";
log.info("길이: "+str.getBytes().length);
service.addData(str);
//위 글이 50바이트보다 크면 샘플2에 안들어가져서 트랜잭션에 의해 둘 다 롤백
//작으면 커밋 (두 테이블 모두 잘 들어감)
}
}
@Transaction 어노테이션에 의해 트랜잭션 처리가 잘 된다.
'Spring' 카테고리의 다른 글
[days16] 파일 업로드 처리 (0) | 2021.02.24 |
---|---|
[days16] 댓글과 댓글 수에 대한 처리 (0) | 2021.02.24 |
[days15] Ajax 댓글 처리 (0) | 2021.02.23 |
[days15] REST 방식으로 전환 (0) | 2021.02.23 |
[days14] 페이징 처리 및 [검색 기능] 구현 (2/2) (0) | 2021.02.22 |