这里用一个简单过程来说明。比如用户输入用户名和密码进行Logon,系统校验用户名密码是不是正确。(这是个演示例子,所以后面的代码一切从简)。
用户点击登陆,请求被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实例
boolean isSuccess = userHandle.check(name,password);
if(isSuccess){
return mapping.findForward("success");
}else{
return mapping.findForward("failed");
}
}
userHandle实例是通过容器(ApplicationContext)获取的,不需要自己写创建对象的代码了。容器实际上是通过读取配置文件加载UserHandle类的,而这个过程对于程序员是透明的。
然后我们再分析下面这一句:
boolean isSuccess = userHandle.check(name,password);
userHanle类的通常实现方法可能是这样,
class UserHandle{
public boolean check(String name,String password){
UserDAO userDao = DBOperator.getUserDAO(); //你自己的DB操作的封装类
return userDao.check(name,password);
}
}
然后就可以想象UserDAO里就是数据库连接,发送SQL的那一套。这样做,程序员就不得不写一个 DBOperator类,用来创建DAO. 而Spring就是用来解放这样的创建工作的,这样 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 会根据配置文件中的类的依赖关系,自动的把UserDAO对象注入到 UserHandle对象里,然后程序员去使用userDao就可以了。
既然是根据配置文件来注入的,那么配置文件是什么样子呢?如下:
<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>
从这个配置文件里我们可以看到它们的引用关系是:1–>2–>3–>4,即 UserHandle引用UserDAO, UserDAO引用 SqlMapClientFactoryBean, SqlMapClientFactoryBean引用 JndiObjectFactoryBean,JndiObjectFactoryBean负责根据jndiName获得 datasource.
通过这样层层引用的配置,spring就能自动生成对象,不但免了程序员手动写对象生成的代码,还很容易的把ibatis串连起来。
前面我们已经根据代码和配置文件知道了UserHandle是怎么引用和注入 UserDAO的方式,现在我们再看UserDAO是怎么引用 SqlMapClientFactoryBean 这个第三方类的.
5.2.2 为什么SqlMapClientFactoryBean与SqlMapClient类型不同也可被注入?
果然看到了getSqlMapClient()和setSqlMapClient()方法。我们仍然根据 UserHandle和UserDAO的注入方式,可以知道UserHandle的属性userDao其实就是UserDAO类的实例注入。同样,配置文件中UserDAO的属性sqlMapClient 也应该是SqlMapClientFactoryBean类的实例注入才对。但 SqlMapClientFactoryBean并不是SqlMapClient类型,这怎么能注入呢?
这是因为Spring的机制的缘故。简单的说,如果一个bean实现了 FactoryBean接口,那么Spring就不会把该bean本身实例化并返回,而是返回该bean的getObject()返回的对象。这是Sprign的游戏规则。我们来看一眼 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));
}
}
}
//这里就是返回的、并会被注入到其它类里的对象
public Object getObject() {
return this.sqlMapClient;
}
}
Spring这种机制的官方说明:
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
到此为止,我们已经知道UserDAO与SqlMapClient的关系,接下来看如何使用IBatis
5.2.3 如何使用IBatis
Ibatis是半自动化数据对象封装方案。我们看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);
}
}
结合者段代码以及前面的配置文件的第三段,相信你已经大体明白了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>
注意这里有一个sql-map-config.xml文件。这个文件的内容如下:
<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>
现在,结合UserDAO类,和三个xml文件,应该容易知道它的原理: 逻辑层调用userDAO,userDAO通过Spring的粘合会去调用 Ibatis,Ibatis读取sql-map-config.xml文件,然后顺藤摸瓜,再读取user.xml文件,从中找到id="userid"的map,传递userid 给#userid#,得到完整的sql,执行sql,返回结果。详细的Ibatis的使用请参考相关手册。