1、关于耦合度。
所谓耦合度,按照本人的理解也就是指程序之间的关联程度。例如A类的运行必须由B类来支持,也就是说A类为了完成
一定的功能必须调用B类,那么A和B之间就存在了一定的关联,在程序设计中我们把这种关系叫做耦合。而在传统的软件
设计中降低程序之间耦合度是一个很重要思想。基于这种思想原则,我们在WEB系统设计中把数据库的访问操作(持久化)
和系统的业务流程控制中分离出来,这样就可以极大的降低了系统的耦合度,提高了程序的可移植性。例如把我们原本运行
在基于MySql数据系统的程序移植到Oracle数据库下。如果把数据库访问操作全部代码写到业务流程控制中(如在Struts的Action
中直接使用JDBC访问数据库)那么我们移植后的修改工作将会十分复杂,并且苦难特别大,尤其当我们在多个模块中采用了上述的
方式,那么这样的工作是多么的令人厌烦和恐惧啊。如果我们把数据库访问的操作放到一个专门的类中去完成,而在我们的WEB前台
流程控制中只是简单的调用这个类的方法,那么当我们的系统需要移植时候我们就可以简单的修改一下数据访问操作层的代码即可。
这样一方面另我们的工作大大的简化,另一方面使我们的代码更加优雅!
2,关于持久层
持久层是一个很大概念,并不是能够用三言两语就可以解释的很透彻的概念。针对我们上面的例子,我们所设计的那个专门用来访问
底层数据库系统的类就可以简单认为它是持久层上的一个类。它的主要任务就是把系统内的数据根据需要存储到数据库系统中,从而使得
数据可以持久的存储到数据库中从而实现数据的持久化过程。总而言之,持久层中的类主要是用来访问底层数据库系统,并允许其他程序
调用自己,从而实现对数据库的增,删,改,查的相关操作。总之持久层中的类大多都是工具类,主要作用是用来被别人调用的!
3、接口的应用
在实际的项目过程中,我们经常要进行面向接口编程,当然这种思想比较复杂,个人体会是面向接口编程一个比较突出的特点是利用接口
我们可以极大的提高程序的灵活度和可扩展性。首先接口定义了一套规则,一套与外部程序进行通信的规则,在WEB开发中的Servlet就极大
的发挥了接口的特性。针对上面提到的用例中,为了更好的在系统中进行分层,我们设计多个DAO接口,比如在一个电子商务系统中,既有用户注册登陆模块,又具
有产品查询等相关模块,而每一个模块都几乎不可避免的要对数据库进行相关的操作,如果只用一个DAO类来实现所有的这些功能是不可能的,
因此我们需要设计多个DAO接口。而每一个接口的实现类可以有多个,这样做的好处在于当我们需要对系统进行升级的时候,我们只需要在业务层改变相关的
传入的实现类即可(比如DAO的实现类可以有利用JDBC技术的来实现的,也有利用Hibernet的实现类,还有利用ibatis的),而在我们设计业务实现代码的过程
中可以充分的利用接口的特性来构造我们的代码,我们在业务层中只需关心我们调用的是否是一个DAO类(或Manager类)而不需要关心我们所接受的DAO类到底是
什么类型的,这样就很好的体现了面向对象思想中的多态的特性展示。从而提高了程序的灵活度和可扩展性。
4,关于Manager类的一些看法。
有人会问:既然DAO的设计已经实现了把分层设计的思想,那么为什么还要设计一个Manager类呢,Manager类的功能到底是什么?我个人的理解是,在
我们的系统中,业务逻辑控制层可以分为前台和后台两个层面,前台程序(比如Struts中的Action类)最好只负责与WEB相关的逻辑控制,它的主要
主要是面对用户的,用来对用户相关操作进行响应,而对于后台程序(比如利用Log4j技术来写入系统日志等)主要是针对开发人员的,我们可以利用
后台程序运行的结果来获取相关信息,这一层面属于后台业务层面。这样我们设计了Manager类,利用它来调用DAO类从而间接的对数据库进行访问,并且
实现了后台的业务控制。而在应用中我们最好也按照设计DAO类的思想来利用接口设计我们的Manager类,从而细化我们我的系统,提高程序的灵活性。
5,Factory类的设计思想。
既然我们的WEB层在运行过程中要调用Manager类和DAO类来实现对数据库系统的访问,那么当我们对系统进行升级和扩展后,那么我们不可避免的要对WEB层的
代码进行改造,这看起来似乎很简单,但当我们的系统毅然庞大时,即使很小的一些改动也是让人畏惧的,并且很有可能引发系统问题。在这样的需求下
我们设计出一个Factory类实现对Manager的“组装”。而在WEB层的调用中直接利用Factory类来获得Manager类来进行操作。Factory类的思想精髓在于利用反射机制
得到相关的类,并把所得到的类进行组装后返回给WEB层,而我们的需要反射进来的类的名字只需写在一个配置文件中即可。这样我们就可以实现不修改源代码,只修改
配置文件即可实现对系统的改变(关于Factory类的具体代码,可以参考Spring的ClassPathXmlApplicationContext的源代码,或者向传智播客的张孝翔老师请教),同时
我们在自行设计Factory时尽量的要把此类做为单例模式,把相关的方法设计成静态的方法从而节省内存空间。
6,Spring的Factory.
Spring是一个功能极其强大的框架,而它的核心类就是一个工厂类,利用它我们可以简化开发人员对工厂类设计。所谓框架,就是一个实现了某个功能的工具系统,
当然它只是一个半成品,我们可以手动的对它进行添加来实现我们的功能(比如Strust就是一个很好的实现了MVC模式的框加,而不严格的说TOMCAT也可以看作是一个
框加)。Spring提供了众多的工厂类,而它们都是ApplicationContext接口的实现类。只是这些工厂类所读取的文件只是XML文件。因此我们利用Spring的工厂类,关键是
对XML文件的配置。
7,Ibatis的简介。
为了实现数据的持久化操作,我们的应用程序必须与底层数据库系统进行交互,一个比较古老但”万金油“的办法是利用JDBC技术(Spring也提供JdbcTemplate来简化我们的
工作),而在面向对象的思想中,我们面对的都是数据都是一个一个的对象,而数据库内存储的是一张张的数据表,当我们访问数据库时需要把一个程序中的一个个的对象
转化成数据库可识别的数据,或者把从数据库中得到的数据封状成一个个的对象,并把这些对象返回给调用程序。而Hibernate,ibates等框架正好提供了这样的操作,可以把
对象到数据库的转化通过框架自动完成,从而避免了在程序中手动转化,而这种技术我们统称为ORM(Object Relation Mapping)技术。Hibernet是一个全自动的框架,它的功能
异常强大,而Ibatis是一种半自动的ORM技术,但它却能完成一些特殊的功能。Ibatis的核心类是SqlMapClient类,它通过对配置文件(sql-map-config.xml)的读取来完成对数据的
操作,在我们的DAO中只需要调用SqlMapClient相关方法即可完成相关操作,而在JDBC技术中需要书写的Sql语句只需要在Ibatis可识别的XML文件中书写即可,从而实现了把SQL语句从
JAVA源代码中分离的效果。
8,Spring如何整合到WEB应用程序中
通过上面的简介,我们对Spring有了一个初步的了解,知道Spring框架的核心就是ApplicationContext的实现工厂,通过这个
工厂类Spring依据ApplicationContext.xml文件的信息自动装配创建JavaBean对象,以后我们想要得到某一个对象实例只需要调用
ApplicationContext的getBean(String name)方法就可以了.当然我们可以直接在我们的应用程序中调用Spring的工厂类通过手工
编码的方式来实现以上的功能,但是这并不能发挥Spring的真正功能,我们知道Spring是一个轻量级的容器,之所以使用Spring的最大
好处是可以通过配置XML文件来节省我们的编码工作量(按照Spring官方的说法是可以节省我们90%的工作量,但个人在工作中感觉使用
Spring之可以节省我们70%左右的工作量),当然在Spring中定义的所有类我们几乎都可以把它直接拿来使用,但这却违背了Spring设计
的初衷!接下来的问题就是我们如何发挥Spring的功能,让我们的WEB应用程序不用通过硬性的创建工厂类呢?也就是说在我们的应用程序刚刚
启动的时候,Spring就帮我们自行创建好了ApplicationContext对象,我们只要得到这个对象,然后再得到相应的JAVABEAN即可(对于上面的我提到
的例子我们最终要得到其实是一个Manager对象),那么这一步又是如何实现的呢?我们知道在Tomcat最近的一些版本中都提供了监听器对象,
而这些监听器对象的作用就是监听WEB应用程序的产生和销毁(具体关于Listener的知识我会在以后相关的文章中介绍),而Spring就利用这一点
定义了一个监听器,监听WEB容器对象的创建销毁。利用这个监听器,Spring可以在WEB应用程序刚刚启动的时候,自行创建一个ApplicationContext
实现类对象(一般的这个实现类对象是XmlWebApplicationContext对象)并在装配好配置文件中的Bean对象,然后把这个对象当作ServletContext(Application域)
的一个属性存入Application域。这样运行在这个WEB容器的每一个WEB应用程序就都可以get到这个工厂类,从而得到我们想要的Bean对象。
写到这里,可能大家都会有个疑问,我知道当我们需要通过域对象得到这个域中所存的属性时候(即通过getAttribute(String name)方法)必须要知道
这个属性对象在域中的名字,那么存入的ApplicationContext对象的名字又是什么呢?通过实验我得出这个名字是一个很长的字符串(到现在我也没办法
记住它),为了方便大家使用,Spring把这个名字定义为了WebApplicationContext的一个常量,常量名为ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;在
编写程序的时候我们只需要通过常量就可以得到ApplicationContext工厂实现类,为了进一步的简化我们的工作Spring特意提供了一个WebApplicationContextUtils
这样一个类。通过这个类,我只需要得到ServletContext对象,并调用WebApplicationContextUtils的getWebApplicationContext(ServeltContext sc)方法就可以
得到我们想要的工厂类对象了。这样我们就整合了实现了Spring与WEB应用程序的初步整合!
9,Spring整合WEB的一些细节。
我们知道ApplicationContext只是一个接口,它下面又有子接口例如WebApplicationContext,在子接口的下面又有些许实现类。刚才我们提到了通过第8步的整合我们
可以得到一个ApplicationContext实现类对象,而默认的这个实现类对象是XmlWebApplicationContext.那么如果我们不想用使用这个实现类而向使用其它的实现类该
怎么做呢?我们可以在WEB应用程序的web.xml文件中配置一个名为"contextClass"的Contextparam的节点下的param,并且让它的value为我们想要使用的ApplicationContext
对象类型即可。而不管什么类型的工厂类,它都需要从一个xml文件中来读取信息,并装配相应的Bean对象,那么从哪里读取呢?读取的文件名字又是什么呢?在默认的情况下
Spring的工厂类会从WEB-INF目录下读取一个名字为applicationContext.xml的文件.但是这个信息也可以通过配置web.xml文件来改变,即在Contextparam的节点下配置一个
contextConfigLocation子节点,并让它的value为/WEB-INF/下的任意个一个以context结尾的XML文件。例如我在web.xml中进行如下配置:
并且可以在value处配置多个xml文件,但要注意的时候,后面的文件将会覆盖掉前面的文件,这样我们ApplicationContext就可以从我们配置的文件中来选择所要读取的XML文件了.
在Spring中的org.springframework.web.context包下还提供了一个ContextLoadServlet类,它的作用与ContextLoadListener相同,但是它的出现主要是为了解决在Servlet2.3,以及Tomcat
4以下的版本中不支持Listener的情况,对于现在我们可以不用关心这方面的内容!