关于JSF1.2 + Spring2.5 + Hibernate3 + Facelets + Annotation整合配置的参考。

    提供一个整合JSF,Spring, Hibernate(JPA), Facelets, 及Annotation的基础环境。对于刚开始使用这种组合的项目,或许可以参考一下,相信使用以上整合环境的项目还是比较少。我一直很喜欢这种组合,JSF组件式的开发,Spring, Hibernate对BackingBean及数据源的管理,Facelets的模版化技术,以及Annotation都大大简化了开发。
    JSF组件的高度封装及高可重用性,使得页面代码在非常简单的情况下快速实现非常复杂的功能。Spring,Hibernate的整合进一步简化了JSF的开发,特别是Annotation的配置使得现在几乎完全不需要去XML中配置Bean这些繁琐的事情,这确实很繁琐,特别是如果再加上配置JSF的导航规则,在开发过程中经常要去碰这些东西,确实很麻烦,又容易出错。所以如果项目允许,还是推荐使用Annotation,并且这也是很多专家推荐的。作为Java5新加入的特性,它确实对开发带来了很大的方便,以前不怎么喜欢,不过现在感觉它很有前途!另一个就是Facelets了,这可是用来替代JSP的视图描述技术,Facelets模版化的威力可是非常强大,虽然不少人认为它的自定义标签功能更强大,具体介绍还是大家网上搜一下吧。下面看一下大概的配置过程,仅供参考,因为网上也有不少类似的教程,所以不作啰嗦,仅取重点。

环境:Netbeans7, Tomcat6.0.18, MySQL5

相关框架:JSF1.2,Spring2.5,Hibernate3(JPA), Facelets, Annotation, MyFaces(附加), QFaces(附加)

1.相关引用的jar包

可以看到引用的jar还是不少,至少加起来有43.2M。最后一个是我自定义的组件包qfaces-1.2.1,刚升级支持Facelets(qfaces在1.2及之前未能够支持Facelets而没有说明,使一些用Facelets的朋友遇到麻烦,这里道歉哦。)

