這里用一個(gè)簡單過程來說明。比如用戶輸入用戶名和密碼進(jìn)行Logon,系統(tǒng)校驗(yàn)用戶名密碼是不是正確。(這是個(gè)演示例子,所以后面的代碼一切從簡)。
用戶點(diǎn)擊登陸,請求被struts送到了Logonaction,如下:
protected ActionForward doExecute(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception{
String name = request.getParameter("name");
String password = request.getParameter("password");
ServletContext sc = request.getSession().getServletContext();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);
UserHandle userHandle = (UserHandle)ctx.getBean("userhandle"); //獲取userhanle實(shí)例
boolean isSuccess = userHandle.check(name,password);
if(isSuccess){
return mapping.findForward("success");
}else{
return mapping.findForward("failed");
}
}
userHandle實(shí)例是通過容器(ApplicationContext)獲取的,不需要自己寫創(chuàng)建對象的代碼了。容器實(shí)際上是通過讀取配置文件加載UserHandle類的,而這個(gè)過程對于程序員是透明的。
然后我們再分析下面這一句:
boolean isSuccess = userHandle.check(name,password);
userHanle類的通常實(shí)現(xiàn)方法可能是這樣,
class UserHandle{
public boolean check(String name,String password){
UserDAO userDao = DBOperator.getUserDAO(); //你自己的DB操作的封裝類
return userDao.check(name,password);
}
}
然后就可以想象UserDAO里就是數(shù)據(jù)庫連接,發(fā)送SQL的那一套。這樣做,程序員就不得不寫一個(gè) DBOperator類,用來創(chuàng)建DAO. 而Spring就是用來解放這樣的創(chuàng)建工作的,這樣 DBOperator這樣的類就沒必要程序員勞神去寫了,只需要這樣做:
class UserHandle{
private UserDAO userDao;
public boolean check(String name,String password){
return userDao.check(name,password);
}
public void setUserDao(UserDAO userDao){
this.userDao = userDao;
}
public UserDAO getUserDao(){
return this.userDao;
}
}
Spring 會(huì)根據(jù)配置文件中的類的依賴關(guān)系,自動(dòng)的把UserDAO對象注入到 UserHandle對象里,然后程序員去使用userDao就可以了。
既然是根據(jù)配置文件來注入的,那么配置文件是什么樣子呢?如下:
<beans>
<!--4-->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>efmweb</value></property>
</bean>
<!--3-->
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean" >
<property name="configLocation"><value>WEB-INF/sql-map-config.xml</value></property>
<property name="dataSource"><ref local="dataSource"/></property>
</bean>
<!--2-->
<bean id="userdao" class="UserDAO">
<property name="sqlMapClient"><ref bean="sqlMapClient"/></property>
</bean>
<!--1-->
<bean id="userhandle" class="UserHandle">
<property name="userDao"><ref bean="userdao" /></property>
</bean>
</beans>
從這個(gè)配置文件里我們可以看到它們的引用關(guān)系是:1–>2–>3–>4,即 UserHandle引用UserDAO, UserDAO引用 SqlMapClientFactoryBean, SqlMapClientFactoryBean引用 JndiObjectFactoryBean,JndiObjectFactoryBean負(fù)責(zé)根據(jù)jndiName獲得 datasource.
通過這樣層層引用的配置,spring就能自動(dòng)生成對象,不但免了程序員手動(dòng)寫對象生成的代碼,還很容易的把ibatis串連起來。
前面我們已經(jīng)根據(jù)代碼和配置文件知道了UserHandle是怎么引用和注入 UserDAO的方式,現(xiàn)在我們再看UserDAO是怎么引用 SqlMapClientFactoryBean 這個(gè)第三方類的.
5.2.2 為什么SqlMapClientFactoryBean與SqlMapClient類型不同也可被注入?
果然看到了getSqlMapClient()和setSqlMapClient()方法。我們?nèi)匀桓鶕?jù) UserHandle和UserDAO的注入方式,可以知道UserHandle的屬性u(píng)serDao其實(shí)就是UserDAO類的實(shí)例注入。同樣,配置文件中UserDAO的屬性sqlMapClient 也應(yīng)該是SqlMapClientFactoryBean類的實(shí)例注入才對。但 SqlMapClientFactoryBean并不是SqlMapClient類型,這怎么能注入呢?
這是因?yàn)镾pring的機(jī)制的緣故。簡單的說,如果一個(gè)bean實(shí)現(xiàn)了 FactoryBean接口,那么Spring就不會(huì)把該bean本身實(shí)例化并返回,而是返回該bean的getObject()返回的對象。這是Sprign的游戲規(guī)則。我們來看一眼 SqlMapClientFactoryBean的源碼片段:
public class SqlMapClientFactoryBean implements FactoryBean,InitializingBean {
private SqlMapClient sqlMapClient;
protected SqlMapClient buildSqlMapClient(Resource configLocation,Properties properties) throws IOException {
InputStream is = configLocation.getInputStream();
if (properties != null) {
if (buildSqlMapClientWithInputStreamAndPropertiesMethodAvailable) {
return SqlMapClientBuilder.buildSqlMapClient(is,properties);
} else {
return SqlMapClientBuilder.buildSqlMapClient(new InputStreamReader(is), properties);
}
} else {
if (buildSqlMapClientWithInputStreamMethodAvailable) {
return SqlMapClientBuilder.buildSqlMapClient(is);
} else {
return SqlMapClientBuilder.buildSqlMapClient(new InputStreamReader(is));
}
}
}
//這里就是返回的、并會(huì)被注入到其它類里的對象
public Object getObject() {
return this.sqlMapClient;
}
}
Spring這種機(jī)制的官方說明:
public interface FactoryBean
Interface to be implemented by objects used within a BeanFactory which are themselves factories. If a bean implements
this interface, it is used as a factory for an object to expose, not directly as a bean instance that will be exposed
itself.
NB: A bean that implements this interface cannot be used as a normal bean. A FactoryBean is defined in a bean style, but
the object exposed for bean references (getObject() is always the object that it creates.
FactoryBeans can support singletons and prototypes, and can either create objects lazily on demand or eagerly on
startup. The SmartFactoryBean interface allows for exposing more fine-grained behavioral metadata.
This interface is heavily used within the framework itself, for example for the AOP ProxyFactoryBean or the
JndiObjectFactoryBean. It can be used for application components as well; however, this is not common outside of
infrastructure code.
NOTE: FactoryBean objects participate in the containing BeanFactory's synchronization of bean creation. There is usually
no need for internal synchronization other than for purposes of lazy initialization within the FactoryBean itself (or
the like).
Since:
08.03.2003
Author:
Rod Johnson, Juergen Hoeller
See Also:
BeanFactory, ProxyFactoryBean, JndiObjectFactoryBean
到此為止,我們已經(jīng)知道UserDAO與SqlMapClient的關(guān)系,接下來看如何使用IBatis
5.2.3 如何使用IBatis
Ibatis是半自動(dòng)化數(shù)據(jù)對象封裝方案。我們看userDao怎么使用Ibatis
class UserDAO{
private SqlMapClientFactoryBean sqlMapClient;
public void setSqlMapClient(SqlMapClientFactoryBean sqlMapClient){
this.sqlMapClient = sqlMapClient;
}
public SqlMapClientFactoryBean getSqlMapClient(){
return this.sqlMapClient;
}
//這里是使用Ibatis的地方
public User getUser(String userid) {
return super.getSqlMapClientTemplate().queryForList("userid",
userid);
}
}
結(jié)合者段代碼以及前面的配置文件的第三段,相信你已經(jīng)大體明白了Ibatis的使用。這里就做一簡單描述。先重新看一下配置文件的第三段:
<!--3-->
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean" >
<property name="configLocation"><value>WEB-INF/sql-map-config.xml</value></property>
<property name="dataSource"><ref local="dataSource"/></property>
</bean>
注意這里有一個(gè)sql-map-config.xml文件。這個(gè)文件的內(nèi)容如下:
<sqlMapConfig>
<sqlMap resource="user.xml"/>
</sqlMapConfig>
再看user.xml文件里是什么?
<sqlMap>
<select id="userid" resultclass="UserDAO" parameterClass="java.lang.String">
select * from user where userid=#userid#
</select>
</sqlMap>
現(xiàn)在,結(jié)合UserDAO類,和三個(gè)xml文件,應(yīng)該容易知道它的原理: 邏輯層調(diào)用userDAO,userDAO通過Spring的粘合會(huì)去調(diào)用 Ibatis,Ibatis讀取sql-map-config.xml文件,然后順藤摸瓜,再讀取user.xml文件,從中找到id="userid"的map,傳遞userid 給#userid#,得到完整的sql,執(zhí)行sql,返回結(jié)果。詳細(xì)的Ibatis的使用請參考相關(guān)手冊。