Java web开发(17)SpirngMVC 详解

注解类和方法大致如下

@Controller
@RequestMapping("/URL跟路径")
public class CLassXXXController{

@RequestMapping("/URL子路径1")
public ModelAndView Method_1(参数1,参数2,...参数n)

......

ModelAndView modelAndView = new ModelAndView();
// 相当 于request的setAttribut,在jsp页面中通过itemsList取数据
modelAndView.addObject("itemsList", itemsList);
modelAndView.setViewName("items/itemsList");
return modelAndView;
}

@RequestMapping("/URL子路径1")
public String Method_2(参数1,参数2,...参数n)

......

//等效上面的
return "items/itemsList";
}

返回值说明

方法1:

上面两种返回都是转发,如果定义了视图解析器

    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置jsp路径的前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 配置jsp路径的后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

那么转发的路径就是"/WEB-INF/jsp/items/itemsList.jsp",全路径也就是host/项目名//WEB-INF/jsp/items/itemsList.jsp

方法2:

返回"redirect:XXX", 或者"forward:XXX",重定向request不共享,转发request共享。

注意:此时路径是相对于类名上的根路径。比如类上的RequestMapping("hello"),那么会转发到host/项目名/hello/XXX

参数绑定

方法中可以有任意数量的参数,可以是HttpServletResponse,HttpServletRequest,HttpSession,ModelMap,

基本类型,自定义的类。

简单类型

@RequestMapping("/hei")

public String Fun1(Integer id,其他参数)

访问该路径时有一个key名称为id,value会自动绑定。

名称不一致的绑定

public String Fun1(@RequestParam(value="id",required=true) Integer xxxd,其他参数)

POJO绑定

public class OneThing{
private Integer id;
private String name;
private String place;
private Date createDate;
private String comment;
}
@RequestMapping("/onething")
public String Fun2(OneThing thing,其他参数){
....
}

访问该路径时,当key/value中key的名称和POJO类属性名称一致,其值会绑定到该pojo对象中。

特殊情况:日期类型。访问页面提供的日期字符需要转换,才能绑定到pojo的Date对象。

创建转换类

public class CustomDateConverter implements Converter<String,Date>{

    public Date convert(String source) {
        
        //实现 将日期串转成日期类型(格式是yyyy-MM-dd HH:mm:ss)
        
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        try {
            //转成直接返回
            return simpleDateFormat.parse(source);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //如果参数绑定失败返回null
        return null;
    }

}

在springmvc.xml中配置

<!-- 自定义参数绑定 -->
    <bean id="conversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <!-- 转换器 -->
        <property name="converters">
            <list>
                <!-- 日期类型转换 -->
                <bean
                    class="com.legion.ssm.controller.converter.CustomDateConverter" />
            </list>
        </property>
    </bean>

    <mvc:annotation-driven
        conversion-service="conversionService"></mvc:annotation-driven>

 包装类型pojo绑定

上面的OneThing类添加一个新的属性,

