ssi框架学习总结

来源:互联网 发布:网络ssc什么意思 编辑:程序博客网 时间:2024/05/18 13:24

原文地址:http://www.open-open.com/lib/view/open1377581482589.html


框架简介:

相信大家对于mvc的三层架构已经灰常熟悉了,在这就不细讲了,个人感觉ssi的框架结构还是比较典型的mvc三层架构,还是比较容易上手的。关于这块的入门我想特别感谢下FrankHui童鞋,在他的帮助下,我才能比较快滴熟悉了这个架构,对我学习ssi的框架还是很有帮助滴。ssi的框架主要是由struts2spring以及ibatis组成,他们负责各层之间的交互与协作,从而实现整个web端的功能实现与整合。Struts目前主要负责数据传递和控制方面,spring则依靠其强大的依赖注入技术实现了类似bean托管和整合等功能,当然这只是spring功能中的冰山一角,而ibatis作为一种轻量级的OR Mapping框架,提供了半自动化对象关系映射的实现,自由度相对于hibernate更高。

框架结构:

这是我在网上找到的一张关于ssi框架的结构图,个人感觉画的还是蛮不错的,对于入门者来说,层次机构很清晰,比较实用(感谢这位大大的分享):

ssi框架学习总结

在这里可以很明显的看出ssi框架的大体结构以及各层之间的交互情况,顶层表现层的话,就java而言主要是jsphtml等视图层技术的编写,其中涉及我们熟悉的javascriptjquery以及extjs等。在控制层来说的话,主要是利用strust2标签功能实现action与视图层的数据交互,当然也可以使用ajax的技术实现同样的功能,这个就按个人喜好而来了。在业务逻辑层。主要是利用spring的依赖注入实现对业务逻辑类和dao类的实例托管,当然各类的实例都可以托管在spring中进行统一管理和关联,包括事务,数据源等等。在持久层,利用ibatis提供的半自动化对象关系映射的实现,开发人员可以根据需要编写具体的sql语句,通过相应的xml的配置实现对数据库的操作。

总之,SSI框架能够降低我们代码的耦合度,增强了代码的健壮性和可重用性,加快了开发速度,但是也有一些不足之处,比如由于三种框架的配置文件较多,也给我们带来了一些不便,特别是对于较小的应用来说更是如此。

相关demo介绍:

基于之前对ssi框架的学习,我也不能免俗滴选择了做一个用户管理的web实现,项目的大致框架如下:

ssi框架学习总结

大体上还是按照三层的分层模型来是分的,具体就不详细说了,相信大家也应该很了解整个结构的。

Ø  开发环境简介:

工具:eclipse3.6+tomcat7+mysql5.1

框架:struts2spring3.0.5ibatis2.3.4.726

开发步骤及配置简介:

第一步,众所周知肯定是导入相关的lib库啦,由于我使用的前端是extjs,所以还需要导入extjs相关的js以及css依赖文件。由于lib库太多就不截图了,只截取extjs的相关依赖文件:

ssi框架学习总结

这样基本的环境就有了,数据库的设计则需要根据model层的对象来确定。 

第二步,建立相关的model,代码如下:

User.java:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
packagecom.broada.demo.entity;
/**
 *
 * @author smm
 *
 */
publicclass User {
 
    privateint id;          //用户id
    privateString name;        //用户名称
    privateString password;    //用户密码
    privateString username;    //用户昵称
    privateString address;     //地址
 
     
    publicString getUsername() {
        returnusername;
    }
 
    publicvoid setUsername(String username) {
        this.username = username;
    }
 
    publicString getAddress() {
        returnaddress;
    }
 
    publicvoid setAddress(String address) {
        this.address = address;
    }
 
    publicint getId() {
        returnid;
    }
 
    publicvoid setId(intid) {
        this.id = id;
    }
 
    publicString getName() {
        returnname;
    }
 
    publicvoid setName(String name) {
        this.name = name;
    }
 
    publicString getPassword() {
        returnpassword;
    }
 
    publicvoid setPassword(String password) {
        this.password = password;
    }
 
}

