728x90
300x250
[Spring-Framework] 37. Spring-JDBCTemplate - 트랜젝션 (어노테이션, Java - AOP)


이번에 소개할 내용은 지난 36번 글에 이어서 AOP를 추가로 적용한 방법에 대해 소개하려고 한다.

어노테이션 방식의 트랜젝션 구축이 이해가 되지 않으면, 이전 글을 참고하면 도움이 된다.


1. [Spring-Framework] 36. Spring-JDBCTemplate - 트랜젝션 (어노테이션, Java 설정), 2020-10-10

- https://yyman.tistory.com/1461


IDE: Eclipse 2020-06

- Spring Framework 4.2.4 Releases

- Spring-JDBC

- Spring-TX

- Spring-Core

- AspeetJ Weaver

- AspectJ


DB: Oracle Databases 11g (Express Edition)



1. 프로젝트 구성도(비교)


AOP 작업을 추가한 프로젝트와 기존의 프로젝트의 구성을 비교해놓은 것이다.

이 글에서는 이전의 작성된 객체 파일을 그대로 복사 붙여넣기하여 AOP패키지에 적용하였다.


LogAdvisor.java 파일 말고는 추가 코드를 작성하진 않았다.

MainTestAOP.java도 복사, 붙여넣기해서 만든 것이다. 가리키고 있는 명칭만 변경하였다.



 

 



그림 1, 그림 2. 
(Spring 트랜젝션 + Spring JDBC) + AOP

그림 3, 그림 4. 이전 프로젝트
(Spring 트랜젝션 + Spring JDBC)



2. pom.xml 설정하기


((중략) - 추가된 사항)
    
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${org.springframework-version}</version>
  </dependency>

  
    
  <!-- AspectJ -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency> 
  

  <!-- AspectJWeaver 추가 -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency>
  





3. LogAdvisor.java(com.website.example.aop)


package com.website.example.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;


//관점, 서비스
@Aspect
@Service
public class LogAdvisor {


   // 2단계 - 전 단계 시야)
 
  @Before("execution(* com.website.example.aop.service.AccountServiceAOP.*(..))")
  // @Before("execution(public void sum())")
  // 반환값 없어도 무방
  
   public void logBefore() {
           System.out.println("전 단계");


   }

 
}


파일명: LogAdvisor.java


[첨부(Attachments)]

LogAdvisor.zip



4. AccountServiceAOP.java(com.website.example.aop.service) - 예(복사, 붙여넣기)


코드 자체를 복사 붙여넣기 한 것이다.

AOP를 왜 하는지 철학을 이해하라고 하나 예로 보여주는 것이다.



package com.website.example.aop.service;

import java.sql.SQLException;


import com.website.example.vo.AccountVO;


public interface AccountServiceAOP {

 void accountCreate(AccountVO vo) throws SQLException;
 void accountTransfer(String sender, String receiver, int money) throws SQLException;
 
}




5. AccountServiceImplAOP.java(com.website.example.aop.service) - 예(복사, 붙여넣기)


하나 어노테이션 @Repository를 달아주었다. 그거 말고는 동일하다.



package com.website.example.aop.service;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.website.example.dao.AccountDAO;
import com.website.example.dao.AccountDAOImpl;
import com.website.example.vo.AccountVO;


@Repository
@Transactional
public class AccountServiceImplAOP implements AccountServiceAOP{

 private AccountDAO accountDAO;
 private DataSource ds = null;
 
 public AccountServiceImplAOP(DataSource ds) {
  this.accountDAO = new AccountDAOImpl(ds);
  this.ds = ds;
 }

 @Override
 @Transactional(propagation=Propagation.NEVER)
 public void accountCreate(AccountVO vo) throws SQLException {
  accountDAO.createAccount(vo); 
  System.out.println("create CurrentTransactionName: " + TransactionSynchronizationManager.getCurrentTransactionName());
 }
 
 @Override
 @Transactional
 public void accountTransfer(String sender, String receiver, int money) throws SQLException {
  
     int balance = accountDAO.getBalance(sender); // 보내는 사람 잔액 체크
     
        if(balance >= money){ // 보내는 돈이 잔액보다 많으면
     
         System.out.println("transfer CurrentTransactionName: " + TransactionSynchronizationManager.getCurrentTransactionName() );
         
   accountDAO.minus(sender, money);
   accountDAO.plus(receiver, money);
   
        } else{

         System.out.println("돈 없음");
         //throw new NoMoneyException();
        }
  
 }


}




6. RootConfigAOP.java(com.website.example.common) - 예(복사, 붙여넣기)



package com.website.example.common;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@Import({DBConfigAOP.class})
@ComponentScan(basePackages = {"com.website.example"})
@EnableAspectJAutoProxy
//@ComponentScan(basePackages = {"com.local.example.beans", "com.local.example.advisor"})
public class RootConfigAOP {


}




7. DBConfigAOP.java(com.website.example.common) - 예(복사, 붙여넣기)


package com.website.example.common;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.website.example.dao.AccountDAO;
import com.website.example.dao.AccountDAOImpl;
import com.website.example.aop.service.AccountServiceAOP;
import com.website.example.aop.service.AccountServiceImplAOP;


@Configuration
@EnableTransactionManagement
public class DBConfigAOP {

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
   