 private Item myitem;

这个属性也是一个pojo类,当访问提供的key/value的时候key名称为

myitem.name等等的形式时,其会绑定到OneThing对象中。

数组类型

出现场景:前端使用复选框

用户提交多个记录,此时items_id会有多个记录,

@RequestMapping("/deleteItems")
    public String deleteItems(int[] items_id) throws Exception {

 List绑定

前端形如

<c:forEach items="${itemsList }" var="item" varStatus="status">
<tr>    

    <td><input name="itemsList[${status.index }].name" value="${item.name }"/></td>
    <td><input name="itemsList[${status.index }].price" value="${item.price }"/></td>
    <td><input name="itemsList[${status.index }].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
    <td><input name="itemsList[${status.index }].detail" value="${item.detail }"/></td>


</tr>
</c:forEach>

http协议数据就是

itemLists[0].name=XXX&itemLists[0].price=XXX&itemLists[0].detail=XX......itemLists[1].name=XXX&itemLists[1].price=XXX.....

@RequestMapping("/onething")
public String Fun2(OneThing thing,其他参数){
....
}
//OneThing类新增一个List类型的属性
private List<Item> itemsList;

http协议 中的数据会绑定到OneThing对象中去

SpringMVC校验

 maven配置,使用了tomcat8

        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.el</artifactId>
            <version>3.0.1-b08</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.7.Final</version>
        </dependency>

在springmvc.xml中配置

    <!-- 校验器 -->
    <bean id="validator"
        class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <!-- hibernate校验器 -->
        <property name="providerClass"
            value="org.hibernate.validator.HibernateValidator" />
        <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties -->
        <property name="validationMessageSource" ref="messageSource" />
    </bean>
    <!-- 校验错误信息配置文件 -->
    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <!-- 资源文件名 -->
        <property name="basenames">
            <list>
                <value>classpath:CustomValidationMessages</value>
            </list>
        </property>
        <!-- 资源文件编码格式 -->
        <property name="fileEncodings" value="utf-8" />
        <!-- 对资源文件内容缓存时间,单位秒 -->
        <property name="cacheSeconds" value="120" />
    </bean>

    <mvc:annotation-driven
        conversion-service="conversionService" validator="validator"></mvc:annotation-driven>

在Pojo类,比如ItemsCustom的属性上添加注解

@Size(min=3,max=30,message="{items.name.length.error}")
private String name;
//已经建立CustomValidationMessages.properties文件
//指定了字符串items.name.length.error的值

关键使用:需要验证的pojo类前面加@Validated,后面跟BindingResult对象,用于错误处理

        @RequestMapping("/editItemsSubmit")
    public String editItemsSubmit( @Validated ItemsCustom itemsCustom,BindingResult br)
{。。。
} 

错误处理

if(br.hasErrors()) {
            List<ObjectError> ae=br.getAllErrors();
            for(ObjectError oe:ae) {
                System.out.println(oe.getDefaultMessage());
            }

分组校验

先建立一个接口VG1,

在pojo类需要验证的属性上这样使用

    @Size(min=5,max=30,message="{items.name.length.error}",groups= {VG1.class})
    private String name;

验证的时候

@RequestMapping("/editItemsSubmit")
 public String editItemsSubmit( @Validated(value={VG1.class}) ItemsCustom itemsCustom,BindingResult br)
{。。。
} 

 数据回显

把对象放到request域中

方法1:

在参数中添加Model 对象m

m.addAttribute("id",对象名);

上传图片

配置springmvc.xml

    <!-- 文件上传 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设置上传文件的最大尺寸为5MB -->
        <property name="maxUploadSize">
            <value>5242880</value>
        </property>
    </bean>

pom中添加commons-fileupload

创建虚拟目录存储图片

或者修改tomcat的配置

在conf/server.xml文件中添加

上传页面的前端代码注意事项:

Form中添加

enctype="multipart/form-data"

        <input type="file"  name="items_pic"/> 

对应处理handler参数中必须有

MultipartFile items_pic

对象名必须和前端的上传name一样

相关处理代码

String originalFilename = items_pic.getOriginalFilename();
//上传图片
if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){
    //存储图片的物理路径
    String pic_path = "D:\我的文档\Pictures\";
    //新的图片名称
    String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
    File newFile = new File(pic_path+newFileName);
    //将内存中的数据写入磁盘
    items_pic.transferTo(newFile);
    //将新图片名称写到itemsCustom中
    itemsCustom.setPic(newFileName);
}

 JSON处理

pom.xml配置

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.6</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.6</version>
        </dependency>

使用Jquery的配置

静态资源配置,在springmvc.xml中配置

    <mvc:resources location="/js/" mapping="/js/**" />
    <mvc:resources location="/img/" mapping="/img/**" />

项目中添加js文件

前端代码

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>json交互测试</title>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript">
//请求json,输出是json
function requestJson(){
    
    $.ajax({
        type:'post',
        url:'${pageContext.request.contextPath }/requestJson',
        contentType:'application/json;charset=utf-8',
        //数据格式是json串,商品信息
        data:'{"name":"手机","price":999}',
        success:function(data){//返回json结果
            alert(JSON.stringify(data));
        }
        
    });
    
    
}
//请求key/value,输出是json
function responseJson(){
    
    $.ajax({
        type:'post',
        url:'${pageContext.request.contextPath }/responseJson',
        //请求是key/value这里不需要指定contentType,因为默认就 是key/value类型
        //contentType:'application/json;charset=utf-8',
        //数据格式是json串,商品信息
        data:'name=手机&price=999',
        success:function(data){//返回json结果
            alert(JSON.stringify(data));
        }
        
    });
    
}
</script>
</head>
<body>
<input type="button" onclick="requestJson()" value="请求json,输出是json"/>
<input type="button" onclick="responseJson()" value="请求key/value,输出是json"/>
</body>
</html>

后端处理代码

@Controller
public class JsonTest {
    

    @RequestMapping("/requestJson")
    public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom ic ) {
        ic.setPic("The legion has returned...");
        return ic;
    }
    
    @RequestMapping("/responseJson")
    public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom){
        
        //@ResponseBody将itemsCustom转成json输出
        return itemsCustom;
    }
}

 拦截器

 类的定义,需要实现HandlerInterceptor接口

