내가 보려고 정리하는/Spring

웹 : Spring , java, web : 0406(3)

보동이용용 2023. 4. 6. 15:26
반응형

filter(상위/하위 컨텍스트)

상위 컨텍스트(웹 관련 없는 것, root) / 하위 컨텍스트 (웹 관련된 것들, child)

<context:component-scan base-package="kr.or.ddit.sample" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

메타 어노테이션을 포함시키고 빼고싶은 것을 뺀다. 

service, repository의 메타 어노테이션 component로 되어있다.

public static void main(String[] args) {
    try(
        ConfigurableApplicationContext rootContext = 
       		new ClassPathXmlApplicationContext("kr/or/ddit/sample/hierarchy/conf/root-context.xml");
        ConfigurableApplicationContext childContext = 
            new ClassPathXmlApplicationContext(new String[] {
                    "kr/or/ddit/sample/hierarchy/conf/child-context.xml"
            }, rootContext);
    ){
        rootContext.registerShutdownHook();
        childContext.registerShutdownHook();
		//자식에서 부모 가능
        SampleService service = childContext.getBean(SampleService.class);
        log.info("child 로부터 주입된 service 객체 : {}", service);
		
        //부모에서 자식은 불가능 오류.
        SampleController controller = rootContext.getBean(SampleController.class);
    }

}

 


 

properties파일도 해보자

dbInfo 라는 properties 파일을 읽어야한다.

xmlns:util="http://www.springframework.org/schema/util" 이것도 추가했다.

convention over configuration (CoC)  >> 리스트 기본은 ArrayList.

<util:list id="sampleList" list-class="java.util.LinkedList">
    <value>element1</value>
    <bean class="java.util.Date" />
</util:list>

사이즈가 미리 채워진채로 컬렉션을 만들어야하기 때문에 utils 네임스페이스를 추가하고 사용하려고 한다.

컬렉션을 만들고 컬렉션을 미리 초기화 시켜준다는 장점이 있다. 

properties파일의 특징 : 휘발성이 아니다. 파일로 저장된다. > location이라는 속성을 갖는다.

<util:properties id="dbInfo" location="classpath:kr/or/ddit/db/dbinfo.properties">
    <prop key="prop1">VALUE1</prop>
    <prop key="prop2">VALUE2</prop>
</util:properties>

<bean class="kr.or.ddit.properties.obj.DBInfo" 
    p:driverClassName="#{dbInfo.driverClassName}"
    p:url="#{dbInfo['url']}"
    p:username="#{dbInfo['username']}"
    p:password="#{dbInfo['password']}"
    p:initialSize="#{dbInfo['initialSize']}"
    p:maxTotal="#{dbInfo['maxTotal']}"
    p:maxWait="#{dbInfo.maxWait}"
/>

EL 표현식이 가능한데, 표현법이 조금 다르다. $ 대신 #

그리고 연산배열구조(ㅋㅋㅋ[ ' ㅋㅋㅋ ' ])가 가능하다. 닷노테이션(ㅋㅋㅋ.ㅋㅋㅋ) 가능하다

p:initialSize="#{dbInfo['initialSize'] + 4}"

jsp에서는 2 + 4 = 6 으로 나온다.( 강제로 파싱한 결과)  그러나 Spring에서는>> 컨켓연산하여  24 로 나온다.

p:maxTotal="#{dbInfo['maxTotal'] * 3}"

jsp에서는 *3 하면 곱하기 3이지만 Spring에서는>> 2를 3배로 늘려서 2, 2, 2하여 결과는 222로 나온다.

 

properties 파일 읽어들이는 방법 2

<context:property-placeholder/> : bean이 등록되는 것이 아니다.

커다란 그릇에 프로퍼티들이 담겨있어 그걸 프로퍼티 소스라고 해

이것에서 코드어시스트 받아보면 로케이션 있어. 밑에서는 그걸로 프로퍼티스를 읽어서 객체로 만들었는데

여기서는 객체로 만들지 않고 이 안에다가 싹 다 넣어주는 것이야.

<context:property-placeholder location="classpath:kr/or/ddit/db/dbinfo.properties"/>
	
<bean id="vo2" class="kr.or.ddit.properties.obj.DBInfo" 
    p:driverClassName="${p:driverClassName}"
/>

이건 플레이스홀더야 여기에는 빈의 아이디가 아닌 프로퍼티네임 그대로 들어가 

 

 

수동으로 등록하지 않는 상황을 만들어보자.

▶Properties-Read-auto.xml

<context:component-scan base-package="kr.or.ddit.properties.obj" />

<util:properties id="dbInfo" location="classpath:kr/or/ddit/db/dbinfo.properties">
<prop key="prop1">VALUE1</prop>
<prop key="prop2">VALUE2</prop>
</util:properties>

▶DBInfo

@Component
@Data //setter가 생김. -> setterInjection 가능
public class DBInfo {
//	@Inject //의미상 맞지가 않아...  특정 프로퍼티의 값을 넣어야해서. 프로퍼티자체가 아니라.
//	@Value("${driverClassName}")
	@Value("#{dbInfo.driverClassName}")
	private String driverClassName;
	@Value("#{dbInfo.url}")
	private String url;
	@Value("#{dbInfo.user}")
	private String username;
	@Value("#{dbInfo.password}")
	private String password;
	@Value("#{dbInfo.initialSize}")
	private int initialSize;
	@Value("#{dbInfo.maxTotal}")
	private int maxTotal;
	@Value("#{dbInfo.maxWait}")
	private long maxWait;
}

 

 

 

자바 컨테이너방식으로 컨피그 설정하기 

