MethodActionConfigurationProvider完整源码
/**
* 集成xwork的config provier,用来加载brick针对struts2的zero config配置
* @author leeon
*/
public class MethodActionConfigurationProvider implements ConfigurationProvider {
private static final Log logger = LogFactory.getLog(MethodActionConfigurationProvider.class);
/**
* 用来获取xwork config对象,通过该对象来加载config
*/
private Configuration configuration = null;
/**
* 从外部传入的参数,用来记录哪些package有zero config的配置类,程序好搜索这些pkg
*/
private String methodActionPackage = null;
/**
* 用于记录哪些文件被读取过,根据是否修改来判断是否进行reload操作
*/
private Map<String, Long> loadedClassUrls = new HashMap<String, Long>();
/**
* xwork的object factory,用来管理result, iterceptor等配置
*/
private ObjectFactory objectFactory = null;
private final static String DEFAULT_METHOD_ACTION_PACKAGE = "brick.web.extend.struts.methodActionPackage";
/**
* 构造方法
* @param methodActionPackage
*/
public MethodActionConfigurationProvider() {
}
/**
* 解构方法,清除loaded url
*/
public void destroy() {
loadedClassUrls.clear();
}
/**
* 继承接口的初始化方法,可以获取到config实例
*/
public void init(Configuration configuration) throws ConfigurationException {
this.configuration = configuration;
this.loadedClassUrls.clear();
}
/**
* 通过xwork inject获取object factory的方法
* @param objectFactory
*/
@Inject
public void setObjectFactory(ObjectFactory objectFactory) {
this.objectFactory = objectFactory;
}
/**
* 通过xwork inject获取methodActionPackage的方法
* @param context
*/
@Inject(value=DEFAULT_METHOD_ACTION_PACKAGE)
public void setMethodActionPackage(String methodActionPackage) {
this.methodActionPackage = methodActionPackage.replace('.', '/');
}
/**
* 继承接口的load pak的方法
*/
public void loadPackages() throws ConfigurationException {
//读取需要寻找Action类的目录
String[] l = this.methodActionPackage.split(",");
//搜索所有package类
List<String> c = new ArrayList<String>();
for (String p:l) {
try {
c.addAll(FileUtil.searchFileFromClassPath(p, ".*\\.class"));
} catch (Exception e) {
logger.warn("search file from method action package error:["+p+"]", e);
}
}
//读取package信息
for (String clsName:c) {
String className = clsName.substring(0, clsName.length() - 6);
className = className.replace('/', '.');
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
logger.warn("class load method action error:["+className+"]", e);
continue;
}
Package p = (Package)clazz.getAnnotation(Package.class);
if (p == null) continue;
//读取package
PackageConfig pc = createPackageConfig(clazz, p);
//将该类的路径加入Loadedfile
try {
loadedClassUrls.put(clsName, BeanUtil.getClassPathFile(clsName).lastModified());
} catch (Exception e) {
logger.warn("get class file last modify date from filesystem error :["+clsName+"]", e);
}
logger.trace("find action package and loaded:["+pc.getName()+","+pc.getNamespace()+"]");
//读取action信息
Method[] m = clazz.getMethods();
for(Method method:m) {
Action action = (Action)method.getAnnotation(Action.class);
if (action == null) continue;
logger.trace("find action method and start parsing:["+action.name()+","+method.getName()+"]");
//读取action的参数
Map<String, Object> ps = this.createParameters(action.param());
//读取action的exception mapping信息
List<ExceptionMappingConfig> le = this.createExceptionMappingConfigs(
(ActionExceptionMappings)method.getAnnotation(ActionExceptionMappings.class));
//读取单个的e m信息
ExceptionMappingConfig em = this.createExceptionMappingConfig((ActionExceptionMapping)method.getAnnotation(ActionExceptionMapping.class));
if (em != null) le.add(em);
//读取action的interceptor信息
List<InterceptorMapping> li = this.createInterceptorMappings(
(ActionInterceptors)method.getAnnotation(ActionInterceptors.class), pc);
//读取单个的i信息
li.addAll(this.createInterceptorMapping(
(ActionInterceptor)method.getAnnotation(ActionInterceptor.class), pc));
//读取action的result信息
Map<String, ResultConfig> msr = this.createResultConfigs(
(ActionResults)method.getAnnotation(ActionResults.class), pc);
//读取单个的action r信息
ResultConfig rc = this.createResultConfig((ActionResult)method.getAnnotation(ActionResult.class), pc);
if (rc != null) msr.put(rc.getName(), rc);
//构建Action config,并添加到package config中
ActionConfig ac = new ActionConfig(method.getName(), clazz, ps, msr, li, le);
pc.addActionConfig(action.name(), ac);
logger.trace("find action method and end parsing:["+action.name()+","+method.getName()+"]");
}
//加载到configuration中
this.configuration.addPackageConfig(pc.getName(), pc);
}
}
/**
* 是否允许热加载
*/
public boolean needsReload() {
Set<String> s = loadedClassUrls.keySet();
for (String clz : s) {
//获取类的上次修改时间
long now = 0;
try {
now = BeanUtil.getClassPathFile(clz).lastModified();
} catch (Exception e) {
logger.warn("get class file last modify date from filesystem error :["+clz+"]", e);
}
//根据修改时间和已经记录的修改时间对比,如果发生修改那么reload
if (loadedClassUrls.get(clz).longValue() != now) {
logger.trace("find action package class modified and will reload:["+clz+"]");
return true;
}
}
return false;
}
/**
* 必须实现但没有作用的方法
*/
public void register(ContainerBuilder arg0, LocatableProperties arg1)
throws ConfigurationException {
}
/**
* 根据参数创建package
* @param clazz
* @param p
* @return
*/
private PackageConfig createPackageConfig(Class clazz, Package p) {
//根据类名创建package
PackageConfig pc = new PackageConfig(clazz.getName());
//获取名字空间
pc.setNamespace(p.namespace());
//搜索parent,如果没有找到抛出警告
PackageConfig parent = this.configuration.getPackageConfig(p.parent());
if (parent != null) pc.addParent(parent);
else ;
//返回
return pc;
}
/**
* 装载exception mapping config
* 默认使用name和result同名
* @param ems
* @return
*/
private List<ExceptionMappingConfig> createExceptionMappingConfigs(ActionExceptionMappings ems) {
//构造返回的List
List<ExceptionMappingConfig> list = new ArrayList<ExceptionMappingConfig>();
if (ems == null) return list;
//读取aem信息
ActionExceptionMapping aems[] = ems.value();
for (ActionExceptionMapping aem : aems) {
ExceptionMappingConfig emc = new ExceptionMappingConfig(
aem.result(), aem.exceptionClass().getName(), aem.result(), createParameters(aem.param()));
if (emc != null) list.add(emc);
}
//返回结果list
return list;
}
/**
* 用于构造单个的action exception mapping
* @param em
* @return
*/
private ExceptionMappingConfig createExceptionMappingConfig(ActionExceptionMapping em) {
if (em == null) return null;
ExceptionMappingConfig emc = new ExceptionMappingConfig(
em.result(), em.exceptionClass().getName(), em.result(), createParameters(em.param()));
return emc;
}
/**
* 装载interceptor 集合
* @param is
* @param pc
* @return
*/
private List<InterceptorMapping> createInterceptorMappings(ActionInterceptors is, PackageConfig pc) {
//构造返回的i集合列表
List<InterceptorMapping> list = new ArrayList<InterceptorMapping>();
if (is == null) return list;
//调用单个的构造方法进行构造
ActionInterceptor ais[] = is.value();
for (ActionInterceptor ai : ais) {
list.addAll(createInterceptorMapping(ai, pc));
}
//返回结果
return list;
}
/**
* 装载单个的interceptor
* @param interceptor
* @param pc
* @return
*/
private List<InterceptorMapping> createInterceptorMapping(ActionInterceptor interceptor, PackageConfig pc) {
List<InterceptorMapping> list = new ArrayList<InterceptorMapping>();
if (interceptor == null) return list;
//i的参数构造
Map param = createParameters(interceptor.param());
//i的名称
String name = interceptor.value();
//通过i的名字去所有的i中查询 i config
Object o = pc.getAllInterceptorConfigs().get(name);
//查出来的是i config对象
if (o instanceof InterceptorConfig) {
InterceptorConfig config = (InterceptorConfig) o;
//通过config去加载 真正的 i
Interceptor inter = null;
try {
inter = objectFactory.buildInterceptor(config, param);
list.add(new InterceptorMapping(name, inter));
return list;
} catch (ConfigurationException ex) {
logger.warn("Unable to load config class "+config.getClassName()+" at "+
ex.getLocation()+" probably due to a missing jar, which might "+
"be fine if you never plan to use the "+config.getName()+" interceptor", ex);
return list;
}
//查出来的是i stack config对象,可直接取到i mapping对象
} else if (o instanceof InterceptorStackConfig) {
InterceptorStackConfig config = (InterceptorStackConfig) o;
list.addAll(config.getInterceptors());
return list;
}
return list;
}
/**
* 创建result 集合
* @param rs
* @param pc
* @return
*/
private Map<String, ResultConfig> createResultConfigs(ActionResults rs, PackageConfig pc) {
Map<String, ResultConfig> map = new HashMap<String, ResultConfig>();
if (rs == null) return map;
ActionResult ars[] = rs.value();
for (ActionResult ar : ars) {
ResultConfig rc = createResultConfig(ar, pc);
if (rc != null) map.put(rc.getName(), rc);
}
return map;
}
/**
* Creates a default ResultConfig,
* using either the resultClass or the default ResultType for configuration package
* associated this ResultMap class.
*
* @param key The result type name
* @param resultClass The class for the result type
* @param location Path to the resource represented by this type
* @return A ResultConfig for key mapped to location
*/
@SuppressWarnings("unchecked")
private ResultConfig createResultConfig(ActionResult result, PackageConfig pc) {
if (result == null) return null;
Map param = createParameters(result.param());
Class clazz = result.type();
//判断是否使用默认的result type,是的话需要去取默认result type的实现类
if (clazz == NullResult.class) {
String defaultResultType = pc.getFullDefaultResultType();
ResultTypeConfig resultType = pc.getAllResultTypeConfigs().get(defaultResultType);
if (resultType.getParams() != null) param.putAll(resultType.getParams());
try {
clazz = Class.forName(resultType.getClazz());
} catch (ClassNotFoundException ex) {
}
}
//设定default param也就是location
String defaultParam;
try {
defaultParam = (String) clazz.getField("DEFAULT_PARAM").get(null);
} catch (Exception e) {
// not sure why this happened, but let's just use a sensible choice
defaultParam = "location";
}
param.put(defaultParam, result.value());
//创建result config返回
return new ResultConfig(result.name(), clazz.getName(), param);
}
/**
* 构建param anno参数的map的方法
* @param ps
* @return
*/
private Map<String, Object> createParameters(Param[] ps) {
Map<String, Object> map = new HashMap<String, Object>();
if (ps == null) return map;
for (Param p: ps) {
map.put(p.name(), p.value());
}
return map;
}
}
两个用zero config配置的action的例子,一个简单的,一个复杂
看起来还是比较清晰的
@Action(name = "TeacherList")
@ActionResult(value = "/sample/lesson/TeacherList.jsp")
public String listTeacher() throws Exception {
return SUCCESS;
}
@Action(name = "Login")
@ActionInterceptors({
@ActionInterceptor("sendTimeoutRequest"),
@ActionInterceptor("brickStack")
})
@ActionExceptionMappings({
@ActionExceptionMapping(exceptionClass = UserNotExistException.class, result = "UserNotExist"),
@ActionExceptionMapping(exceptionClass = PasswordNotAvailException.class, result = "PasswordNotAvail")
})
@ActionResults({
@ActionResult(name = "UserNotExist", value = "/sample/lesson/Login.jsp"),
@ActionResult(name = "PasswordNotAvail", value = "/sample/lesson/Login.jsp"),
@ActionResult(name = "input", value = "/sample/lesson/Login.jsp"),
@ActionResult(value = "/sample/lesson/Main.jsp")
})
public String login() throws Exception {
return SUCCESS;
}