
对于做Java Web开发的人来说,肯定都遇到过表单提交,并且,每一个表单的提交后面至少跟一个对象,比如说一个登录的表单,后面一定跟着一个登录对象(也有可能直接就是User对象),既然有对象,势必需要数据绑定到对象的属性了,在原生的Servlet开发中,我们用HttpServletRequest对象的getParamter(String s)的方法去获取表单中对应name属性的值;如果是使用Srping框架的,那么一定用过@ModelAttribute这个注解来实现表单的数据绑定。原生的Servlet方法我们就不讲了,这个太easy了,今天我们要来说的是如何原生实现@ModelAttribute注解的数据绑定功能
首先,我们先创建两个Java文件,类型为Annotation

一个我们命名为SetMethod,另一个命名为GetMethod,代码如下:
GetMethod 方法
1 2 3 4 5 6 7 8
| import java.lang.annotation.*;
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface GetMethod { public String value(); }
|
SetMethod 方法
1 2 3 4 5 6 7 8
| import java.lang.annotation.*;
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SetMethod { public String value(); }
|
之所以定义SetMethod和GetMethod,是因为在Java Web开发中,我们为实体对象的属性设置了get和set方法用于获取实体的属性。现在,我们来说说这两个注解中的@Target、@Retention、@Document是什么意思:
- @Target :指示注释类型适用的上下文,在这里,我们设定适用的上下文为方法——Method
- @Retention:指示要注释具有注释类型的注释的保留时间。 如果注释类型声明中没有保留注释,则保留策略默认为
RetentionPolicy.CLASS
。保留元注释仅在元注释类型直接用于注释时才起作用。 如果元注释类型用作另一注释类型的成员类型,则它不起作用。在这里,我们将注解的保留时间为运行时
- @Document:表示具有类型的注释默认情况下由javadoc和类似工具记录
现在,我们成功的写了两个annotation——SetMethod 和 GetMethod,但是才一个方法呢?
别小看这个方法,在GetMethod类中,我们这个方法通过GetMethod(String s)可以直接获取对象的属性值,String s的值就是对象过的属性名;在SetMethod类中,我们可以通过SetMethod(String s)来为你的set方法进行注解,而String s的值就是对象属性名,就这样,我们轻松的实现了数据绑定的前期工作;使用这两个方法如下:
GetMethod、SetMethod使用方法
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| package com.work.entity;
import java.util.Date;
public class User {
private String username; private String password; private String role; private int id; private String link; private String adress; private Date birthday; private String depart;
public User() { }
@GetMethod("username") public String getUsername() { return username; }
@SetMethod("username") public void setUsername(String username) { this.username = username; }
@GetMethod("password") public String getPassword() { return password; }
@SetMethod("password") public void setPassword(String password) { this.password = password; }
public String getRole() { return role; }
@SetMethod("role") public void setRole(String role) { this.role = role; }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getLink() { return link; }
public void setLink(String link) { this.link = link; }
public String getAdress() { return adress; }
@SetMethod("address") public void setAdress(String adress) { this.adress = adress; }
public Date getBirthday() { return birthday; }
@SetMethod("birthday") public void setBirthday(Date birthday) { this.birthday = birthday; }
public String getDepart() { return depart; }
@SetMethod("depart") public void setDepart(String depart) { this.depart = depart; }
@Override public String toString() { return "User{" + "username='" + username + '\'' + ", password='" + password + '\'' + ", role='" + role + '\'' + ", id=" + id + ", link='" + link + '\'' + ", adress='" + adress + '\'' + ", birthday=" + birthday + ", depart='" + depart + '\'' + '}'; } }
|
(这里给个小建议,为你的每一个对象都生成toString方法,这会为后期调试提供巨大便利)
现在,我们完成了百分之五十的工作,那么,怎么实现从HttpServletRequest对象将表单数据绑定到我们定义的对象中呢?我们还需要创建一个类,就叫做DataBind吧,先附上代码:
数据绑定代码
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 34 35 36 37 38 39 40 41 42 43 44
| package com.work.entity;
import javax.servlet.http.HttpServletRequest; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat;
public class DataBind<T> {
public static Object Request2Bean(HttpServletRequest request, Object object) { if (object == null) { return null; } Method[] methods = object.getClass().getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(SetMethod.class)) { SetMethod sm = method.getAnnotation(SetMethod.class); String filedName = sm.value(); Object fieldValue = request.getParameter(filedName); Type[] params = method.getGenericParameterTypes(); try { if (params[0].getTypeName().contains("Date")) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); fieldValue = fmt.parse((String) fieldValue); } method.invoke(object, fieldValue); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } } } return object; } }
|
我们在这个DataBind类中写了一个方法——Request2Bean,这个方法里面有两个参数,一个是HttpServletRequest,另一个是我们Object;我们都过Java提供的反射机制,获取Object对象的所有方法
1
| Method[] methods = object.getClass().getMethods();
|
然后,我们遍历方法数组,在循环中,首先我们先判断这个方法是后被SetMethod这个注解作用了,如果是,我们就获取这个SetMethod这个对象实例,并获取SetMethod(String s)中s的值
1 2 3
| method.isAnnotationPresent(SetMethod.class) SetMethod sm = method.getAnnotation(SetMethod.class); String filedName = sm.value();
|
这个时候,我们通过fileName这个String对象从HttpServletRequest对象的getParamter方法取值
1
| Object fieldValue = request.getParameter(filedName);
|
由于我们的User对象中有Date对象,如果我们直接
1
| method.invoke(object, fieldValue);
|
会直接报 IllegalAccessException 错误,因为我们的getParamter方法返回的是一个String类型的对象,因此,我们还需要对方法中的参数类型做一个判断,在数据绑定的时候进行数据类型转换:
1
| Type[] params = method.getGenericParameterTypes();
|
这样我们就获取了方法中所有参数的类型,接着,我们获取参数类型的name,进行字符串的比较来实现数据类型的手动转换:
1 2 3 4
| if (params[0].getTypeName().contains("Date")) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); fieldValue = fmt.parse((String) fieldValue); }
|
随后,我们只要调用
1
| method.invoke(object, fieldValue);
|
就可以完成set方法的调用,将表单的value绑定到我们的User对象属性。那么如何使用?这个就很简单啦,就像下面这样调用即可:
1
| User user = (User) Request2Bean(request, new User());
|
我们通过视频来看看效果吧!
YouTube 站点
[youtube https://www.youtube.com/watch?v=Ze2_GJvrF3U&w=560&h=315]
哔哩哔哩站点
https://www.bilibili.com/video/av17431081/