    @Bean
    public DataSource dataSource() {
     
     DataSource dataSource = new MyDataSourceFactory().getOracleDataSource();
     
     /* Apache DBCP
     
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/testDb?useUnicode=true&characterEncoding=utf8");
        dataSource.setUsername("test");
        dataSource.setPassword("test123!@#");
       
        */
        return dataSource;
    }
   
    @Bean
    public AccountDAO accountDAOImpl() {
     
     AccountDAO dao = new AccountDAOImpl(dataSource());
     return dao;
    }
   
    @Bean
    public AccountServiceAOP accountServiceImplAOP() {
     
     AccountServiceAOP service = new AccountServiceImplAOP(dataSource());

     return service;
    }
   
}




8. MainTestAOP.java(com.website.example.unit) - 예(복사, 붙여넣기)



package com.website.example.unit;


import java.sql.SQLException;

import java.sql.Timestamp;

import javax.sql.DataSource;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.website.example.common.MyDataSourceFactory;
import com.website.example.common.RootConfigAOP;

import com.website.example.dao.AccountDAOImpl;
import com.website.example.aop.service.AccountServiceAOP;
import com.website.example.aop.service.AccountServiceImplAOP;

import com.website.example.vo.AccountVO;


class MainTestAOP {

 @Test
 void test() throws SQLException {
  
       @SuppressWarnings("resource")
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfigAOP.class);

  
        AccountServiceAOP service = (AccountServiceAOP) applicationContext.getBean("accountServiceImplAOP");
  
  AccountVO vo = new AccountVO();
  
  // 1. 계정 생성
  vo.setName("홍길동2");
  vo.setBalance(10000);
  vo.setRegidate(Timestamp.valueOf("2020-01-20 11:05:20"));
  service.accountCreate(vo);
  
  // 2. 계정 생성
  vo.setName("홍길자2");
  vo.setBalance(0);
  vo.setRegidate(Timestamp.valueOf("2020-01-20 23:05:20"));
  service.accountCreate(vo);
  
  // 3. 거래 처리
  service.accountTransfer("홍길동", "홍길자", 500);
  
  
 }


}




* 맺음글(Conclusion)


AOP를 쉽고 간단하게 트랜젝션과 연결해서 사용하는 방법에 대해서 소개하였다.



반응형
728x90
300x250
[Spring Framework] 33. AOP(Aspect-Oriented-Programming) - XML 방식


XML 기반으로 작성된 AOP 구현 방법에 대해서 소개하려고 한다.

AOP의 관점에 대한 5가지 방식을 모두 적용해보았다.


[태스트 환경]

* IDE: Eclipse 2020-06

* Spring Framework 4.2.4.RELEASE
* Aspectjweaver 1.6.10
* Spring-aop 4.2.4.RELEASE
* JUnit 5




1. 프로젝트 구성도


프로젝트 구성도이다.



그림 1, 그림 2. 프로젝트 구성도




2. 프로젝트 생성


Spring Legacy Project로 프로젝트를 생성한다.

참고로 Spring MVC Project로 선택하고 생성해야 한다.



3. Build Path, Java Compiler, Project Factes 버전 맞춰주기


* Build Path: JRE 버전을 1.8로 변경해준다.
               - Add Library로 JUnit 5를 등록해준다.

* Java Compiler: Compiler compliance level - 1.8로 변경해준다.

* Project Factes: Java 버전을 1.8로 변경해준다.




4. POM.xml 설정하기


(중략)
 <properties>
  <java-version>1.8</java-version>
  <org.springframework-version>4.2.4.RELEASE</org.springframework-version>
  <org.aspectj-version>1.6.10</org.aspectj-version>
  <org.slf4j-version>1.6.6</org.slf4j-version>
 </properties>

~~~

(중략)
    <!-- AspectJ -->
    <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjrt</artifactId>
     <version>${org.aspectj-version}</version>
    </dependency>
   
    <!-- AspectJWeaver -->
    <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>${org.aspectj-version}</version>
    </dependency>
   
    <!-- Spring AOP 추가(Java) --> 
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${org.springframework-version}</version>
    </dependency> 
(중략)






5. applicationContext.xml (src/main/resources/applicationContext.xml)


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:p="http://www.springframework.org/schema/p"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
 
 <!-- 방법1 - JAVA -->
 <!-- JAVA 방식(어노테이션)의 AOP - AspectJ Weaver -->
 <!--  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>  어노테이션 작업시 필수 선언해야 함. -->
 
 <!--  XML 방식의 AOP - AsepectJ Weaver -->
 <bean id="resultAOP" class="com.website.example.test.ResultAOP"></bean>
 <bean id="txAdviceXML" class="com.website.example.aop.LogAdvisorXML"></bean>

 <aop:config>
  <!-- txAdviceXML 하나 영역임 -->
  <aop:aspect ref='txAdviceXML'>

   <!-- 진입영역 -->
   <!-- 1개만 가능함 -->
   <!-- <aop:pointcut id="point1" expression="execution(* com.website.example.test..*())"/> -->
   <aop:pointcut id="point1" expression="execution(* com.website.example.test.ResultAOP..*())"/>           
          
          <!-- before -->
          <aop:before method="beforeAdvice" pointcut-ref="point1"/>
          <!-- after -->
          <aop:after method="afterAdvice" pointcut-ref="point1"/>
          
          <!-- around(메서드 자체를 가로채기) -->
          <aop:around method="aroundAdvice" pointcut-ref="point1"/>
          
          <!-- afterThrowing -->
          <aop:after-throwing method="afterThrowing" pointcut-ref="point1"/>
          
          <!-- afterReturning -->
          <aop:after-returning method="afterReturning" pointcut-ref="point1"/>
          
        </aop:aspect>
       
        <!-- 2번째 선언자 -->
 </aop:config>
 
