Spring集成web开发环境
使用Maven的webapp模板
①pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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.itheima</groupId>
<artifactId>itheima_springMVC_01</artifactId>
<version>1.0-SNAPSHOT</version>
<!--解决java版本错误-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<!--spring基本包-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--servlet基本包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!--jsp基本包-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<!--MySQL驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!--c3p0数据库驱动包-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--druid数据库驱动包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--Junit基本包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--Spring集成Junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
<!--jdbc模板-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!--事务管理-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<!--普通单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
②applicationContext.xml
<!--配置userDao-->
<bean id="userDao" class="com.itheima.dao.Impl.UserDaoIpml"></bean>
<!--配置UserService-->
<bean id="userService" class="com.itheima.service.Impl.UserServiceIpml">
<property name="dao" ref="userDao"/>
</bean>
③Dao
public class UserDaoIpml implements UserDao {
public void save() {
System.out.println("save running.....");
}
}
④Service
public class UserServiceIpml implements UserService {
private UserDao dao; //将依赖作为成员变量
public void setDao(UserDao dao) { //定义set方法
this.dao = dao;
}
//调用dao
public void save() {
dao.save();
}
}
⑤Servlet
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、加载配置文件,获取容器对象
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、从容器中获取对象
UserService service = (UserService) app.getBean("userService");
//3、执行service
service.save();
}
}
⑥web.xml
在SpringMVC中Controller替代了原始的Servlet
<!--配置servlet名字和绑定类-->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.itheima.web.UserServlet</servlet-class>
</servlet>
<!--配置servlet访问路径-->
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>
Spring集成web开发环境:弊端【了解】
//1、加载配置文件,获取容器对象
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、从容器中获取对象
UserService service = (UserService) app.getBean("userService");
//3、执行service
service.save();
从servlet代码中,我们发现我们每次要执行1个doGet/doPost方法都要加载Spring配置文件,创建Spring容器才能使用应用上下文获取对象。但是我们一般web项目开发中,是有很多servlet的,这就意味着我们要多次要加载文件和多次创建Spring容器,这样是不是很多此一举又影响性能?
那么应该怎么解决呢?
首先,实现ServletContextListener接口成为监听器,并且监听事件的创建(web应用启动),当web应用一启动,监听器就会加载Spring配置文件,创建ApplicationContext对象并将其存储到ServletContext容器中供给所有服务器资源共享。(ServletContext是唯一容器(域对象),我要用我就拿就行了,不用自己加载文件再创建容器获取Bean对象)
什么是ServletContext?
Spring集成web开发环境:实现监听器【了解】
在上班原有的基础上做出改变
1、ContextLoadListener类
contextInitialized方法是在web应用一启动就执行
public class ContextLoadListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//1、加载Spring核心加载文件,创建容器对象(applicationContext)
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、获取ServletContext 对象域
ServletContext servletContext = sce.getServletContext();
//3、将Spirng容器对象存储 到 ServletContext容器中(对象域)
servletContext.setAttribute("app",app);
System.out.println("Spring容器创建成功。。。。");
}
}
2、web.xml配置监听器
【web应用一启动就将spring容器存储到ServletContext】
<!-- 配置监听器 -->
<listener>
<listener-class>com.itheima.listener.ContextLoadListener</listener-class>
</listener>
3、UserServlet
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、从Servlet对象域中获取容器对象
ServletContext servletContext = request.getServletContext();//获取对象域
//ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app"); 【方法1】
ApplicationContext app = (ApplicationContext) this.getServletContext().getAttribute("app"); //【方法2】
//2、从容器中获取对象
// UserService service = (UserService) app.getBean("userDao"); 【方法1】
UserService service = app.getBean(UserService.class); //【方法2】
//3、执行service
service.save();
}
}
输出:
Spring容器创建成功。。。。
save running.....
Spring集成web开发环境:优化监听器【了解】
(在上边代码基础上进行修改)
在以上代码中,我们有2个地方需要优化:
1、在ContextLoadListener类中加载配置文件,配置文件是通过写死的字符串进行获取。
假设如果我们不使用applicationContext.xml了,我们还得修改源代码,这样就耦合了。
//1、加载Spring核心加载文件,创建容器对象(applicationContext)
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
2、UserServlet中通过开发者指定的app名称获取ServletContext对象域中的Spring容器
app这个名字,是你自己命名并想通过它来获取Spring容器,但是当别人来使用你的代码的时候,别人可能就不知道了。因为别人可能习惯不是取这个名字。
ApplicationContext app = (ApplicationContext) this.getServletContext().getAttribute("app");
解决:
问题①
我要在web.xml中配置全局参数,指定1个名字跟需要加载的配置文件进行绑定;
在加载配置文件的时候,我们获取ServletContext对象域,通过此并获取全局参数
,返回的字符串就是名称,映射的就是配置文件。
web.xml
【自定义名称与配置文件绑定,使用自定义名称就等于使用了该配置文件-】
<!--全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name> 【自定义名称】
<param-value>applicationContext.xml</param-value> 【绑定配置文件】
</context-param>
,tomcat在启动项目的时候,通过ContextLoaderListener监听器和DispatcherServlet分别初始化了service层的bean和controller层的bean。
ContextLoadListener
public class ContextLoadListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//1、获取ServletContext对象域
ServletContext servletContext = sce.getServletContext();
//2、读取web.xml中的全局参数
String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
//3、加载Spring核心加载文件,创建容器对象(applicationContext)
ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation);
//4、将Spirng容器对象存储 到 ServletContext容器中(对象域)
servletContext.setAttribute("app",app);
System.out.println("Spring容器创建成功。。。。");
}
}
问题②
首先,创建1个工具类。
在使用ServletContext对象域获取ApplicationContext对象时,我们只需要传入ServletContext对象域,调用这个工具类的方法即可返回容器对象。使用者,无需知道存储在ServletContext中ApplicationContext的Key,也可以获取。
WebApplicationContextUtils
public class WebApplicationContextUtils {
//定义通过ServletContext对象域 获取 ApplicationContext对象
public static ApplicationContext getWebApplicationContext(ServletContext sc){
return (ApplicationContext) sc.getAttribute("app");
}
}
UserServlet
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、获取ServletContext对象域
ServletContext servletContext = request.getServletContext();//获取对象域
// ApplicationContext app = (ApplicationContext) this.getServletContext().getAttribute("app");
//2、通过ServletContext对象域 获取 ApplicationContext对象
ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
//3、从容器中获取对象
UserService service = app.getBean(UserService.class);
//4、执行service
service.save();
}
}
Spring工具:集成web开发环境
意思是说,上面的优化,Spring已经给我们封装好了,我们配置直接用就行了。
①pom.xml如何配置
<!--web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
④如何配置web.xml
<!--全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
③如何Servlet获取Spring容器里的对象
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、获取ServletContext对象域
ServletContext servletContext = request.getServletContext();//获取对象域
//2、获取ApplicationContext容器
//WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext); 【WebApplicationContext】是ApplicationContext子类
ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
//3、从容器中获取对象
UserService service = app.getBean(UserService.class);
//4、执行service
service.save();
}
SpringMVC:代码演示
SpringMVC框架将Servlet相同而重复的代码进行了封装。这就意味着,我们不需要在web.xml中配置servlet了,我们配置前端控制器,使用访问前端控制器,其他业务逻辑的代码交由给自定义的controller,这个controller的数据是先被前端控制拦截处理过的。【前端控制器调用controller,也就是说访问资源时,访问的是前端控制器,前端控制再调用指定的POJO】
(POJO指普通的java类(controller))
①pom.xml导入SpringMVC包
<!--Spring-mvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
②创建spring-mvc.xml:配置controller组件扫描
- 记得配context命名空间
- 为什么要提取spring-mvc.xml另外扫描Controller?
spring-mvc扫web层的,spring扫其他的。各扫各的,这是习惯,
<!--Controller组件扫描:让Spring去指定类扫描注解-->
<context:component-scan base-package="com.itheima.controller"/>
③web.xml
1、配置Servlet
2、配置Cotroller
3、加载spring-mvc配置文件
<!--配置SpringMVC前端控制器:共有Servlet-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--当Servlet初始化时,就加载spring-mvc.xml文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup><!--服务器一启动就创建serclet-->
</servlet>
<!--配置前端控制器和url的映射关系-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern> <!--包括静态资源,所有的请求都找前端控制器(记得在springmvc开放资源)-->
</servlet-mapping>
注意:::
如果在web.xml中servlet-mapping的url-pattern设置的是/。
表示将所有的文件,包含静态资源文件都交给spring mvc处理。就需要用到<mvc:annotation-driven />了(springmvc.xml配置)。如果不加,DispatcherServlet则无法区分请求是资源文件还是mvc的注解,而导致controller的请求报404错误。
④Controller
@Controller //告诉Spring帮我实例化Bean
public class UserController {
@RequestMapping("/quick") //请求映射:请求xxx/quick时,访问此方法
public String save(){
System.out.println("Controller save....");
return "sucess.jsp"; //返回页面
}
}
SpringMVC:流程介绍
controller通过return响应页面。
绿色区域是共有的控制器(SpringMVC前端控制器)。
SpringMVC:组件解析之执行流程
SpringMVC:组件解析之注解解析
如果在类上添加("/xxx")必须在return的页面前添加“/”.
不然,当你访问这个方法并返回页面时,会404提示—提示/xxx/succes.jsp不存在。因为return succes.jsp的访问路径是相对于这个方法的访问路径的,你加了"/"它才会去wepapp目录下去寻找资源。
SpringMVC:组件解析之注解解析2
我们一般是这样写
<!--Controller组件扫描:让Spring去指定类扫描注解-->
<context:component-scan base-package="com.itheima.controller"/>
也有这样写
只扫org.springframework.stereotype.Controller
的注解(这个是Controller注解的全限定名)
<context:component-scan base-package="com.itheima">
<context:exclude-filter type="aspectj" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
只扫 除开org.springframework.stereotype.Controller
的注解
<context:component-scan base-package="com.itheima">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
SpringMVC:组件解析3
SpringMVC除了扫描组件,其实还有很多组件。
这里只介绍1个类的部分。
代码演示
重定向与转发
视图名称前缀和后缀
假设场景
如果是上述这样的话,那么我们每次写方法返回都要写这么长,太烦了。
怎么解决?
- Spring-mvc.xml:
- UserController
视图解析器可以配置页面路径的前后缀,如果你配置添加了路径它会对ModelAndView返回的页面和return “ xxx “ 进行自动添加前后缀,但是对于方法的指定跳转方式的返回值是不会自动添加的。
return “跳转方式+/xxx.jsp”
补充:return “xxx”,默认是转发跳转,但是不指定是转发或者重定向都会自动加前后缀的。