当前位置: 首页 > 工具软件 > GWT INjection > 使用案例 >

GWT + Spring MVC + JPA整合

公冶光亮
2023-12-01
GWT + Spring MVC + JPA Web Application
by IntelliBitz Technologies
http://www.intellibitz.com

===========
Objective:
===========
Create and run a fully functional GWT + Spring MVC + Hibernate JPA Web
Application.


We'll have 3 parts, explaining each of the GWT, Spring MVC, JPA
integration part.


=========================================
PART ONE - GWT with RemoteServiceServlet
=========================================


Lets first make sure we can independently build a GWT RPC application:
We can use the standard tools available (Netbeans, eclipse gwt
plugins) and create our GWT RPC application.


We'll name our package to be 'com.ibt.intelligrade'. Now based on the
GWT coding conventions we need to have the following packages:


'com.ibt.intelligrade' ==> holds the gwt.xml configuration file
'com.ibt.intelligrade.public' ==> holds the html file displayed in
browser
'com.ibt.intelligrade.client' ==> holds the client file which are
translated to Javascript
'com.ibt.intelligrade.server' ==> holds the servlet file (RPC)


We'll look at each of the packages one by one with their respective
source files.


package: 'com.ibt.intelligrade'
============================
CourseServiceClient.gwt.xml
============================
<module>
<inherits name="com.google.gwt.user.User"/>
<entry-point class="com.ibt.intelligrade.client.CourseServiceClient"/


<servlet path='/gwt.smvc'
class='com.ibt.intelligrade.server.CourseServiceClientImpl'/>
</module>

package: 'com.ibt.intelligrade.public'
============================
CourseServiceClient.html
============================
<html>
<head>
<meta name='gwt:module' content='CourseServiceClient'>
<title>CourseServiceClient</title>
</head>
<body bgcolor="white">
<script language="javascript" src="gwt.js"></script>
</body>
</html>


package: 'com.ibt.intelligrade.client'
============================
CourseServiceClient.java
============================
package com.ibt.intelligrade.client;


import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;


public class CourseServiceClient implements EntryPoint {
// Variable that stores the string retrieved from the server
String returnedString = null;


public void onModuleLoad() {


final Label label = new Label ("status");


Button b = new Button("GWT + Spring MVC + Hibernate JPA Demo",
new ClickListener() {
public void onClick(Widget sender) {
// Call remote service
runCourseService(label);
}
});


RootPanel.get().add(b);
RootPanel.get().add(label);
}


// In this method, you do invoke a method of the remote service.
// You also provide the callback method.
void runCourseService(final Label status){


// (1) Create the client proxy. Note that although you are
creating the
// service interface proper, you cast the result to the async
version of
// the interface. The cast is always safe because the
generated proxy
// implements the async interface automatically.
//
CourseServiceAsync myserv = (CourseServiceAsync) GWT
.create(CourseService.class);


// (2) Specify the URL at which our service implementation is
running.
// Note that the target URL must reside on the same domain and
port from
// which the host page was served.
//
ServiceDefTarget endpoint = (ServiceDefTarget) myserv;
String moduleRelativeURL = GWT.getModuleBaseURL() +
"gwt.smvc";
endpoint.setServiceEntryPoint(moduleRelativeURL);


// (3) Create an async callback to handle the result.
//
AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
status.setText ((String)result);
}


public void onFailure(Throwable caught) {
status.setText ("Failure occurred:
"+caught.getMessage());
}
};


// (4) Make the call. Control flow will continue immediately
and later
// 'callback' will be invoked when the RPC completes.
//
// Provide your own name.
myserv.runGwtSprMvcHibJpaDemo("Testing Demo", callback);
}


}


====================
CourseService.java
====================
package com.ibt.intelligrade.client;

import com.google.gwt.user.client.rpc.RemoteService;


public interface CourseService extends RemoteService{
public String runGwtSprMvcHibJpaDemo(String s);


}


============================
CourseServiceAsync.java
============================
package com.ibt.intelligrade.client;

import com.google.gwt.user.client.rpc.AsyncCallback;