public class HandlerInterceptor1 implements HandlerInterceptor {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // TODO Auto-generated method stub
        System.out.println("拦截器11 之前");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("拦截器11 之后");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub
        System.out.println("拦截器11完成");
    }

}

在springmvc.xml中配置

    <!--拦截器 -->
    <mvc:interceptors>
        <!--多个拦截器,顺序执行 -->
    <mvc:interceptor>
    <!-- /**表示所有url包括子url路径 -->
    <mvc:mapping path="/**" />
    <bean class="com.legion.ssm.interceptor.HandlerInterceptor1"></bean>
    </mvc:interceptor>

    <mvc:interceptor>
    <mvc:mapping path="/**" />
    <bean class="com.legion.ssm.interceptor.HandlerInterceptor2"></bean>
    </mvc:interceptor>
    </mvc:interceptors>

测试总结

两个拦截器都放行

HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle

HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle

HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion

总结:
preHandle方法按顺序执行,
postHandle和afterCompletion按拦截器配置的逆向顺序执行。

拦截器1放行,拦截器2不放行
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion

总结:
拦截器1放行,拦截器2 preHandle才会执行。
拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。
只要有一个拦截器不放行,postHandle不会执行。

拦截器1不放行,拦截器2不放行
HandlerInterceptor1...preHandle

拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。
拦截器1 preHandle不放行,拦截器2不执行。

使用拦截器实现登录认证

    如果请求的url是公开地址(无需登陆即可访问的url),让放行
    如果用户session 不存在跳转到登陆页面
    如果用户session存在放行,继续操作。
public class LoginInterceptor implements HandlerInterceptor {

    
    //进入 Handler方法之前执行
    //用于身份认证、身份授权
    //比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        
        //获取请求的url
        String url = request.getRequestURI();
        //判断url是否是公开 地址(实际使用时将公开 地址配置配置文件中)
        //这里公开地址是登陆提交的地址
        if(url.indexOf("login")>=0){
            //如果进行登陆提交,放行
            return true;
        }
        
        //判断session
        HttpSession session  = request.getSession();
        //从session中取出用户身份信息
        String username = (String) session.getAttribute("username");
        
        if(username != null){
            //身份存在,放行
            return true;
        }
        
        //执行这里表示用户身份需要认证,跳转登陆页面
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        
        //return false表示拦截,不向下执行
        //return true表示放行
        return false;
    }

    //进入Handler方法之后,返回modelAndView之前执行
    //应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        
        System.out.println("HandlerInterceptor1...postHandle");
        
    }

    //执行Handler完成执行此方法
    //应用场景:统一异常处理,统一日志处理
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        
        System.out.println("HandlerInterceptor1...afterCompletion");
    }

}
@Controller
public class LoginController {

    // 登陆
    @RequestMapping("/login")
    public String login(HttpSession session, String username, String password)
            throws Exception {

        // 调用service进行用户身份验证
        // ...
        System.out.println("用户名是:"+username);
        // 在session中保存用户身份信息
        session.setAttribute("username", username);
        // 重定向到商品列表页面
        return "redirect:/items/queryItems";
    }

    // 退出
    @RequestMapping("/logout")
    public String logout(HttpSession session) throws Exception {

        // 清除session
        session.invalidate();

        // 重定向到商品列表页面
        return "redirect:/items/queryItems";
    }

 额外内容:访问Webapp下的静态资源。

比如在Webapp文件夹下面放了XXX.json。现在访问http://localhost/XXX.json不行,

需要在web.xml中配置如下:

    <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.json</url-pattern>
    </servlet-mapping>
原文地址:https://www.cnblogs.com/legion/p/9599837.html