第三步,strust的相关配置,这里就不详细讲述strust的单独配置及原理了,直接上实例中的相关配置文件,主要是strust.xml以及web.xml的相关配置,部分主要配置如下:

Web.xml:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- struts2模块 -->
    <filter>
        <filter-name>struts2</filter-name>
       <!-- 这个就是struts2的核心过滤器 -->
        <filter-class>
           org.apache.struts2.dispatcher.FilterDispatcher
        </filter-class>
         
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

strust.xml:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <!-- 这是一个重要的地方,很多人在使用<s:include>子页面后,发现子页面乱码,怎么改都不行,原因就在次,struts2的默认编码为UTF-8,乱码的同志请看看你的jsp页面上的编码是不是和这个不一致呢。只要把这里和jsp编码改一致就行了 -->
    <constantname="struts.i18n.encoding"value="UTF-8"/>
    <!-- 告诉struts2,我要用spring装配工厂,其实默认就是这个了-_-!!! -->
    <constantname="struts.objectFactory"value="spring"/>
    <!-- struts2的扩展名,比如struts1的时候,用的.do,struts2默认为.action,可以改成其它的,比如.dxd -->
    <constantname="struts.action.extension"value="action"/>
    <!-- 资源文件 -->
    <constantname="struts.custom.i18n.resources"
        value="messageResource">
    </constant>
 
    <!-- 用户注册类 -->
    <!-- abstract属性就说明了该action继承自自己定义的基础action,而class采用的registerAction是由spring产生的 -->
    <packagename="register"extends="struts-default">
        <actionname="register"class="registerAction"method="addUser">
            <!-- 注册成功 -->
            <resultname="success">success.jsp</result>
            <!-- 注册失败 -->
            <resultname="input">error.jsp</result>
        </action>
        <actionname="login"class="registerAction"method="loginUser">
            <!-- 注册成功 -->
            <resultname="success">success.jsp</result>
            <!-- 注册失败 -->
            <resultname="error">error.jsp</result>        
        </action>
    </package>
</struts>

这样strust的配置大致就完成了。 

第四步:配置ibatis的相关配置文件,主要是jdbc.propertiesSqlMapConfig.xml以及User.xml的配置,jdbc.properties主要用于配置数据库的数据源参数,会在加载spring的时候自动初始化,ibatis数据源的配置到时可以托管给spring初始化,所以这里就不细讲了。SqlMapConfig.xml主要是配置ibatis的配置文件的位置,User.xml则用于编写相关的数据库语句等,配置大致如下:

jdbc.properties:
?
1
2
3
4
5
6
7
8
9
10
11
12
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/userinfo
jdbc.user=root
jdbc.password=123456
jdbc.minPoolSize=5
jdbc.maxPoolSize=20
jdbc.maxIdleTime=1800
jdbc.acquireIncrement=5
jdbc.maxStatements=50
jdbc.initialPoolSize=10
jdbc.idleConnectionTestPeriod=1800
jdbc.acquireRetryAttempts=30

详细的参数含义在spring的配置文件会提及,就不细说了。

SqlMapConfig.xml

?
1
2
3
4
5
6
7
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<!-- 用户信息表 -->
<sqlMap resource="com/broada/demo/dao/ibaties/map/User.xml"/>
</sqlMapConfig>

