谁说java不能实现敏捷开发,利用java反射实现敏捷开发

跟朋友聊天,无意中聊起了敏捷开发,扯到了 约定优于配置原则,我想到我最近做的一个电影下载网站,没用任何框架,纯servlet+jsp实现 。因为是个个人网站,用的tomcat,一切都用约定来代替配置,下面让我说说我的思路。

我想下面的代码大家一定都写腻了,再写都想吐了:

<servlet>
     <servlet-name>ArticleAddServlet</servlet-name>
     <servlet-class>xxx.xxx.ArticleAddServlet</servlet-class>
</servlet>
<servlet-mapping>
     <servlet-name>ArticleAddServlet</servlet-name>
     <url-pattern>/article_add.do</url-pattern>
</servlet-mapping>



<action name="article_add" class="xxx.xxx.ArticleAction" method="add">
      <result name="success">/messages.jsp</result>
</action>


其实我们仔细观察,这个很有规律可循,完全可以依靠约定优于配置实现:
只是将  /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<ConstraintViolation<Object>> 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;
    Map<String, Integer> params;
    
    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跳转,当然,还是建议约定优于配置。

到这里,我贴上使用以上我的方法构建的完整网站例子,一个电影在线观看和下载的网站:
海盗港电影



你可能感兴趣的:(java,mvc,反射,敏捷开发,约定优于配置)