</beans>


파일명: applicationContext.xml


[첨부(Attachments)]

applicationContext.zip



6. ResultAOP.java - com.website.example.text


핵심 로직에 해당되는 부분이다. 이 부분을 비즈니스 로직이라고 표현하기도 한다.


package com.website.example.test;


public class ResultAOP {


     public void method1() {
           System.out.println("[중간]:");
  
           // afterThrowing 유발 코드
           // int d = 2/0;
  
           System.out.println("결과: 메서드");
     }
 
}


파일명: ResultAOP.java


[첨부(Attachments)]

ResultAOP.zip


applicationContext.zip



7. LogAdvisor.java - com.website.example.aop


인터페이스 정의이다.

package com.website.example.aop;

import org.aspectj.lang.ProceedingJoinPoint;

public interface LogAdvisor {

        public void beforeAdvice();
        public void afterAdvice();
        public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable;
        public void afterThrowing();
        public void afterReturning();
 
}


파일명: LogAdvisor.java


[첨부(Attachments)]

LogAdvisor.zip




8. LogAdvisorXML.java - com.website.example.aop


package com.website.example.aop;

import org.aspectj.lang.ProceedingJoinPoint;


public class LogAdvisorXML implements LogAdvisor{
  
       // around advice
       @Override
       public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
  
               System.out.println("2단계A?:");
               System.out.println("aroundMethod 호출 1");
  
               // 원래의 메소드를 호출한다.
               Object obj = pjp.proceed();

               System.out.println("2단계B?:");
               System.out.println("aroundMethod 호출 2");
  
               return obj;
       }


       // before advice
       @Override
       public void beforeAdvice() {
              System.out.println("1단계:");
              System.out.println("beforeMethod 호출");
  
       }


       // after
       @Override
       public void afterAdvice() {
              System.out.println("5단계:");
              System.out.println("afterMethod 호출");
  
       }


       // afterThrowing
      @Override
      public void afterThrowing() {
              System.out.println("4단계:");
              System.out.println("afterThrowing 호출");
      }

      // afterReturning
      @Override
      public void afterReturning() {
              System.out.println("3단계:");
              System.out.println("afterReturning 호출");
  
      }
 
}


파일명: LogAdvisorXML.java


[첨부(Attachments)]

LogAdvisorXML.zip


LogAdvisor.zip




9. TestMain.java - com.website.example.unit


package com.website.example.unit;


import org.junit.jupiter.api.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import com.website.example.test.ResultAOP;


public class TestMain {

      
       // 사용방법1
       @Test
       public void sample() {

                AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
   
                ResultAOP rAOP = (ResultAOP) factory.getBean("resultAOP");

                rAOP.method1();
  
                factory.close();
       }
 

       // 사용방법2
       @Test
       public void sample2() {

               ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
               ResultAOP rAOP = ctx.getBean("resultAOP", ResultAOP.class);
               //rAOP.method1();
  
               ctx.close();
       }
 
}


파일명: TestMain.java


[첨부(Attachments)]

TestMain.zip




10. 동작 결과


구현한 코드의 동작 결과이다.



그림 3. 출력 결과 1



그림 4. 출력 결과 2




11. 맺음글(Conclusion)


직관적으로 AOP를 아주 쉽게 이해할 수 있도록 작성하였다.

반응형
728x90
300x250
[Spring Framework] 32. AOP(Aspect-Oriented-Programming) - Java 방식2(Context.xml)


Spring Framework에서 사용되는 AOP 구현방식을 소개하려고 한다.

AOP를 다루고자 하는 사람들은 조금 방법이 다양하게 있으니, 원하는 방식 하나 택해서 사용하면 될듯 싶다.


[배경 입문 지식]

1. [Spring-Framework] 30. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (1), 2020-10-04

- https://yyman.tistory.com/1447

2. [Spring-Framework] 30. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (2), 2020-10-04

- https://yyman.tistory.com/1448


셋팅 방법에서 다르다.

동일하게 생각하고 작성하면, 오류를 경험할 수 있다.


AOP를 다루는 건 큰 틀에서는 같은 일인데 불구하고, Service 어노테이션 정의부터 다소 셋팅이 달라져버릴 수가 있어서 신중하게 사용하는 것을 권장한다. 셋팅을 먼저 한 경우라면, 거기에 맞춰서 흐름을 타주는 방식으로 가야 하겠다.


XML, 순수한 Java 방식, Context.xml 및 Java 방식(어노테이션) 크게 3가지 방법으로 AOP를 설정할 수 있다.

이론적으로는 같으나 실질적인 구현에서는 호환이 안 되니 주의하여 사용할 것을 권한다.


3가지 방법 중 하나를 택해서 AOP 대상을 두었으면 그 방법을 따라가는 형태로 진행하는 것을 권장한다.

