仿struts读取配置文件,完成如下要求:
0. 读取配置文件struts.xml1. 根据actionName找到相对应的class , 例如LoginAction, 通过反射实例化(创建对象)据parameters中的数据,调用对象的setter方法, 例如parameters中的数据 是 ("name"="test" , "password"="1234") , 那就应该调用 setName和setPassword方法
2. 通过反射调用对象的exectue 方法, 并获得返回值,例如"success"
3. 通过反射找到对象的所有getter方法(例如 getMessage), 通过反射来调用, 把值和属性形成一个HashMap , 例如 {"message": "登录成功"} , 放到View对象的 parameters
4. 根据struts.xml中的 <result> 配置,以及execute的返回值, 确定哪一个jsp,放到View对象的jsp字段中。
自己的思路不清晰,代码杂乱不堪,完全是想一点写一点。
现将刘老师TDD实现思路及面向对象大法整理如下:
public class ConfigurationTest {
Configuration cfg=new Configuration("struts.xml");
@Test
public void testGetClassName() {
String clzName=cfg.getClassName("login");
Assert.assertEquals("domain.LoginAction", clzName);
clzName = cfg.getClassName("logout");
Assert.assertEquals("domain.LogoutAction", clzName);
}
@Test
public void testResultView(){
String jsp=cfg.getResultView("login","success");
Assert.assertEquals("/jsp/homepage.jsp",jsp);
}
}
一个用来读取配置文件并将class转换成其静态内部类,然后通过静态内部类的属性对应上类名。
public class Configuration {
Map<String,ActionConfig> actions = new HashMap<>();
public Configuration(String fileName) {
String packageName = this.getClass().getPackage().getName();
packageName = packageName.replace('.', '/');
InputStream is = this.getClass().getResourceAsStream("/" + packageName + "/" + fileName);
parseXML(is);
try {
is.close();
} catch (IOException e) {
throw new ConfigurationException(e);
}
}
private void parseXML(InputStream is){
SAXBuilder builder = new SAXBuilder();
try {
Document doc = builder.build(is);
Element root = doc.getRootElement();
for(Element actionElement : root.getChildren("action")){
String actionName = actionElement.getAttributeValue("name");
String clzName = actionElement.getAttributeValue("class");
ActionConfig ac = new ActionConfig(actionName, clzName);
for(Element resultElement : actionElement.getChildren("result")){
String resultName = resultElement.getAttributeValue("name");
String viewName = resultElement.getText().trim();
ac.addViewResult(resultName, viewName);
}
this.actions.put(actionName, ac);
}
} catch (JDOMException e) {
throw new ConfigurationException(e);
} catch (IOException e) {
throw new ConfigurationException(e);
}
}
public String getClassName(String action) {
ActionConfig ac = this.actions.get(action);
if(ac == null){
return null;
}
return ac.getClassName();
}
public String getResultView(String action, String resultName) {
ActionConfig ac = this.actions.get(action);
if(ac == null){
return null;
}
return ac.getViewName(resultName);
}
private static class ActionConfig{
String name;
String clzName;
Map<String,String> viewResult = new HashMap<>();
public ActionConfig(String actionName, String clzName) {
this.name = actionName;
this.clzName = clzName;
}
public String getClassName(){
return clzName;
}
public void addViewResult(String name, String viewName){
viewResult.put(name, viewName);
}
public String getViewName(String resultName){
return viewResult.get(resultName);
}
}
}
TDD加对象的职责划分,用一个工具类来处理反射。
public class ReflectionUtilTest {
@Test
public void testGetSetterMethod() throws Exception{
String name="domain.LoginAction";
Class<?> clz=Class.forName(name);
List<Method>methods =ReflectionUtil.getSetterMethods(clz);
Assert.assertEquals(2, methods.size());
List<String> expectedNames=new ArrayList<>();
expectedNames.add("setName");
expectedNames.add("setPassword");
Set<String> acctualNames=new HashSet<>();
for(Method m:methods){
acctualNames.add(m.getName());
}
Assert.assertTrue(acctualNames.containsAll(expectedNames));
}
@Test
public void testSetParameters() throws Exception{
String name="domain.LoginAction";
Class<?>clz=Class.forName(name);
Object o=clz.newInstance();
Map<String,String>params=new HashMap<String,String>();
params.put("name", "test");
params.put("password", "1234");
ReflectionUtil.setParameters(o,params);
Field f=clz.getDeclaredField("name");
f.setAccessible(true);
Assert.assertEquals("test", f.get(o));
f=clz.getDeclaredField("password");
f.setAccessible(true);
Assert.assertEquals("1234", f.get(o));
}
@Test
public void testGetGetterMethod()throws Exception{
String name="domain.LoginAction";
Class<?>clz=Class.forName(name);
List<Method>methods=ReflectionUtil.getGetterMethods(clz);
Assert.assertEquals(3, methods.size());
List<String>expectedNames=new ArrayList<>();
expectedNames.add("getMessage");
expectedNames.add("getName");
expectedNames.add("getPassword");
Set<String> acctualNames=new HashSet<>();
for(Method m:methods){
acctualNames.add(m.getName());
}
Assert.assertTrue(acctualNames.containsAll(expectedNames));
}
@Test
public void testGetParamters() throws Exception{
String name="domain.LoginAction";
Class<?> clz=Class.forName(name);
LoginAction action=(LoginAction) clz.newInstance();
action.setName("test");
action.setPassword("123456");
Map<String,Object> params=ReflectionUtil.getParamterMap(action);
Assert.assertEquals(3, params.size());
Assert.assertEquals(null, params.get("message"));
Assert.assertEquals("test", params.get("name"));
Assert.assertEquals("123456", params.get("password"));
}
}
工具类用来封装对象的get、set方法,来对对象的属性值的射入和获取。
public class ReflectionUtil {
public static List<Method> getSetterMethods(Class<?> clz) {
return getMethods(clz, "set");
}
private static List<Method> getMethods(Class<?> clz, String startWithName) {
List<Method> methods = new ArrayList<>();
for (Method m : clz.getDeclaredMethods()) {
if (m.getName().startsWith(startWithName)) {
methods.add(m);
}
}
return methods;
}
public static void setParameters(Object o, Map<String, String> params) {
List<Method> methods = getSetterMethods(o.getClass());
for (String name : params.keySet()) {
String methodName = "set" + name;
for (Method m : methods) {
if (m.getName().equalsIgnoreCase(methodName)) {
try {
m.invoke(o, params.get(name));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static List<Method> getGetterMethods(Class<?> clz) {
return getMethods(clz, "get");
}
public static Map<String, Object> getParamterMap(Object o) {
Map<String, Object> params = new HashMap<>();
List<Method> methods = getGetterMethods(o.getClass());
for (Method m : methods) {
String methodName = m.getName();
String name = methodName.replaceFirst("get", "").toLowerCase();
try {
Object value = m.invoke(o);
params.put(name, value);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return params;
}
}
最后用来实现上述要求的方法简单如斯:
public class Struts {
private final static Configuration cfg = new Configuration("struts.xml");
public static View runAction(String actionName, Map<String,String> parameters) {
String clzName = cfg.getClassName(actionName);
if(clzName == null){
return null;
}
try {
Class<?> clz = Class.forName(clzName);
Object action = clz.newInstance();
ReflectionUtil.setParameters(action, parameters);
Method m = clz.getDeclaredMethod("execute");
String resultName = (String)m.invoke(action);
Map<String,Object> params = ReflectionUtil.getParamterMap(action);
String resultView = cfg.getResultView(actionName, resultName);
View view = new View();
view.setParameters(params);
view.setJsp(resultView);
return view;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
附:<?xml version="1.0" encoding="UTF-8"?>
<struts>
<action name="login" class="domain.LoginAction">
<result name="success">/jsp/homepage.jsp</result>
<result name="fail">/jsp/showLogin.jsp</result>
</action>
<action name="logout" class="domain.LogoutAction">
<result name= "success">/jsp/welcome.jsp</result>
<result name= "error">/jsp/error.jsp</result>
</action>
</struts>