Struts2的入口类是StrutsPrepareAndExecuteFilter.java。这个类继承Filter接口,必须实现init()、doFilter()、destroy()这3个方法。其中,最重要的是doFilter()方法
public abstract void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain)
throws IOException, ServletException;
这里我经常搞混Filter接口的doFileter()方法入参中的ServletRequest、ServletResponse和HttpServlet抽象类的doPost()和doGet()方法的入参HttpServletRequest、HttpServletResponse。其实HttpServletRequest和HttpServletResponse分别是ServletRequest和ServletResponse的子接口,只不过增加了对Http协议的支持。
实际上,可以看到很多doFilter()方法里面的代码将ServletRequest和ServletResponse强制转换成HttpServletRequest和HttpServletResponse以便使用子接口中的方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
...
chain.doFilter(request, response);
...
}
现在看下StrutsPrepareAndExecuteFilter这个类中的主要的成员变量PrepareOperations和ExecuteOperations和方法init()、doFilter()、destroy()。
就代码看PrepareOperations这个类主要是为运行的环境做准备工作,比如创建Action运行的上下文,初始化一个重要的类Dispatcher以及从配置中取得struts的action映射;而ExecuteOperations这个类主要是加载静态资源和用之前PrepareOpertions中已经初始化完成的Dispatcher去执行Action
首先是日志的处理。对于日志的处理有很多实现,包括很多第三方的SLF4J,Apache自己的Common-logging,其实JDK本身也有日志的实现。
Struts2的日志是这样处理的:首先set指定的日志工厂,然后需要的时候再取得。取的时候,
第1步.先根据运行参数设置的xwork.loggerFactory
然后使用反射获得日志工厂
第2步.如果第1步获取不到日志工厂,则使用apache的commons-logging的日志工厂
第3步.如果第2步获取不到日志工厂,则使用slf4j的日志工厂
第4步.如果第3步获取不到日志工厂,则使用jdk的日志工厂
具体实现类在/struts2-core/xwork/com/opensymphony/xwork2/util/logging/LoggerFactory.java
对应代码如下:
protected static LoggerFactory getLoggerFactory() {
lock.readLock().lock();
try {
if (factory != null) {
return factory;
}
} finally {
lock.readLock().unlock();
}
lock.writeLock().lock();
try {
if (factory == null) {
String userLoggerFactory = System.getProperty(XWorkConstants.XWORK_LOGGER_FACTORY);
if (userLoggerFactory != null) {
try {
Class clazz = Class.forName(userLoggerFactory);
factory = (LoggerFactory) clazz.newInstance();
} catch (Exception e) {
throw new XWorkException("System property [" + XWorkConstants.XWORK_LOGGER_FACTORY +
"] was defined as [" + userLoggerFactory + "] but there is a problem to use that LoggerFactory!", e);
}
} else {
try {
Class.forName("org.apache.commons.logging.LogFactory");
factory = new com.opensymphony.xwork2.util.logging.commons.CommonsLoggerFactory();
} catch (ClassNotFoundException ex) {
//commons-logging not found try slf4j LogFactory
try {
Class.forName("org.slf4j.LoggerFactory");
factory = new Slf4jLoggerFactory();
} catch (ClassNotFoundException cnfex) {
// slf4j not found, falling back to jdk logging
factory = new JdkLoggerFactory();
}
}
}
}
return factory;
} finally {
lock.writeLock().unlock();
}
}
对于这个类中的代码,我觉得有2点值得学习。其一:对于涉及I/O的操作使用读写锁;其二:对于程序运行时各种状况的预判,使得代码鲁棒性更好。
使用ReentrantReadWriteLock这个读写锁的实现非常简单:
private static final ReadWriteLock lock = new ReentrantReadWriteLock();
public void readOperation() {
lock.readLock().lock();
try {
// some read operation...
}
finally {
lock.readLock().unlock();
}
}
public void writeOperation() {
lock.writeLock().lock();
try {
// some write operation...
}
finally {
lock.writeLock().unlock();
}
}
一般在处理多线程互斥资源的时候首先会想到synchronized这个关键字,使用它修饰方法或是将操作放到synchronized代码快里面。这里使用ReentrantReadWriteLock的好处是:1.ReentrantReadWriteLock更加面向对象;2.当资源被大量并发读取,却偶尔被修改的情况下,使用synchronized是有性能问题的,因为不管你是读还是写,都要锁。而ReentrantReadWriteLock却不然。