public interface CourseServiceAsync {
public void runGwtSprMvcHibJpaDemo(String s, AsyncCallback
callback);


}


package: 'com.ibt.intelligrade.server'
============================
CourseServiceImpl.java
============================
package com.ibt.intelligrade.server;

import com.ibt.intelligrade.client.CourseService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;


public class CourseServiceImpl extends RemoteServiceServlet implements
CourseService {


// Provide implementation logic.
public String runGwtSprMvcHibJpaDemo(String s) {
// run server logic here
// we'll add our spring mvc + jpa integration in next part
return "DONE " + s + "!";
}


}


We'll compile and run our GWT RPC application now. The GWT jars
required are:

gwt-user.jar
gwt-dev-linux.jar (or windows based on your operating system)


Compile using GWT compiler and make sure the application can be run in
hosted mode without any issues. Bundle the
'com.ibt.intelligrade.server' package as a jar, so we can use them in
our Spring MVC - GWT RPC integration project.


=========================
Here's the second part...
=========================


========================================
PART TWO - GWT + SPRING MVC INTEGRATION
========================================


We now create a Spring MVC + JPA Web application project. We'll group
all our application source files under 'com.ibt.intelligrade' package
again.


We'll focus only on the GWT + Spring MVC integration and will ignore
the usual spring configuration steps.


Add the following GWT jars to your library:


gwt-servlet.jar


========
STEP 1:
========
Copy the entire translated GWT web files
('com.ibt.intelligrade.CourseServiceClient') into your web folder.


Edit 'CourseServiceClient.html' in
'com.ibt.intelligrade.CourseServiceClient' folder under web packages
and make the following changes:


==================================================================
com.ibt.intelligrade.CourseServiceClient/CourseServiceClient.html
==================================================================
<html>
<head>
<!-- -->
<!-- IMPORTANT: CHANGE from
content='CourseServiceClient' -->
<!-- IMPORTANT: to


content='com.ibt.intelligrade.CourseServiceClient=com.ibt.intelligrade.Cour­seServiceClient'
-->
<!-- -->
<meta name='gwt:module'
content='com.ibt.intelligrade.CourseServiceClient=com.ibt.intelligrade.Cour­seServiceClient'>
<title>Hello</title>
</head>
<body bgcolor="white">
<!-- -->
<!-- IMPORTANT: CHANGE from
src="gwt.js" -->
<!-- IMPORTANT: to


src="com.ibt.intelligrade.CourseServiceClient/gwt.js" -->
<!-- -->
<script language="javascript"
src="com.ibt.intelligrade.CourseServiceClient/gwt.js"></script>
</body>
</html>


========
STEP 2:
========
Write an abstract GWTSpringController for GWT RPC servlet integration.


==========================
GWTSpringController.java
==========================
package com.ibt.intelligrade.mvc;


import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.WebApplicationContext;
import
org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;


public abstract class GWTSpringController extends RemoteServiceServlet
implements Controller, ServletContextAware {


private ServletContext servletContext;


public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}


public ServletContext getServletContext() {
return servletContext;
}


public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
super.doPost(request, response);
return null;
}


}


Now make the GWT RPC Servlet 'CourseServiceImpl' to extend this one,
instead of the RemoteServiceServlet.
NOTE: This already exists from the GWT project, edit the same one and
make the changes as mentioned above.

=======================
CourseServiceImpl.java
=======================
package com.ibt.intelligrade.server;


import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.ibt.intelligrade.client.CourseService;
import com.ibt.intelligrade.mvc.GWTSpringController;
import com.ibt.intelligrade.service.CourseManager;
import org.springframework.web.context.WebApplicationContext;
import
org.springframework.web.context.support.WebApplicationContextUtils;


public class CourseServiceImpl extends GWTSpringController implements
CourseService {


// Provide implementation logic.
public String runGwtSprMvcHibJpaDemo(String s) {
// run server logic here
WebApplicationContext applicationContext =
(WebApplicationContext)


WebApplicationContextUtils.getWebApplicationContext(getServletContext());
CourseManager courseManager = (CourseManager)
applicationContext.getBean("courseManager");
String result = courseManager.testSpringMvcJpaIntegration();
return result;
}


}


