[Spring] 07. Spring DI (2)

Hyeongmin Jung·2023년 6월 27일
0

Spring

목록 보기
7/17

이론

⚙️ Bean

  • JavaBeans : GUI가 있어 복잡, 재사용 가능한 컴포넌트, 상태(iv), getter&setter, no-args constructor
  • Servlet & JSP bean : Server만 있어서 간단, MVC의 Mode(data전달), EL, scope, JSP container가 관리
  • EJB(Enterprise Java Beans) : 대기업, 복잡한 규칙, EJB container가 관리(생성/소멸)
  • Spring Bean : POJO(Plain Old Java Object ↔ EJB), 단순독립적, Spring container가 관리

Bean : Spring Container가 관리하는 객체
Spring container : Bean 저장소, Bean을 저장, 관리, 생성/소멸, 연결(@Autoweird)
➊ BeanFactory: Bean을 생성, 연결 등의 기본 기능을 정의
➋ ↪확장 | ApplicationContext: BeanFactory를 확장해서 여러 추가 기능 정의

AC의 종류XMLJava Config
non-WebGenericXmlApplicationContextAnnotationConfigApplicationContext
WebXmlWebApplicationContextAnnotationConfigWebApplicationContext

cf. java코드는 컴파일러가 check해주지만 XML은 단순text이기 때문에(validator이 설정한다 하더라도) java가 더 유리

🌳 Root AC와 Servlet AC

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

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

ContextLoaderListener(이벤트처리기)를 이용하여 톰캣 시작 시 이벤트 체크
✔️ contextConfigLocation에 설정파일 위치(/root-context.xml)를 이용하여XmlWebApplication()생성
➡️ Root AC

➋<sevlet>태그 안에는 DispatcherServlet을 서블릿으로 등록
✔️ 초기화하면서 servlet-context.xml 설정 파일을 이용하여 XmlWebApplicationContext() 다시 생성
➡️ Servlet AC

➌ 만들어진 2개의 XmlWebApplicationContext()을 연결
➡️ Root AC가 부모, Servlet AC가 자식
✔️ 자식이 부모 참조, 자식을 먼저 찾고 없으면 그 다음 부모를 찾음
✔️ 모듈 별로 자식을 여러개 둘 수 있음
✔️ 부모: 공통 bean, 웹이랑은 관련없는 non-Web 빈
✔️ 자식: 개별적인 bean, 각 모듈에서 사용하는 빈

실습

@Controller
public class HomeController {
	@Autowired
	WebApplicationContext servletAC; // Servlet AC

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, HttpServletRequest request, Model model) {
		// 원래는 request.getServletContext()지만, 컨트롤러는 HttpServlet을 상속받지 않아서 아래와 같이 해야함.
		ServletContext sc = request.getSession().getServletContext(); // ApplicationContextFacade
		WebApplicationContext rootAC = WebApplicationContextUtils.getWebApplicationContext(sc); // Root AC

		System.out.println("webApplicationContext = " + rootAC);
		System.out.println("servletAC = " + servletAC);

		System.out.println("rootAC.getBeanDefinitionNames() = " + Arrays.toString(rootAC.getBeanDefinitionNames()));
		System.out.println("servletAC.getBeanDefinitionNames() = " + Arrays.toString(servletAC.getBeanDefinitionNames()));

		System.out.println("rootAC.getBeanDefinitionCount() = " + rootAC.getBeanDefinitionCount());
		System.out.println("servletAC.getBeanDefinitionCount() = " + servletAC.getBeanDefinitionCount());

		System.out.println("servletAC.getParent()==rootAC = " + (servletAC.getParent() == rootAC)); // servletAC.getParent()==rootAC = true
		return "home";
	}
}


📃 ApplicationContext

  • Context : 모듈 자체
  • ApplicationContext: 모듈을 둘러싼 모든 환경들을 묶어놓은 것
  • ApplicationContextFacade: ApplicationContext + cache
    (Facade 패턴: 라이브러리/프레임워크에 대한 복잡한 집합을 단순화된 인터페이스로 제공하는 구조적 디자인 패턴)

ApplicationContext 주요 메서드

Return TypeMethodDescription
intgetBeanDefinitionCount()정의된 빈의 개수 반환
String[]getBeanDefinitionCount()정의된 빈의 이름을 배열로 반환
-
빈 얻기
getBean(Class<T> requiredType)타입으로 빈 검색
Car car2 = (Car) ac.getBean(Car.class);
ObjectgetBean(Class<T> requiredType, Objecct... args)
getBean(String name)이름으로 빈 검색
Car car = (Car) ac.getBean("car");
getBean(String name, Class<T> requiredType)
getBean(String name, Object... args)
-
빈 존재 유무 확인
BooleancontainsBean(String name)"name"의 빈이 존재하는 지 확인
BooleancontainsBeanDefinition(String beanName)빈의 정의가 포함되어 있는지 확인
BooleancontainsLocalBean(String name)
AnotationfindAnnotaionOnBean(String beanName, Class annotationType)빈에 Annotaion 존재 유무 확인(@Component가 붙어있으면 반환)
-
타입확인
BooleanisPrototype(String name)빈이 프로토타입인지 확인
isSingleton(String name)빈이 싱글톤인지 확인
isTypeMatch(String name, Class<T> typeToMatch)"name"라는 이름의 빈의 타입이 Class<T>인지 확인
isTypeMatch(String name, ResolvableType typeToMatch)
-
String[]getAliases(String name)
getBeanNamesForAnnotation(Class annotationType)
getBeanNamesFoType(Class type, boolean includeNonSingletons, boolean allowEagerInit)@Component가 붙은 빈의 이름을 배열로 반환
getBeanNamesFoType(Class type)Class 또는 그 자손 타입인 빈의 이름을 배열로 반환
getBeanNamesFoType(ResolvableType type)
-
ClassgetType(String name)
MapgetBeanOfType(Class<T> type)
MapgetBeanOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
@Component
@Scope("prototype")
class Door {}
@Component class Engine {}
@Component class TurboEngine extends Engine {}
@Component class SuperEngine extends Engine {}

