求Spring/GWT项目中无SpringBoot的Spring Data JPA Maven示例项目
Got it, let's build a straightforward non-Spring Boot Maven project that combines Spring Data JPA (for Postgres access) and GWT. I'll walk you through every key part so you can avoid Spring Boot's "magic" and have full control over servlet registration.
Here's the clean, standard layout we'll use—following Maven web app conventions:
spring-data-gwt-non-boot/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ ├── com/ │ │ │ │ ├── example/ │ │ │ │ │ ├── config/ # Spring configuration classes │ │ │ │ │ ├── entity/ # JPA entities │ │ │ │ │ ├── repository/ # Spring Data JPA repositories │ │ │ │ │ └── gwt/ # GWT service classes │ │ │ │ │ ├── client/ # GWT client-side code │ │ │ │ │ └── server/ # GWT server-side servlet │ │ ├── resources/ │ │ │ └── db.properties # Postgres connection details │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ ├── web.xml # Servlet and Spring config │ │ │ └── gwt/ │ │ │ └── Example.gwt.xml # GWT module config │ │ └── index.html # GWT entry point └── pom.xml
We'll include only the necessary dependencies—no Spring Boot starters. Here's the core setup:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>spring-data-gwt-non-boot</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <spring.version>5.3.24</spring.version> <spring.data.jpa.version>2.7.5</spring.version> <hibernate.version>5.6.14.Final</hibernate.version> <postgres.version>42.5.1</postgres.version> <gwt.version>2.10.0</gwt.version> <servlet.api.version>4.0.1</servlet.api.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <!-- Spring Core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring Data JPA --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${spring.data.jpa.version}</version> </dependency> <!-- Hibernate (JPA Provider) --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <!-- Postgres JDBC Driver --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>${postgres.version}</version> </dependency> <!-- GWT --> <dependency> <groupId>com.google.gwt</groupId> <artifactId>gwt-servlet</artifactId> <version>${gwt.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>com.google.gwt</groupId> <artifactId>gwt-user</artifactId> <version>${gwt.version}</version> <scope>provided</scope> </dependency> <!-- Servlet API --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlet.api.version}</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <!-- GWT Compiler Plugin --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>gwt-maven-plugin</artifactId> <version>${gwt.version}</version> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> <configuration> <module>com.example.gwt.Example</module> <runTarget>index.html</runTarget> </configuration> </plugin> <!-- Maven War Plugin --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> <configuration> <failOnMissingWebXml>true</failOnMissingWebXml> </configuration> </plugin> </plugins> </build> </project>
Create SpringConfig.java in com.example.config to set up the database, JPA, and Spring Data:
package com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.Properties; @Configuration @ComponentScan(basePackages = "com.example") @PropertySource("classpath:db.properties") @EnableJpaRepositories(basePackages = "com.example.repository") public class SpringConfig { private final Environment env; public SpringConfig(Environment env) { this.env = env; } @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getProperty("db.driver")); dataSource.setUrl(env.getProperty("db.url")); dataSource.setUsername(env.getProperty("db.username")); dataSource.setPassword(env.getProperty("db.password")); return dataSource; } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); em.setPackagesToScan("com.example.entity"); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); Properties jpaProperties = new Properties(); jpaProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); jpaProperties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); em.setJpaProperties(jpaProperties); return em; } @Bean public PlatformTransactionManager transactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); return transactionManager; } }
Add db.properties in src/main/resources with your Postgres details:
db.driver=org.postgresql.Driver db.url=jdbc:postgresql://localhost:5432/your_db_name db.username=your_db_user db.password=your_db_password hibernate.hbm2ddl.auto=update hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
First, a simple JPA entity User.java in com.example.entity:
package com.example.entity; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; // Getters, setters, and default constructor public User() {} public User(String name, String email) { this.name = name; this.email = email; } // ... add getters and setters for all fields }
Then the Spring Data repository UserRepository.java in com.example.repository:
package com.example.repository; import com.example.entity.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { // Add custom queries here if needed }
First, the client-side service interface UserService.java in com.example.gwt.client:
package com.example.gwt.client; import com.example.entity.User; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; import java.util.List; @RemoteServiceRelativePath("userService") public interface UserService extends RemoteService { List<User> getAllUsers(); User saveUser(User user); }
The required async interface UserServiceAsync.java:
package com.example.gwt.client; import com.example.entity.User; import com.google.gwt.user.client.rpc.AsyncCallback; import java.util.List; public interface UserServiceAsync { void getAllUsers(AsyncCallback<List<User>> callback); void saveUser(User user, AsyncCallback<User> callback); }
Now the server-side servlet implementation UserServiceImpl.java in com.example.gwt.server. Critical: Extend SpringBeanAutowiringSupport to enable Spring dependency injection in the GWT servlet:
package com.example.gwt.server; import com.example.entity.User; import com.example.gwt.client.UserService; import com.example.repository.UserRepository; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.context.support.SpringBeanAutowiringSupport; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import java.util.List; public class UserServiceImpl extends RemoteServiceServlet implements UserService { @Autowired private UserRepository userRepository; @Override public void init(ServletConfig config) throws ServletException { super.init(config); SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); } @Override public List<User> getAllUsers() { return userRepository.findAll(); } @Override public User saveUser(User user) { return userRepository.save(user); } }
Add the GWT module config Example.gwt.xml in src/main/webapp/WEB-INF/gwt:
<?xml version="1.0" encoding="UTF-8"?> <module rename-to='example'> <inherits name='com.google.gwt.user.User'/> <inherits name='com.google.gwt.user.theme.clean.Clean'/> <source path='client'/> <source path='shared'/> <!-- Use this if you have shared client/server classes --> <entry-point class='com.example.gwt.client.EntryPoint'/> </module>
And a basic GWT entry point EntryPoint.java in com.example.gwt.client to test the service:
package com.example.gwt.client; import com.example.entity.User; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import java.util.List; public class EntryPoint implements EntryPoint { private final UserServiceAsync userService = UserService.App.getInstance(); @Override public void onModuleLoad() { // Test fetching users userService.getAllUsers(new AsyncCallback<List<User>>() { @Override public void onSuccess(List<User> users) { Window.alert("Fetched " + users.size() + " users!"); } @Override public void onFailure(Throwable caught) { Window.alert("Error fetching users: " + caught.getMessage()); } }); } }
This is where we register the GWT servlet and set up Spring's context loader. Create web.xml in src/main/webapp/WEB-INF:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- Spring Context Loader --> <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.example.config.SpringConfig</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- GWT Remote Service Servlet --> <servlet> <servlet-name>UserService</servlet-name> <servlet-class>com.example.gwt.server.UserServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>UserService</servlet-name> <url-pattern>/example/userService</url-pattern> </servlet-mapping> <!-- Welcome File --> <welcome-file-list> <welcome-file>index.html</welcome-file-list> </welcome-file-list> </web-app>
Add a simple index.html in src/main/webapp:
<!DOCTYPE html> <html> <head> <title>Spring Data + GWT Non-Boot Example</title> <script type="text/javascript" language="javascript" src="example/example.nocache.js"></script> </head> <body> <h1>Spring Data + GWT Non-Boot App</h1> </body> </html>
- Set up Postgres: Create a database matching the
db.urlindb.properties. - Compile GWT: Run
mvn gwt:compileto build the client-side code. - Build the WAR: Run
mvn packageto generate the WAR file. - Deploy: Deploy the WAR to a servlet container like Tomcat or Jetty.
- Test: Access
http://localhost:8080/spring-data-gwt-non-boot—you'll see an alert showing the number of users (initially 0, but you can extend the entry point to save a user first).
This setup gives you full control over servlet registration and Spring configuration, no Spring Boot magic involved. You can extend it with more services, entities, or GWT UI components as needed.
内容的提问来源于stack exchange,提问作者Alex Worden

