作者:niumdQ{载请注明出处Q谢?nbsp;
发表旉Q?010 q?nbsp;03 ?nbsp;17 ?nbsp;
原文链接Q?a mce_href="/admin/blogs/618449">http://ari.iteye.com/admin/blogs/618449
一、Spring JDBC 概述
Spring 提供了一个强有力的模板类JdbcTemplate化JDBC操作QDataSource,JdbcTemplate都可以以Bean的方式定义在想xml配置文gQJdbcTemplate创徏只需注入一个DataSourceQ应用程序Dao层只需要承JdbcDaoSupport, 或者注入JdbcTemplateQ便可以获取JdbcTemplateQJdbcTemplate是一个线E安全的c,多个Dao可以注入一个JdbcTemplateQ?/p>
<!-- Oracle数据? --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@oracle.devcake.co.uk:1521:INTL"/> <property name="username" value="sa"/> <property name="password" value=""/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- set注入方式获取jdbcTemplate --> <bean id="customerDao" class="JdbcCustomerDao" > <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> <!-- 注入dataSourceQcustomerDao通过l承JdbcDaoSupport ,使用this.getJdbcTemplate()获取JdbcTemplate --> <bean id="customerDao" class="JdbcCustomerDao" > <property name="dataSource" ref="dataSource"/> </bean>
然后jdbcTemplate对象注入自定义的Dao、或者承JdbcDaoSupportQ例如:
public class JdbcCustomerDao extends JdbcDaoSupport implements CustomerDao { } public class JdbcCustomerDao implements CustomerDao { private JdbcTemplate jdbcTemplate public void setJdbcTemplate()JdbcTemplate jdbcTemplate{ this.jdbcTemplate=jdbcTemplate } }
二?nbsp;JdbcTemplate 提供以下主要Ҏ化JDBC操作Q?/strong>
2.1、List query(String sql,Ojbect[] args,RowMapper rowMapper)
说明Q常用的查询Qsql待执行的sql语句Qargs是sql语句的参敎ͼrowMapper负责每一行记录{化ؓjava对象存放在listQƈ最l返回,例如Q?/p>
public List<Book> queryByAuthor(String author) { String sql = "select * from book where author=?"; Collection c = getJdoTemplate().find(sql, new Object[] { author },new BookRowMapper()); List<Book> books = new ArrayList<Book>(); books.addAll(c); return books; } class BookRowMapper implements RowMapper{ public Object mapRow(ResultSet res, int index) throws SQLException { Book book = new Book(); book.setId(rs.getInt("id")); //省略set return bookQ? } }
更新、删除、其他查询操作类|举例如下Q详l细节请参考spring apiQ?/p>
//q回gؓ一个长整Ş public long getAverageAge() { return getJdbcTemplate().queryForLong("SELECT AVG(age) FROM employee"); } //q回一个整? public int getTotalNumberOfEmployees() { return getJdbcTemplate().queryForInt("SELECT COUNT(0) FROM employees"); } //更新操作 this.jdbcTemplate.update( "insert into t_actor (first_name, surname) values (?, ?)", new Object[] {"Leonor", "Watling"});
2.2、spring 2.5新功能,另类的jdbc ORMQBeanPropertyRowMapper
上面我们索时必须实现RowMapperQ将l果集{化ؓjava对象。Spring2.5 化了q一操作Q得我们不必再实现RowMapperQ实现此功能的俩个神奇东东便是:ParameterizedRowMapperQParameterizedBeanPropertyRowMapperQ貌似通过java反射机制实现了将resultset字段映射到java对象Q但是数据表的列必须和java对象的属性对应,没有研究源码Q有点类gapache 的BeanUtilQ不知ؓ何这部分在spring开发参考手册没有,N不是l典?/p>
//使用ParameterizedBeanPropertyRowMapper @SuppressWarnings({"unchecked"}) public List<Customer> getAll() { return getJdbcTemplate().query("select * from t_customer", ParameterizedBeanPropertyRowMapper.newInstance(Customer.class)); } //使用BeanPropertyRowMapper @SuppressWarnings({"unchecked"}) public List<Customer> getAll() { return getJdbcTemplate().query("select * from t_customer", new BeanPropertyRowMapper(Customer.class)); }
注意QParameterizedBeanPropertyRowMapper是BeanPropertyRowMapper子类。另外表的字D名U必d实体cȝ成员变量名称一_
2.3、spring之JDBC扚w操作
jdbcTemplate.batchUpdate(final String[] sql) QAPI解释QIssue multiple SQL updates on a single JDBC Statement using batchingQ翻译过来大致ؓQ解军_个sql的插入、更新、删除操作在一个Statement中。性能一般?/span>
jdbcTemplate.batchUpdate(String sql, final BatchPreparedStatementSetter pss),cM于JDBC的PreparedStatementQ性能较上着有所提高?/span>
我们举例说明如何使用Q示例如?
final int count = 2000; final List<String> firstNames = new ArrayList<String>(count); final List<String> lastNames = new ArrayList<String>(count); for (int i = 0; i < count; i++) { firstNames.add("First Name " + i); lastNames.add("Last Name " + i); } jdbcTemplate.batchUpdate( "insert into customer (id, first_name, last_name, last_login, comments) values (?, ?, ?, ?, ?)", new BatchPreparedStatementSetter() { //为prepared statement讄参数。这个方法将在整个过E中被调用的ơ数 public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setLong(1, i + 10); ps.setString(2, firstNames.get(i)); ps.setString(3, lastNames.get(i)); ps.setNull(4, Types.TIMESTAMP); ps.setNull(5, Types.CLOB); } //q回更新的结果集条数 public int getBatchSize() { return count; } }); }
BatchSqlUpdatecLSqlUpdate 的子c,适用于插入、删除、更新批量操作,内部使用PreparedStatementQ所以效率很高,扚w语句辑ֈ讑֮的batchSizeQ或者手动调用flush才会执行扚w操作。注意:此类是非U程安全的,必须为每个用者创Z个实例,或者在同一个线E中使用前调用reset?/span>
下面我们举例说明如何使用BatchSqlUpdateQ来执行扚w操作。示例如下:
class BatchInsert extends BatchSqlUpdate { private static final String SQL = "insert into t_customer (id, first_name, last_name, last_login, " + "comments) values (?, ?, ?, ?, null)"; BatchInsert(DataSource dataSource) { super(dataSource, SQL); declareParameter(new SqlParameter(Types.INTEGER)); declareParameter(new SqlParameter(Types.VARCHAR)); declareParameter(new SqlParameter(Types.VARCHAR)); declareParameter(new SqlParameter(Types.TIMESTAMP)); setBatchSize(10); } }
int count = 5000; for (int i = 0; i < count; i++) { batchInsert.update(new Object[] { i + 100L, "a" + i, "b" + i, null }); }
xQspring JDBC主要的应用基本上都简单罗列一番,所有代码均为文章D例,不是很严谨,仅ؓ演示每一U用法,抛砖引玉Q希望有独特见解的拍砖,有问题的h明问题所?谢谢
目中有个业务处理类大小117KQ代?700行,看此cd炚wLQ如今如要增加业务逻辑大约20个吧Q此cd果随着目工程的二期、三期如ơ添加逻辑q早有一天大达到MQ噢、mygod。细心研d人的工作ȝQ发现其中有点可攚w的蛛丝马迹(本h很笨、别W我才发现如何改??/p>
下面我们对业务流E、以及涉及的相关c进行介l,Msg代表接受到客L的一个消息报文,消息报文l构Q消息头+消息体,消息头参数固定、消息体参数不定Q下面是一个简单的cdQ这只是一个模拟场景,****Req代表各户端请求类Q?span lang="EN-US">***Rsp代表q回l客L的参数类。实际比此复杂,为描q问题我们简单摘除几个类介绍Q别问我Zq么设计l承。类?span lang="EN-US">msg?span lang="EN-US">msgHead是组合关pM许画错了、不当之处请指出Q勿恶语向伤Q?/span>
处理hHandlercȝ代码逻辑如下Q?/p>
//cM主要Ҏ如下 public void execute(Object object) { Message message = (Message)object; int opcode = message.getOpcode(); int connectId = message.getConnectId(); //消息头已l解析,获取消息体,卛_cd性字节数l? byte[] bytes = message.getBytes(); if (opcode == MsgInfo.ADD_RING) { // 订购彩铃 orderRing(connectId, bytes); } else if (opcode == MsgInfo.PRESENT_RING) { // 赠送彩? presentRing(connectId, bytes); } else if (opcode == MsgInfo.DEL_RING) { // 删除个h铃音 delPersonalRing(connectId, bytes); } //此处省略n个else if } //其他删除、赠送与省略的else if中的处理逻辑与之基本相同 private void orderRing(int connectId, byte[] bytes) { //处理Ҏ分ؓ四步Q具体代码省? //1、解析字节数lؓ订购铃音c? //2、处理订购关p? //3、处理结果封装ؓ订购响应c? //4、发送回客户? } //省略presentRing、delPersonalRing{一pd其他ҎQ所有的处理Ҏ参数相同……
鉴于此、想C用命令模式改造此c,如果不了解命令模式请阅读相关书籍Q大话设计模式或设计与模式,q里我们仅给出大致的定于与类图?/p>
何谓命o模式Q将一个请求封装ؓ一个对象,从而是你可用不同的h对客L参数化,对请求排队或记录日志Q以及支持可撤销的操作?/p>
Shit、这句话很难理解哦,那就先别理解了,我们看下命o模式的类图,然后介绍如何使用命o模式攚w上面的elseif?/p>
cd先省略,上班L写的Q?/p>
下面q入正题Q对Handler手术开始,主要考虑如下Q?/p>
1?/span>提炼Ҏ
每?span lang="EN-US">if语句块中的逻辑提取Z个方法,q里我们?span lang="EN-US">handler已经实现Q就是:orderRing?span mce_style="font-family: 'Courier New'; color: black; font-size: 10pt;" style="font-family: 'Courier New'; color: black; font-size: 10pt; ">presentRing?span mce_style="font-family: 'Courier New'; color: black; font-size: 10pt;" style="font-family: 'Courier New'; color: black; font-size: 10pt; ">delPersonalRing?#8230;…?/p>
2?/span>提炼c?/strong>
每个业务处理方法提取ؓ以各c,然后对具体类q行抽象Q提取父cL者接口;代码如下Q?/p>
public Abstract class Command{ public void execute() ? public class OrderRingCommand extends Command { private Handler hander; public OrderRingCommand(Handler hander){ this.hander = hander; } public void execute(int connectId, byte[] bytes){ //1、解析字节数lؓ订购铃音c? //2、增加订购关p? //3、处理结果封装ؓ订购响应c? //4、发送回客户? } /** * 1、解析字节数lؓ订购铃音c? */ public void method1(){ } /** * 2、处理订购关p? */ public void method2(){ } /** * 3、处理结果封装ؓ订购响应c? */ public void method3(){ } /** * 4、结果发送回客户? */ public void method4(){ } } public class DelRingCommand extends Command { private Handler hander; public DelRingCommand(Handler hander){ this.hander = hander; } public void execute(int connectId, byte[] bytes){ //1、解析字节数lؓ订购铃音c? //2、删除购关系 //3、处理结果封装ؓ订购响应c? //4、发送回客户? } //提取Ҏ }
3?/span>命o模式攚w替?/strong>elseifQ?/strong>
Map<Integer, Command> map = new HashMap<Integer,Command>(); static{ map.put(MsgInfo.ADD_RING, new OrderRingCommand()); //省却其他Q这里仅为演C,实际目中实例化c通过spring容器或者其他方? } public void execute(Object object) { Message message = (Message)object; int opcode = message.getOpcode(); int connectId = message.getConnectId(); //消息头已l解析,获取消息体,卛_cd性字节数l? byte[] bytes = message.getBytes(); map.get(opcode).execute(connectId,bytes); }
命o模式替换else if代码坏味道的重构l束Q众多的if条g块烟消云散,取而代之的是一个个_的类Qdoc版本在附件中
一、ThreadLocal概述
学习JDK中的c,首先看下JDK APIҎcȝ描述Q描q如下:
API表达了下面几U观点:
1、ThreadLocal不是U程Q是U程的一个变量,你可以先单理解ؓU程cȝ属性变量?/p>
2、ThreadLocal 在类中通常定义为静态类变量?/p>
3、每个线E有自己的一个ThreadLocalQ它是变量的一?#8216;拯’Q修改它不媄响其他线E?/p>
既然定义为类变量Qؓ何ؓ每个U程l护一个副本(姑且成ؓ‘拯’Ҏ理解Q,让每个线E独立访问?多线E编E的l验告诉我们Q对于线E共享资源(你可以理解ؓ属性)Q资源是否被所有线E共享,也就是说q个资源被一个线E修Ҏ否媄响另一个线E的q行Q如果媄响我们需要用synchronized同步Q让U程序讉K?/p>
ThreadLocal适用于资源共享但不需要维护状态的情况Q也是一个线E对资源的修改,不媄响另一个线E的q行Q这U设计是‘I间换时?#8217;Qsynchronized序执行?strong>‘旉换取I间’?/p>
二、ThreadLocalҎ介绍
T |
get() q回此线E局部变量的当前U程副本中的倹{?/td> |
protected T |
initialValue() q回此线E局部变量的当前U程?#8220;初始?#8221;?/td> |
void |
remove() U除此线E局部变量当前线E的倹{?/td> |
void |
set(T value) 此U程局部变量的当前U程副本中的D|ؓ指定倹{?/td> |
三、深入源?/strong>
ThreadLocal有一个ThreadLocalMap静态内部类Q你可以单理解ؓ一个MAPQ这?#8216;Map’为每个线E复制一个变量的‘拯’存储其中?/p>
当线E调用ThreadLocal.get()Ҏ获取变量?首先获取当前U程引用Q以此ؓkey去获取响应的ThreadLocalMapQ如果此‘Map’不存在则初始化一个,否则q回其中的变量,代码如下Q?/p>
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }
调用getҎ如果此Map不存在首先初始化Q创建此mapQ将U程为keyQ初始化的vlaue存入其中Q注意此处的initialValueQ我们可以覆盖此ҎQ在首次调用时初始化一个适当的倹{setInitialValue代码如下Q?/p>
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
setҎ相对比较单如果理解以上俩个方法,获取当前U程的引用,从map中获取该U程对应的mapQ如果map存在更新~存|否则创徏q存储,代码如下Q?/p>
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
对于ThreadLocal在何处存储变量副本,我们看getMapҎQ获取的是当前线E的ThreadLocalcd的threadLocals属性。显然变量副本存储在每一个线E中?/p>
/** * 获取U程的ThreadLocalMap 属性实? */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
上面我们知道变量副本存放于何处,q里我们单说下如何被java的垃圾收集机制收集,当我们不在用是调用set(null)Q此时不在将引用指向?#8216;map’Q而线E退出时会执行资源回收操作,申L资源q行回收Q其实就是将属性的引用讄为null。这时已l不在有M引用指向该mapQ故而会被垃圾收集?/p>
四、ThreadLocal应用CZ
在我的另一文章,对ThreadLocal的用做了一个实例,此示例也可以用作生环境Q请参见Q?a mce_href="/blog/757641">http://ari.iteye.com/blog/757641
如有问题La讨论Q谢?/p>
本文借花献佛Q引用Tim Cull的博?#8220;SimpleDateFormat: Performance Pig”介绍下ThreadLocal的简单用,同时也对SimpleDateFormat的用有个深入的了解?/p>
大致意思:Tim Cull到一个SimpleDateFormat带来的严重的性能问题Q该问题主要有SimpleDateFormat引发Q创Z个SimpleDateFormat实例的开销比较昂贵Q解析字W串旉旉J创建生命周期短暂的实例D性能低下。即使将SimpleDateFormat定义为静态类变量Q貌D解决q个问题Q但是SimpleDateFormat是非U程安全的,同样存在问题Q如果用‘synchronized’U程同步同样面问题Q同步导致性能下降Q线E之间序列化的获取SimpleDateFormat实例Q?/p>
Tim Cull使用Threadlocal解决了此问题Q对于每个线ESimpleDateFormat不存在媄响他们之间协作的状态,为每个线E创Z个SimpleDateFormat变量的拷贝或者叫做副本,代码如下Q?/p>
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * 使用ThreadLocal以空间换旉解决SimpleDateFormatU程安全问题? * @author * */ public class DateUtil { private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; @SuppressWarnings("rawtypes") private static ThreadLocal threadLocal = new ThreadLocal() { protected synchronized Object initialValue() { return new SimpleDateFormat(DATE_FORMAT); } }; public static DateFormat getDateFormat() { return (DateFormat) threadLocal.get(); } public static Date parse(String textDate) throws ParseException { return getDateFormat().parse(textDate); } }
创徏一个ThreadLocalcd量,q里创徏时用了一个匿名类Q覆盖了initialValueҎQ主要作用是创徏时初始化实例。也可以采用下面方式创徏Q?/p>
//W一ơ调用get返回null private static ThreadLocal threadLocal = new ThreadLocal()Q? //获取U程的变量副本,如果不覆盖initialValueQ第一ơgetq回nullQ故需要初始化一个SimpleDateFormatQƈset到threadLocal? public static DateFormat getDateFormat() { DateFormat df = (DateFormat) threadLocal.get(); if(df==null){ df = new SimpleDateFormat(DATE_FORMAT) threadLocal.set(df); } return df; }
我们看下我们覆盖的initialValueҎQ?/p>
protected T initialValue() { return null;//直接q回null }
作者:niumd
blogQ?a mce_href="/">http://ari.iteye.com
一、概q?/strong>
Struts2的核心是一个FilterQAction可以qweb容器Q那么是什么让httph和action兌在一LQ下面我们深入源码来分析下Struts2是如何工作的?/p>
鉴于常规情况官方推荐使用StrutsPrepareAndExecuteFilter替代FilterDispatcherQ我们此文将剖析StrutsPrepareAndExecuteFilterQ其在工E中作ؓ一个Filter配置在web.xml中,配置如下Q?/p>
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
二、源码属性方法简?/strong>
下面我们研究下StrutsPrepareAndExecuteFilter源码Q类的主要信息如下:
属性摘?/strong> | |
---|---|
protected List<Pattern> |
excludedPatterns |
protected ExecuteOperations |
execute |
protected PrepareOperations |
prepare |
StrutsPrepareAndExecuteFilter与普通的Filterq无区别Q方法除l承自Filter外,仅有一个回调方法,W三部分我们按照FilterҎ调用序Q由init?gt;doFilter?gt;destroy序地分析源码?/p>
Ҏ摘要 | |
---|---|
void |
destroy() l承自FilterQ用于资源释?/td> |
void |
doFilter(ServletRequest req, ServletResponse res, FilterChain chain) l承自FilterQ执行方?/td> |
void |
init(FilterConfig filterConfig) l承自FilterQ初始化参数 |
protected void |
postInit(Dispatcher dispatcher, FilterConfig filterConfig) Callback for post initializationQ一个空的方法,用于Ҏ回调初始化) |
三、源码剖?nbsp;
1、initҎ
init是FilterW一个运行的ҎQ我们看下struts2的核心Filter在调用initҎ初始化时做哪些工作:
public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); try { //装filterConfigQ其中有个主要方法getInitParameterNames参数名字以String格式存储在List? FilterHostConfig config = new FilterHostConfig(filterConfig); // 初始化struts内部日志 init.initLogging(config); //创徏dispatcher Qƈ初始化,q部分下面我们重点分析,初始化时加蝲那些资源 Dispatcher dispatcher = init.initDispatcher(config); init.initStaticContentLoader(config, dispatcher); //初始化类属性:prepare 、execute prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); //回调I的postInitҎ postInit(dispatcher, filterConfig); } finally { init.cleanup(); } }
首先看下FilterHostConfig Q源码如下:
public class FilterHostConfig implements HostConfig { private FilterConfig config; /** *构造函? */ public FilterHostConfig(FilterConfig config) { this.config = config; } /** * Ҏinit-param配置的param-name获取param-value的? */ public String getInitParameter(String key) { return config.getInitParameter(key); } /** * q回初始化参数名的List */ public Iterator<String> getInitParameterNames() { return MakeIterator.convert(config.getInitParameterNames()); } public ServletContext getServletContext() { return config.getServletContext(); } }
只有短短的几行代码,getInitParameterNames是这个类的核心,Filter初始化参数名U有枚Dcd转ؓIterator。此cȝ主要作ؓ是对filterConfig 装?/p>
重点来了Q创建ƈ初始化Dispatcher
public Dispatcher initDispatcher( HostConfig filterConfig ) { Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init(); return dispatcher; }
创徏DispatcherQ会d filterConfig 中的配置信息Q将配置信息解析出来Q封装成Z个MapQ然后根lservlet上下文和参数Map构造Dispatcher Q?/p>
private Dispatcher createDispatcher( HostConfig filterConfig ) { Map<String, String> params = new HashMap<String, String>(); for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) { String name = (String) e.next(); String value = filterConfig.getInitParameter(name); params.put(name, value); } return new Dispatcher(filterConfig.getServletContext(), params); }
Dispatcher初始化,加蝲struts2的相关配|文Ӟ按照顺序逐一加蝲Qdefault.propertiesQstruts-default.xml,struts-plugin.xml,struts.xmlQ?#8230;…
/** *初始化过E中依次加蝲如下配置文g */ public void init() { if (configurationManager == null) { configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } try { //加蝲org/apache/struts2/default.properties init_DefaultProperties(); // [1] //加蝲struts-default.xml,struts-plugin.xml,struts.xml init_TraditionalXmlConfigurations(); // [2] init_LegacyStrutsProperties(); // [3] //用户自己实现的ConfigurationProvidersc? init_CustomConfigurationProviders(); // [5] //Filter的初始化参数 init_FilterInitParameters() ; // [6] init_AliasStandardObjects() ; // [7] Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if (!dispatcherListeners.isEmpty()) { for (DispatcherListener l : dispatcherListeners) { l.dispatcherInitialized(this); } } } catch (Exception ex) { if (LOG.isErrorEnabled()) LOG.error("Dispatcher initialization failed", ex); throw new StrutsException(ex); } }
初始化default.propertiesQ具体的初始化操作在DefaultPropertiesProvidercM
private void init_DefaultProperties() { configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); }
下面我们看下DefaultPropertiesProvidercL码:
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings("org/apache/struts2/default"); } catch (Exception e) { throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e); } loadSettings(props, defaultSettings); }
其他的我们再ơ省略,大家可以览下各个初始化操作都加载了那些文g
3、doFilterҎ
doFilter是过滤器的执行方法,它拦截提交的HttpServletRequesthQHttpServletResponse响应Q作为strtus2的核心拦截器Q在doFilter里面到底做了哪些工作Q我们将逐行解读其源码,源码如下Q?/p>
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { //父类向子c{Q强转ؓhttph、响? HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { //讄~码和国际化 prepare.setEncodingAndLocale(request, response); //创徏Action上下文(重点Q? prepare.createActionContext(request, response); prepare.assignDispatcherToThread(); if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { request = prepare.wrapRequest(request); ActionMapping mapping = prepare.findActionMapping(request, response, true); if (mapping == null) { boolean handled = execute.executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else { execute.executeAction(request, response, mapping); } } } finally { prepare.cleanupRequest(request); } }
setEncodingAndLocale调用了dispatcherҎ的prepareҎQ?/p>
/** * Sets the request encoding and locale on the response */ public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) { dispatcher.prepare(request, response); }
下面我们看下prepareҎQ这个方法很单只是设|了encoding 、locale Q做的只是一些辅助的工作Q?/p>
public void prepare(HttpServletRequest request, HttpServletResponse response) { String encoding = null; if (defaultEncoding != null) { encoding = defaultEncoding; } Locale locale = null; if (defaultLocale != null) { locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale()); } if (encoding != null) { try { request.setCharacterEncoding(encoding); } catch (Exception e) { LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e); } } if (locale != null) { response.setLocale(locale); } if (paramsWorkaroundEnabled) { request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request } }
Action上下文创建(重点Q?/strong>
ActionContext是一个容器,q个Ҏ主要存储request、session、application、parameters{相关信?ActionContext是一个线E的本地变量Q这意味着不同的action之间不会׃nActionContextQ所以也不用考虑U程安全问题。其实质是一个MapQkey是标Crequest、session?#8230;…的字W串Q值是其对应的对象Q?/p>
static ThreadLocal actionContext = new ThreadLocal(); Map<String, Object> context;
下面我们看下如何创徏action上下文的Q代码如下:
/** *创徏Action上下文,初始化thread local */ public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) { ActionContext ctx; Integer counter = 1; Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER); if (oldCounter != null) { counter = oldCounter + 1; } //注意此处是从ThreadLocal中获取此ActionContext变量 ActionContext oldContext = ActionContext.getContext(); if (oldContext != null) { // detected existing context, so we are probably in a forward ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap())); } else { ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext)); //stack.getContext()q回的是一个Map<StringQObject>Q根据此Map构造一个ActionContext ctx = new ActionContext(stack.getContext()); } request.setAttribute(CLEANUP_RECURSION_COUNTER, counter); //ActionContext存如ThreadLocal ActionContext.setContext(ctx); return ctx; }
上面代码中dispatcher.createContextMapQ如何封装相兛_敎ͼ
public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping, ServletContext context) { // request map wrapping the http request objects Map requestMap = new RequestMap(request); // parameters map wrapping the http parameters. ActionMapping parameters are now handled and applied separately Map params = new HashMap(request.getParameterMap()); // session map wrapping the http session Map session = new SessionMap(request); // application map wrapping the ServletContext Map application = new ApplicationMap(context); //requestMap、params、session{Map装成ؓ一个上下文MapQ逐个调用了map.put(Map p). Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context); if (mapping != null) { extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); } return extraContext; }
我们单看下RequestMapQ其他的省略。RequestMapcdC抽象MapQ故其本w是一个MapQ主要方法实玎ͼ
//map的get实现 public Object get(Object key) { return request.getAttribute(key.toString()); } //map的put实现 public Object put(Object key, Object value) { Object oldValue = get(key); entries = null; request.setAttribute(key.toString(), value); return oldValue; }
下面是源码展CZ如何执行Action控制器:
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { dispatcher.serviceAction(request, response, servletContext, mapping); } public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { //装执行的上下文环境Q主要讲相关信息存储入map Map<String, Object> extraContext = createContextMap(request, response, mapping, context); // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); boolean nullStack = stack == null; if (nullStack) { ActionContext ctx = ActionContext.getContext(); if (ctx != null) { stack = ctx.getValueStack(); } } if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); //获取命名I间 String namespace = mapping.getNamespace(); //获取action配置的name属? String name = mapping.getName(); //获取action配置的method属? String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration(); //Ҏ执行上下文参敎ͼ命名I间Q名U等创徏用户自定义Action的代理对? ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy( namespace, name, method, extraContext, true, false); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); // if the ActionMapping says to go straight to a result, do it! //执行executeҎQƈ转向l果 if (mapping.getResult() != null) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { proxy.execute(); } // If there was a previous value stack then set it back onto the request if (!nullStack) { request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch (ConfigurationException e) { // WW-2874 Only log error if in devMode if(devMode) { String reqStr = request.getRequestURI(); if (request.getQueryString() != null) { reqStr = reqStr + "?" + request.getQueryString(); } LOG.error("Could not find action or result\n" + reqStr, e); } else { LOG.warn("Could not find action or result", e); } sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) { sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } finally { UtilTimerStack.pop(timerKey); } }
文中对如何解析Struts.xmlQ如何将URL与action映射匚w为分析,有需要的我后l补全,因ؓStrutsXmlConfigurationProviderl承XmlConfigurationProviderQƈ在registerҎ回调父类的registerQ有兴趣的可以深入阅M下XmlConfigurationProvider源码Q?/p>
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException { if (servletContext != null && !containerBuilder.contains(ServletContext.class)) { containerBuilder.factory(ServletContext.class, new Factory<ServletContext>() { public ServletContext create(Context context) throws Exception { return servletContext; } }); } //调用父类的registerQ关键点所? super.register(containerBuilder, props); }
struts2-core-2.2.1.jar包中struts-2.1.7.dtd对于Action的定义如下:
<!ELEMENT action (param|result|interceptor-ref|exception-mapping)*> <!ATTLIST action name CDATA #REQUIRED class CDATA #IMPLIED method CDATA #IMPLIED converter CDATA #IMPLIED >
从上qDTD中可见Action元素可以含有name 、class 、method 、converter 属性?/p>
XmlConfigurationProvider解析struts.xml配置的Action元素Q?/p>
protected void addAction(Element actionElement, PackageConfig.Builder packageContext) throws ConfigurationException { String name = actionElement.getAttribute("name"); String className = actionElement.getAttribute("class"); String methodName = actionElement.getAttribute("method"); Location location = DomHelper.getLocationObject(actionElement); if (location == null) { LOG.warn("location null for " + className); } //methodName should be null if it's not set methodName = (methodName.trim().length() > 0) ? methodName.trim() : null; // if there isnt a class name specified for an <action/> then try to // use the default-class-ref from the <package/> if (StringUtils.isEmpty(className)) { // if there is a package default-class-ref use that, otherwise use action support /* if (StringUtils.isNotEmpty(packageContext.getDefaultClassRef())) { className = packageContext.getDefaultClassRef(); } else { className = ActionSupport.class.getName(); }*/ } else { if (!verifyAction(className, name, location)) { if (LOG.isErrorEnabled()) LOG.error("Unable to verify action [#0] with class [#1], from [#2]", name, className, location.toString()); return; } } Map<String, ResultConfig> results; try { results = buildResults(actionElement, packageContext); } catch (ConfigurationException e) { throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement); } List<InterceptorMapping> interceptorList = buildInterceptorList(actionElement, packageContext); List<ExceptionMappingConfig> exceptionMappings = buildExceptionMappings(actionElement, packageContext); ActionConfig actionConfig = new ActionConfig.Builder(packageContext.getName(), name, className) .methodName(methodName) .addResultConfigs(results) .addInterceptors(interceptorList) .addExceptionMappings(exceptionMappings) .addParams(XmlHelper.getParams(actionElement)) .location(location) .build(); packageContext.addActionConfig(name, actionConfig); if (LOG.isDebugEnabled()) { LOG.debug("Loaded " + (StringUtils.isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace() + "/") : "") + name + " in '" + packageContext.getName() + "' package:" + actionConfig); } }
工作中不涉及Struts2Q本周工作有?天的I档期,E微看了下struts2的文档,写了个demoQ从源码的角度研I了下运行原理,如有分析不当h出,我后l逐步完善更正Q大家共同提高?/p>
作者:niumd
Blog:http://ari.iteye.com
转蝲h明出处,谢谢
开发的应用采用F5负蝲均衡交换机,F5请求{发给5台hp unix服务器,每台服务器有多个webserver实例Q对外提供web服务和socket{接口服务。之初,曾有个小的疑问Z不采用开源的apache、Nginx软g负蝲QF5讑֤动辄几十万,h昂贵Q自׃个比较幼E的问题Q后l明白:F5是操作于IOS|络模型的传输层QNginx、apache是基于http反向代理方式Q位于ISO模型的第七层应用层。直白些是TCP UDP 和http协议的区别,Nginx不能为基于TCP协议的应用提供负载均衡?/p>
了解了二者之间的区别于应用场景,对Nginx产生厚的兴,阅读张宴?lt;实战Nginx>Q这?5q的伙子年L为M?妒忌Q,搞明白了大致原理和配|,Ubuntu10.10Qwindow下对Nginx+tomcat负蝲均衡做了配置试Q将全部h转发到tomcatQƈ未做静态,动态分开Q图片防盗链{配|?br />
Nginx 介绍
Nginx Q发韛_ engine xQ是一ƾ轻量的Web 服务器/反向代理服务器及电子邮gQIMAP/POP3Q代理服务器Qƈ在一个BSD-like 协议下发行?nbsp; 其特Ҏ占有内存,q发能力强,事实上nginx的ƈ发能力确实在同类型的|页伺服器中表现较好.目前中国大陆使用nginx|站用户有:新浪、网易?腾讯,另外知名的微|志Plurk也用nginx?/p>
上面的全是Nginx介绍基本上是废话Q下面{入正题,图文l合展示基本配置Q首先是window环境、其ơ是Ubuntu环境QVbox虚拟Q。本文主要基于Nginx下配|两台tomcatQ结构如下图Q?br />
Window xp环境QNginx+Tomcat6
1、下载地址
http://nginx.org/en/download.html Q这里我们推荐下载稳定版Qstable versionsQ,本文采用nginx-0.8.20?/p>
2、目录结?/strong>
Nginx-
|_ conf 配置目录
|_ contrib
|_ docs 文档目录
|_ logs 日志目录
|_ temp 临时文g目录
|_ html 静态页面目?/p>
|_ nginx.exe ȝ?/p>
window下安装Nginx极其单,解压~到一个无I格的英文目录即可(个h习惯Q担心中文出问题Q,双击nginx启动Q这里我安装刎ͼD:\server目录Q下面涉及到的tomcat也安装在此目录?/p>
DOS环境启动
若果惛_止nginxQdos环境q行命oQnginx -s stop
3、nginx.conf配置
Nginx配置文g默认在conf目录Q主要配|文件ؓnginx.confQ我们安装在D:\server\nginx-0.8.20、默认主配置文g为D:\server\nginx-0.8.20\nginx.conf。下面是nginx作ؓ前端反向代理服务器的配置?/p>
#Nginx所用用户和l,window下不指定 #user niumd niumd; #工作的子q程数量Q通常{于CPU数量或?倍于CPUQ? worker_processes 2; #错误日志存放路径 #error_log logs/error.log; #error_log logs/error.log notice; error_log logs/error.log info; #指定pid存放文g pid logs/nginx.pid; events { #使用|络IO模型linuxepollQFreeBSD采用kqueueQwindow下不指定? #use epoll; #允许最大连接数 worker_connections 2048; } http { include mime.types; default_type application/octet-stream; #定义日志格式 #log_format main '$remote_addr - $remote_user [$time_local] $request ' # '"$status" $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log off; access_log logs/access.log; client_header_timeout 3m; client_body_timeout 3m; send_timeout 3m; client_header_buffer_size 1k; large_client_header_buffers 4 4k; sendfile on; tcp_nopush on; tcp_nodelay on; #keepalive_timeout 75 20; include gzip.conf; upstream localhost { #Ҏip计算请求分配各那个后端tomcatQ许多h误认为可以解决session问题Q其实ƈ不能? #同一机器在多|情况下Q\由切换,ip可能不同 #ip_hash; server localhost:18081; server localhost:18080; } server { listen 80; server_name localhost; location / { proxy_connect_timeout 3; proxy_send_timeout 30; proxy_read_timeout 30; proxy_pass http://localhost; } } }
代理讄如下Q?/p>
proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 10m; client_body_buffer_size 128k; proxy_connect_timeout 300; proxy_send_timeout 300; proxy_read_timeout 300; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k;
gzip压羃相关配置如下Q?/p>
gzip on; gzip_min_length 1000; gzip_types text/plain text/css application/x-javascript;
4、Tomcat配置
对于tomcat大家都很熟悉Q只需要修改server.xml配置文g卛_Q这里我们以apache-tomcat-6.0.14ZQ分别在server目录Q解压羃q命名ؓQapache-tomcat-6.0.14_1、apache-tomcat-6.0.14_2?/p>
W一处端口修改:
<!-- 修改port端口Q?8006 俩个tomcat不能重复Q端口随意,别太?-> <Server port="18006" shutdown="SHUTDOWN">
W二处端口修改:
<!-- port="18081" tomcat监听端口Q随意设|,别太?--> <Connector port="18081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
W三处端口修改:
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
Engine元素增加jvmRoute属性:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
两个tomcat的端口别重复Q保证能启动hQ另一个tomcat配置希捷省略Q监听端口ؓ18080Q附件中我们上传所有的配置信息?/p>
5、验证配|与试负蝲均衡
首先试nginx配置是否正确Q测试命令:nginx -t (默认验证:conf\nginx.conf),也可以指定配|文件\径?/p>
此例nginx安装目录QD:\server\nginx-0.8.20Qdos环境下图画面成功CZQ?br />
其次验证tomcatQ启动两个tomcatQ不出现端口冲突即ؓ成功Qtomcat依赖的java{搞“挨踢”的就废话不说了)Q?/p>
最后验证配|负载均衡设|,http://localhost/ ?a href="http://localhost/index.jsp" mce_href="http://localhost/index.jsp">http://localhost/index.jsp 。我修改了index.jsp面Q增加日志输Z息,便于观察。注意:左上角小猫头上的Qaccess tomcat2、access tomcat1。说明访问了不同的tomcat?/p>
xwindow下nginx+tomcat负蝲均衡配置l束Q关于tomcat Session的问题通常是采用memcachedQ或者采用nginx_upstream_jvm_route Q他是一?Nginx 的扩展模块,用来实现Z Cookie ?Session Sticky 的功能。如果tomcatq多不徏议session同步Qserver间相互同步session很耗资源,高ƈ发环境容易引起Session风暴。请Ҏ自己应用情况合理采纳session解决Ҏ?/p>
作者:niumd
Blog:http://ari.iteye.com
Ubuntu10.10环境QNginx+Tomcat6
我们下面单说下ubuntu10.10下如何安装配|,主要以图片ؓ主,单解释?/p>
1、下载Nginx
地址Q?a mce_>http://nginx.org/en/download.html Qlinux版本Qnginx-0.8.20.tar.。解压羃命oQ?/p>
tar -zxvf nginx-0.8.20.tar.gz
2、编译安装Nginx
Nginx依赖一些其他PCRE、opensslQ依赖libssl-devQ,本hW记本Ubuntu环境已经安装PCREQ仅需安装依赖的opensslQ下面我们简单说下如何安装PCRE和openssl{?/p>
PCRE下蝲地址Q?a href="ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/" mce_href="ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/">ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
tar zxvf pcre-8.01.tar.gz cd pcre-8.01 sudo ./configure sodu make sodu make install
openssl通过apt-get install安装Q命令、截囑֦下:
sudo apt-get install openssl sudo apt-get install libssl-dev //如缺其他包Q请采用此方法安装,ubuntu有依赖提C?
依赖的Y件包安装完毕Q下面来~译NginxQ?/p>
#window׃n目录软g拯到当前工作目? cp /mnt/fileshare/nginx-0.8.20.tar.gz ./ #解压~Y件包 tar zxvf nginx-0.8.20.tar.gz cd nginx-0.8.20 //~译源码,默认使用nobodyQ指定本机已存在的用Pl,启用nginx-status功能Q监控nginx状态。启动debug sudo ./configure --user=niumd --group=niumd --with-debug --with-http_stub_status_module sudo make sudo make install
截图 如下Q?br />
安装l果截图如下Q?/p>
~译安装正确l束Q按照上qwindow下方法检查默认配|,然后在默认配|下启动nginxQ访?a mce_>http://127.0.0.1 Q如下图说明成功
Nginx配置成功后我们对window下nginx.conf做修改Q如下:
#Nginx所用用户和l? user niumd niumd; #工作的子q程数量Q通常{于CPU数量或?倍于CPUQ? worker_processes 2; #错误日志存放路径 #error_log logs/error.log; #error_log logs/error.log notice; error_log logs/error.log info; #指定pid存放文g pid logs/nginx.pid; events { #使用|络IO模型linuxepollQFreeBSD采用kqueue use epoll; #允许最大连接数 worker_connections 2048; } http { include mime.types; default_type application/octet-stream; #定义日志格式 #log_format main '$remote_addr - $remote_user [$time_local] $request ' # '"$status" $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log off; access_log logs/access.log; client_header_timeout 3m; client_body_timeout 3m; send_timeout 3m; client_header_buffer_size 1k; large_client_header_buffers 4 4k; sendfile on; tcp_nopush on; tcp_nodelay on; #keepalive_timeout 75 20; include gzip.conf; upstream localhost { #ip_hash #ip_hash; server localhost:18081; server localhost:18080; } server { listen 80; server_name localhost; location / { proxy_connect_timeout 3; proxy_send_timeout 30; proxy_read_timeout 30; proxy_pass http://localhost; } } }
对于上面关于ubuntu下Nginx配置和window下基本相同,区别在用的IO|络模型Qlinux下徏议用epollQ另外就是运行所用的用户和组Q?/p>
3、配|tomcat
请参考window下配|,完全相同?/p>
4、启动停止nginx
ubuntu下启动nginx与windowE有不同Q大致启动停止方法如下?/p>
#nginx目录执行 sbin/nginx 或通过-c 指定配置文g sbin/nginx -c usr/local/nginx8.20/conf/nginx/conf
niumd@niumd-laptop:/usr/local/nginx$ pwd /usr/local/nginx niumd@niumd-laptop:/usr/local/nginx$ sudo sbin/nginx -t the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok configuration file /usr/local/nginx/conf/nginx.conf test is successful niumd@niumd-laptop:/usr/local/nginx$ sudo sbin/nginx -v nginx version: nginx/0.8.20 niumd@niumd-laptop:/usr/local/nginx$ sudo sbin/nginx -V nginx version: nginx/0.8.20 built by gcc 4.4.3 (Ubuntu 4.4.3-4ubuntu5) configure arguments: --user=niumd --group=niumd --with-debug --with-http_sub_module niumd@niumd-laptop:/usr/local/nginx$ sudo sbin/nginx niumd@niumd-laptop:/usr/local/nginx$ ps -ef|grep nginx root 5158 1 0 22:32 ? 00:00:00 nginx: master process sbin/nginx niumd 5159 5158 0 22:32 ? 00:00:00 nginx: worker process niumd 5161 1577 0 22:32 pts/0 00:00:00 grep --color=auto nginx niumd@niumd-laptop:/usr/local/nginx$
我们通过ps -ef|grep nginx,看到如下l果:
注意Q在启动时linux提示一句警告【warn?#8230;…Q是因ؓ我们讄?#允许最大连接数 worker_connections 2048Q超qlinux默认1024的限制?/p>
停止Qkill -信号cd pid
nginx/logs目录下有个nginx。pid的文Ӟ此文件记录了每次q行的pidQ也可以通过ps命o查询?/p>
信号cd如下Q?/p>
信号cd | 描述 |
RERM.INT | 快速关?/td> |
HUP | qx重启Q加载配|?/td> |
USR1 | 重新加蝲日志 |
USER2 | qx升执行E序 |
WINCH | 从容关闭工作q程 |
QUIT | 从容关闭 |
参考资料:
http://www.oschina.net/bbs/thread/9301
oschina.net 生配置Q此|站采用java语言QnginxQtomcat服务器?/p>
张宴Q?lt;<实战Nginx>>
Ubuntu11.04一发布p不及待的鲜Q虚拟机环境不支持Unity 3D,试了下Unity 2DQ效果很炫。安装很单,l大家分享下?/p>
Terminal 键入如下命oQ?/span>
sudo add-apt-repository ppa:unity-2d-team/unity-2d-daily sudo apt-get update sudo apt-get install unity-2d
q三行命令就不解释,Ubuntu新手老手都应该清楚,l过10-20分钟的等待(取决你的|速和选择更新源)QUbuntu安装Unity-2d和依赖包Q安装完毕,log outQ这Ҏwindows强,xp都是提示重启的?/p>
上图Q登陆界面选择Unity 2D?/p>
上图U桌面Q?/span>
Linux提供了用h制例行Q务的命ocrontabQ常用于每间隔一定时间@环执行一些脚本,此处我们暂时UCؓQLinux定时d?/p>
#问下男hcrontab的用? man crontab crontab [ -u user ] { -l | -r [ -i ] | -e } 参数Q? -uQ只有root才可以执行此d -l Q查看crontab工作内容 -eQ编辑crontab工作内容 -r Q删除crontab工作内容
crontab应用场景举例Q定旉集远E服务器文g
手机发送短信,短信回以文本形式记录在交换机上Ş成短信话单,短信话单通常是达?MQ如果不?m则每五分钟Ş成一个话单(不同的交换机可能存在差异Q。如果对话单计费Q当焉要对短信话单q行采集Q然后进行后l计费工作。我们假N用shell或者python脚本采集Q暂且用shell举例Q假N集shell为acquisition.sh Q每间隔1分钟采集一ơ?/p>
命ol端执行Q?/p>
#标示~辑例行d crontab -e
然后我们会看到如下画面Q?/p>
已经q入VI ~辑模式Q在里面输入下面q行Q按下ESC->Q?>wq׃存了
#每间隔一分钟执行一ơ采集脚? */1 * * * * crontab /路径/acquisition.sh
crontab的格式ؓQ?/p>
分钟 时 ?nbsp; ??crontab 待执行命令或者脚?nbsp;
其中Q? 代表M旉都接受,如上例小时、日期、月份、周都ؓ*
Q代表分割时间段Q如分钟修改为:0,1,2,3,4,5 即Q何小时地1,2Q?#8230;…6分钟都执?/p>
- 代表旉D上面的每间隔一分钟可以标示?-59Q?/p>
/n 代表每间隔,分钟位置Q?/5标示每间隔五分钟
注意Q??之间只有一个空|
EOF
前段旉阅读mina源码Ӟ?/span>?span mce_style="font-family: monospace; white-space: pre;" style="font-family: monospace; white-space: pre; ">Selector实例化机制细节有点疑惑疑惑,主要?/span>SelectorProvider的细节实现方面?/span>
通常创徏一个SelectorQ通过静态openҎ创徏一个实例?/span>代码如下Q?/p>
观察JDK源码发现Select的创建通过SelectorProvider辅助cL完成Q?/p>
q一步观?span mce_style="white-space: pre;" style="white-space: pre; ">SelectorProvidercproviderҎ源码Q引用到csun.nio.ch.DefaultSelectorProviderQ开始的时候由于在JDK APISelector selector = Selector.open();
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
中没扑ֈ该类Q源码里面也没找刎ͼ比较疑惑如何创徏的?/span>今天在rt.jar扑ֈ了该c,q找到其Ҏ码?/p>
?span mce_style="white-space: pre;" style="white-space: pre; ">createҎҎ不同的操作系l构Z同的
/**
* Returns the default SelectorProvider.
*/
public static SelectorProvider create() {
PrivilegedAction pa = new GetPropertyAction("os.name");
String osname = (String) AccessController.doPrivileged(pa);
if ("SunOS".equals(osname)) {//1、如果SunOS
return new sun.nio.ch.DevPollSelectorProvider();
}
//2、Linux 内核>=2.6
// use EPollSelectorProvider for Linux kernels >= 2.6
if ("Linux".equals(osname)) {
pa = new GetPropertyAction("os.version");
String osversion = (String) AccessController
.doPrivileged(pa);
String[] vers = osversion.split("\\.", 0);
if (vers.length >= 2) {
try {
int major = Integer.parseInt(vers[0]);
int minor = Integer.parseInt(vers[1]);
if (major > 2 || (major == 2 && minor >= 6)) {
return new sun.nio.ch.EPollSelectorProvider();
}
} catch (NumberFormatException x) {
// format not recognized
}
}
}
return new sun.nio.ch.PollSelectorProvider();
}
通过epoll?/span>获取pȝ环境中的os.name、os.version观察下不同^台的l节?/span>
System.out.println(System.getProperty("os.name")); System.out.println(System.getProperty("os.version")); System.out.println(java.nio.channels.spi.SelectorProvider.provider());
通过在不同的操作pȝ上执行如下代码即可区分:
win XP sp3Q?/p>
ubuntu 11.04Q?/p>
JDK对linux内核2.6以上版本默认采用epollQLinux下性能得到一定幅度提升?/p>
参考:
DefaultSelectorProvider源码Q?/span>http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Platform/solaris/sun/nio/ch/DefaultSelectorProvider.java.htm