Spring MVC例子
学习Spring MVC的一个例子,参考书籍《Servlet、JSP、Spring MVC学习指南》,简单总结一下。
代码下载:https://github.com/PeiranZhang/springmvc-demo
本文参考链接:
https://www.cnblogs.com/yhtboke/p/5780140.html
https://blog.csdn.net/liubin5620/article/details/79922692
项目目录
这里,没有采用maven来管理依赖,实际项目中可以使用maven方便管理项目,项目依赖的jar包如下所示
项目在IDEA中打包、部署到tomcat,可以参考博客:https://www.cnblogs.com/darange/p/10965700.html
web.xml配置
<?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"> <!-- 配置前端控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- contextConfigLocation配置springmvc加载的配置文件适配器、处理映射器等--> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/config/springmvc-config.xml</param-value> </init-param> <!--值不为0表示,tomcat启动该应用时,加载servlet,而不是等到第一请求到来时再加载servlet--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!-- 所有访问都由DispatcherServlet进行解析--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
springmvc-config.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 与spring注解配合使用,扫描指定包及其子包下面的带有Controller、Service注解下的类,生成bean--> <context:component-scan base-package="app16a.controller"></context:component-scan> <context:component-scan base-package="app16a.service"></context:component-scan> <!-- 如果在配置前端控制器时拦截了所有的请求,不做特殊处理就会导致部分静态资源无法使用。如果是这种情况就可以使用下面的配置来访问静态资源文件,所以使用resource配置--> <!-- 没有<annotation-driven/>,<resources/>元素会阻止任意控制器被调用--> <!-- 配置注解的处理器映射器和处理器适配器--> <mvc:annotation-driven/> <mvc:resources mapping="/*.html" location="/"/> <!-- 配置视图解析器,在Controller中设置视图名的时候会自动加上前缀和后缀 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"/> </bean> </beans>
Service
@Service注解可以指定名称,当一个接口有多个实现类时,在Controller里面注入时,可以指定实现类
//service加上名称,在一个接口多个实现类时,可以这样区分 @Service("productService") public class ProductServiceImpl implements ProductService { private Map<Long, Product> products = new HashMap<Long, Product>(); private AtomicLong generator = new AtomicLong(); @Override public Product add(Product product) { long newId = generator.incrementAndGet(); product.setId(newId); products.put(newId, product); System.out.println("Add" + product); return product; } @Override public Product get(long id) { return products.get(id); } }
Controller
Controller中使用@Qualifier指定具体的service
1.通过@RequestMapping,进行url与处理方法的映射,处理方法返回的是视图的名称,不需要写绝对路径和jsp后缀,这是因为web.xml中配置了视图解析器
2.处理方法里面参数可以自己定义,其中Model对象有spring生成,可以在model中添加对象,作为request对象,发送给jsp
3.注意转发与重定向的区别,代码编写方式可以参考代码。重定向返回的视图名字前加上"redirect:"
https://blog.csdn.net/liubin5620/article/details/79922692
1、转发使用的是getRequestDispatcher()方法;重定向使用的是sendRedirect();
2、转发:浏览器URL的地址栏不变。重定向:浏览器URL的地址栏改变;
3、转发是服务器行为,重定向是客户端行为;
4、转发是浏览器只做了一次访问请求。重定向是浏览器做了至少两次的访问请求;
5、转发2次跳转之间传输的信息不会丢失,重定向2次跳转之间传输的信息会丢失(request范围)。
转发是要比重定向快,因为重定向需要经过客户端,而转发没有。有时候,采用重定向会更好,若需要重定向到另外一个外部网站,则无法使用转发。另外,重定向还有一个应用场景:避免在用户重新加载页面时两次调用相同的动作。
例如,当提交产品表单的时候,执行保存的方法将会被调用,并执行相应的动作;这在一个真实的应用程序中,很有可能将表单中的所有产品信息加入到数据库中。但是如果在提交表单后,重新加载页面,执行保存的方法就很有可能再次被调用。同样的产品信息就将可能再次被添加,为了避免这种情况,提交表单后,你可以将用户重定向到一个不同的页面,这样的话,这个网页任意重新加载都没有副作用;
但是,使用重定向不太方便的地方是,使用它无法将值轻松地传递给目标页面。而采用转发,则可以简单地将属性添加到Model,使得目标视图可以轻松访问。由于重定向经过客户端,所以Model中的一切都会在重定向时丢失。但幸运的是,在Spring3.1版本以后,我们可以通过Flash属性,解决重定向时传值丢失的问题。
要使用Flash属性,必须在Spring MVC的配置文件中添加一个<annotation-driven/>。然后,还必须再方法上添加一个新的参数类型:org.springframework.web.servlet.mvc.support.RedirectAttributes。
4.路径变量的使用,@PathVariable
5.获取请求参数,在处理方法中增加参数,并使用注解@RequestParam
Controller代码:
@Controller public class ProductController { @Autowired //按照名称,注入指定的接口实现类 @Qualifier("productService") ProductService productService; private static final Log logger = LogFactory.getLog(ProductController.class); @RequestMapping(value = "/product_input") public String inputProduct() { logger.info("inputProduct called"); return "ProductForm"; } //参数productForm中的字段与提交表当中的text文本框name一一对应 @RequestMapping(value = "/product_save") public String saveProduct(ProductForm productForm, Model model, RedirectAttributes redirectAttributes){ logger.info("saveProduct called"); Product product = new Product(); product.setName(productForm.getName()); product.setDescription(productForm.getDescription()); try { product.setPrice(Float.parseFloat( productForm.getPrice())); } catch (NumberFormatException e) { } productService.add(product); //转发 //model里面的内容作为request属性传递给jsp_servlet //model.addAttribute("product", product); //return "ProductDetails"; //重定向 // 客户端重新发送请求,Model无效 //通过addFlashAttribute,可以在jsp中引用对象 redirectAttributes.addFlashAttribute(product); return "redirect:/product_details"; } @RequestMapping(value = "/product_details") public String productDetail(){ logger.info("productDetail called"); return "ProductDetails"; } //路径变量的使用 //http://localhost:8080/app16a/product/1 @RequestMapping(value = "/product/{id}") public String productGet(@PathVariable Long id,Model model){ Product product = productService.get(id); model.addAttribute(product); logger.info("productGet called"); return "ProductDetails"; } //获取请求参数 //http://localhost:8080/app16a/product?id=1 @RequestMapping(value = "/product") public String productGetWithParams(@RequestParam Long id,Model model){ Product product = productService.get(id); model.addAttribute(product); logger.info("productGetWithParams called"); return "ProductDetails"; } }
视图
PoductForm.jsp:form表单中各个文本输入控件的name作为request请求参数的键,发送给action指定的url
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Add Product Form</title> </head> <body> <!--product_save与product_save.action效果一样--> <div id="global"> <form action="product_save.action" method="post"> <fieldset> <legend>Add a product</legend> <p> <label for="name">Product Name: </label> <input type="text" id="name" name="name" tabindex="1"> </p> <p> <label for="description1">Description: </label> <input type="text" id="description1" name="description" tabindex="2"> </p> <p> <label for="price">Price: </label> <input type="text" id="price" name="price" tabindex="3"> </p> <p id="buttons"> <input id="reset" type="reset" tabindex="4"> <input id="submit" type="submit" tabindex="5" value="Add Product"> </p> </fieldset> </form> </div> </body> </html>
ProductDetails.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Save Product</title> </head> <body> <div id="global"> <h4>The product has been saved.</h4> <p> <h5>Details:</h5> <%-- Controller里面的Model中的product对象其实是作为request属性传递给jsp的,所以这里用requestScope来引用--%> Product Name: ${requestScope.product.name}<br/> Description: ${requestScope.product.description}<br/> Price: $${requestScope.product.price} </p> </div> </body> </html>
以上只是对Spring MVC的简单使用