공통 로직을 여러번 작성하지 않고 한번만 작성해서 처리

예를 들어 detail, modify, delete 등 각 메소드마다 찍어줬던 로그를 aop로 한번만 작성해서 처리할 수 있음 (공통 로직이니까)
어드바이스 동작 시점

어드바이스 동작 순서
@Around -> @Before -> @After
--> around는 특별한 경우 아니면 안쓰는게 좋다. 이전에도 동작하고 이후에도 동작하기 땜에 이전과 이후에 모두 호출해야 하는 경우에 사용
표현식(Expression) 작성 방법
1) 형식
execution(반환타입 패키지.클래스.메소드(매개변수))
2) 의미
(1) 반환타입
① : 모든 반환타입
② void : void 반환타입
③ !void : void를 제외한 반환타입
(2) 매개변수
① .. : 모든 매개변수
② : 1개의 모든 매개변수
@Slf4j // private static final Logger log = LoggerFactory.getLogger(BeforeAop.class);
@Aspect
@Component
public class BeforeAop {
// 포인트컷 : 언제 동작하는가?
@Pointcut("execution(* com.gdu.app10.controller.*Controller.*(..))")
public void setPointCut() { } // 이름만 제공하는 메소드(이름은 마음대로 본문도 필요 없다.)
// 어드바이스 : 무슨 동작을 하는가?
@Before("setPointCut()")
public void beforeAdvice(JoinPoint joinPoint) {
/*
* Before 어드바이스
* 1. 반환타입 : void
* 2. 메소드명 : 마음대로
* 3. 매개변수 : JoinPoint
*/
/* 모든 컨트롤러의 모든 메소드가 동작하기 전에 요청(방식/주소/파라미터) 출력하기 */
// 1. HttpServletRequest (그동안과 달리 받아오거나 선언되어 있는 상황 아니라서 리퀘스트 받아오지 못함. 그래서 구구절절 써준거)
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
// 2. 요청 파라미터 -> Map 변환 (파라미터 종류가 다 다르므로)
Map<String, String[]> map = request.getParameterMap();
// 3. 요청 파라미터 출력 형태 만들기
String params = "";
if(map.isEmpty()) {
params += "No Parameter";
} else {
for(Map.Entry<String, String[]> entry : map.entrySet()) {
params += entry.getKey() + ":" + Arrays.toString(entry.getValue()) + " ";
}
}
// 4. 로그 찍기 (치환 문자 {} 활용)
log.info("{} {}", request.getMethod(), request.getRequestURI()); // 요청 방식, 요청 주소
log.info("{}", params); // 요청 파라미터
}
}
@Aspect를 붙여준다.
현재 실행할 코드