@Component
class Car {
    @Value("red") String color;
    @Value("100") int oil;
    @Autowired    Engine engine;
    @Autowired    Door[] doors;
}


🖇️loC와 DI

loC(Inversion Of flow Control 제어의 역전):
제어의 흐름을 전통적인 방식과 다르게 뒤바꾸는 것(flow Control: if문, for문)
DI(Dependency Injection 의존성 주입):
사용할 객체를 외부에서 주입받는 것

+) 전략패턴(Strategy Pattern):
✔️ 비슷하지만 다르게 구현되어야하는 행위들을 캡슐화하는 공통 인터페이스를 정의하고, 이를 각각의 클래스에서 실제 행위를 구현하여 동적으로 바꾸도록 하는 패턴
✔️ 객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정할 필요없이 전략을 변경하여 행위를 유연하게 확장 가능


🔨 스프링 에너테이션

@Autowired

인스턴스 변수(iv), setter, 참조형 매개변수를 가진 생성자, 메서드에 적용

❶ 생성자 주입: ✨BEST
✔️ 의존관계에 있는 객체들을 private final로 선언하고, 생성자를 통해 해당 객체들을 주입받도록 함
✔️ 불변하게 설계가능, 누락방지, final 사용

❷ 수정자(setter) 주입: 의존관계에 있는 객체들을 private final로 선언하고, 해당 변수에 대한 Setter 메서드를 생성하고 앞에 @Autowired를 붙여서 사용

❸ 필드 주입: 변수 앞에 @Autowired를 붙여서 사용

✔️ Spring container에서 타입으로 빈을 검색해서 참조 변수에 자동 주입(DI)

✴️ 검색된 빈이 n개이면, 그 중 참조변수와 이름이 일치하느 것을 주입 | SuperEngine
✴️ 주입 대상이 변수일 때, 검색된 빈이 1개 아니면 예외발생(반드시 1개) | SuperEngine
✴️ 주입 대상이 배열일 때, 검색된 빈이 n개라도 예외발생X(0개면 예외발생) | TurboEngine, SuperEngine
✴️ @Autowired(required=fale)일 때, 주입할 빈을 못찾아도 예외 발생X

@Resource

✔️ Spring container에서 이름으로 빈을 검색해서 참조 변수에 자동 주입(DI)
✔️ 일치하는 이름의 빈이 없으면, 예외 발생
✔️ 빈 이름을 생략하면 참조변수 이름 → 빈의 이름

@Component

✔️ <component-scan>로 @Component가 클래스를 자동 검색해서 빈으로 등록
✔️ @Controller, @Service, @Repository, @ControllerAdvice의 메타 애너테이션(애너테이션 내에 @Component 포함)

@Value / @PropertySource

✅ @Value 에너테이션을 이용하여 systemPropeties와 systemEnvironment 이용
✴️ @Value(“#{systemProperties[‘user.timezone’]}”) : 사용자의 시간대_GMT(+9)
✴️ @Value(“#{systemEnvironment[‘PWD’]}”) : 현재 작업 디렉토리

✅ 등호(=)를 구분자로 key/value를 저장해두면 외부파일로부터 값을 불러올수 있음


📋 스프링 애너테이션 vs. 표준 애너테이션(JSR-330)

✔️ JSR: Java Spec Request
javax.inject-1.jar : @Inject, @Named, @Qualifier, @Scope, @Singleton
annotations-api.jar : @Resource, @ManagedBean, @PreDestroy, @PostContruct

스프링(spring 제공)표준(Java제공)비고
@Autowired@Inject@Injectd에는 required 속성X
@Qualifier@Qualifier, @Named스프링의 @Qualifier와 표준의 @Named 유사
-@Resource스프링에는 이름검색 기능 없음
@Scope("singletone")@Singleton표준에서의 portotype이 디폴트
@Component@Named, @ManagedBean표준에서는 반디시 이름 필요

cf. spring의 @Scope와 표준의 @Scope와 다름(@Qualifier도 다른 기능임)
cf. 스프링에는 이름검색 기능이 없어 annotations-api.jar 필요


🔄️ 빈의 초기화

<property>를 이용한 초기화: .setter를 이용
✔️ <property> 태그 = setter로 초기화
(두개는 같은 역할을 하므로 <property> 사용 시 별도의 setter 초기화 불필요)

<constructor-arg>를 이용한 초기화: 생성자를 이용
✔️ 생성자가 있어야만 <constructor-arg>태그 사용 가능

<list>, <set>, <map>


참고) 자바의 정석 | 남궁성과 끝까지 간다

0개의 댓글