User.xml:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"     
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap>
    <typeAlias alias="User"type="com.broada.demo.entity.User"/>
    <!-- 保存注册信息 -->
    <insert id="insertUser"parameterClass="User">
        insert into
        user (name,password,username,address)values(#name#,#password#,#username#,#address#)
    </insert>
     
    <select id="selsectUser"parameterClass="java.lang.String"resultClass="User">
        select * from user
        where name = #name#;
    </select>
</sqlMap>

这样,ibatis的配置大致就完成。

第五步:配置spring的相关配置文件,主要是整合ibatis以及strust中用到的bean,需要配置web.xml以及applicationContext-web.xml两个配置文件:

web.xml:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<listener>
        <!-- 这个就是今后用到的WebApplicationUtilContent -->
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <!-- springframework config files -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!-- 把spring的配置文件放到了/WEB-INF/下的springframework包里,方便统一管理,命名规则是以applicationContent-开头的xml文件,初始化时会自动搜索所有符合规则的配置文件 -->
        <param-value>
            /WEB-INF/spring/applicationContext-*.xml
        </param-value>
</context-param>

applicationContext-web.xml:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?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-2.0.xsd">
    <!-- 配置数据源,连接池采用的是c3p0,具体各参数代表意义参看c3p0自带的doc,非常详细。 -->
    <bean id="dataSource"
       class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass"value="${jdbc.driverClass}"/>
        <property name="jdbcUrl"value="${jdbc.url}"/>
        <property name="user"value="${jdbc.user}"/>
        <property name="password"value="${jdbc.password}"/>
        <property name="minPoolSize"value="${jdbc.minPoolSize}"/>
        <property name="maxPoolSize"value="${jdbc.maxPoolSize}"/>
        <property name="maxIdleTime"value="${jdbc.maxIdleTime}"/>
        <property name="acquireIncrement"
            value="${jdbc.acquireIncrement}"/>
        <property name="maxStatements"value="${jdbc.maxStatements}"/>
        <property name="initialPoolSize"
            value="${jdbc.initialPoolSize}"/>
        <property name="idleConnectionTestPeriod"
            value="${jdbc.idleConnectionTestPeriod}"/>
        <property name="acquireRetryAttempts"
            value="${jdbc.acquireRetryAttempts}"/>
    </bean>
 
     
   <!-- 上面的数据源的value值用的是表达式,原因就在这里,这将配置文件放到了iBatis目录下,也就是jdbc.properties,设置了c3p0的各项参数 -->
    <bean id="propertyConfig"
       class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>/WEB-INF/ibatis/jdbc.properties</value>
        </property>
    </bean>
     
    <!-- 配置iBatis的sqlMapClient,这里当然是交给了spring去处理,其中,将SqlMapConfig文件放到了WEB-INF的iBatis目录下,也是便于管理 -->
    <bean id="sqlMapClient"
       class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation">
            <value>/WEB-INF/ibatis/SqlMapConfig.xml</value>
        </property>
        <!-- 这里使用的数据源就是上面配置的数据源 -->
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
    </bean>
 
    <bean id="userdaoId" class="com.broada.demo.daoImpl.UserDaoImpl">
        <property name="sqlMapClient"ref="sqlMapClient"></property>
    </bean>
      
    <bean id="userDaoServiceId"class="com.broada.demo.serviceImpl.UserDaoServiceImpl">
      <property name="userdao"ref="userdaoId">
    </property>
    </bean>
     <!-- 用户注册action-->
    <bean id="registerAction"name="registerAction" class="com.broada.demo.action.RegisterAction"scope="prototype">      
      <property name="userdaoServiceInter"ref="userDaoServiceId"></property>
    </bean>
     
     
</beans>

这样,ssi框架的大致配置就完成了。 

最后编写相关的dao层,service层,action层以及jsp等等,我就不详细说明了,直接上相关代码:

RegisterAction.java:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
packagecom.broada.demo.action;
 
/**
 * @author smm
 */
 
importcom.broada.demo.entity.User;
importcom.broada.demo.service.UserDaoServiceInter;
importcom.opensymphony.xwork2.ActionSupport;
 
publicclass RegisterAction extendsActionSupport {
     
    privatestatic final long serialVersionUID = 1L;
     
privateUserDaoServiceInter userdaoServiceInter;   
 
    publicUserDaoServiceInter getUserdaoServiceInter() {
        returnuserdaoServiceInter;
    }
 
    publicvoid setUserdaoServiceInter(UserDaoServiceInter userdaoServiceInter) {
        this.userdaoServiceInter = userdaoServiceInter;
    }
 
    privateString name;    //用户名
    privateString password;    //密码
    privateString username;    //昵称
    privateString address;     //地址
 
    publicString getUsername() {
        returnusername;
    }
 
    publicvoid setUsername(String username) {
        this.username = username;
    }
 
    publicString getAddress() {
        returnaddress;
    }
 
    publicvoid setAddress(String address) {
        this.address = address;
    }
 
    publicString getName() {
        returnname;
    }
 
    publicvoid setName(String name) {
        this.name = name;
    }
 
    publicString getPassword() {
        returnpassword;
    }
 
    publicvoid setPassword(String password) {
        this.password = password;
    }
 
    publicString addUser() {
        System.out.println("添加成功!");
        User user = newUser();
         
        String name = this.name;
        String password = this.password;
        String username = this.username;
        String address = this.address;
         
        user.setName(name);
        user.setPassword(password);
        user.setUsername(username);
        user.setAddress(address);
         
        booleanb = userdaoServiceInter.insertUser(user);
         
        if(b==true) {
            returnSUCCESS;
        else
            returnINPUT;
    }
     
    publicString loginUser(){
        System.out.println("登陆=======");
         
        String name = this.name;
        String password = this.password;
         
        User user = userdaoServiceInter.querybyname(name);
         
        if(user != null&& password.equals(user.getPassword())){
            returnSUCCESS;
        else
            returnERROR;
    }
}

UserDao.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
packagecom.broada.demo.dao;
 
/**
 * @author smm
 */
 
importjava.util.List;
 
importcom.broada.demo.entity.User;
 
publicinterface UserDao {
     
    /**
     * 用户注册
     * @param user
     * @return
     */
     
    publicboolean insertUser(User user);
     
    /**
     * 根据用户名获取用户信息
     * @param name
     * @return
     */
     
    publicUser queryByname(String name);  
}

UserDaoImpl.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
packagecom.broada.demo.daoImpl;
/**
 * @author smm
 */
importorg.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
 
importcom.broada.demo.dao.UserDao;
importcom.broada.demo.entity.User;
 
publicclass UserDaoImpl extendsSqlMapClientDaoSupport implementsUserDao{
 
 
 
    @Override
    publicboolean insertUser(User user) {
        try{
            getSqlMapClientTemplate().insert("insertUser", user);
            returntrue;
        catch(Exception e) {
            e.printStackTrace();
            returnfalse;
        }  
    }
 
    @Override
    publicUser queryByname(String name) {
        // TODO Auto-generated method stub
        try{
            User user =(User) getSqlMapClientTemplate().queryForObject("selsectUser", name);
            returnuser;
        catch(Exception e) {
            e.printStackTrace();
            returnnull;
        }      
    }
     
}

UserDaoServiceInter.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
packagecom.broada.demo.service;
 
/**
 * @author smm
 */
 
importcom.broada.demo.entity.User;
 
publicinterface UserDaoServiceInter {
     
    /**
     * 用户注册服务接口
     * @param user
     * @return
     */
     
    publicboolean insertUser(User user);
     
    /**
     * 根据用户名获取用户信息接口
     * @param name
     * @return
     */
     
    publicUser querybyname(String name);
}

UserDaoServiceImpl.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
packagecom.broada.demo.serviceImpl;
 
/**
 * @author smm
 */
 
importcom.broada.demo.dao.UserDao;
importcom.broada.demo.entity.User;
importcom.broada.demo.service.UserDaoServiceInter;
 
publicclass UserDaoServiceImpl implementsUserDaoServiceInter {
 
    private UserDao userdao;
    publicUserDao getUserdao() {
        returnuserdao;
    }
    publicvoid setUserdao(UserDao userdao) {
        this.userdao = userdao;
    }
    @Override
    publicboolean insertUser(User user) {
        returnuserdao.insertUser(user);
    }
    @Override
    publicUser querybyname(String name) {
        // TODO Auto-generated method stub
        returnuserdao.queryByname(name);
    }
 
}

Index.jsp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<%@ page language="java"contentType="text/html; charset=utf8"
    pageEncoding="utf8"%>
    <%@ taglib prefix="s"uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"content="text/html; charset=utf8">
<link rel="stylesheet"type="text/css"href="css/ext-all.css"/>
<link rel="stylesheet"type="text/css"
    href="css/ext-theme-classic-all.css"/>
<script type="text/javascript"src="js/ext-all.js"></script>
 
 
<title>用户登陆</title>
<script type="text/javascript">
Ext.onReady(function(){
        initPanel();    
    });
     
    initPanel=function(){
         var formPanel = newExt.FormPanel({
                id: 'loginPanel',
                labelWidth: 75,
                frame:true,
                bodyStyle:'margin:0 auto',
                width: 350,
                defaults: {width: 250},
                defaultType: 'textfield',
                items: [{id: 'username',fieldLabel: '用户名',name:'name',allowBlank: false,blankText: '账号不能为空'},
               {id: 'password', fieldLabel: '密 码',name:'password',inputType: 'password',allowBlank: false,blankText: '密码不能为空'}]
                     });
        var win = newExt.Window({  
               title:'登陆界面',
               width:380,
               autoHeight: 'true',
               resizable: false,
               modal:true,
               closeAction: 'hide',
               buttonAlign:'center',
               items:formPanel,
               buttons:[{text: '登陆',handler: function(){land();}},
                        {text: '重置',handler: function(){
                           Ext.getCmp('username').setValue("");
                           Ext.getCmp('password').setValue("");
                           }},{text: '注册',handler: function(){register();}}]
                  });
                          
        win.show();
    };
     
    var win1;
     
    register = function(){ 
         
        var registerPanel = newExt.FormPanel({
            id: 'registerPanel',
            labelWidth: 75,
            frame:true,
            bodyStyle:'margin:0 auto',
            width: 350,
            defaults: {width: 250},
            defaultType: 'textfield',
            items: [{id: 'registername',fieldLabel: '用户名',name:'name1',allowBlank: false,blankText: '账号不能为空'},
           {id: 'registerpassword', fieldLabel: '登陆密 码',name:'password1',inputType: 'password',allowBlank: false,blankText: '密码不能为空'},
           {id: 'registerpassword2', fieldLabel: '密码确认',name:'password2',inputType: 'password',allowBlank: false,blankText: '密码不能为空'},
           {id: 'nichen',fieldLabel: '昵称',name:'nichen1'},
           {id: 'address',fieldLabel: '地址',name:'address1'}]
                 });
         
        win1 = newExt.Window({  
               title:'注册界面',
               width:380,
               autoHeight: 'true',
               resizable: false,
               modal:true,
               buttonAlign:'center',
               items:registerPanel,
               buttons:[{text: '提交',handler: function(){login();}},
                        {text: '取消',handler: function(){win1.close();}}]
                  });
                          
        win1.show();
    };
     
    login=function(){
        var name = Ext.getCmp('registername').getValue();
        var password = Ext.getCmp('registerpassword').getValue();
        var password2 = Ext.getCmp('registerpassword2').getValue();
        var nichen = Ext.getCmp('nichen').getValue();
        var address = Ext.getCmp('address').getValue();
         
        if(name==" "|| password==" " || password2==""){
            Ext.MessageBox.alert('提示','账号或密码不能为空!');
        }
        elseif(password != password2){
            Ext.MessageBox.alert('提示','两次密码输入不一致!');
        }else{
         Ext.Ajax.request({
             url: "<s:url value='/register.action'/>",
             method: 'post',
            params:{
                name:name,
                password:password,
                username:nichen,
                address:address
            },
             success: function (response, options) {
                Ext.MessageBox.alert('提示','注册成功',function(e){
                    if(e == "ok"){
                        win1.close();
                    }
                 });
                 
             },
             failure: function (response, options) {
                 Ext.MessageBox.alert('注册失败''请检查您的注册信息!');
             }
         });
        }
    };
     
    land=function(){
        var name = Ext.getCmp('username').getValue();
        var password = Ext.getCmp('password').getValue();
         
         
        if(name == " " || password == ""){
             Ext.MessageBox.alert('提示''账号或密码不能为空!');
        }
        else{
         Ext.Ajax.request({
             url: "<s:url value='/login.action'/>",
             method: 'post',
            params:{
                name:name,
                password:password              
            },
             success: function (response, options) {
                Ext.MessageBox.alert('提示','登陆成功!');
                 
             },
             failure: function (response, options) {
                 Ext.MessageBox.alert('登陆失败''您输入的账号或密码错误!');
             }
         });
        }
    };
     
</script>
 
</head>
<body>
     
</body>
</html>

大致的步骤就是这样,个人觉得ssi的框架还是蛮不错的,起码定制和自由度都比较好。


0 0