========
STEP 3:
========
Create 'intelligrade-servlet.xml' in WEB-INF folder to wire our GWT
RPC controller configuration along with the other Spring MVC beans.

=========================
intelligrade-servlet.xml
=========================
<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">


<beans>


<!-- View Resolver for JSPs -->
<bean id="viewResolver"


class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="requestContextAttribute" value="rc"/>
<property name="viewClass"


value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/"/>
<!-- -->
<!-- NOTE: can be *.jsp if you can copy CourseServiceClient.html
to CourseServiceClient.jsp -->
<!-- <property name="suffix" value=".jsp"/> -->
<!-- NOTE: mapping *.html to avoid an extra step -->
<!-- -->
<property name="suffix" value=".html"/>
</bean>


<bean id="urlMapping"


class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- CourseServiceClientController to display
CourseServiceClient.html -->
<prop key="/course.smvc">courseController</prop>
<!-- CourseServiceClientServiceController for Servlet RPC
invocation -->
<!-- -->
<!-- IMPORTANT: DO NOT DO THIS: /gwt.smvc -->
<!-- IMPORTANT: DO THIS: /com.ibt.intelligrade.CourseServiceClient/
gwt.smvc -->
<!-- -->
<prop key="/com.ibt.intelligrade.CourseServiceClient/
gwt.smvc">courseServiceController</prop>
</props>
</property>
</bean>


<bean id="courseController"
class="com.ibt.intelligrade.mvc.CourseController">
<!-- -->
<!-- IMPORTANT: DO NOT DO THIS: /CourseServiceClient -->
<!-- IMPORTANT: DO THIS: /com.ibt.intelligrade.CourseServiceClient/
CourseServiceClient -->
<!-- -->
<property name="viewName" value="/
com.ibt.intelligrade.CourseServiceClient/CourseServiceClient"/>
</bean>


<bean id="courseServiceController"
class="com.ibt.intelligrade.server.CourseServiceImpl"/>
</beans>


=========================
Here's the final part...
=========================


=================================================
PART THREE - GWT + SPRING MVC + JPA INTEGRATION
=================================================


========================
1. CONFIGURATION FILES
========================
Under Web folder


WEB-INF
========
web.xml
========
<?xml version="1.0" encoding="UTF-8"?>


<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">


<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-intelligrade.xml
</param-value>
</context-param>


<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>


<servlet>
<servlet-name>intelligrade</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>


<servlet-mapping>
<servlet-name>intelligrade</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>


<session-config>
<session-timeout>
30
</session-timeout>
</session-config>


<welcome-file-list>
<welcome-file>
index.jsp
</welcome-file>
</welcome-file-list>


</web-app>


WEB-INF
===================================
intelligrade-servlet.xml (already defined above)
====================================
applicationContext-intelligrade.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: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.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">


<!-- Spring JPA Entity Manager Factory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="IntelliGradePU"/>
</bean>


<!-- Spring JPA Transaction Manager -->
<bean id="txManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory"
ref="entityManagerFactory"/>
</bean>


<!-- JPA annotations bean post processor -->
<!-- Required to load the EntityManager in the DAO -->
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostPro­cessor"/


<bean id="courseDao"
class="com.ibt.intelligrade.service.CourseDaoImpl"/>

<!-- SERVICE LAYER for Transaction Demaracation -->
<bean id="courseManager"
class="com.ibt.intelligrade.service.CourseManagerImpl">
<property name="courseDao" ref="courseDao"/>
</bean>


<!-- AOP Transaction Injection -->
<aop:config>
<aop:pointcut id="courseManagerMethods"
expression="execution(*
com.ibt.intelligrade.service.CourseManager.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-
ref="courseManagerMethods"/>
</aop:config>


<!-- Transaction Propogation advice for the SERVICE layer -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="persist*" propagation="REQUIRES_NEW"/>
<tx:method name="testSpringMvcJpaIntegration*"
propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS" read-
only="true"/>
</tx:attributes>
</tx:advice>