로그찍혔다.
완성 코드 결과 아님. 완성 코드는 detail:6을 지우고 Before 어드바이스 동작으로 대체하게 된다. 지금은 before 어드바이스 타이밍 본 것.
@Slf4j
@Aspect
@Component
public class AfterAop {
// 포인트컷 : 언제 동작하는가?
@Pointcut("execution(* com.gdu.app10.controller.*Controller.*(..))")
public void setPointCut() { }
// 어드바이스 : 무슨 동작을 하는가?
@After("setPointCut()")
public void afterAdvice(JoinPoint joinPoint) {
/*
* After 어드바이스
* 1. 반환타입 : void
* 2. 메소드명 : 마음대로
* 3. 매개변수 : JoinPoint
*/
// 로그 찍기
log.info("==================================================================");
}
}
@Slf4j
@Aspect
@Component
public class AroundAop {
// 포인트컷 : 언제 동작하는가?
@Pointcut("execution(* com.gdu.app10.controller.*Controller.*(..))")
public void setPointCut() { }
// 어드바이스 : 무슨 동작을 하는가?
@Around("setPointCut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
/*
* Around 어드바이스
* 1. 반환타입 : Object
* 2. 메소드명 : 마음대로
* 3. 매개변수 : ProceedingJoinPoint
*/
log.info("=================================================================="); // 포인트컷 실행 이전에 실행(@Before 이전에 동작)
Object obj = proceedingJoinPoint.proceed(); // 포인트컷이 실행되는 시점
log.info("{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())); // 포인트컷 실행 이후에 실행(@After 이전에 동작)
return obj;
}
}
@EnableAspectJAutoProxy
@Configuration
public class AppConfig {
// DataSource : CP(Connection Pool)을 처리하는 javax.sql.DataSource 인터페이스
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource(); // DriverManagerDataSource : CP(Connection Pool)을 처리하는 스프링 클래스
dataSource.setDriverClassName("net.sf.log4jdbc.sql.jdbcapi.DriverSpy");
dataSource.setUrl("jdbc:log4jdbc:oracle:thin:@localhost:1521:xe");
dataSource.setUsername("GD");
dataSource.setPassword("1111");
return dataSource;
}
// JdbcTemplate : Jdbc를 처리하는 스프링 클래스(Connection, PreparedStatement, ResultSet 처리 담당)
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
// TransactionManager : 트랜잭션을 처리하는 스프링 인터페이스
@Bean
public TransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
/* 지금부터 AOP를 이용한 트랜잭션 처리를 위해 필요한 Bean */
// TransactionInterceptor : 트랜잭션 처리를 위해 언제 rollback 할 것인지 정의하는 스프링 클래스
@Bean
public TransactionInterceptor transactionInterceptor() {
// 규칙
RuleBasedTransactionAttribute ruleBasedTransactionAttribute = new RuleBasedTransactionAttribute();
ruleBasedTransactionAttribute.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
MatchAlwaysTransactionAttributeSource matchAlwaysTransactionAttributeSource = new MatchAlwaysTransactionAttributeSource();
matchAlwaysTransactionAttributeSource.setTransactionAttribute(ruleBasedTransactionAttribute);
// 반환
return new TransactionInterceptor(transactionManager(), matchAlwaysTransactionAttributeSource);
}
// Advisor : 트랜잭션 동작을 언제 할 것인지 결정하는 스프링 인터페이스
@Bean
public Advisor advisor() {
// 포인트컷
AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
aspectJExpressionPointcut.setExpression("execution(* com.gdu.app10.service.*Impl.*(..))");
// 어드바이스 반환
return new DefaultPointcutAdvisor(aspectJExpressionPointcut, transactionInterceptor());
}
}
앞으로도 트랜잭션은 무조건 하겠다고 가정하고 이 세개의 빈은 항상 가지고 다니자
// DataSource : CP(Connection Pool)을 처리하는 javax.sql.DataSource 인터페이스
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource(); // DriverManagerDataSource : CP(Connection Pool)을 처리하는 스프링 클래스
dataSource.setDriverClassName("net.sf.log4jdbc.sql.jdbcapi.DriverSpy");
dataSource.setUrl("jdbc:log4jdbc:oracle:thin:@localhost:1521:xe");
dataSource.setUsername("GD");
dataSource.setPassword("1111");
return dataSource;
}
// JdbcTemplate : Jdbc를 처리하는 스프링 클래스(Connection, PreparedStatement, ResultSet 처리 담당)
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
// TransactionManager : 트랜잭션을 처리하는 스프링 인터페이스
@Bean
public TransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}

appconfig 계속 하는중,,,
contactserviceImpl도 하는중,,,
@RequiredArgsConstructor // private final ContactDao contactDao;에 @Autowired를 하기 위한 코드이다.
@Service // ContactService 타입의 객체(Bean)을 Spring Container에 저장한다.
public class ContactServiceImpl implements ContactService {
private final ContactDao contactDao;
@Override
public int addContact(ContactDto contactDto) {
int addResult = contactDao.insert(contactDto);
return addResult;
}
@Override
public int modifyContact(ContactDto contactDto) {
int modifyResult = contactDao.update(contactDto);
return modifyResult;
}
@Override
public int deleteContact(int contact_no) {
int deleteResult = contactDao.delete(contact_no);
return deleteResult;
}
@Override
public List<ContactDto> getContactList() {
return contactDao.selectList();
}
@Override
public ContactDto getContactByNo(int contact_no) {
return contactDao.selectContactByNo(contact_no);
}
@Override
public void txTest() {
// AOP를 활용한 트랜잭션 처리 테스트 메소드
// "성공1개+실패1개" DB처리를 동시에 수행했을 때 모두 실패로 되는지 확인하기
// 성공
contactDao.insert(new ContactDto(0, "이름", "전화번호", "이메일", "주소", null));
// 실패
contactDao.insert(new ContactDto()); // NAME 칼럼은 NOT NULL이므로 전달된 이름이 없으면 Exception이 발생한다.
}
}
aop랑 트랜잭션 수업을 같이 해서 이제 트랜잭션 수업도 끝났다.
동작할 패키지만 바꿔주면 된다. 앞으로는..
단점이라면 트랜잭션 안돌아도 될 메소드 포함 모든 메소드에서 트랜잭션이 돌고 있다는 것. 이걸 구분할거면 안 돌 메소드, 돌 메소드 나눠서 트랜잭션 표시를 해주면 되는데(나중에 배움) 이럴거면 aop를 할 필요가 없ㄷ아
mybatis 쓸 때 spring-jdbc도 가져다쓰고 부트에서 기본으로 채택한 커넥션 풀인 hikariCP (기존엔 Datasauce 썼었음) 이용할거임, 커넥션 얻어내는 애 -> 디펜던시 추가 작업 생략
hikariCP 3.4.5
mybatis 3.5.13
mybatis spring 2.0.7