(구현 관점에서보면, 표준화가 진행이 덜된 느낌이 있다.) 시간이 나면, 소개해주겠다.


[태스트 환경]

* IDE: Eclipse 2020-06 (Spring Tools 3 Addon.....)
* Spring Framework Version: 4.2.4.RELEASE

* JUnit 5(태스트 실행)




1. 프로젝트 생성


-> Spring Legacy Project -> Spring MVC Project로 진행하였음.


프로젝트 생성 및 셋팅 방법에 대해서는 "이전 글들"을 참고하면 된다. (동일하게 진행함.)




2. pom.xml


매우 중요한 부분이다. 노란색 불은 들어오는데 빌드하면 오류가 생긴 경우도 있을 수도 있다.

셋팅을 잘해야 한다.


-> Spring Tool-Suites 4.7로 작업하면 14버전을 사용해도 무방하다. 다만, 이전 버전의 Eclipse 등에서 프로젝트 구성에서 오류가 생길 수도 있다.

지금 pom 셋팅은 공통적인 작업이다.


(중략)

 <properties>
  <java-version>1.8</java-version>
  <org.springframework-version>4.2.4.RELEASE</org.springframework-version>
  <org.aspectj-version>1.6.10</org.aspectj-version>
  <org.slf4j-version>1.6.6</org.slf4j-version>
 </properties>

(중략)
    
  <!-- AspectJ -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency> 
  
  <!-- AspectJWeaver -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.8.8</version>
  </dependency>
  
  <!-- Spring AOP 추가(Java) -->  
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${org.springframework-version}</version>
  </dependency>

(중략)




3. 미리 살펴보는 프로젝트 구성도


이번 작업에서는 JUnit5로 TDD 방식으로 구동한 프로젝트이다.



그림 1. 프로젝트 구성도

작업할 프로젝트 구성이다.




4. 프로젝트 환경설정(Build Path, Java Compiler, Project Factes)


Build Path의 Libraries에 들어가서 버전을 1.8로 설정한다.

JUnit 5도 추가해준다.


참고로 JUnit 5은 자바 1.8버전에서부터 지원한다.



그림 2. 프로젝트 구성도


Build Path를 설정해준다. "Add Library"를 클릭해서 JUnit 5을 추가할 수 있다.




그림 3. 자바 컴파일러 환경설정


JDK를 잘 설치하였음에도 1.8버전 등으로 변경하였을 때 이클립스에서 오류로 간주해버리는 경우가 있다.

오류가 생기는 이유는 Project Factes 옵션을 조작할 때 발생한다.


JDK Compliance의 Compiler compliance level 버전을 맞춰주면 오류가 사라지는 것을 확인할 수 있다.



그림 4. Project Factes


Java의 버전을 1.8으로 변경한다.




