2017年4月12日 星期三

Spring Web (Spring3.x 二十五)


import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
    
@WebListener
public class SpringListener implements ServletContextListener {
    private ApplicationContext context;
    
    @Override
    public void contextInitialized(ServletContextEvent e) {
        String ooo = e.getServletContext().getInitParameter("ooo");
        context = new ClassPathXmlApplicationContext(ooo);
        e.getServletContext().setAttribute("xxx", context);
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        ((ClassPathXmlApplicationContext) context).close();
    }
}

※自己模擬一支啟動時的 Spring


※web.xml

<context-param>
    <param-name>ooo</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
    
<!-- <listener> -->
<!-- <listener-class>package.className</listener-class> -->
<!-- </listener> -->

※尤於上面已經有Annotation,所以此時的 listener 可以不要

※Spring API裡的 org.springframework.web.context.ContextLoaderListener
建構子有說明使用 contextClass 或 contextConfigLocation
意思就是我上面的ooo,Spring 取名為 contextConfigLocation,對應的是xml設定檔
但如果用的不是設定檔,是Class,那就寫成 contextClass
寫 ooo 是無效的,不要被誤導了

而ApplicationContext,也就是如上的xxx,被存成一個常數,叫
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE

ContextLoaderListener 的父類別 ContextLoader 還有說
預設會去 /WEB-INF/applicationContext.xml 抓設定檔
所以如果你的設定檔放在這個路徑,而且也叫 applicationContext.xml
那 context-param 就可以不用寫



※使用Spring 的Web

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

※ContextLoaderListener 是 ServletContextListener 的子類



※測試

※pom.xml

<properties>
    <jdk.version>1.8</jdk.version>
    <spring.version>3.2.15.RELEASE</spring.version>
</properties>
    
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
</dependencies>
    
<build>
    <finalName>SpringWeb</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <source>${jdk.version}</source>
                <target>${jdk.version}</target>
            </configuration>
        </plugin>
    </plugins>
</build>



※applicationContext.xml

<bean id="b" class="Book">
    <property name="bookName" value="DragonBall" />
    <property name="bookMoney" value="70" />
</bean>



※bean

public class Book {
    private String bookName;
    private int bookMoney;
    // setter/getter...
}



※jsp頁面

<form action="xxx" method="post">
    <input type="submit" value="送出" />
</form>



※Servlet

@WebServlet(urlPatterns = { "/xxx" })
public class ServletTest extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String app = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
        ApplicationContext ctx = (ApplicationContext) getServletContext().getAttribute(app);
    
        System.out.println(ctx.getBeanDefinitionCount());
        for (String s : ctx.getBeanDefinitionNames()) {
            System.out.println(s);
        }
    
        Book b = ctx.getBean("b", Book.class);
        System.out.println(b);
    }
}

※使用 Annotation 時,在啟動時出現 @EnableAsync annotation metadata was not injected 的錯誤,我將 Book.java 增加 package 就解決了,也有可能是 component-Scan 寫錯



※使用 annotation 的設定


<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>bean.Animal</param-value>
</context-param>
    
<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
    
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

※並不一定要使用常數的方式取得 applicationContxt,還可以用 ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

※多個 class 可用「,」隔開,或者用 @ComponentScan



※取得 @Autowired

super.init();
// super.init(config);
WebApplicationContextUtils.getWebApplicationContext(getServletContext())
    .getAutowireCapableBeanFactory()
    .autowireBean(this);
    
// WebApplicationContextUtils.getWebApplicationContext(getServletContext())
//     .getAutowireCapableBeanFactory()
//     .configureBean(this, "animal");

※覆寫 init 方法,有兩個,隨便一個都可以,有參數的可用 config.getServletContext() 取得 ServletContext

※兩者選其一即可

※animal 可以寫 @ComponentScan,這樣就可以抓到很多了