[Spring MVC]使用全注解(无xml文件)配置SpringMVC项目遇到的问题及解决方法

在实际项目中遇到两个问题,

1. 在项目中无法使用properties资源文件中的properties.即@Value注解无法得到property的值。

 解决办法:在下面的RootConfig.java中添加PropertyPlaceholderConfigurer作为bean组件

@Bean
public static PropertyPlaceholderConfigurer properties() {
        final PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        ppc.setIgnoreResourceNotFound(true);
        final List<Resource> resourceLst = new ArrayList<Resource>();


        if(System.getProperty(SPRING_CONFIG_LOCATION) != null){
            String configFilePath = System.getProperty(SPRING_CONFIG_LOCATION);
            String[] configFiles = configFilePath.split(",|;");

            FileSystemResource res =null;
            for (String configFile : configFiles) {
                if (configFile.startsWith("file:")){
                    resourceLst.add(new FileSystemResource(configFile));
                }else {
                    resourceLst.add( new ClassPathResource(configFile));
                }
            }
        }else {
            resourceLst.add(new ClassPathResource("config/application.properties"));
            resourceLst.add(new ClassPathResource("config/kafka.properties"));
        }
        ppc.setLocations(resourceLst.toArray(new Resource[]{}));
        return ppc;
    }

2. 解决问题1之后,发现在Service组件中可以使用@Value了, 但是在Controller中依然不能使用@Value.

解决办法: 在WebConfig类中再次添加PropertyPlaceholderConfigurer作为bean组件。因为Spring和SpringMVC是不同的容器,

原因:

Spring容器导入的properties配置文件,只能在Spring容器中用而在SpringMVC容器中不能读取到。 需要在SpringMVC 的配置文件中重新进行导入properties文件,并且同样在父容器Spring中不能被使用,导入后使用@Value("${key}")在java类中进行读取。

 

详细原因请阅读:
https://www.cnblogs.com/hafiz/p/5875740.html

 

 

 

=====================================================================================

参考文章:https://blog.csdn.net/classicer/article/details/50753019

 

pring MVC 无XML(纯 Java)配置入门示例

本示例是从《Spring in Action, Fourth Edition》一书而来,涉及的是书中5.1节部分内容,书中其实说的很详细,但是没有工程实现细节的描述,这篇博文记录了我自己的实现过程,也算是书本的一(gou)种(wei)拓(xu)展(diao)吧。由于本人也是初学(看在下内容惨淡的博客页面=.=),所以有什么写得不对的地方还忘各位看官海涵。
(1) Spring MVC对 request 做了什么?

当一个 Request 离开浏览器(下图标号1)时,携带了用户的请求信息,包括url以及表单等,它到达的第一站就是 DispatcherServlet 。像大多数基于 Java 的 web 框架一样,Spring MVC 首先通过一个前端控制器处理,把 Request  交给其他组件处理(类似于前台姐姐把客户带到要去的各种部门),在 Spring MVC 里面,充当前端控制器这一角色的就是 DispatcherServlet 。

DispatcherServlet 目的是把 Request 送到其他的 Controller,对 Request 作进一步处理,但是一般情况下程序中会有很多的 Controller,它们各自负责处理不同的 Request ,于是 DispatcherServlet 需要一些协助来决定一个 Request 应该送到哪个 Controller 。因此 DispatcherServlet 会询问 Handler mappings (上图标号2),一个 Request 应该何去何从,Handler mappings 谢邀之后不敢怠慢,通过验看 Request 携带的 URL 进行抉择。

Handler mappings 的结果出来之后,DispatcherServlet 欢天喜地地把 Request 送给对应的 Controller (如上图标号3)。到达之后,Request 卸下重担(携带着的由用户提交的信息)并耐心地等待 Controller 处理这些信息(而实际上,一个机智的Controller 自己几乎不做任何处理,而是把活儿交给其他负责业务逻辑的 service 对象)。

