Spring的一些零碎知識點整理
要想在Web工程中配置Spring,首先需要在工程加入spring-web包,我這裏使用的是maven的web工程,pom.xml配置文件配置的依賴如下:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.14.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.14.RELEASE</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.0</version> <scope>test</scope> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.44</version> </dependency> </dependencies>
然後在工程的web.xml文件中配置Spring的監聽器,web.xml內容如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app 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 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>Archetype Created Web Application</display-name> <!-- 配置Spring的監聽器類 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置Spring配置文件的路徑 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:app.xml</param-value> </context-param> </web-app>
這裏的配置要說明一下,在配置Spring配置文件的路徑時,如果你的配置文件放在WEB-INF下是不需要加上 classpath: 前綴的,只需要寫上路徑即可,例如:
<!-- 配置Spring配置文件的路徑 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/app.xml</param-value>
</context-param>
完成以上的配置之後,我們先來寫一個簡單的Servlet,並配置上Spring的註解:
package org.zero01.test;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component("login")
@WebServlet("/login")
public class LoginServlet extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
然後寫一個測試的Servlet,看看能否從Spring容器中獲得實例對象,在web項目中我們可以通過WebApplicationContextUtils類來幫我們獲取Spring的管理對象:
package org.zero01.test;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.zero01.pojo.Student;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 獲得web工程中的Spring管理對象
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
// 同樣的我們可以通過這個管理對象來獲得我們需要的實例對象
LoginServlet loginServlet = (LoginServlet) webApplicationContext.getBean("login");
Student student = (Student) webApplicationContext.getBean("stu");
// 先打印一下,看看能否獲取到對象
PrintWriter printWriter = resp.getWriter();
printWriter.println(loginServlet);
printWriter.println(student);
}
}
輸出結果如下:
org.zero01.test.LoginServlet@5afad74f
org.zero01.pojo.Student@1ff85274
如上,可以看到對象已經成功獲取到了,那就代表我們的配置沒有問題,這樣我們就可以在web項目中愉快的使用Spring了。
簡單說明一下WebApplicationContext對象的加載流程:
-
我們都知道Tomcat啟動時會去加載web.xml文件中的配置,而我們在web.xml配置了Spring的監聽類以及Spring配置文件的路徑
- 然後Spring的ContextLoaderListener監聽類會監聽著ServletContext的初始化以及銷毀,這一點查看源碼即可得知:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.web.context;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
- 當ServletContext初始化時會調用該監聽類的contextInitialized方法,而該方法調用了initWebApplicationContext方法並傳遞了ServletContext對象,從方法名的字面意思也可以看出這個方法是用於初始化WebApplicationContext對象的
- 當WebApplicationContext被初始化時,通過ServletContext對象得到了我們在web.xml中配置的初始化參數,也就是Spring配置文件的路徑,於是Spring的配置文件也被加載起來了,成功加載後,WebApplicationContext對象也就初始化完成了。如果沒被加載起來就會報錯,初始化失敗
- WebApplicationContext對象初始化完成後,就會存放在ServletContext的屬性裏,這裏就完成了整個加載流程
- 但是由於我們不知道鍵/值是什麽,所以我們無法直接在ServletContext裏獲得WebApplicationContext對象,而是得通過Spring提供的工具類WebApplicationContextUtils來獲得
多個Spring配置文件
有些情況下,我們可能需要多個配置文件,每個配置文件配置不同的模塊,如下:
當需要將這些配置文件都引入一個總的配置文件時,可以使用以下語句:
<import resource="app-aop.xml"/>
<import resource="app-ioc.xml"/>
<import resource="app-tran.xml"/>
Spring配置屬性文件
Spring支持使用屬性文件來配置一些參數,但是這種使用屬性文件來配置參數的方式用得不多,因為很雞肋,而且還會導致到處都是屬性文件的散亂情況。不過有可能在一些特殊的情況下會用到,所以在此也記錄一下,這裏通過配置數據源對象的參數來演示一下如何通過Spring配置屬性文件:
首先創建一個屬性文件dataSourceInfo.properties,文件內容如下:
database.driver=com.mysql.jdbc.Driver
database.url=jdbc:mysql:///school
database.user=root
database.pw=your_password
database.max.pool.size=10
database.min.pool.size=1
database.loginTimeout=2000
然後編輯Spring的配置內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<!-- 配置屬性文件的路徑 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<array>
<value>datasourceinfo.properties</value>
</array>
</property>
</bean>
<!-- 配置數據源對象 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${database.driver}"
p:jdbcUrl="${database.url}"
p:user="${database.user}"
p:password="${database.pw}"
p:maxPoolSize="${database.max.pool.size}"
p:minPoolSize="${database.min.pool.size}"
p:loginTimeout="${database.loginTimeout}"
/>
</beans>
測試代碼如下:
package org.zero01.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class Test {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("app.xml");
DataSource dataSource = (DataSource) app.getBean("dataSource");
try {
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
打印結果:
com.mchange.v2.c3p0.impl.NewProxyConnection@3578436e
從打印結果可以看到,數據源對象已經成功拿出來了。但是從整個配置流程可以看到有了Spring後還使用屬性文件來配置參數就有些繞彎子了,這些參數都是可以在Spring配置文件中直接配置的,而且本身Spring的目的之一就是避免存在大量的屬性文件。
Spring在web項目中的初始化類
如果你希望在Tomcat服務器啟動時,初始化一個類,並調用該類中的某個方法時,那麽你就可以通過實現Spring中的兩個接口來完成。首先是InitializingBean接口,實現這個接口的類會在Tomcat服務器啟動時被初始化並調用其中的afterPropertiesSet方法:
package org.zero01.test;
import org.springframework.beans.factory.InitializingBean;
public class InitBean implements InitializingBean {
public void afterPropertiesSet() throws Exception {
System.out.println("我被初始化了 —— InitializingBean");
}
}
另一個接口是 ApplicationListener ,這個接口的方法有數據源參數,所以這個方法可以完成更多的事情,而且當你有某個邏輯是必須要等到所有的bean都被處理完成之後再執行的話,就適合使用這個接口,因為所有的bean都被處理完成之後這個接口的方法才會被調用:
package org.zero01.test;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
public class AppInit implements ApplicationListener{
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println("我被初始化了 —— ApplicationListener");
}
}
然後需要配置一下這兩個類,Spring配置文件內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<bean class="org.zero01.test.AppInit"/>
<bean class="org.zero01.test.InitBean"/>
</beans>
完成配置後啟動Tomcat服務器,Tomcat服務器啟動時輸出結果如下:
可以看到,實現 InitializingBean 接口的類比實現 ApplicationListener 接口的類先被初始化。所以它們之間還是有一些區別的,需要根據具體的業務場景來進行使用哪一個接口。
通過實現接口獲得ApplicationContext或者BeanFacrory的對象
通過實現接口獲得ApplicationContext對象,代碼如下:
package org.zero01.test;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class Test implements ApplicationContextAware {
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
LoginServlet loginServlet = (LoginServlet) applicationContext.getBean("login");
}
}
通過實現接口獲得BeanFacrory對象,代碼如下:
package org.zero01.test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
public class Test implements BeanFactoryAware {
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
LoginServlet loginServlet = (LoginServlet) beanFactory.getBean("login");
}
}
Spring的一些零碎知識點整理