5. applicationJavaAOP.xml (src/main/resources/applicationJavaAOP.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:p="http://www.springframework.org/schema/p"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
 
 <aop:aspectj-autoproxy />
 <context:component-scan base-package="com.website.example" />
 <bean id="loggingAspect" class="com.website.example.aop.LogAdvisorJava" />
 
</beans>



AOP 스키마 정의는 servlet-context.xml에서 간단한 조작으로 추가하였다.


그림 5. aop 환경설정해주기 - 네임스페이스 정의


양식 틀은 /src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml에서 복사 붙여넣기하였다.


작성한 코드는 <aop>.....</bean> 이 정도이다.


* 파일명: applicationJavaAOP.xml


[첨부(Attachments)]

applicationJavaAOP.zip




6. ResultAOPJava.java (com.website.example.text)


package com.website.example.test;

import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Service;


@Service
public class ResultAOPJava {
 
     @Pointcut("execution(* com.website.example.test.*(..))")
      public void method1() {
              System.out.println("결과: 메서드");
      }
 
}


* 파일명: ResultAOPJava.java


[첨부(Attachments)]

ResultAOPJava.zip




7. LogAdvisor.java (com.website.example.aop)


관점에 대한 인터페이스이다.
객체의 다형성을 생각하면 좋을 것으로 보인다.


package com.website.example.aop;

import org.aspectj.lang.annotation.Aspect;


@Aspect
public interface LogAdvisor {

       public void beforeAdvice();
       public void afterAdvice();
       public void aroundAdvice();
 
}


* 파일명: LogAdvisor.java


[첨부(Attachments)]

LogAdvisor.zip

ResultAOPJava.zip




8. LogAdvisorJava.java (com.website.example.aop)


클래스명을 Impl로 해도 되었을 법한데 태스트용이니 생략하도록 하자.


package com.website.example.aop;


import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;


// 어노테이션 방식
@Aspect
public class LogAdvisorJava implements LogAdvisor{


         @Before("execution(* com.website.example.test.ResultAOPJava.*(..))")
          public void beforeAdvice() {
                 System.out.println("전 단계");
         }

         @After("execution(* com.website.example.test.ResultAOPJava.*(..))")
         public void afterAdvice() {
                 System.out.println("후 단계");
         }


         public void aroundAdvice() {
   
          }
 
}


* 파일명: LogAdvisorJava.java


[첨부(Attachments)]

LogAdvisorJava.zip


LogAdvisor.zip

ResultAOPJava.zip



9. TestMain.java (com.website.example.test)


JUnit 태스트 도구로 실행 영역을 구현하였다.


package com.website.example.test;

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

public class TestMain {


     @Test
     public void test() {


              // 방법 1
             @SuppressWarnings("resource")
             AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationJavaAOP.xml");
  
             ResultAOPJava cal = factory.getBean(ResultAOPJava.class);
             cal.method1();
  
     }
 
}


* 파일명: TestMain.java


[첨부(Attachments)]

TestMain.zip



10. 출력 결과


아래의 그림처럼 AOP가 잘 출력되는 것을 확인할 수 있다.



그림 6. AOP 화면 출력 결과


정상적으로 AOP 환경설정이 이뤄진 것을 확인할 수 있다.



* 참고자료(References)


1. Spring AOP – AspectJ Annotation Config Example - HowToDoInJava, https://howtodoinjava.com/spring-aop/spring-aop-aspectj-example-tutorial-using-annotation-config/, Accessed by 2020-10-09, Last Modified 2015-02.


-> 추천(25점): 구현에 대해서 많은 도움을 주었다.


2. 스프링 부트(Spring Boot) - AOP와 트랜잭션(Transaction) 설정하기 [개발을 시작해봐요!], https://congsong.tistory.com/25, Accessed by 2020-10-09, Last Modified 2020-04-28.

-> 추천(25점): 스프링 부트로 작성되었지만, AOP 개발 방법의 흐름을 살펴볼 수 있었음.

반응형
728x90
300x250

[Spring-Framework] 31. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (2)


1부에서는 통상적인 개발 방법으로 간단한 Calculator 클래스에 정의된 sum 함수(또는 매서드)를 출력하는 방법을 살펴보았다.

참고로 앞에 코드는 편집이 불가능한 코드라고 가정하고 접근해야 한다.


1. [Spring-Framework] 31. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (1)

https://yyman.tistory.com/1447



8. aop 패키지를 추가로 작성하여 copy & paste하기


aop 패키지를 별도로 둘 것이다.

코드를 따로 작성해야 하는가? 시점 부분만 작성하고 크게 많이 작성하진 않는다.



그림 15. Copy & Paste 하기


태스트 코드를 복사할 것이다.


(1단계) com.local.example.service에 만들어놓았던 Calculator.java를 파일 복사한다. (Ctrl + C)

(2단계) com.local.example.aop 폴더(또는 패키지)를 만든다.

(3단계) com.local.example.aop 폴데 Calculator.java를 붙여넣는다. (Ctrl + V)

(4단계) 소스 코드가 빨간 불이 들어오는 패키지 부분을 com.local.example.aop로 변경해준다.



9. 작업 방법론 - 정리해보기


직관적으로 코드 작성 전략을 소개해주려고 한다.

이런 형태로 작업을 할 것이다.



그림 16. 작업 방법


빌드하는 방법은 웹 서버에 올려서 확인하면 된다.




9. AOP 필수(코어), 핵심사항 - RootConfig.java (환경설정 파일 만들기)


패키지 경로: com.local.example.aop


반드시 만들어야 할 파일이다.

이 셋팅 파일이 없으면 동작 자체를 안 한다.



그림 17. RootConfig.java


이 코드는 AspectJ Proxy의 핵심 환경설정 파일이다.

이걸 정의하지 않으면, 동작이 안 된다.


package com.local.example.aop;


import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.EnableAspectJAutoProxy;


@Configuration

@ComponentScan(basePackages = {"com.local.example.aop"})

//@ComponentScan(basePackages = {"com.local.example.beans", "com.local.example.advisor"})

@EnableAspectJAutoProxy

public class RootConfig {


}


파일명: RootConfig.java


[첨부(Attachments)]

RootConfig.zip




10. AOP 적용, 핵심사항 - Calculator.java (살짝 수정하기)


패키지 경로: com.local.example.aop


작업 파일 원본의 권한을 가진 사람에게 하나 요청해도 무방하다. (초기 생성자 부분)

여기에서는 그런 거 신경쓰지 않고, 작업하겠다.


package com.local.example.aop;


import org.springframework.stereotype.Service;


@Service("cal")

public class Calculator {


private long x;

private long y;

private long z;

public Calculator() {

this.x = 1;

this.y = 2;

this.z = 3;

}

public Calculator(long x, long y, long z) {

this.x = x;

this.y = y;

this.z = z;

}

// 작업 원본(A 프로그래머 작성함)

public long sum() {

long result = x + y + z;

System.out.println("비즈니스 로직");

// long d = result / 0; - 일부러 만든 코드(After Throws 보여줄려고)

return result;

}

}



파일명: Calculator.java


[첨부(Attachments)]

Calculator-Aspect.zip


(참고)

주석 친 부분을 주석 풀면, After Throws를 정의한 경우라면 메시지가 출력되는 것을 볼 수 있다.


색깔 칠한 코드는 Calculator(long x, long y, long z) 코드 때문에 Calculator.class 파일로 불러오면 Error를 경험할 수 있다.

그래서 파란색깔로 칠한 코드 Calculator()를 정의해서 문제를 해결한 것이다.




11. AOP 구현(시점 구현) - 시점 예제 (Log.java)


패키지 경로: com.local.example.aop


많은 고민을 한 끝에 간단하면서 실용적인 형태로 코드를 작성하였다.

참고로 시점 정의를 안해도 빌드해보면, HomeController.java에 정의가 잘 되어있으면 돌아가긴 돌아간다.

무슨 말인지는 태스트를 해보면 알 수 있다.


package com.local.example.aop;


import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.After;

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.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Service;


// 관점, 서비스

@Aspect

@Service


// 콤포넌트 사용하지 말것

public class Log {

Logger logger =  LoggerFactory.getLogger(Log.class);

// 섞어적어놓아도 순서를 찾아서 강제 인식함.

    

// 2단계 - 전 단계 시야)

@Before("execution(* com.local.example.aop.Calculator.*(..))")

// @Before("execution(public void sum())")

// 반환값 없어도 무방

    public void logBefore() { 

logger.info("전 단계 관점 - 구현(Before)");

    }

    

// 1단계 - Before보다 전 단계 실행

    // Calculator 클래스의 모든 메서드

// 무조건 반환값이 지정되어야 함. (ProceedingJoinPoint에 대한 Object 반환값 필수 정의되어야 함)

// @Around 이 친구만 Pjp가 있어야 함.

    @Around("execution(* com.local.example.aop.Calculator.*(..))")

    /*@Around("execution(* com.example.demo.controller..*.*(..))")*/

    /*@Around("execution(* com.example.demo..*.*(..))")*/

    public Object logAround(ProceedingJoinPoint pjp) throws Throwable { 

   

logger.info("Around 단계 - 구현(Around)");

Object result = pjp.proceed();

return result;

    }

    

    // 3단계는 (중간) - 임의로 지정해줄 수 있는 것이 아님.

// 4단계: After advice

//@After("execution(* method1())")  -- method1 함수만 실행 

    // (이렇게 하면 안 됨. sum()을 콜하니깐)

    // @After("execution(* method1())")

    @After("execution(* sum())")

public void afterMethod() {

   

    logger.info( "after Method 호출" );

   

}

    

    // 5단계: 맨 마지막 단계

// after-throwing advice /// 오류가 발생할 때 호출함(무조건 보여지는 영역은 아님

    // 쉽게 비유하면, try to catch finally에서 catch 단계로 보면 됨.

    // 오류도 출력되고 after Throwing 로그 메시지도 출력된다.

    // 이 화면을 보려면, 오류나는 코드에 try to catch finally를 적용하면 안 됨.

    // 원본 비즈니스 로직 코드는 그대로 둘 것

    @AfterThrowing(

        pointcut = "execution(* com.local.example.aop.Calculator.sum(..))", 

        throwing = "ex"

    )

public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {

logger.info( "after Throwing 호출" + ex.getMessage());

}


    

}


파일명: Log.java


[첨부(Attachments)]

Log.zip





12. AOP 사용시, 핵심사항 - HomeController.java 변경하기


패키지 경로: com.local.example


HomeController.java 코드를 수정할 것이다.


package com.local.example;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import org.springframework.context.annotation.EnableAspectJAutoProxy;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


import com.local.example.aop.Calculator;

import com.local.example.aop.RootConfig;


/**

 * Handles requests for the application home page.

 */

@EnableAspectJAutoProxy

@Controller

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

/**

* Simply selects the home view to render by returning its name.

*/


@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

// Java 방식 - AOP

AnnotationConfigApplicationContext context = 

new AnnotationConfigApplicationContext(RootConfig.class);

// 1. 이전 방식

// Calculator cal = new Calculator(1, 2, 3);


// 2. Aspect 적용

// 소문자로 입력(Calculator -> calculator로)

Calculator cal = (Calculator)context.getBean("cal", Calculator.class);

System.out.printf("result of sum: %d", cal.sum());

model.addAttribute("serverTime", formattedDate );

return "home";

}

}