Controller 处理完后得到的结果往往需要返回给用户并在浏览器中显示,这个结果被称作 model, 它比较粗糙,需要补充额外信息转换成用户友好型的格式,例如 HTML 。出于这个目的呢,这一 model 需要交给一个 view , 典型的有 JavaServer Page (JSP)。

所以,Controller 最后需要做的就是把 model 数据打包并且确定一个到时候用来渲染这个 model 的 view,然后把 Request、model 数据以及 view 的名字一块发送回 DispatcherServlet(如上图标号4)。

因此 Controller 实现了与特定 view 的解耦, DispatcherServlet 拿到的 view 名也并不直接确定这个 view 是 JSP,DispatcherServlet 只知道凭借这一 view 名找到的 view 可以处理手头上的 model 数据,于是它把 view 名给 view resolver 来帮它找到对应的 view(如上图标号5) 。

DispatcherServlet 终于得到了渲染 model 数据的 view,Request 的旅程也即将走到尽头, 它的最后一站是在 view  的实现部分(上图标号6),而view 的实现典型的有 JSP ;这一步使用 model 数据进行输出结果的渲染,之后通过响应对象把渲染结果送回到客户端。

(2) Spring MVC 入门工程实现的准备工作;

需要的工具

Eclipse IDE for Java EE Developers ; 
Apache Tomcat ; 
Spring framework 工具包,例如 spring-framework-4.2.5.RELEASE-dist.zip ; 
以及其他一些依赖包:commons-logging-1.1.3.jar,jstl-1.2.jar,log4j-1.2.17.jar;(没有的请自行查找,不知道上哪找? 看这里看这里,具体版本不一定按照这里列出的)

建立 Eclipse 工程

推荐使用优雅的 Maven,但这里没有…请稍后自行建立…

File->New->Other…->Web->Dynamic Web project->Next;

弹出了一个框,工程命名为 spittr(不要在意,原作者奇思妙想取的…下文会有介绍),没什么其他事的话直接 Finish;

完了之后,找到 spittr\WebContent\WEB-INF\lib,把 jar 包们 copy 进去….找目录的时候可以直接在lib上右键->show in->system explorer ( 对于windows系统来说 ),Spring 的那些 jar 包们在spring-framework-x.x.x.RELEASE\libs下(不管三七21一个 *RELEASE.jar 搜索,将代码包全部Copy过来了,其他包日后也会用得到吧….)

这一切做完应该就可以开始写代码了…

(3) Spring MVC的Java配置;

经过开头那幅图的一通介绍,看起来 Spring MVC 需要的配置会很复杂…相对来说,以前确实很复杂(用 xml 配置),然而现在只需简单几步就能搞定。

配置 DispatcherServlet

DispatcherServlet 是 Spring MVC 的核心组件,它是一个 request 首先到达的地方,负责 request 在其他各个组件间的传递加工,在过去,像DispatcherServlet 这样的 servlets 是使用 web.xml 文件配置的,当然现在也可以这样做….但是由于时代的进步,基于 Servlet 3 和 Spring 3.1 的一些新特性,我们可以用更简单的方式来配置,即使用 Java 代码。

代码清单1:SpittrWebAppInitializer.java