2.web.xml文件配置
  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <web-app version="2.5" 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_2_5.xsd">
  3     <context-param>
  4         <param-name>com.sun.faces.verifyObjects</param-name>
  5         <param-value>false</param-value>
  6     </context-param>
  7     <context-param>
  8         <param-name>com.sun.faces.validateXml</param-name>
  9         <param-value>true</param-value>
 10     </context-param>
 11     <context-param>
 12         <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
 13         <param-value>client</param-value>
 14     </context-param>
 15 
 16     <!-- Config:Facelets -->
 17 
 18     <context-param>
 19         <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
 20         <param-value>.xhtml</param-value>
 21     </context-param>
 22 
 23     <!-- Config:Spring -->
 24 
 25     <servlet>
 26         <servlet-name>dispatcher</servlet-name>
 27         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 28         <load-on-startup>2</load-on-startup>
 29     </servlet>
 30     <servlet-mapping>
 31         <servlet-name>dispatcher</servlet-name>
 32         <url-pattern>*.htm</url-pattern>
 33     </servlet-mapping>
 34     <listener>
 35         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 36     </listener>
 37     <listener>
 38         <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
 39     </listener>
 40     <listener>
 41         <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
 42     </listener>
 43     <listener>
 44         <listener-class>com.sun.faces.application.WebappLifecycleListener</listener-class>
 45     </listener>
 46 
 47     <!-- Config:MyFaces -->
 48 
 49     <filter>
 50         <filter-name>MyFacesExtensionsFilter</filter-name>
 51         <filter-class>
 52             org.apache.myfaces.webapp.filter.ExtensionsFilter
 53         </filter-class>
 54         <init-param>
 55             <param-name>uploadMaxFileSize</param-name>
 56             <param-value>100m</param-value>
 57         </init-param>
 58         <init-param>
 59             <param-name>uploadThresholdSize</param-name>
 60             <param-value>80k</param-value>
 61         </init-param>
 62     </filter>
 63     <filter-mapping>
 64         <filter-name>MyFacesExtensionsFilter</filter-name>
 65         <servlet-name>Faces Servlet</servlet-name>
 66     </filter-mapping>
 67     <filter-mapping>
 68         <filter-name>MyFacesExtensionsFilter</filter-name>
 69         <url-pattern>/faces/myFacesExtensionResource/*</url-pattern>
 70     </filter-mapping>
 71 
 72     <!-- Config:QFaces -->
 73 
 74     <servlet>
 75         <servlet-name>QFaces</servlet-name>
 76         <servlet-class>name.huliqing.qfaces.FacesServlet</servlet-class>
 77     </servlet>
 78     <servlet-mapping>
 79         <servlet-name>QFaces</servlet-name>
 80         <url-pattern>*.qfaces</url-pattern>
 81     </servlet-mapping>
 82 
 83     <!-- End -->
 84 
 85     <servlet>
 86         <servlet-name>Faces Servlet</servlet-name>
 87         <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
 88         <load-on-startup>1</load-on-startup>
 89     </servlet>
 90     <servlet-mapping>
 91         <servlet-name>Faces Servlet</servlet-name>
 92         <url-pattern>*.faces</url-pattern>
 93     </servlet-mapping>
 94     <session-config>
 95         <session-timeout>
 96             30
 97         </session-timeout>
 98     </session-config>
 99     <welcome-file-list>
100         <welcome-file>welcome.jsp</welcome-file>
101     </welcome-file-list>
102     </web-app>
103 
这是web.xml,可以看到默认的配置很简单,并没有什么特别的,在网上大都可以看到类似。相关部分都有注识。上面Facelets配置的默认后缀就是xhtml了,这样当我们访问如 test.faces这样的页面时,就会由Facelets的ViewHandler最后解析到test.xhtml。所以我们的页面后缀就需要是.xhtml。另外两个额外的配置MyFaces,QFaces,如果不需要,都可以直接注释掉,不会影响。

3.faces-config.xml
文件位置: WEB-INF/faces-config.xml
 1 <?xml version='1.0' encoding='UTF-8'?>
 2 
 3 <!-- =========== FULL CONFIGURATION FILE ================================== -->
 4 
 5 <faces-config version="1.2" 
 6     xmlns="http://java.sun.com/xml/ns/javaee" 
 7     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 8     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
 9     <application>
10         <!-- 国际化支持 -->
11         <locale-config>
12             <default-locale>zh</default-locale>
13             <supported-locale>zh</supported-locale>
14             <supported-locale>en</supported-locale>
15         </locale-config>
16         <message-bundle>resource</message-bundle>
17         <resource-bundle>
18             <base-name>resource</base-name>
19             <var>text</var>
20         </resource-bundle>
21         <!-- Facelet, Spring -->
22         <view-handler>
23             com.sun.facelets.FaceletViewHandler
24         </view-handler>
25         <variable-resolver>
26             org.springframework.web.jsf.DelegatingVariableResolver
27         </variable-resolver>
28     </application>
29 </faces-config>
重点注意Facelets view-handler及Spring variable-resolver的配置就可以。

4.dispatcher-servlet.xml
文件位置:WEB-INF/dispatcher-servlet.xml
<?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"
       xmlns:aop
="http://www.springframework.org/schema/aop"
       xmlns:tx
="http://www.springframework.org/schema/tx"
       xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
    
    
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
   

    
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        
<property name="mappings">
            
<props>
                
<prop key="/index.htm">indexController</prop>
            
</props>
        
</property>
    
</bean>
    
    
<bean id="viewResolver"
          class
="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix
="/WEB-INF/jsp/"
          p:suffix
=".jsp" />
   

    
<bean name="indexController"
          class
="org.springframework.web.servlet.mvc.ParameterizableViewController"
          p:viewName
="index" />
    
</beans>
这也是Spring的默认配置

5.applicationContext.xml
文件位置:WEB-INF/applicationContext.xml
<?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"
       xmlns:aop
="http://www.springframework.org/schema/aop"
       xmlns:tx
="http://www.springframework.org/schema/tx"
       xmlns:context
="http://www.springframework.org/schema/context"
       xsi:schemaLocation
="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-2.5.xsd"
>
    
<aop:aspectj-autoproxy/>
    
<context:annotation-config/>
    
<context:component-scan base-package="name.huliqing.magic"/>
    
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        
<property name="location" value="classpath:jdbc.properties"/>
    
</bean>
    
<bean id="_data_source" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        
<property name="driverClassName" value="${jdbc.driverClassName}"/>
        
<property name="url" value="${jdbc.url}"/>
        
<property name="username" value="${jdbc.username}"/>
        
<property name="password" value="${jdbc.password}"/>
    
</bean>
    
<alias name="_data_source" alias="dataSource"/>
    
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        
<property name="persistenceUnitName" value="SpringJPADbUnit"/>
        
<property name="dataSource" ref="dataSource"/>
        
<property name="jpaVendorAdapter">
            
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                
<property name="databasePlatform" value="${jdbc.dialect}"/>
                
<property name="showSql" value="${jdbc.showSql}"/>
                
<property name="generateDdl" value="${jdbc.generateDdl}"/>
            
</bean>
        
</property>
    
</bean>
    
<bean id="jDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/> 
    
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        
<property name="entityManagerFactory" ref="entityManagerFactory"/>
        
<property name="dataSource" ref="dataSource"/>
        
<property name="jpaDialect" ref="jDialect"/>
    
</bean>
    
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

<aop:aspectj-autoproxy/> 提供对AspectJ的支持
<context:annotation-config/> 提供对annotation的支持
<context:component-scan base-package="name.huliqing.magic"/> 指定需要被Spring进行扫描的类包,base-package下的类及子包都会被扫描以提供依赖注入,注意修改为自己的包名。
<tx:annotation-driven transaction-manager="transactionManager"/> 对Annotation进行事务管理的支持。
其它定义的一些bean主要是对数据源的配置,更详细的信息请查阅相关的资料。

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        
<property name="location" value="classpath:jdbc.properties"/>
</bean>
注意这里指定了数据源配置文件的位置classpath:jdbc.properties.

6.jdbc.properties

文件位置 classpath:jdbc.properties
jdbc.dialect = org.hibernate.dialect.MySQLDialect
jdbc.driverClassName 
= com.mysql.jdbc.Driver
jdbc.url 
= jdbc:mysql://localhost:3306/magic?characterEncoding=UTF-8
jdbc.username = root
jdbc.password 
=
jdbc.showSql 
= false
jdbc.generateDdl 
= false

上面是jdbc文件的配置内容,下面显示了文件的位置,同时显示了Spring的两个配置文件的位置。


上面提供了对数据库的连接信息,数据库magic,用户root,密码空。基本上JSF+Spring+Hibernate+Facelets就是这个配置了。
下面为了测试环境,将在magic库中创建一个数据表test,并创建一些相应的类,进行测试一下。

7.测试环境

首先创建一个数据表magic.test
DROP TABLE IF EXISTS magic.test;

CREATE database IF NOT EXISTS magic DEFAULT charset utf8 COLLATE utf8_general_ci;

use magic;

create table magic.test (
    num 
int not null auto_increment,
    id 
varchar(32not null,
    password 
varchar(64not null,
    des 
varchar(64not null,
    
primary key  (num)
) engine 
= InnoDB default charset = utf8;

接下来将创建以下东东:
Dao.java - Dao接口:
DaoBase.java - Dao基类,实现Dao.java的接口

TestDa.java - Dao,继承DaoBase.java
TestEn.java - Entity,持久化类,对应数据表magic.test
TestSe.java - Service,业务逻辑层
TestWe.java - BackingBean
test.xhtml  - 测试页面

下面是目录结构,因为这是测试,所以我并没有把它们放在相应的目录。其它几个类可以不管。


下面是各个类的代码:
TestEn.java
package name.huliqing.magic.test;

import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.Table;

@Entity
@Table(name 
= "test")
@NamedQueries({})
public class TestEn implements Serializable {
    
private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    @Basic(optional 
= false)
    @Column(name 
= "num")
    
private Integer num;
    @Basic(optional 
= false)
    @Column(name 
= "id")
    
private String id;
    @Basic(optional 
= false)
    @Column(name 
= "password")
    
private String password;
    @Basic(optional 
= false)
    @Column(name 
= "des")
    
private String des;

    
public TestEn() {
    }

    
public TestEn(Integer num) {
        
this.num = num;
    }

    
public TestEn(Integer num, String id, String password, String des) {
        
this.num = num;
        
this.id = id;
        
this.password = password;
        
this.des = des;
    }

    
public Integer getNum() {
        
return num;
    }

    
public void setNum(Integer num) {
        
this.num = num;
    }

    
public String getId() {
        
return id;
    }

    
public void setId(String id) {
        
this.id = id;
    }

    
public String getPassword() {
        
return password;
    }

    
public void setPassword(String password) {
        
this.password = password;
    }

    
public String getDes() {
        
return des;
    }

    
public void setDes(String des) {
        
this.des = des;
    }

    @Override
    
public int hashCode() {
        
int hash = 0;
        hash 
+= (num != null ? num.hashCode() : 0);
        
return hash;
    }

    @Override
    
public boolean equals(Object object) {
        
// TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof TestEn)) {
            
return false;
        }
        TestEn other 
= (TestEn) object;
        
if ((this.num == null && other.num != null|| (this.num != null && !this.num.equals(other.num))) {
            
return false;
        }
        
return true;
    }

    @Override
    
public String toString() {
        
return "name.huliqing.magic.domain.TestEn[num=" + num + "]";
    }

}
这个Entity是Netbeans通过数据表magic.test生成的代码,相关的注解都看到了,不明白的可以查阅一下相关资料

接口Dao.java
package name.huliqing.magic.dao;

import java.io.Serializable;

public interface Dao<T, PK extends Serializable>{

    
public T save(final T t);

    
public T update(final T t);

    
public void delete(final T t);

    
public T find(final PK id);
}

基类:BaseDao.java
package name.huliqing.magic.dao;

import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTemplate;

public class DaoBase<T, PK extends Serializable> implements Dao<T, PK>{

    
protected Class<T> type;
    
protected JpaTemplate jpaTemplate;
    
protected EntityManagerFactory entityManagerFactory;
    
protected DriverManagerDataSource dateSource;

    
public DaoBase(Class<T> type) {
        
this.type = type;
    }

    @PersistenceUnit
    
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
        
this.entityManagerFactory = entityManagerFactory;
        
this.jpaTemplate = new JpaTemplate(entityManagerFactory);
    }

    @Autowired
    
public void setDataSource(DriverManagerDataSource dateSource) {
        
this.dateSource = dateSource;
    }

    
public T save(T t) {
        
this.jpaTemplate.persist(t);
        
this.jpaTemplate.flush();
        
return t;
    }

    
public T update(T t) {
        
this.jpaTemplate.merge(t);
        
this.jpaTemplate.flush();
        
return t;
    }

    
public void delete(T t) {
        T _o 
= this.jpaTemplate.merge(t);
        
this.jpaTemplate.remove(_o);
        
this.jpaTemplate.flush();
    }

    
public T find(PK id) {
        
return (T) this.jpaTemplate.find(type, id);
    }

    
public List<T> findByObject(T t, String fuzzy) {
        EntityManagerFactory emf 
= this.jpaTemplate.getEntityManagerFactory();
        EntityManager em 
= emf.createEntityManager();
        Query q 
= DaoQueryMake.makeQuery(em, t, fuzzy);
        List
<T> result = q.getResultList();
        
return result;
    }
}
Dao.java, DaoBase.java都使用了泛型,
Dao.java主要定义了相关的基本接口,
DaoBase.java主要提供了Spring对数据源的注入及管理,同时实现了Dao.java的接口,这样其它Dao只要继承自DaoBase.java就可以毫不费劲的获得几个很基本的功能:save, update, delete, find

测试类Dao: TestDa.java
package name.huliqing.magic.test;

import name.huliqing.magic.dao.*;
import org.springframework.stereotype.Component;

@Component
public class TestDa extends DaoBase{

    
public TestDa() {
        
super(TestEn.class);
    }
}

测试类Service: TestSe.java
package name.huliqing.magic.test;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class TestSe {

    @Autowired
    private TestDa testDa;

    @Transactional
    public TestEn save(TestEn testEn) {
        return (TestEn) testDa.save(testEn);
    }

    @Transactional
    public TestEn update(TestEn testEn) {
        return (TestEn) testDa.update(testEn);
    }

    @Transactional
    public void delete(TestEn testEn) {
        testDa.delete(testEn);
    }

    public List<TestEn> findByObject(Object o, String... fuzzy) {
        return testDa.findByObject(o, fuzzy);
    }
}

测试类Backing: TestWe.java
package name.huliqing.magic.test;

import name.huliqing.magic.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(
"request")
public class TestWe implements java.io.Serializable{

    @Autowired
    
private TestSe testSe;
    
private TestEn testEn;

    
public TestWe() {
        
this.testEn = new TestEn();
    }

    
public TestEn getTestEn() {
        
return testEn;
    }

    
public void setTestEn(TestEn testEn) {
        
this.testEn = testEn;
    }

    
public void save() {
        TestEn _testEn 
= testSe.save(testEn);
        
if (_testEn != null) {
            Message.addInfoMessage(
"注册成功");
        }
    }
}
看一下几个Test类,主要用到几个注解:@Component @Autowired @Transactional
@Component 标明了获得Spring管理的Bean, 默认名称为类名的首字母小写,主要注意Component的保存scope。
@Autowired 实现Bean的快速注入。
@Transactional,通过这个注解,方法将自动获得事务支持。

下面是测试页面:test.xhtml
 1 <ui:composition xmlns="http://www.w3.org/1999/xhtml"
 2         xmlns:h="http://java.sun.com/jsf/html"
 3         xmlns:f="http://java.sun.com/jsf/core"
 4         xmlns:t="http://myfaces.apache.org/tomahawk"
 5         xmlns:ui="http://java.sun.com/jsf/facelets"
 6         template="/WEB-INF/layout/template.xhtml">
 7     <ui:define name="content">
 8         <ui:insert name="messages">
 9             <h:messages globalOnly="true" showDetail="true" infoClass="colorGreen" errorClass="colorRed" fatalClass="colorOrange"/>
10         </ui:insert>
11         <h:form>
12         <h:panelGrid columns="3" styleClass="border">
13             <h:outputText value="用户ID" />
14             <h:inputText id="id" value="#{testWe.testEn.id}" />
15             <h:outputText value="请填写你的ID" />
16 
17             <h:outputText value="设置密码" />
18             <h:inputSecret id="password" value="#{testWe.testEn.password}" />
19             <h:outputText value="建议由字母与数字混合组成" />
20 
21             <h:outputText value="个人说明" />
22             <h:inputTextarea id="des" value="#{testWe.testEn.des}" />
23             <h:outputText value="个人的一些备注" />
24 
25             <h:panelGroup />
26             <h:commandButton value="申请" action="#{testWe.save}" />
27             <h:panelGroup />
28         </h:panelGrid>
29         </h:form>
30     </ui:define>
31 </ui:composition>
关于Facelets视图技术,不明白的请查阅相关的资料,下面是最终测试结果。

以上整合配置有经过实际正式上线项目验证,如果你觉得有什么错漏,请多多指教。

1.示例源码下载,没带jar,需要自己去下载上面所说的相关jar包。(94K)
http://www.blogjava.net/Files/huliqing/Magic-JSH-Facelets-Annotation.rar

2.示例源码下载,完整版,包含所有相关jar,不过网盘可能不够稳定。(38M)
http://cid-1c243f9a849aade7.skydrive.live.com/self.aspx/.Public/Magic/Magic%7C5JSF+Spring+Hibernate+Facelets+Annotation%7C6.rar


- huliqing@huliqing.name
- http://www.huliqing.name

posted on 2009-03-30 18:11 huliqing 阅读(2843) 评论(3)  编辑  收藏 所属分类: JSF

评论

# re: 关于JSF1.2 + Spring2.5 + Hibernate3 + Facelets + Annotation整合配置的参考。 2009-03-30 20:51 CoderDream

不错,这个要支持,感谢博主分享!  回复  更多评论   

# re: 关于JSF1.2 + Spring2.5 + Hibernate3 + Facelets + Annotation整合配置的参考。 2009-03-30 21:26 胜客

re  回复  更多评论   

# re: 关于JSF1.2 + Spring2.5 + Hibernate3 + Facelets + Annotation整合配置的参考。 2009-04-01 10:28 舞命小丢

不错!支持一下  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

公告

文章原创,欢迎转载
——转载请注明出处及原文链接

随笔分类(60)

随笔档案(33)

最新评论

评论排行榜