자바 기본 지식 (4)

리진아·2023년 1월 16일
0
post-thumbnail

✔︎ 스프링 삼각형과 설정 정보


1️⃣ Ioc/DI

✔︎ DI : 객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 (의존성 주입)
✔︎ Ioc : 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아닌, 외부에서 결정되는 것. 즉, 컨테이너에 객체 생성과 공급을 위임

자동차는 타이어에 의존함
운전자는 자동차를 사용함
운전자가 자동차에 의존한다고 봐도 됨
-> 자동차의 생성자 코드에서 타이어 속성에 새로운 타이어를 생성해서 참조할 수 있게 해주었다.

개인적으로 수학적 귀납법에 의거하는 것 같다..

① 스프링 없이 의존성 주입하기

✔︎ 생성자를 통한 의존성 주입

운전자가 타이어를 생산한다.
운전자가 자동차를 생산하면서 타이어를 장착한다.
->
Tire tire = new KoeaTire();
Car car = new Car(tire);

✔︎ 속성을 통한 의존성 주입

운전자가 타이어를 생산한다.
운전자가 자동차를 생산한다.
운전자가 자동차에 타이어를 장착한다.
->
Tire tire = new KoeaTire();
Car car = new Car();
car.setTire(tire);

② 스프링을 통한 의존성 주입

✔︎ XML 설정을 통한 방법

xml 파일을 사용하여 요소를 정의
applicationContext.xml에 beans, c, context 네임스페이스 추가

ex) com.user.school 패키지에 School 클래스, Student 클래스, Pencil 클래스를 만든 후 학생에게 "공부해"라는 메서드 하나만 만들고 실행

  //연필.java
  public class Pencil {}
  
  
  //학생.java
	public class Student {
    	private Pencil pencil;
    	public void doStudy(String subject) {
        	sout(subject + "을 공부하다.");
    	}
	}
  
  
  //학교.java
    import org.springframework.context.support.AbstractApplicationContext;
    import org.springframework.context.support.GenericXmlApplicationContext;

    public class School {
        public static void main(String[] args) {
            AbstractApplicationContext context = new GenericXmlApplicationContext("classpath:com/di/school1/xml/applicationContext.xml");

            try {
                Student student = (Student)context.getBean("student"); //DI컨테이너에서 생성된 학생객체 가져오기
                student.doStudy("스프링");
            } finally {
                context.close(); //자원 닫기
            }
        }
    }
//applicatoinContext.xml에 추가
    <bean id="student" class="com.di.school1.xml.Student" />

-> Student클래스를 생성하여 DI컨테이너에 보관하는 작업

result : 스프링을 공부하다.

✔︎ 애너테이션을 이용한 방법

애너테이션을 클래스에 사용해서 DI컨테이너가 빈 자동 등록

  • @Autowired : 컨테이너가 빈과 다른 빈과의 의존성을 자동으로 주입
  • @Component : 컨테이너가 인젝션을 위한 빈을 설정
    @Controller : 프레젠테이션 층 스프링 MVC용 애너테이션
    @Service : 비즈니스 로직 층 Service용 애너테이션
    @Repository : 데이터 액세스 층의 DAO용 애너테이션
    @Configuration : JavaConfig용 애너테이션
  • <context:annotation-config/> : @Autowired, @Resource, @Required를 이용할 때 선언
    xml에 이미 등록된 빈들의 애너테이션 기능을 적용해줌

2️⃣ AOP

✔︎ AOP : 관점 지향 프로그래밍. 횡단 관심사를 추출하고 여러곳에서 호출할 수 있게..
주 비지니스 로직 앞, 뒤로 부가적인 기능을 추가하고 싶을때 사용

장점
코드의 중복을 최소화
주 업무 로직과 부가적인 로직을 분리ㄱㄴ

사용방법)

  1. 의존성 추가
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
  2. 로직 구현
    public interface CalcService {
      int sum(int x, int y);
      int subtract(int x, int y);
      int multiply(int x, int y);
      int divide(int x, int y);
    }
    
    public class CalcServiceImpl implements CalcService{
      @Override
      public int sum(int x, int y) {
          return x + y;
      }
      @Override
      public int subtract(int x, int y) {
          return x - y;
      }
      @Override
      public int multiply(int x, int y) {
          return x * y;
      }
      @Override
      public int divide(int x, int y) {
          return x / y;
      }
    }
    

3.Aspect를 구현

@Aspect
@Component
public class LogAspect {  // Aspect : 부가 기능 구현체들을 포함하고 있는 모듈

  private final Logger logger = LoggerFactory.getLogger(this.getClass());

  // PointCut : 적용할 지점 또는 범위 선택
  @Pointcut("execution(public * com.example.demo.service..*(..))")
  private void publicTarget() { }

  // Advice : 실제 부가기능 구현부
  @Around("publicTarget()")
  public Object calcPerformanceAdvice(ProceedingJoinPoint pjp) throws Throwable {
      logger.info("성능 측정을 시작합니다.");
      StopWatch sw = new StopWatch();
      sw.start();

      // 비즈니스 로직 (메인 로직)
      Object result = pjp.proceed();

      sw.stop();
      logger.info("성능 측정이 끝났습니다.");
      logger.info("걸린시간: {} ms", sw.getLastTaskTimeMillis());
      return result;
  }
}
  1. 실행

    @SpringBootTest
    public class AopTest {
    
      @Autowired
      private CalcService calcService;
    
      @Test
      @DisplayName("AOP - Around 테스트")
      void aopAroundTest() {
          calcService.sum(3, 5);
      }
    }
    

2023-01-16 11:03:36.237 INFO 19720 --- [main] com.example.demo.LogAspect : 성능 측정을 시작합니다.
2023-01-16 11:03:36.246 INFO 19720 --- [main] com.example.demo.LogAspect : 성능 측정이 끝났습니다.
2023-01-16 11:03:36.246 INFO 19720 --- [main] com.example.demo.LogAspect : 걸린시간: 9 ms


3️⃣ PSA

DB에 접근하는 방법은 기본적으로 Jdbc를 통해 접근할 수 있으며, ORM을 이용한다면 JPA를 통해서 접근ㄱㄴ. 어떠한 경우라도 @Transactional 애너테이션을 이용하면 트랜잭션을 유지하는 기능을 추가할 수 있음. 따라서

PSA : 하나의 추상화로 여러 서비스를 묶어둔 것

public void Test() throw Exception {
  // 1. DB Connection 생성
  // 2. 트랜잭션(Transaction) 시작
  try {
      // 3. DB 쿼리 실행
      // 4. 트랜잭션 커밋
  } catch(Exception e) {
      // 5. 트랜잭션 롤백
      throw e;
  } finally {
      // 6. DB Connection 종료
  }
}

위의 코드는 3번을 제외하고 모두 트랜잭션 관리하는 코드임.

private PlatformTransactionManger transactionManager;

public constructor(PlatformTransactionManger transactionManager) { // 생성자
this.transactionManager = transactionManager; // 해당 주입 instance의 변경으로 JPA, hibernate, JDBC로 쉽게 변경 가능.
}

public void Test() throw Exception {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
  // 3. DB 쿼리 실행
  transactionManager.commit(status);
} catch(Exception e) {
  transactionManager.rollback(status);
  throw e;
}
}

✔︎ 이처럼 트랜잭션 관리를 유지하고도 여러 기술을 사용할 수 있는 PSA 코드

profile
알맹이가 가득 찬 개발자가 되기 위해 한 걸음 더 다가가는,

0개의 댓글