package spittr.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer  {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] {RootConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"};
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
或许建工程的时候你就很在意 spittr 是干嘛的,但还是那句话,下文有介绍…先看代码…代码里是一个叫做 SpittrWebAppInitializer 的类,在spittr.config包里,顾名思义,它是起一个初始化配置的作用,并且相当于一个程序的入口类,在整个程序启动时加载。

首先需要介绍的就是这个名字很拉风的类,即被 SpittrWebAppInitializer 继承的 AbstractAnnotationConfigDispatcherServletInitializer ,乍看一头雾水…..哥们你干嘛的….简单来说,它自动被加载,负责应用程序中 servlet 上下文中的 DispatcherServlet 和 Spring 其他上下文的配置。

关于 AbstractAnnotationConfigDispatcherServletInitializer
各位好,如果以上介绍不过瘾,这里是复杂来说…
在 Servlet 3.0 环境下,Servlet 容器会在 classpath 下搜索实现了 javax.servlet 
.ServletContainerInitializer 接口的任何类,找到之后用它来初始化 Servlet 容器。
Spring 实现了以上接口,实现类叫做 SpringServletContainerInitializer, 它会依次搜寻实现了 WebApplicationInitializer的任何类,并委派这个类实现配置。之后,Spring 3.2 开始引入一个简易的 WebApplicationInitializer 实现类,这就是 AbstractAnnotationConfigDispatcherServletInitializer。
所以 SpittrWebAppInitializer 继承 AbstractAnnotationConfigDispatcherServletInitializer之后,也就是间接实现了 WebApplicationInitializer,在 Servlet 3.0 容器中,它会被自动搜索到,被用来配置 servlet 上下文。
当然可以直接实现 WebApplicationInitializer,但作为入门来说,AbstractAnnotationConfigDispatcherServletInitializer 是一个不错的选择。

回到代码,SpittrWebAppInitializer 需要重写3个方法,第一个,getServletMappings(),为 DispatcherServlet 提供一个或更多的Servlet 映射;这里是被映射到 /,指示它为默认的 servlet,用来操作所有来到程序的 Request。为了理解另外两个方法的作用,需要先明白 DispatcherServlet 和一个叫做 ContextLoaderListener 的 servlet 监听器之间的关系。

A TALE OF TWO APPLICATION CONTEXTS1
DispatcherServlet 开始启动时,会产生一个 Spring 应用程序上下文,把它和配置文件中声明的 bean 或者类一起加载进来。通过getServletConfigClasses() 方法,设置 DispatcherServlet 通过 WebConfig 配置类来完成 Spring 上下文和 bean 的加载。
但是在 Spring web 程序中,往往还有另外一个应用程序上下文,它是由 ContextLoaderListener 产生的。通过调用 getRootConfigClasses()方法返回的类就是用来配置 ContextLoaderListener 产生的上下文。
其中,DispatcherServlet 是用来加载涉及 web 功能的 beans,例如 controllers, view resolvers, 和 handler mappings;而 ContextLoaderListener 则是用来载入程序中其余的 beans,例如一些中间层和数据层组件,完成的是程序后端功能。
于是,我们知道,AbstractAnnotationConfigDispatcherServletInitializer 产生了一个 DispatcherServlet 和一个 ContextLoaderListener,以上代码清单1通过两个方法分别得到两个上下文的配置类(使用@Configuration注解)。和 web.xml 的配置方法不一样的是,这种方法只适用于使用了 Servlet 3.0 的服务器,例如 Apache Tomcat 7 或 7+。不过 Servlet 3.0 规范在 2009 年 12 月形成最终版本, 几乎不用担心遇到不支持 Servlet 3.0 的 servlet 容器 。

但是,如果你的服务器实在是不能支持 Servlet 3.0,那么 Java 配置的方法不适用于你了,你适合使用 web.xml 的配置方法,在原作者书中,第七章有介绍,不过本文不讨论这种方法,因为这种方法已经很成熟…网上一搜一大堆,请自行搜索….

启用 Spring MVC 功能

基于 Java 的方式超级简单,你的配置类只需要带有一个@EnableWebMvc注解,就像这样子:

package spittr.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@EnableWebMvc
public class WebConfig {}
1
2
3
4
5
6
7
8
是不是很简洁?这样就可以开始使用 Spring MVC 框架了,但是各种组件还没配置:

View Resolver:虽然 Spring 会默认使用 BeanNameViewResolver,它会搜索 ID 和 view 名一致并且实现了 view 接口的 bean。

Controller:要让 Spring 能够加载各种 Controllers,必须对它们进行显式声明,然后启用 Component-scanning 功能自动搜索。

完善 Request 的映射: 按照之前的配置,对于一些静态资源(图片、样式表等)也是映射到默认的 Servlet,而这显然是不合理的。

完善之后就是像下面这样子:

代码清单2:WebConfig.java

package spittr.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.
DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.
WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.
InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("spittr.web")
public class WebConfig extends WebMvcConfigurerAdapter {

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}
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
首先可以发现的是,代码中添加了@ComponentScan注解,因此spitter.web 包将会被扫描以搜索 Controller。

然后,代码添加了一个 ViewResolver bean, 具体来说是一个 InternalResourceViewResolver,再具体的话这里不再讨论,请查阅其他资料….或参看原书第六章,就这里而言,只需要知道它是用来查找 view(JSP)的,并且会对 view 的名字使用前缀和后缀进行包裹就行了 ( 举个栗子,在上述代码中,名字为 home 的 view 将被转换为 /WEB-INF/views/home.jsp )。

最后, WebConfig 类继承 WebMvcConfigurerAdapter 并重写了它的 configureDefaultServletHandling() 方法,通过调用所给的DefaultServletHandlerConfigurer 对象的 enable() 方法,告诉DispatcherServlet 转发对静态资源的 Request 到 Servlet 容器的默认Servlet, 而不是自己处理。

搞定了 WebConfig ,接下来搞定 RootConfig,由于这里集中讨论 Spring 的 web 开发,所以简单贴出 RootConfig的代码,不做额外解释了:

代码清单3:RootConfig.java

package spittr.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan(basePackages={"spittr"},
excludeFilters={
    @Filter(type=FilterType.ANNOTATION, value=EnableWebMvc.class)
})

public class RootConfig {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Spittr 的介绍

是的,终于到了这里。

之前所做的已经足够了,现在可以开始去构思一个 web 程序的实现了,按照原作者的说法,原作者打算做一个简易的 Twitter,于是,对 Twitter 和 Spring 各取一半就有了 Spitter,为了逼格更高一点,参考热门的 Flickr,扔掉一个 e 就有了 Spittr。Spittr 的用户叫做 spitters,他们发布的状态叫做spittles,是不是很有意思?

好了,就到这里,接下来写完一个简单的 Controller 就收工…..

(4) 一个简单的Controller;

在 Spring MVC中,Controller 就是类,只是类中的方法们带有@RequestMapping注解,以表明它们各自处理什么样的 Request。

让我们看一个简单的Controller示例,它用来处理目标路径是/的 Request:

代码清单4: HomeController

package spittr.web;

import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {

    @RequestMapping(value="/", method=GET)
    public String home() {
        return "home";
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
第一眼你注意到的应该就是这个@Controller注解,显然它是用来声明一个 Controller,然而它和 Spring MVC 却没有什么关系,它的功能和 @Component 一致,都是便于component-scanning 能够查找到它所注解的类。唯一不用 @Component 的原因是,它的表现力没有这么强,你不会一眼就知道这个它所标注的类是在起到一个 Controller 的作用。

HomeController 唯一的方法是 home(), 使用了@RequestMapping注解,其中 value 属性指定了方法所操作的 Request 路径,即“/”;method 属性则说明了它所接受的HTTP访问方式。

每当一个 HTTP GET Request 访问 / 路径的时候,home() 方法就会被调用:返回一个“home”的String字符串。接着 Spring MVC 将“home”作为view 名,之前说过,DispatcherServlet 会找 view resolver ,把逻辑上的 view 名映射到一个实际的 view。

按照已配置的 InternalResourceViewResolver,“home”将被映射到 /WEB-INF/views/home.jsp,所以接下来的工作就是 home.jsp 的编写:

代码清单5: home.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
    <head>
        <title>Spittr</title>
    </head>
    <body>
        <h1>Welcome to Spittr</h1>
    </body>
</html>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值