@Configuration //beans
@Lazy
@ComponentScan(basePackages = "kr.or.ddit.container.obj", useDefaultFilters = true)
public class JavaConfigContainerConfiguration {
	
//	@Bean //메서드 이름이 bean id, 싱글턴
//	public Bar bar1() {
//		return new Bar();
//	}
//	
//	@Bean //메서드 이름이 bean id, 싱글턴
//	public Bar bar2() {
//		return new Bar();
//	}
//	
//	@Bean
//	public Baz baz() {
//		return new Baz();
//	} 
//컴포넌트 설정에 필요한 베이스 패키지가 없어서 실행되지 않는다.
//어노테이션이 있어도 베이스 패키지 등록이 없다면 실행 오류.
	
	@Bean
	@Scope("prototype")
	public Foo foo(@Autowired @Qualifier("bar") Bar bar, Baz baz) {
		Foo foo = new Foo(bar);
		foo.setBaz(baz);
		return foo;
	}
}

 

 

 

어노테이션 기반의 이벤트 리스너

버튼을 : 이벤트 타겟

클릭하면 : 이벤트 종류

alert 메세지 : 이벤트 핸들러

>> 마지막 : 이벤트 타겟과 이벤트 핸들러 연결 

 

<<컨테이너가 구동되면 로그메세지 출력!>>

컨테이너 : 이벤트 타겟

구동되면 : 이벤트 종류

로그메세지 출력 : 이벤트 핸들러

>>마지막 : 이벤트 타겟과 이벤트 핸들러 연결 

@Slf4j
@Component //애매한 객체에게 사용. 핸들러와 타겟을 연결
public class CustomEventListener {
	
	@EventListener(ContextRefreshedEvent.class) //이 핸들러는 이 이벤트만 처리할거야.
	public void eventHandler(ContextRefreshedEvent event) {
		log.info("============ 컨텍스트 로딩 완료! =============");
	}
}
/**
 * 이벤트 처리 단계
 * 1. 이벤트 타겟 결정
 * 2. 처리할 이벤트 종류 결정
 * 3. 이벤트 처리 핸들러 구현
 * 4. 이벤트 타겟과 핸들러를 연결.
 * let handle = function(event){ //핸들러는 이벤트를 받는다.
 * 		alert("메시지" + event.target);  //모든 핸들러는 타겟에 대한 레퍼런스를 갖는다.
 * }
 * ${"button").on("click", handler);
 * //컨테이너가 구동이되면 로그메세지 출력 
 */
public class SpringEventHandlerDesc {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = 
				new AnnotationConfigApplicationContext(JavaConfigContainerConfiguration.class);
		context.registerShutdownHook();
	}
}

 

 

 

Web Application 한정으로 쓸 수 있는 Context가 있다.

아직 우리는 등록해두지 않았다.

등록하자.

 <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${org.springframework-version}</version>
</dependency>

 

web용 컨테이너 설정해야한다.

DS HM HA VR - spring-webmvc

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

web.xml

ContextLoaderListener     implements    ServletContextListener

ServletContextListener :  Interface for receiving notification events about ServletContext lifecycle changes.

>> 제일먼저 발생한다. >그곳에 container를 시작해야한다. >그것의 리스너가 web.xml에 등록된것이 임플리먼츠하는 것이다.

 

/**
 * Initialize the root web application context. 
 * // 우리 어플리케이션의 엔트리포인트
 * // 맨처음 실행되는 곳
 */
@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}


/**
 * Close the root web application context.
 */
@Override
public void contextDestroyed(ServletContextEvent event) {
    closeWebApplicationContext(event.getServletContext());
    ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
<context:component-scan base-package="egovframework">
   <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

상위 컨테이너 

mapper, transaction 상위로 빼둔것을 볼 수 있다.

<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/egovframework/springmvc/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

하위 컨테이너 ㄴ

dummySpring 

리스너가 상위 컨테이너를 생성하고 컨텍스트 파람으로 넘기고 있다.

서블릿이 하위 컨테이너를 생성하고 있고 하위 설정파일로 들어가 있다.

 

1.웹과 상관없거나 2. 기존의 시스템과 연결되어야하는 경우 상위로 빠진다.

<context:component-scan base-package="kr.or.ddit" /> 어노테이션만 붙이면 무조건 다 하위로 들어가도록 하는 설정

분리하려면 선택적인 필터링이 필요하다.

자동으로 등록

<annotation-driven />

명시적으로 등록

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
</beans:bean>
정적자원 일괄등록
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />


<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

톰캣을 살펴보자

톰캣도 어플리케이션이라서 서블릿이 있다. jsp도 서블릿

default 서블릿 등록되어 있다. WS의 정적자원처리를 톰캣이 해주고 있는데 그럴 수 있었던 이유는 default서블릿 덕분이다. 이 default의 url은 "/" 정적자원 담당 url 웬만하면 이 주소로 맵핑걸지 말라고 되어 있다. spring이 이 주소를 가져가버림. 그럼 우리는 정적 자원을 처리할 수 없다. 정적, 동적 요청을 모두 디스패처서블릿이 감당해야한다. 그래서 우리가 감당하지 않기 위해 넘긴것. 정적자원을 담당하는 컨트롤러를 만들지 않아도 된다. "/resources/**" (/** 몇개로 쪼개져도 상관 없다.) requestMapping처럼 트레이싱 할 수 있도록 맵핑의 역할을 한다. 장점 : 일괄처리가 가능하다.  cache-period="0"/ 캐시를 남기지 말아라. 

 

컨테이너 계층구조 사용하기

myBatis연동해보기

반응형