纯手工系列–零配置实现Spring MVC+Spring Data JPA
最近忙于一个项目,将以前Spring boot搭建的系统,降为Spring传统的一个系统。事情的缘由是因为我们发现Spring boot搭建的系统出现了几次莫名奇妙的错误,然后又莫名奇妙的好了。后来我们又在每个环节做了单元测试,也没有出现问题。但是问题必须要解决啊,所以小组决定改成传统的Spring,方便查找到问题的所在,好除去错误。这里又出现了一个问题,就是按照原来的配置方法搭建Spring,不能真正的体现现在Spring boot零配置的优势,也不能帮我们理解Spring boot隐藏的一些细节,所以我们决定使用JavaConfig的方式来配置我们的系统。下面就进入主题。
作者使用的开发环境是Linux,使用的IDE是NetBeans8.2,但是这里将使用纯手工进行,但首先需要大家把JDK1.8与Maven的环境变量配置好,环境配置这里就不多说了,网上有很多的资料。我们使用Maven的命令创建一个web项目命令如下:
mvn archetype:create
-DgrounpId=packageName //项目的包名
-DartifactId=appName //项目的名称
-DarchetypeArtifactId=maven-archetype-webapp //告诉Maven创建的是一个Web项目
创建好项目后我们需要添加依赖,打开pom.xml文件。这里只给出依赖的部分。代码如下:
<dependencies>
<!--后面使用WebSocket-->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--spring基于注释的校验机制 支持J2EE的JSR303规范 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<!-- hibernate基于注释的校验,也支持J2EE的JSR303规范 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
<type>jar</type>
</dependency>
<!--日志库-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<!--spring javaConfig的主要使用的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<!--spring对事务的处理依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<!--java持久化依赖-->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
<!--阿里的数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.26</version>
</dependency>
<!-- 这里需要注意:这个适配spring-tx依赖的 -->
<dependency>
<groupId>org.lucee</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<!-- javax.servlet只是在编译时使用 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!--spring mvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<!--支持JSTL标签-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--spring 测试-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.3.RELEASE</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<!--我们通过javaConfig来实现它的@EntityScan
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>1.4.1.RELEASE</version>
<type>jar</type>-->
</dependency>
<!--这里偷一个懒,不然还要添加hibernate-core、spring-aspect、hibernate-entitymanager、javax.transaction-api等一系列的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.4.1.RELEASE</version>
</dependency>
<!--之前项目使用的Json库,延续以前的,建议新项目中不使用它,使用jackson之类的,听说java 9自带-->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>1.1</version>
<type>jar</type>
<classifier>jdk15</classifier>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
<!-- commons工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
</dependencies>
接下来就是编写java类来配置Spring了,首先,我们使用servlet3.0的特性,通过java类来启动spring的服务,代码如下:
//文件名:WebInitializer.java
//表明这个类同时还是一个控制器
@RestController
//这里的package.component"根据自己的实际情况填写
@ComponentScan("package.component")
//通过WebApplicationinitializer表示可以在web应用程序启动时被加载
public class WebInitializer implements WebApplicationInitializer{
@Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext ctx=new
AnnotationConfigWebApplicationContext();
//加载配置文件类,这里与xml配置是对应的,每个被加载的类需要使用@Configuration注解进行标注
//WebConfig.class是对spring MVC的配置
//JpaConfiguration是对spring data jpa的配置
ctx.register(WebConfig.class,JpaConfiguration.class);
//增加一个跨域访问的过滤器
sc.addFilter("CorsFilter", CorsFilter.class);
ctx.setServletContext(sc);
//动态加载DispatcherServlet,它时spring mvc的核心
Dynamic springmvc=sc.addServlet("springmvc", new DispatcherServlet(ctx));
springmvc.addMapping("/");
springmvc.setLoadOnStartup(1);
springmvc.setMultipartConfig(new MultipartConfigElement("/tmp/uploads"));
}
下面来看看,WebConfig.java和JpaConfiguration.java这个两个文件的说明
//文件名:WebConfig.java
//告诉spring我是一个配置类
@Configuration
//使用Spring MVC功能
@EnableWebMvc
//请按实际情况进行配
@ComponentScan("package.component.controller")
public class WebConfig extends WebMvcConfigurerAdapter {
//创建一个spring MVC视图解析器的Bean,当Controller将请求处理结果放入到ModelAndView中以后,DispatcherServlet会根据ModelAndView选择合适的视图进行渲染
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
//通过默认的servlet容器处理静态资源的请求
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//增加静态资源的配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("/web/**").addResourceLocations("/WEB-INF/web/");
//文件上传表单的视图解析器
@Bean
public MultipartResolver multipartResolver()throws IOException{
return new StandardServletMultipartResolver();
}
//这个方法一般用来处理中文乱码的问题
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters){
StringHttpMessageConverter converter =new StringHttpMessageConverter();
MediaType mediaType1=new MediaType("data", "json", Charset.forName("UTF-8"));
MediaType mediaType2=new MediaType("text", "html", Charset.forName("UTF-8"));
List<MediaType> mtList=new ArrayList<>();
mtList.add(mediaType1);
mtList.add(mediaType2);
converter.setSupportedMediaTypes(mtList);
converters.add(converter);
}
//文件名:JpaConfiguration.java
//最高优先级
@Order(Ordered.HIGHEST_PRECEDENCE)
//告诉spring我是一个配置类
@Configuration
//开启事务管理
@EnableTransactionManagement(proxyTargetClass = true)
//使用这个注释,开启spring data jpa的功能,并要告诉要使用那个包的文件
@EnableJpaRepositories(basePackages = "package.**.repository")
//获得在resources文件夹下面的datasource.properties文件
@PropertySource("classpath:datasource.properties")
public class JpaConfiguration implements EnvironmentAware{
private Environment env;
@Bean
public DataSource dataSource() {
//通过数据库线程池创建数据库连接,这里使用的是阿里的 DruidDataSource
//它提供了一个简单的后台监视功能
//这里,没有进行过多的配置,在spring boot中可以通过xml或者yml文件进行配置
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.username"));
dataSource.setPassword(env.getProperty("jdbc.password"));
return dataSource;
}
/**
如果Spring部署在J2EE的应用服务器中,Spring可能已经创建好EntityManagerFactory并将其置于JNDI中使用,这种情况可使用JNDI来获取对EntityManagerFactory的引用
@Bean
@Profile("javaee")
public JndiObjectFactoryBean dataSource() throws IllegalArgumentException {
JndiObjectFactoryBean dataSource = new JndiObjectFactoryBean();
dataSource.setExpectedType(DataSource.class);
dataSource.setJndiName(env.getProperty("jdbc.jndiDataSource"));
return dataSource;
}
**/
//相当于在文件顶部使用Spring boot中的@EntityScan(basePackages = "package.**.entities")注释
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
//LocalContainerEntityManagerFactoryBean说明我们仅使用JPA进行数据访问
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean=new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan("package.**.entities");
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
return entityManagerFactoryBean;
}
@Bean
public JpaVendorAdapter jpaVendorAdapter(){
//使用Hibernate的数据库适配器
//当然还要其他的例如EclipseLinkJpaVendorAdapter、OpenJpaVendorAdapter、TopLinkJpaVendorAdapter
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(false);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);
return hibernateJpaVendorAdapter;
}
//异常处理
@Bean
PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){
return new PersistenceExceptionTranslationPostProcessor();
}
//属性管理API,可实现不同数据源的无缝切换方便的访问property属性,包含系统属性,环境变量和自定义的。并且是可配置的,可以加入自定义的property,这基于spring使用PropertySources 来管理不同的PropertySource
@Override
public void setEnvironment(Environment environment) {
env = environment;
}
//对事务的处理
@Bean(name = "transactionManager")
public PlatformTransactionManager creaPlatformTransactionManager(EntityManagerFactory entityManagerFactory){
JpaTransactionManager transactionManager=new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
在文件JpaConfiguration.java中我们提到了
@EnableJpaRepositories(basePackages = “package.**.repository”),来开启实现JPA持久化的功能,使用它的好处就是我们不用在写实现类了,直接提供接口进行,剩下的事情由spring来完成。这里假设你已经写好了一个User的实体类,下面看一个例子,还说明它是怎么工作的:
//文件名称:UserRepository.java
//告诉spring 这个类用于数据访问组件
@Repository
public interface UserRepository extends JapRepository<User,Integer>{
}
好了,这样就实现了存取数据库的功能,现在就可以对数据库进行增删改查、进行分页查询和指定排序等操作了,是不是很方便。
最后我们通过给个UserController类使用Spring MVC来时模拟实现RESTful服务,代码如下:
//文件名称:UserController.java
@RestController
@RequestMapping({"/user"})
public class UserController {
private static Logger logger = LoggerFactory.getLogger(LoginController.class);
//进行自动状态之前的JPA
@Autowired
UserRepository userRepository;
//设置请求的路径与请求的方法
@RequestMapping(value = "/getUserInfo",method = RequestMethod.GET)
public String getUserInfo() throws Exception{
List<User> users=userRepository.findAll();
JSONArray jsonArray = JSONArray.fromObject(users);
return jsonArray.toString();
}
这样一个完整的零配置实现Spring MVC+Spring Data JPA就可以工作了。