原文:10 steps to becoming the developer everyone wants
作者:Andrew C. Oliver
译文:开发者如何提升和推销自己
译者:wangguo
编写出伟大的代码,对于开发者来说已经到达一个层级了。 但是不断提升、适当的推销自己,可以令你得到更多。 以下是一些有效的途径,希望能够帮到你。
1. 写博客建立自己的博客,并至少每月发布一篇文章。 文章中要尽量有一些你真正的研究项目,这样会确保你看起来有些水平。
2. 参与开源投身开源领域,发布自己的开源项目。 别人可以通过你开放的源代码,来了解你的水平和方向。 并试着在你的项目中,用最简单的方式来解决复杂的问题。
3. 不要频繁跳槽,也不要举步不前不要每 6 个月就换一次工作,那样你什么都得不到。 但也不要停留在一个地方,做同样的工作 10 年,那样你会被模式化。 比如在IBM,你不能只会用 IBM 的方式编写 IBM 的栈。 我不会雇用在 IBM 或类似组织工作超过 1、2 年的,他们往往在面试中会给我留下印象,但他们往往无法通过编程测试。
4. 放眼未来,着手实际年轻的开发者往往倾向于新的技术,Ruby 是我最喜欢的语言,但是它的市场要远远小于 Java。 同时,也不要停留在一个技术上太久,因为未来的你会相当于现在的 COBOL 或 PowerBuilder 开发者。
5. 写你自己的文档我之前参加的项目中,很多次都被拉进项目经理级别的会议上,就是因为我写了一个他们能够看到和理解的文档或演示。
6. 简洁是灵魂项目中的事情或问题,试图给出最短、最简洁的答案或解决方案。 一旦你的回复过于冗长或复杂,往往会令人不知所云。
7. 在公共场合演讲试着在公共场合演讲或介绍一个项目。 研究一个主题,让自己至少有一个专家头衔。 如果你的演讲使别人感兴趣,那么效果会更好。
8. 成为现实主义者你应该知道一种以上的语言和知识,包括新的、热门的话题。 永远不要说“我不会编程,除非使用 Erlang”类似的话。 比如,你认为 NoSQL 可能更适合你的小项目,但是你的公司不会在一个小型的、一次性的系统上使用它,这种情况下,关系型数据库会更适合。
9. 擅于使用工具解决疑难杂症投入时间去学习一些其他人一般不知道的工具,这样可以让你比身边的人更有效率。
10. 保持谦虚这应该成为你的基本技能。 你可能做了某一个工作,让你获得了 Geek 称号,但下周该称号可能就会消失。 所以,不要傲娇,时刻保持一个谦虚的态度。 借用《搏击俱乐部》中 Tyler Durden 的一句话“You are not special”。
怎么样才算成功?看看你的左右,如果他们在和你做同样的工作,说明你还没有到达这一地步。 如果你坐在他们中间,他们都在看着你,告诉你你的演讲很棒,并很重视你的意见,恭喜你,你已经做到了。
但是,这些名誉和成功可能很短暂。 此外,比较讽刺的是,当你成为一个比较抢手的开发者时,你的编码时间也会越来越少。
总体来说,不是每个软件开发者都能成为优秀的那一个,但是有效的自我推销者会比安静的人更容易得到这个机会。 前提是,你也必须锻炼好自己的基本技能。
转载:请求/应答通道:IRequestChannel和IReplyChannel
在请求/应答消息交换模式里,消息的参与者都要发送和接收消息。发送者发送消息给接收者,然后等待回复;接收者接收请求消息,然后发送一个回复消息。
public interface IRequestChannel : IChannel, ICommunicationObject { EndpointAddress RemoteAddress { get; } Uri Via { get; } IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state); IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state); Message EndRequest(IAsyncResult result); Message Request(Message message); Message Request(Message message, TimeSpan timeout); }
Request方法接收Message类型的参数,然后返回Message类型的实例。
public interface IReplyChannel : IChannel, ICommunicationObject { EndpointAddress LocalAddress { get; } IAsyncResult BeginReceiveRequest(AsyncCallback callback, object state); IAsyncResult BeginReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state); IAsyncResult BeginTryReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state); IAsyncResult BeginWaitForRequest(TimeSpan timeout, AsyncCallback callback, object state); RequestContext EndReceiveRequest(IAsyncResult result); bool EndTryReceiveRequest(IAsyncResult result, out RequestContext context); bool EndWaitForRequest(IAsyncResult result); RequestContext ReceiveRequest(); RequestContext ReceiveRequest(TimeSpan timeout); bool TryReceiveRequest(TimeSpan timeout, out RequestContext context); bool WaitForRequest(TimeSpan timeout); }
IReplyChannel里没有直接返回一个Message实例的成员。相反,IReplyChannel接口支持通过RequestContext类型访问接收到的Message实例。
RequestContext类型包装了请求消息,而且提供了发送应答消息给发送者的方法。在RequestContext中,可以通过RequestMessage属性查看请求信息。RequestContext的Reply方法提供了发送应答消息的途径。
public abstract class RequestContext : IDisposable { [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] protected RequestContext(); public abstract Message RequestMessage { get; } public abstract void Abort(); public abstract IAsyncResult BeginReply(Message message, AsyncCallback callback, object state); public abstract IAsyncResult BeginReply(Message message, TimeSpan timeout, AsyncCallback callback, object state); public abstract void Close(); public abstract void Close(TimeSpan timeout); protected virtual void Dispose(bool disposing); public abstract void EndReply(IAsyncResult result); public abstract void Reply(Message message); public abstract void Reply(Message message, TimeSpan timeout); }
之前使用SSH三大经典框架的时候,写了一个简单的统计Action每个方法执行时间的功能类,代码如下:
import javax.servlet.http.HttpServletRequest; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.StopWatch; import org.apache.struts.actions.DispatchAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.aop.framework.ReflectiveMethodInvocation; /** * 统计方法执行时间的拦截器,采用Spring AOP方式实现. * * @author Kanine */ @Service("runTimeHandler") public class RunTimeHandler implements MethodInterceptor { private static Logger logger = LoggerFactory.getLogger("code.coolbaby"); @SuppressWarnings("unchecked") public Object invoke(MethodInvocation methodInvocation) throws Throwable { Object[] args = methodInvocation.getArguments(); String method = methodInvocation.getMethod().getName(); String action = methodInvocation.getMethod().getDeclaringClass().getName(); /** * 由于Spring使用了Cglib代理,导致不能直接取得目标类名,需作此转换 */ if (methodInvocation instanceof ReflectiveMethodInvocation) { Object proxy = ((ReflectiveMethodInvocation) methodInvocation).getProxy(); action = StringUtils.substringBefore(proxy.toString(), "@"); /** * 如使用了DispatchAction,将不能直接取得目标方法,需作此处理 */ if (proxy instanceof DispatchAction) { for (Object arg : args) { if (arg instanceof HttpServletRequest) method = ((HttpServletRequest) arg).getParameter("method"); } } } /** * 方法参数类型,转换成简单类型 */ Class[] params = methodInvocation.getMethod().getParameterTypes(); String[] simpleParams = new String[params.length]; for (int i = 0; i < params.length; i++) { simpleParams[i] = params[i].getSimpleName(); } String simpleMethod = method + "(" + StringUtils.join(simpleParams, ",") + ")"; logger.info("{} 开始执行[{}]方法", action, method); StopWatch clock = new StopWatch(); clock.start(); Object result = methodInvocation.proceed(); clock.stop(); logger.info("执行[{}]方法共消耗{}毫秒", simpleMethod, clock.getTime()); return result; } }在applicationcontext.xml加入以下配置:
<context:component-scan base-package="code.coolbaby"/> <aop:config> <aop:advisor advice-ref="runTimeHandler" pointcut="execution(* code.coolbaby.core.BaseDispatchAction.*(..))"/> </aop:config>就可以正确地以AOP的方式完成原本比较繁琐的功能了。
最近把框架升级到SS2H,顺便把Spring AOP实现由原来的Schema方式改为AspectJ方式,代码如下:
import org.apache.commons.lang.time.StopWatch; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * 统计方法执行时间的工具类,采用Spring AOP方式实现. * * @author Kanine */ @Aspect @Component public class RunTimeHandler { private static Logger logger = LoggerFactory.getLogger("code.coolbaby"); @Pointcut("execution(public String *()) && !execution(public String toString())" + " && target(code.coolbaby.core.CRUDActionSupport)") void timer() { } @Around("timer()") public Object time(ProceedingJoinPoint joinPoint) throws Throwable { String clazz = joinPoint.getTarget().getClass().getSimpleName(); String method = joinPoint.getSignature().getName(); StopWatch clock = new StopWatch(); clock.start(); Object result = joinPoint.proceed(); clock.stop(); String[] params = new String[] { clazz, method, clock.getTime() + "" }; logger.info("[{}]执行[{}]方法共消耗[{}]毫秒", params); return result; } }struts.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <struts> <constant name="struts.convention.default.parent.package" value="crud-default" /> <constant name="struts.convention.package.locators" value="web" /> <constant name="struts.convention.result.path" value="/" /> <!-- 用于CRUD Action的parent package --> <package name="crud-default" extends="convention-default"> <!-- 基于paramsPrepareParamsStack, 增加store interceptor保证actionMessage在redirect后不会丢失 --> <interceptors> <interceptor-stack name="crudStack"> <interceptor-ref name="store"> <param name="operationMode">AUTOMATIC</param> </interceptor-ref> <interceptor-ref name="paramsPrepareParamsStack" /> </interceptor-stack> </interceptors> <default-interceptor-ref name="crudStack" /> </package> <!-- 使用Convention插件,实现约定大于配置的零配置文件风格. 特殊的Result路径在Action类中使用@Result设定. --> </struts>在applicationcontext.xml加入以下配置:
<context:component-scan base-package="code.coolbaby"/> <aop:aspectj-autoproxy proxy-target-class="true"/>理论上讲,AOP的功能应该可以正确实现了,实际则不然,以UserAction举例说明,
package code.coolbaby.basal.web.security; //限于篇幅,省略import语句 /** * 用户管理Action. * * 使用Struts2 convention-plugin Annotation定义Action参数. * * @author Kanine */ @SuppressWarnings("serial") public class UserAction extends CRUDActionSupport<User> { @Autowired private UserManager userManager; private User entity; private Long id; private Page<User> page = new Page<User>(5);//每页5条记录 public User getModel() { return entity; } @Override protected void prepareModel() throws Exception { if (id != null) { entity = userManager.get(id); } else { entity = new User(); } } public void setId(Long id) { this.id = id; } public Page<User> getPage() { return page; } @Override public String list() throws Exception { HttpServletRequest request = Struts2Utils.getRequest(); List<PropertyFilter> filters = HibernateWebUtils.buildPropertyFilters(request); page = userManager.search(page, filters); return SUCCESS; } //限于篇幅,省略其他的代码 }
测试的结果是,userManager注入失败,在执行list()方法的时候报错,NullPointer!
接下来反复Debug后,发现个很奇怪的现象,在AOP执行的RunTimeHandler内部,Debug视图中methodInvocation的proxy的userManager属性是正确地注入的,而其target中的userManager却为null,当跳转到list()时,userManager亦为null,这是怎么回事呢?!
变换了几种测试方法,发现如果是对service层的EntityManager(里面有使用了@Autowired的entityDAO)切面,不会出现NPE,Debug视图中proxy的entityDAO为null而target中的entityDAO正确注入;如果去掉AOP,UserAction运行正常,不会发生userManager注入失败的情况;但是该AOP在Struts1的环境下却执行正确,也没有发生注入失败的问题!
尝试了几种解决方案后,发现如果加入userManager的setter方法,即便不加@Autowired也不会有NPE,功能运转正常,但是理论上置于field上的@Autowired已经无需sette