我想下面的代码大家一定都写腻了,再写都想吐了:
ArticleAddServlet xxx.xxx.ArticleAddServlet ArticleAddServlet /article_add.do
/messages.jsp
其实我们仔细观察,这个很有规律可循,完全可以依靠约定优于配置实现:
只是将 /action_name/method_name.do 映射到 xxx.xxx.ActionName 的 methodName 方法而已
完全可以依靠反射实现,我们只需要配置一个Controller servlet,然后类似如下代码:
... //取得请求的路径,获取要调用的action类和方法 String contextPath = req.getContextPath(); String requestURI = req.getRequestURI(); String actionStr = requestURI.substring(contextPath.length()+"/".length(),requestURI.length()-".do".length()); String[] actions = actionStr.split("/"); String objName = actions[0]; objName = "xxx.xxx." + objName.substring(0, 1).toUpperCase() + objName.substring(1) + "Action"; String methodName = actions[1]; Class objCls = Class.forName(objName); Method method = objCls.getMethod(methodName, Class.forName("javax.servlet.http.HttpServletRequest"), Class.forName("javax.servlet.http.HttpServletResponse")); //通过java反射调用action方法 Object obj = objCls.newInstance(); method.invoke(obj, new Object[]{req,res}); ...
这样我们就可以很轻松地利用约定简略了繁复枯燥的配置文件。
然后,我们再来看看如何处理请求参数
我再一个工具类中实现了这样一个绑定请求参数到bean的方法 bind(HttpServletRequest req, Object bean, String[] paramNames, ...){},以下是方法体的代码片段:
... for (String paramName : paramNames) { //通过反射取得set方法 Class beanCls = bean.getClass(); Field field = beanCls.getDeclaredField(paramName); Class fieldCls = field.getType(); String setMethodName = "set" + paramName.substring(0, 1).toUpperCase() + paramName.substring(1); Method method = beanCls.getDeclaredMethod(setMethodName, fieldCls); //转换字符串类型的请求参数为Java对象,执行set方法绑定到bean String value = req.getParameter(paramName); if (fieldCls.equals(String.class)) { method.invoke(bean, new Object[]{value}); } else { Object convertValue = convertController.convert(value, fieldCls); method.invoke(bean, new Object[]{convertValue}); } //使用bean validation来验证参数合法性 Locale locale = req.getLocale(); Validator validator = getValidator(locale); Set> constraintViolations; constraintViolations = validator.validateProperty(bean, paramName); } ... //具体实现复杂一些,比如绑定到集合,绑定上传的文件对象。 //在以上方法体中我们可以将参数的转换、绑定、验证消息写到消息对象
然后我们在action的方法中这样调用就可以了
paramUtil.bind(req, bean, new String[]{"paramName1", "paramName2","..."}, ...);
bind方法建议返回一个代表参数是否完全合法的boolean值,由action方法决定后续操作。
然后执行业务逻辑。
这里再说一下jdbc的查询参数和结果集的绑定。其实apache有工具类可直接用,这里我贴上我的简陋实现,抛砖引玉吧,
绑定结果集我这里用了几个静态方法,贴上核心的:
//将结果集绑定到bean,这里需要约定查询字段和bean的字段名一致 static public void bind0(ResultSet rs, Object o) throws SQLException, IllegalAccessException, InvocationTargetException { ResultSetMetaData rsmd = rs.getMetaData(); for (int i = 1; i <= rsmd.getColumnCount(); i++) { String name = rsmd.getColumnLabel(i); Object value = rs.getObject(name); BeanUtils.setProperty(o, name, value); } }
绑定命名参数我使用了一个类
public class PreparedStatementWarper { private PreparedStatement stmt; Mapparams; public PreparedStatementWarper(Connection conn, String namedParamSql) throws SQLException{ params = new HashMap<>(); StringBuffer sb = new StringBuffer(); Pattern p = Pattern.compile(":\\w+"); Matcher m = p.matcher(namedParamSql); int i = 1; while (m.find()) { String paramName = m.group().substring(1); params.put(paramName, i); m.appendReplacement(sb, "?"); i++; } m.appendTail(sb); stmt = conn.prepareStatement(sb.toString()); } public void bind(Object bean) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, SQLException{ for(String paramName : params.keySet()){ Object paramValue = PropertyUtils.getProperty(bean, paramName); getStmt().setObject(params.get(paramName), paramValue); } } public PreparedStatement getStmt() { return stmt; } } //调用代码片段 String sql = "insert into `article` (`xx1`,`xx2`,`xx3`,`xx4`) values(:xx1,:xx2,:xx3,:xx4);"; PreparedStatementWarper stmtWarper = new PreparedStatementWarper(conn, sql); stmtWarper.bind(movie); stmtWarper.getStmt().executeUpdate();
到这里,该结束了,最后,action方法可以返回结果字符串交回Controller跳转,当然,还是建议约定优于配置。
到这里,我贴上使用以上我的方法构建的完整网站例子,一个电影在线观看和下载的网站:
海盗港电影