</beans>


In Source folder..
Create an Persistence Unit configuration file called persistence.xml
- Name must be persistence.xml (the required META-INF folder will be
automatically created by NetBeans)


META-INF
================
persistence.xml
================


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/
persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="IntelliGradePU" transaction-
type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>


<property name="hibernate.connection.url"
value="jdbc:derby://localhost:1527/sample"/>
<property name="hibernate.connection.driver_class"
value="org.apache.derby.jdbc.ClientDriver"/>
<property name="hibernate.connection.password" value="app"/


<property name="hibernate.connection.username" value="app"/


<property name="hibernate.c3p0.min_size" value="5"/>
<property name="hibernate.c3p0.max_size" value="20"/>
<property name="hibernate.c3p0.timeout" value="300"/>
<property name="hibernate.c3p0.max_statements" value="50"/


<property name="hibernate.c3p0.idle_test_period"
value="3000"/>

<property name="hibernate.cache.provider_class"
value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>


<property name="hibernate.dialect"
value="org.hibernate.dialect.DerbyDialect"/>


<!-- SQL stdout logging -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>


<!-- Scan for annotated classes and Hibernate mapping XML
files -->
<property name="hibernate.archive.autodetection"
value="class, hbm"/>


</properties>
</persistence-unit>
</persistence>


==================
2. DOMAIN CLASSES
==================
Create the following Domain Classes
- We have 2 domain objects named 'Course.java' and
'QuestionBank.java'


============
Course.java
============
package com.ibt.intelligrade.domain;


import com.ibt.intelligrade.domain.QuestionBank;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;


@Entity
@Table(name="course")
public class Course implements java.io.Serializable {


@Id @GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Basic()
private String name;
@Basic()
private String description;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
private Set<QuestionBank> questionBanks = new
HashSet<QuestionBank> (10);


/** Creates a new instance of Course */
public Course() {
}


public Long getId() {
return id;
}


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


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


public String getDescription() {
return description;
}


public void setDescription(String description) {
this.description = description;
}


public Set<QuestionBank> getQuestionBanks() {
return questionBanks;
}


public void setQuestionBanks(Set<QuestionBank> questionBanks) {
this.questionBanks = questionBanks;
}


public String toString() {
String retValue;


retValue = " Course Id: " + getId()
+ " Course Name: " + getName()
+ " Course Description: " + getDescription();
return retValue;
}


}


==================
QuestionBank.java
==================
package com.ibt.intelligrade.domain;

import java.io.Serializable;
import javax.persistence.*;


@Entity
@Table(name="questionbank")
public class QuestionBank implements Serializable{


@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Basic()
private String name;
@Basic()
private String description;
@ManyToOne(fetch=FetchType.LAZY)
private Course parent;


/** Creates a new instance of QuestionBank */
public QuestionBank() {
}


public Long getId() {
return id;
}


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


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


public String getDescription() {
return description;
}


public void setDescription(String description) {
this.description = description;
}


public Course getParent() {
return parent;
}


public void setParent(Course parent) {
this.parent = parent;
}


public String toString() {
String retValue;


retValue = " QuestionBank Id: " + getId()
+ " QuestionBank Parent : " + getParent().getName()
+ " QuestionBank Name: " + getName()
+ " QuestionBank Description: " + getDescription();
return retValue;
}


}


===============================
3. Spring Service / DAO layer
===============================

===============
CourseDao.java
===============
package com.ibt.intelligrade.service;


import com.ibt.intelligrade.domain.Course;
import java.util.List;


public interface CourseDao {


void persist (Course course);
Course getReference (Course course);
List<Course> getAllCourse ();
void remove(Course crs);


}


===================
CourseDaoImpl.java
===================
package com.ibt.intelligrade.service;

import com.ibt.intelligrade.domain.Course;
import java.util.List;
import javax.persistence.*;
import org.springframework.orm.jpa.support.JpaDaoSupport;