파일명: HomeController.java


[첨부(Attachments)]

HomeController-aspect.zip




13. 동작 결과 구경하기


두 가지 결과로 작성하였다. 하나는 오류가 없는 코드이다.

하나는 오류가 있어서 After Throws가 호출되는 코드이다.



그림 17. 결과1의 오류 없는 코드(1)



그림 18. 결과1의 오류 없는 코드(2)




그림 19. 결과2의 오류 있는 코드(1)



그림 20. 결과2의 오류 있는 코드(2)





* 맺음글(Conclusion)


AOP 프로그래밍을 실질적으로 쉽게 사용하는 방법에 대해서 소개하였다.



* 참고자료(References)


1. [스프링] Error creating bean with name '***Controller': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named '***Service' is defined 에러 해결방법, https://zzznara2.tistory.com/275, Accessed by 2020-10-04, Last Modified 2015-06-18.


참고: 시중에 있는 AOP 자료를 보고 따라하다보면, 오류 먼저 구경할 것이다. AOP는 조금 어느 정도 프로그래밍 지식이 있을 때 사용하는 걸 권장하고 싶다. (쉽지 않은 부분이다.)


2. Spring : AOP with Java Config - 설정하기와 @Before advice, https://kogle.tistory.com/51, Accessed by 2020-10-04, Last Modified 2020-05-11.


추천(40점): 구축 원리에 대해서 매우 잘 나와있다. 그러나 몇 가지는 수정작업을 해줘야 한다.


3. AspectJ Weaver를 사용한 애노테이션 기반의 스프링 AOP 구현 방법, https://atoz-develop.tistory.com/entry/AspectJ-Weaver%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%95%A0%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B0%98%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-%EA%B5%AC%ED%98%84-%EB%B0%A9%EB%B2%95, Accessed by 2020-10-04, Last Modified 2020-04-27.


추천(25점): 구축 원리를 잘 정리해놨다. 2번 참고자료하고 같이 보는 것을 권장한다. (예제가 살짝 아쉬움)


4. Spring AOP 스프링이 해줄건데 너가 왜 어려워 해? Spring boot에서 aop logging 사용법 제일 쉽게 알려드립니다!, https://jeong-pro.tistory.com/171, Accessed by 2020-10-04, Last Modified 2018-11-23.

반응형
728x90
300x250

[Spring-Framework] 30. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (1)


참고로 AOP는 Spring-Framework에만 적용되는 방법론이다.

새로 만들어진 개념은 아니고, 자바의 Proxy(패턴 아님. 탈취기법이 있음.)에서 핵심 영역의 관점으로 진화한 개념이다.


교과서나 시중 책 등에서는 이런 표현은 안 쓰긴 하지만, "탈취 방법"이라고 하는 게 적절하다고 주장하다.


해당 작업은 Spring Framework에서만 가능한 작업이다.



IDE: Spring Tool-Suite 4-4.7.2 Release(2020-08월)
Library: 

- Spring Framework 5.2.9.RELEASE (2020-09-15)

- Java Version: 14

- AspectJ: 1.9.6 (2020-07-22)

- AspectJ Weaver: 1.9.6 (2020-07-22)

- javax.servlet-api 4.0.1 (2018-04-20)


(순정 상태로 셋팅하였음.)



Project: Spring Legacy Project로 생성할 것


-> 안 보이는 사람들은 Help->Eclipse Marketplace -> STS 검색 후 "Spring Tools 3 Add-On for Spring Tools 4 3.9.14.RELEASE" 설치할 것



그림 1. STS-Addon (Eclipse Marketplace)



1. 이전의 개발 방법론(Proxy)


지금도 물론 이런 방법은 안 쓰일 수는 없다.

AOP가 먼저 나온 것이 결코 아니다. 아마 c#에도 이런 게 있는 걸로 알고 있다. (오래되서)


프로그래밍 개발을 하다보면, 로직을 구현하는데 있어서 코어 코드를 수정해서 작업을 하는 일이 많이 있다.

핵심 규칙은 x, y, z만 더해서 결과만 출력하면 되는 요구사항을 가진 문제인데 구현을 하다보면, 현실적인 문제라는 게 발생하게 된다.


그림 2. 이전의 로직 구현 방법 - 개발


그림 1과 같은 문제는 자주 생기는 문제이기도 하다.

이런 방법으로 구현하는 것이 아예 사라졌다는 게 아니다.


그림 1에서 (1단계)의 핵심 규칙을 재사용할 기회를 상실해버리는 문제가 발생하게 된다.



그림 3. 문제 인식


그림 2처럼 문제가 생겼다고 하자.

물론 머리가 조금 좋은 분들은 객체의 상속 관계로 처리해도 해결할 수 있지 않겠냐고 할 수도 있다. 

(객체지향 프로그래밍에서도 설계를 다시 해서 이런 문제를 나눠볼 수도 있겠음.)


허나 쉬운 일이 결코 아니다. 코드라는 게 이쪽에서 사용되고 있는지, 저기에서 사용되고 있는지 작성을 하게 되면 수정이 정말 어렵다는 것이다.


물론 C#을 논하는 자리가 아니어서 자바를 예로 들면, Proxy 탈취 방법이 존재한다.




그림 4. Proxy 탈취 방법 - 자바


자바에서는 


Type of Classes username new Proxy.newProxyInstance(Classes.class.getClassLoader(), new Class[] { classes.class } , 

new InvocationHandler(){

          public Object invoke(Object proxy, Method method, Object args[] throws Throwable{

                     

                  // 전단계 호출

                  Object result = method.invoke(sum, args);

                  // 후단계 호출

         }


     }


};


슈도 코드(Pseudo Code) 1. 자바의 Proxy 호출 방법


이런 방법으로 핵심 로직을 탈취하는 방법이 있다.

기본적인 원리는 이런 원리에 입각해서 만들어졌다고 보면 된다.


AOP의 용어 설명은 나중에 할 것이다. 절대로 지금 바로 알면 안 된다고 생각한다.


3단계를 기본으로 한다. "전 단계, 핵심 로직, 후 단계"

이 단계의 확장적인 개념이다.


Spring AOP에서는 5단계로 확장되었다.


전단계:    {Around 단계, Before 단계}, 

핵심로직:         핵심 로직(중간)

후단계:    {after Method 호출, AfterThrowing(선택 - 예외 처리 발생 때만)}


조금 더 이해하기 쉬워졌을 것으로 보인다.


[첨부(Attachments)]

background-aop.zip





2. 코드로 살펴보는 AOP


AOP를 이론으로만 봐서는 이해가 무척 안 될 것이다. 구현을 통해서 단계적으로 살펴보도록 하겠다.

프로젝트 생성부터 단계적으로 소개하겠다.



그림 5. 프로젝트 생성하기


File을 클릭한다.

New -> Spring Legacy Project를 클릭한다.



그림 6. 프로젝트 생성하기


Spring MVC Project를 선택한다.

Project name을 입력한다.

Next를 누른다.




그림 7. 프로젝트 생성하기


Top-level-package를 입력한다. (예: com.local.example)

Finish를 누른다.



3. POM.xml 설정하기


http://mvnrepository.com 사이트에 접속한다.

검색 키워드에 "aspectj"라고 입력한다. (세 가지 검색을 해서 버전을 획득해야 함)

검색 키워드에 "spring"라고 입력한다.

검색 키워드에 "servlet"이라고 입력한다.


두 가지의 Maven 주소를 획득해야 한다.



그림 8. MVNRepository 검색 결과의 예 (1) - AspectJ



그림 9. MVNRepository 검색 결과의 예 (1) - AspectJ


버전을 클릭하면, Maven Pom이 나온다.

이걸 복사 붙여넣기를 해도 된다.


다만 <version>만 복사해서 관리하는 방법도 있으니 자세한 건 POM.xml 소스를 참고하면 좋겠다.



그림 10. pom.xml - 변경 작업 모습(1)


<java-version>14</java-version>으로 변경한다.

<org.springframework-version>5.2.9.RELEASE</org........>로 변경한다.

<org.aspectj-version>1.9.6</org......>으로 변경한다.


<?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>com.local</groupId>

<artifactId>example</artifactId>

<name>springAOP-javaConfig</name>

<packaging>war</packaging>

<version>1.0.0-BUILD-SNAPSHOT</version>

<properties>

<!-- 자바 버전 변경함 -->

<java-version>14</java-version>

<!-- 스프링프레임워크 버전 변경함 -->

<org.springframework-version>5.2.9.RELEASE</org.springframework-version>

<!-- AspectJ 변경함 -->

<org.aspectj-version>1.9.6</org.aspectj-version>

<org.slf4j-version>1.6.6</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>

<!-- AspectJWeaver 추가 -->

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</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>

<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 -->

            <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> 

            <dependency> 

                  <groupId>javax.servlet</groupId>

                  <artifactId>javax.servlet-api</artifactId>

                  <version>4.0.1</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 -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.7</version>

<scope>test</scope>

</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>2.5.1</version>

                <configuration>

                    <source>1.6</source>

                    <target>1.6</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>



파일명: pom.xml


[첨부(Attachments)]

pom.zip





4. Project의 Properties - Build Path, Project Factes 설정하기 (Java Version 변경)


프로젝트의 자바 버전을 변경할 것이다.



그림 11. 프로젝트의 마우스 오른쪽 버튼 메뉴 모습


프로젝트를 선택한 후 마우스 오른쪽 버튼을 누른다.

Properties를 클릭한다.




그림 12. Java Build Path


Java Build Path를 클릭한다.

JRE System Library를 [JavaSE-14]로 변경한다.




그림 13. Project Factes


Project Factes를 클릭한다.

Java의 버전을 14로 바꿔준다.




5. web.xml - 서블릿 스팩 (2.5에서 4.0으로 변경)


서블릿 스팩을 변경할 것이다.

web.xml에서 변경해주면 된다.



그림 14. web.xml (servlet 4.0 스팩으로 변경)



<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"

    version="4.0">


<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/spring/root-context.xml</param-value>

</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<!-- Processes application requests -->

<servlet>

<servlet-name>appServlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>appServlet</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>


</web-app>



파일명: web.xml


[첨부(Attachments)]

web.zip



6. service - Calculator.java


복잡한 설명을 적기보다는 작업화면하고 코드를 소개하겠다.



그림 15. Calculator.java


package com.local.example.service;


public class Calculator {


private long x;

private long y;

private long z;

public Calculator(long x, long y, long z) {

this.x = x;

this.y = y;

this.z = z;

}

// 작업 원본(A 프로그래머 작성함)

public long sum() {

long result = x + y + z;

return result;

}


}


파일명: Calculator.java


[첨부(Attachments)]

 Calculator-original.zip


참고로 지금 코드로는 AspectJ가 동작되지 않는 코드이다.
이유는 초기 매개변수가 없는 생성자가 없어서 그렇다.

(다음 페이지에서 소개하겠음.)



7. Controller - HomeController.java


아마 순정 코드에 Calculator의 sum()를 호출한 것을 보면, 다음과 같이 되어있다.


package com.local.example;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import org.springframework.context.annotation.EnableAspectJAutoProxy;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


import com.local.example.aop.Calculator;

import com.local.example.aop.RootConfig;


/**

 * Handles requests for the application home page.

 */

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

/**

* Simply selects the home view to render by returning its name.

*/


@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);


Calculator cal = new Calculator(1, 2, 3);

System.out.printf("result of sum: %d", cal.sum());

model.addAttribute("serverTime", formattedDate );

return "home";

}

}



파일명: HomeController.java


이렇게 하면, 잘 동작할 것이다.

결과가 "result of sum: 6"이 출력될 것이다.



* 2부에서 AOP 사용 코드로 전환하는 방법에 대해서 소개하겠다.


1. [Spring-Framework] 30. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (2), 2020-10-04

https://yyman.tistory.com/1448


반응형

+ Recent posts