SpringMVC 的制作过程和上述流程几乎是一致的,具体的实现流程是什么?
1.创建 web 工程(Maven 结构)
2.设置 tomcat 服务器,加载 web 工程(tomcat 插件)
3.导入坐标(SpringMVC+Servlet)
4.定义处理请求的功能类(UserController)
5.设置请求映射(配置映射关系)
6.将 SpringMVC 设定加载到 Tomcat 容器中
因为 SpringMVC 是一个 Web 框架,将来是要替换 Servlet,所以先来回顾下以前 Servlet 是如何进行开发的?
1.创建 web 工程(Maven 结构)
2.设置 tomcat 服务器,加载 web 工程(tomcat 插件)
配置 Tomcat 插件
<build>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
3.导入坐标(Servlet)
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
说明:servlet 的坐标为什么需要添加
<scope>provided</scope>
?
compile
在在编译、运行、测试时均有效
4.定义处理请求的功能类(UserServlet)
创建配置类
@Configuration
@ComponentScan("com.north")
public class SpringConfig {
}
创建 Controller 类
@Controller
public class UserController {
@RequestMapping("/save")
public void save(){
System.out.println("user save ...");
}
}
5.设置请求映射(配置映射关系)
使用配置类替换 web.xml ——> 将 web.xml 删除,换成 ServletContainersInitConfig
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
// 加载SpringMVC配置类
protected WebApplicationContext createServletApplicationContext() {
// 初始化WebApplicationContext
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
// 加载指定配置类
context.register(SpringConfig.class);
return context;
}
// 设置由SpringMVC控制器处理的请求映射路径
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
// 加载Spring配置类
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
6.将 SpringMVC 设定加载到 Tomcat 容器中
注意事项:
spring-webmvc
jar 包的原因是它会自动依赖 spring 相关坐标
名称 | @Controller |
---|---|
类型 | 类注解 |
位置 | SpringMVC 控制器类定义上方 |
作用 | 设定 SpringMVC 的核心控制器 bean |
名称 | @RequestMapping |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC 控制器类或方法定义上方 |
作用 | 设置当前控制器方法请求访问路径 |
相关属性 | value(默认),请求访问路径 |
名称 | @ResponseBody |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC 控制器类或方法定义上方 |
作用 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
为了更好的使用 SpringMVC,我们将 SpringMVC 的使用过程总共分两个阶段来分析,分别是启动服务器初始化过程
和单次请求过程
/save
和 save 方法的对应关系
/
代表所拦截请求的路径规则,只有被拦截后才能交给 SpringMVC 来处理请求
http://localhost/save
入门案例的内容已经做完了,在入门案例中我们创建过一个SpringMvcConfig
的配置类,再回想前面咱们学习 Spring 的时候也创建过一个配置类SpringConfig
。这两个配置类都需要加载资源,那么它们分别都需要加载哪些内容?
我们先来看下目前我们的项目目录结构:
controller、service 和 dao 这些类都需要被容器管理成 bean 对象,那么到底是该让 SpringMVC 加载还是让 Spring 加载呢?
分析清楚谁该管哪些 bean 以后,接下来要解决的问题是如何让 Spring 和 SpringMVC 分开加载各自的内容。
在 SpringMVC 的配置类SpringMvcConfig
中使用注解@ComponentScan
,我们只需要将其扫描范围设置到 controller 即可,如
在 Spring 的配置类SpringConfig
中使用注解@ComponentScan
,当时扫描的范围中其实是已经包含了 controller,如:
从包结构来看的话,Spring 已经多把 SpringMVC 的 controller 类也给扫描到,所以针对这个问题该如何解决,就是咱们接下来要学习的内容。
概括的描述下咱们现在的问题就是因为功能不同,如何避免 Spring 错误加载到 SpringMVC 的 bean?
针对上面的问题,解决方案也比较简单,就是:
具体该如何排除:
方式一:修改 Spring 配置类,设定扫描范围为精准范围。
@Configuration
@ComponentScan({"com.itheima.service","comitheima.dao"})
public class SpringConfig {
}
说明:
上述只是通过例子说明可以精确指定让 Spring 扫描对应的包结构,真正在做开发的时候,因为 Dao 最终是交给MapperScannerConfigurer
对象来进行扫描处理的,我们只需要将其扫描到 service 包即可。
方式二:修改 Spring 配置类,设定扫描范围为 com.itheima,排除掉 controller 包中的 bean
@Configuration
@ComponentScan(value="com.itheima",
excludeFilters=@ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
大家只需要知道第一种 ANNOTATION 即可
如何测试 controller 类已经被排除掉了?
public class App{
public static void main (String[] args){
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
System.out.println(ctx.getBean(UserController.class));
}
}
如果被排除了,该方法执行就会报 bean 未被定义的错误
注意:测试的时候,需要把 SpringMvcConfig 配置类上的@ComponentScan 注解注释掉,否则不会报错
出现问题的原因是,
com.itheima
SpringMvcConfig
上有一个@Configuration 注解,也会被 Spring 扫描到最后一个问题,有了 Spring 的配置类,要想在 tomcat 服务器启动将其加载,我们需要修改 ServletContainersInitConfig
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
}
对于上述的配置方式,Spring 还提供了一种更简单的配置方式,可以不用再去创建AnnotationConfigWebApplicationContext
对象,不用手动register
对应的配置类,如何实现?
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
名称 | @ComponentScan |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置 spring 配置类扫描路径,用于加载使用注解格式定义的 bean |
相关属性 | excludeFilters:排除扫描路径中加载的 bean,需要指定类别(type)和具体项(classes)includeFilters:加载指定的 bean,需要指定类别(type)和具体项(classes) |