public class CourseDaoImpl
implements CourseDao{


@PersistenceContext()
private EntityManager entityManager;


/** Creates a new instance of CourseDaoImpl */
public CourseDaoImpl() {
}


public void persist (Course course)
{
getEntityManager().persist(course);
}


public Course getReference (Course course)
{
return getEntityManager().getReference(course.getClass(),
course.getId());
}


public List<Course> getAllCourse (){
return getEntityManager().createQuery("from
Course").getResultList();
}


public void remove(Course crs) {
getEntityManager().remove (crs);
}


public EntityManager getEntityManager() {
return entityManager;
}


public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}


}


===================
CourseManager.java
===================
package com.ibt.intelligrade.service;

import com.ibt.intelligrade.domain.Course;
import java.util.List;


public interface CourseManager {


void persistCourse (Course course);
String testSpringMvcJpaIntegration();


}


=======================
CourseManagerImpl.java
=======================
package com.ibt.intelligrade.service;

import com.ibt.intelligrade.domain.Course;
import com.ibt.intelligrade.domain.QuestionBank;
import java.util.List;
import java.util.Set;


public class CourseManagerImpl implements CourseManager{


private CourseDao courseDao;


/**
* Creates a new instance of CourseManagerImpl
*/
public CourseManagerImpl() {
}


public CourseDao getCourseDao() {
return courseDao;
}


public void setCourseDao(CourseDao courseDao) {
this.courseDao = courseDao;
}


public void persistCourse (Course course)
{
courseDao.persist (course);
}


// Business method.. Testing the JPA Integration through DAO
public String testSpringMvcJpaIntegration() {
// PERSIST COURSE
Course course = new Course();
course.setName("JAVA");
course.setDescription("JAVA Standard Edition");


persistCourse (course);
StringBuilder stringBuilder = new StringBuilder ();
stringBuilder.append("<br>");
stringBuilder.append(" SAVED Course: "+course);
stringBuilder.append("<br>");


// PERSIST COURSE WITH QUESTION BANK COLLECTION
QuestionBank questionBank = new QuestionBank();
questionBank.setName("Java Question Bank");
questionBank.setDescription("JAVA question bank descritpion");
questionBank.setParent(course);
course = courseDao.getReference(course);
course.getQuestionBanks().add(questionBank);
persistCourse (course);
stringBuilder.append("<br>");
stringBuilder.append(" SAVED Course with QuestionBank:
"+questionBank);
stringBuilder.append("<br>");


// RETREIVE THE COURSE AND TEST IT
// DELETE THEM FINALLY
List<Course> courses = courseDao.getAllCourse();
for (Course crs : courses) {
stringBuilder.append("<br>");
stringBuilder.append(" RETREIVED Course: "+crs);
stringBuilder.append("<br>");
Set<QuestionBank> qbs = crs.getQuestionBanks();
for (QuestionBank qb : qbs) {
stringBuilder.append("<br>");
stringBuilder.append(" QuestionBank: "+qb);
stringBuilder.append("<br>");
}
// DELETE COURSE (AND THE QUESTION BANK COLLECTION)
courseDao.remove(crs);
stringBuilder.append("<br>");
stringBuilder.append("REMOVED Course: "+crs);
stringBuilder.append("<br>");
}
return stringBuilder.toString();
}


}


=========================
4. The Spring Controller
=========================
Write a pure spring 'CourseController' which would be the landing
page.

=======================
CourseController.java
=======================
package com.ibt.intelligrade.mvc;


import com.ibt.intelligrade.domain.Course;
import com.ibt.intelligrade.domain.QuestionBank;
import com.ibt.intelligrade.service.CourseManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import
org.springframework.web.servlet.mvc.ParameterizableViewController;


import javax.persistence.*;
import java.util.*;


public class CourseController extends ParameterizableViewController {


/** Creates a new instance of HomeController */
public CourseController() {
}


protected ModelAndView handleRequestInternal(HttpServletRequest
request, HttpServletResponse response)
throws Exception {
return super.handleRequestInternal(request, response);
}


}


====================================
GWT + Spring MVC + JPA integration DONE!
**. Run your project and Enjoy!!
====================================
 类似资料: