✔︎ 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에 이미 등록된 빈들의 애너테이션 기능을 적용해줌
✔︎ AOP : 관점 지향 프로그래밍. 횡단 관심사를 추출하고 여러곳에서 호출할 수 있게..
주 비지니스 로직 앞, 뒤로 부가적인 기능을 추가하고 싶을때 사용
장점
코드의 중복을 최소화
주 업무 로직과 부가적인 로직을 분리ㄱㄴ
사용방법)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
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;
}
}
실행
@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
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 코드