-
说说ROLLER的PageServlet类
2014-02-11 16:49:36发现网上roller可参考的...roller已经集成权限管理、菜单管理等模块,如果在其上做二次开发,完完全全是实现自己的业务功能模块即可,另外一个重要的原因是可以通过优秀的开源软件学习别人优秀的设计理念和经验。发现网上roller可参考的东西很少,都是些如何安装部署的文章,无疑对我们这些有点小小水平的帮助不大。决定边学边写,权当笔记吧,不对的地方各位大侠指正,谢谢。为什么要学习roller,其实我是想在上面做二次开发。roller已经集成权限管理、菜单管理等模块,如果在其上做二次开发,完完全全是实现自己的业务功能模块即可,另外一个重要的原因是可以通过优秀的开源软件学习别人优秀的设计理念和经验。
roller是一个多用户博客管理系统,分前台博客页面展现和后台博客管理页面。前者主要是通过velocity来展现,后台通过jsp+tiles。
今天要说的是前台博客页面相关的内容。在这里这个类PageServlet是不得不说的,通过名字我们大概可以了解此类是处理页面的,具体做些什么工作,下面再说。
先来说说此servlet是如何被触发的,在web.xml中它的url映射是如下配置的:
<servlet> <servlet-name>PageServlet</servlet-name> <servlet-class>org.apache.roller.weblogger.ui.rendering.servlets.PageServlet</servlet-class> <load-on-startup>5</load-on-startup> </servlet> <servlet-mapping> <servlet-name>PageServlet</servlet-name> <url-pattern>/roller-ui/rendering/page/*</url-pattern> </servlet-mapping>
当url符合/roller-ui/rendering/page/*时,此servlet的doGet方法就被执行。什么时候、哪些连接才会符合此url呢。后台管理页面相关的连接是有的,但这里说点曲折一点的东西!
经过阅读源代码和观察,当直接进入某个博客时此servlet也比执行。但是博客的url一般都不包含/roller-ui/rendering/page/*这样的字符,即使是这样,这博客地址也太长了点吧。roller是怎么样做的呢?当我们输入一个博客地址,比如:
http://localhost:8080/roller/abc
我们想直接进入abc这个博客页面。当用户在浏览器地址栏里输入这个博客地址时,roller有一个filter优先被触发,见如下web.xml配置:
<filter> <filter-name>RequestMappingFilter</filter-name> <filter-class>org.apache.roller.weblogger.ui.rendering.filters.RequestMappingFilter</filter-class> </filter> <filter-mapping> <filter-name>RequestMappingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
这个 RequestMappingFilter主要的工作是把请求映射到真正能处理它的地方,比如前面说的进入abc这个博客页面。页面在哪里?对不起,这里没有一个jsp或一个controller对应一个url的说法。所有的都到PageServlet去啦。为什么呢?
原来在RequestMappingFilter中,会遍历所有的映射处理器,具体来说就是交给类WeblogRequestMapper判断,如果恰好被命中,请求的uri就被修改并forward。否则请求就交个下一个filter处理了。
WeblogRequestMapper修改uri,使之符合PageServlet的映射要求,请求就交给PageServlet处理了。这时,请求才真正被传给PageServlet处理。
前面我们了解的PageServlet被触发的过程,现在来了解它做写什么工作。这个类的doGet方法实在是长,而且很多if-else判断(我最怕这样的代码),看得人有点晕晕的,至今我也不是完全明白这个方法的所有处理流程。先挑些能看懂的讲吧,权当笔记。
这个类大概的流程是将用户要打开的博客页面从缓存中取出来展现,如果缓存中没有就去读取博客内容相关的信息,通过velocity渲染成页面并把页面整个缓存起来备用。
当然很多细节就不说了,比如判断是否是站点首页,是否缓存过期等等,还有很多,大家自己去看吧。我说的也不一定对。
roller是如何展现一个博客页面的呢?又如何缓存的呢?菜单又是如何管理?
有空的时候再拿出来给大家交流。
-
说说Promise~
2020-07-26 12:46:35ES6中有一个非常重要和好用的特性就是Promise 那么Promise到底是做什么的呢? 简单来说,Promise就是异步编程的一种解决方案 拿上图做讲解: 从语法上来讲,Promise实际上是一个对象,可以获取异步操作的消息 ...
ES6中有一个非常重要和好用的特性就是Promise
那么Promise到底是做什么的呢?基本概念
简单来说,Promise就是异步编程的一种解决方案,其实就是一个构造函数。自己身上有resolve、reject、all这几个方法。原型链上有then、catch等方法。
两大特点
- 对象的状态不受外界影响。Promise对象代表着一个异步操作,它有三种状态:pending(进行中)、fulfilled(已完成)、rejected(已失败)。
只有异步操作的结果,可以决定当前是哪一种状态,任何操作都无法改变这个状态。
2.一旦状态改变,就不会再变,任何时候都可以得到这个状态。
Promise对象的状态改变,只有两种结果:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会在改变了,会一直保持这个结果。
基本使用
先new 一个最基本的Promise对象
let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log('执行完成Promise'); resolve('要返回的任何数据:接口数据等等'); },2000) })
刷新页面会打印如下结果
它的执行过程是:执行了一个异步操作(setTimeout),2秒之后,输出图中结果,并且调用了resolve方法。
注意:这里我只是new了一个对象,并没有调用它,里面传进去的函数就已经执行了。所以一般情况下我们用Promise的时候都是包在一个函数中,在需要的时候去运行这个函数。
<div onclick='promiseClick()'>开始异步请求</div> const promiseClick = () => { console.log('点击方法被执行') let p = new Promise((resolve, reject) => { setTimeout(() => { console.log('执行完成Promise'); resolve('要返回的任何数据:接口数据等等'); }, 2000) }) return p }
此时刷新页面,控制台没有打印任何信息,当我们点击div之后,打印台先后打出
这样我们只有在调用函数的时候才会被执行,那resolve是什么?
如上图我们在包装好的函最后,return 出一个Promise对象§,也就是说,执行这个函数我们得到了一个Promise对象。resolve/then
接下来就可以使用Promise对象身上的then方法了。
promiseClick().then(item=>{ console.log(item); })
第一行代码直接出现,2秒之后打印Promise对象里面的,当我们多次点击之后,resolve执行的代码依旧只打印一次。
验证了Promise对象的状态一旦发生,就不会在改变。
这个时候你会发现一件事情,then这不是和平时的回调函数一样嘛,在promiseClick这个异步函数执行完成之后被执行。那么Promise的精髓就来了,需要多层回调的时候,Promise就能简化层层回调的写法。
function run1() { let p = new Promise((resolve, reject) => { setTimeout(() => { console.log('异步任务1完成'); resolve('随便的数据1') }, 2000) }) return p } function run2() { let p = new Promise((resolve, reject) => { setTimeout(() => { console.log('异步任务2完成'); resolve('随便的数据2') }, 2000) }) return p } promiseClick() .then(item => { console.log(item); return run1(); }) .then(item => { console.log(item); return run2() }) .then(item=>{ console.log(item); })
在控制台打印的结果如下
间隔2秒打印一个红框里面的内容,这样就能够按照顺序间隔2秒输出一个异步回调中的内容。reject/catch
以上是对promise的resolve用法进行了解释,相当于resolve是对promise成功时候的回调,它把promise的状态修改为fullfiled,那么,
reject就是失败的时候的回调,他把promise的状态修改为rejected,这样我们在catch中就能捕捉到,然后执行“失败”情况的回调。
new Promise((resolve, reject) => { setTimeout((data) => { //成功的时候调用resolve resolve('hello World'); //失败的时候执行reject reject('error message'); }) }).then((data)=>{ console.log(data) }).catch((err)=>{ console.log(err) })
all
与then同级的一个方法,该方法提供了并行执行异步操作的能力,
在所有异步执行并执行结果都是成功的时候进行回调。
function run1() { let p = new Promise((resolve, reject) => { setTimeout(() => { let nums = Math.ceil(Math.random() * 10) console.log('随机生成的数字:' + nums); if (nums <= 5) { resolve(nums) } else { reject('数字太大了') } }, 2000) }) return p; } function run2() { let p = new Promise((resolve, reject) => { setTimeout(() => { let nums = Math.ceil(Math.random() * 10) console.log('随机生成的数字:' + nums); if (nums <= 5) { resolve(nums) } else { reject('数字太大了') } }, 2000) }) return p; } function run3() { let p = new Promise((resolve, reject) => { setTimeout(() => { let nums = Math.ceil(Math.random() * 20) console.log('随机生成的数字:' + nums); if (nums <= 10) { resolve(nums) } else { reject('数字太大了') } }, 2000) }) return p; } Promise .all([run1(),run2(),run3()]) .then(result=>{ console.log(result); })
race
all 是等所有异步操作执行完毕且成功之后在执行then方法,race正好是相反的,
谁先执行完毕谁就先执行回调。先执行完的不管是进行了race的成功回调还是失败回调,其余的将不会再进入race的任何回调。
function run1() { let p = new Promise((resolve, reject) => { setTimeout(function () { let num = Math.ceil(Math.random() * 20); //生成1-10的随机数 console.log('2s随机数生成的值:', num) if (num <= 10) { resolve(num); } else { reject('2s数字大于10了即将执行失败回调'); } }, 2000); }) return p } function run2() { let p = new Promise((resolve, reject) => { setTimeout(function () { let num = Math.ceil(Math.random() * 20); //生成1-10的随机数 console.log('3s随机数生成的值:', num) if (num <= 10) { resolve(num); } else { reject('3s数字大于10了即将执行失败回调'); } }, 3000); }) return p } function run3() { let p = new Promise((resolve, reject) => { setTimeout(function () { let num = Math.ceil(Math.random() * 20); //生成1-10的随机数 console.log('4s随机数生成的值:', num) if (num <= 10) { resolve(num); } else { reject('4s数字太于10了即将执行失败回调'); } }, 4000); }) return p } Promise .race([run1(), run2(), run3()]) .then(function (results) { console.log('成功', results); }, function (reason) { console.log('失败', reason); });
例子
在一个请求在10s内请求成功的话就走then方法,如果10s内没有请求成功的话进入reject回调执行另一个操作。
function requestData(){ let p = new Promise((resolve,reject)=>{ //请求的数据 resolve(res); }) return p; } function TimeOut(){ let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('请求超时'); },10000) }) return p; } Promise.race([requestData(),TimeOut]) .then(res=>{ console.log(res); }) .catch(err=>{ console.log(err); })
- 对象的状态不受外界影响。Promise对象代表着一个异步操作,它有三种状态:pending(进行中)、fulfilled(已完成)、rejected(已失败)。
-
自定义一个日历
2016-05-19 17:31:34一个偶然的机会,现在的项目需要日历,我再也无法容忍了,因为日历样式很复杂,所以本人决定自己写一个日历。下面来说说日历最重要的东西。 首先,一年12个月中有哪些是大月哪些是小月。还有个2月痕特别。处理方法...刚开始做android的时候,遇到日历根本无法下手,加上经验不足,花了很长时间去修改别人写的日历。一个偶然的机会,现在的项目需要日历,我再也无法容忍了,因为日历样式很复杂,所以本人决定自己写一个日历。下面来说说日历最重要的东西。
首先,一年12个月中有哪些是大月哪些是小月。还有个2月痕特别。处理方法如下:
private void setSmallMonth(List<String> list) { for(int i=0;i<30;i++){ list.add((i+1)+""); } } private void setBigMonth(List<String> list) { for(int i=0;i<31;i++){ list.add((i+1)+""); } } private void set2Month(List<String> list,int currentYear) { if(currentYear%4==0){ for(int i=0;i<29;i++){ list.add((i+1)+""); } }else{ for(int i=0;i<28;i++){ list.add((i+1)+""); } } } 其次,知道当前月的第一天是周几:
Calendar instance = Calendar.getInstance();
instance.set(Calendar.DAY_OF_MONTH, 1); dayOfWeekInMonth = instance.get(Calendar.DAY_OF_WEEK); 再来就是当前日期了:
currentYear = instance.get(Calendar.YEAR); currentShowYear=currentYear; currentMonth = instance.get(Calendar.MONTH); currentMonth=currentMonth+1; realCurrentMonth=currentMonth; dayOfMonth = instance.get(Calendar.DAY_OF_MONTH);
有了这些数据,基本就可以自定义一个简单的日历了 。
我这里是用的GrideView布局,根据每月天数,GrideView的格子数是固定的42个,根据当月第一天是周几可以判定从日期第几个格子开始
放置。根据当前日期我们可以把当前日期设置为特定的颜色等等
我这里是把日历封装成了一个dialogpackage com.achievo.haoqiu.widget.view; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.os.Handler; import android.support.annotation.NonNull; import android.view.Gravity; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.GridView; import android.widget.TextView; import com.achievo.haoqiu.R; import com.achievo.haoqiu.activity.adapter.BottomDateCalendarAdapter; import com.achievo.haoqiu.util.ScreenUtils; import java.util.ArrayList; import java.util.Calendar; import java.util.List; /** * Created by 石志华 on 2016/3/3. */ public class BottomDateDialog extends Dialog{ public BottomDateDialog(Context context) { super(context); } public static class Builder implements View.OnClickListener, AdapterView.OnItemClickListener { private Activity context; private BottomDialog dialog; private int currentMonth;//记录当前显示的是哪个月份 private BottomDateCalendarAdapter calendarAdapter; private int dayOfMonth; private int dayOfWeekInMonth; private TextView tv_title; private List<String> list; private int currentYear;//当前真实年份 private int realCurrentMonth;//记录当前真实月份 private boolean isBeforeCurrentMonth=true; private boolean isCurrentMonth=true; private OnDialogClick onDialogClick; private String selectedDate=""; private int currentShowYear; public Builder(Activity context,String selectedDate) { this.context = context; this.selectedDate=selectedDate; } public BottomDialog create() { BottomDialog.Builder builder = new BottomDialog.Builder(context); View inflate = View.inflate(context, R.layout.bottom_date_dialog, null); View tv_cancel = inflate.findViewById(R.id.tv_cancel); View iv_left = inflate.findViewById(R.id.iv_left); View iv_right = inflate.findViewById(R.id.iv_right); tv_title = (TextView) inflate.findViewById(R.id.tv_title); GridView grideView = (GridView) inflate.findViewById(R.id.gridView); grideView.setOnItemClickListener(this); //获取当前日期 Calendar instance = Calendar.getInstance(); currentYear = instance.get(Calendar.YEAR); currentShowYear=currentYear; currentMonth = instance.get(Calendar.MONTH); currentMonth=currentMonth+1; realCurrentMonth=currentMonth; dayOfMonth = instance.get(Calendar.DAY_OF_MONTH); //设置选中日期 if(selectedDate==null||"".equals(selectedDate)){ isCurrentMonth=true; }else{ String[] split = selectedDate.split("-"); if(currentYear==Integer.valueOf(split[0]) && currentMonth==Integer.valueOf(split[1])){ isCurrentMonth=true; }else{ isCurrentMonth=false; } currentYear=Integer.valueOf(split[0]); currentMonth = Integer.valueOf(split[1]); instance.set(Calendar.YEAR,Integer.valueOf(split[0])); instance.set(Calendar.MONTH,Integer.valueOf(split[1])-1); } //获取当月第一天是周几 instance.set(Calendar.DAY_OF_MONTH, 1); dayOfWeekInMonth = instance.get(Calendar.DAY_OF_WEEK); tv_title.setText(currentYear + context.getResources().getString(R.string.text_year) + currentMonth + context.getResources().getString(R.string.text_month)); list = new ArrayList<>(); setDays(currentYear,currentMonth); calendarAdapter = new BottomDateCalendarAdapter(context,list,dayOfWeekInMonth,dayOfMonth,isBeforeCurrentMonth,isCurrentMonth); String[] split = selectedDate.split("-"); if(Integer.valueOf(split[0])==currentYear && Integer.valueOf(split[1])==currentMonth){ calendarAdapter.setSelectedDay(Integer.valueOf(split[2])); }else{ calendarAdapter.setSelectedDay(0); } grideView.setAdapter(calendarAdapter); tv_cancel.setOnClickListener(this); iv_left.setOnClickListener(this); iv_right.setOnClickListener(this); dialog = builder.create(); Window window = dialog.getWindow(); WindowManager.LayoutParams lp = dialog.getWindow().getAttributes(); lp.width = ScreenUtils.getScreenWidth(context); dialog.getWindow().setAttributes(lp); dialog.getWindow().setWindowAnimations(R.style.AnimationPreview); window.setGravity(Gravity.BOTTOM); dialog.setContentView(inflate); return dialog; } public void setOnDialogClick(OnDialogClick onDialogClick){ this.onDialogClick=onDialogClick; } @NonNull private void setDays(int currentYear,int currentMonth) { list.clear(); if(currentMonth ==1|| currentMonth ==3|| currentMonth ==5|| currentMonth ==7|| currentMonth ==8|| currentMonth ==10|| currentMonth ==12){ setBigMonth(list); }else if(currentMonth ==2){ set2Month(list, currentYear); }else{ setSmallMonth(list); } } private void setSmallMonth(List<String> list) { for(int i=0;i<30;i++){ list.add((i+1)+""); } } private void setBigMonth(List<String> list) { for(int i=0;i<31;i++){ list.add((i+1)+""); } } private void set2Month(List<String> list,int currentYear) { if(currentYear%4==0){ for(int i=0;i<29;i++){ list.add((i+1)+""); } }else{ for(int i=0;i<28;i++){ list.add((i+1)+""); } } } @Override public void onClick(View v) { int year=0; switch (v.getId()){ case R.id.tv_cancel: dialog.dismiss(); break; case R.id.iv_left: year = setDate(-1,currentShowYear); setDays(year,currentMonth); tv_title.setText(year + context.getResources().getString(R.string.text_year) + currentMonth + context.getResources().getString(R.string.text_month)); calendarAdapter.notifyDataSetChanged(); break; case R.id.iv_right: year = setDate(1,currentShowYear); setDays(year,currentMonth); tv_title.setText(year + context.getResources().getString(R.string.text_year) + currentMonth + context.getResources().getString(R.string.text_month)); calendarAdapter.notifyDataSetChanged(); break; } } public int setDate(int addOrDown,int year){ Calendar instance = Calendar.getInstance(); currentMonth = currentMonth+addOrDown; if(currentMonth>12){ instance.set(Calendar.YEAR,year+1); instance.set(Calendar.MONTH,0); currentMonth=1; }else if(currentMonth<1){ instance.set(Calendar.YEAR,year-1); instance.set(Calendar.MONTH,11); currentMonth=12; }else{ instance.set(Calendar.YEAR,year); instance.set(Calendar.MONTH,currentMonth-1); } //当前显示年份在真是年份之后 if(instance.get(Calendar.YEAR)>currentYear){ isBeforeCurrentMonth=false; isCurrentMonth=false; //当前显示年份就是真实年份 }else if(instance.get(Calendar.YEAR)==currentYear){ if(currentMonth>realCurrentMonth){ isBeforeCurrentMonth=false; isCurrentMonth=false; }else if(currentMonth==realCurrentMonth){ isBeforeCurrentMonth=true; isCurrentMonth=true; }else{ isBeforeCurrentMonth=true; isCurrentMonth=false; } //当前显示年份在真实年份之前 }else{ isBeforeCurrentMonth=true; isCurrentMonth=false; } calendarAdapter.setIsCurrentMonth(isCurrentMonth); calendarAdapter.setIsBeforeCurrentMonth(isBeforeCurrentMonth); //获取当月第一天周几 instance.set(Calendar.DAY_OF_MONTH, 1); dayOfWeekInMonth=instance.get(Calendar.DAY_OF_WEEK); calendarAdapter.setDayOfWeek(dayOfWeekInMonth); String[] split = selectedDate.split("-"); if(Integer.valueOf(split[0])==instance.get(Calendar.YEAR) && Integer.valueOf(split[1])==currentMonth){ calendarAdapter.setSelectedDay(Integer.valueOf(split[2])); }else{ calendarAdapter.setSelectedDay(0); } currentShowYear=instance.get(Calendar.YEAR); return currentShowYear; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { TextView tv_day = (TextView) view.findViewById(R.id.tv_day); if((tv_day.getCurrentTextColor()==context.getResources().getColor(R.color.font_333333)||tv_day.getCurrentTextColor()==context.getResources().getColor(R.color.white))|| tv_day.getCurrentTextColor()==context.getResources().getColor(R.color.blue_55c0ea)){ if(position>=dayOfWeekInMonth-1){ String trim = tv_title.getText().toString().trim(); if(trim.length()==7){//单月 //月份和日必须是两位数 if(list.get(position-(dayOfWeekInMonth-1)).length()==1){ selectedDate=trim.substring(0,4)+"-0"+trim.substring(5,6)+"-0"+list.get(position-(dayOfWeekInMonth-1)); }else{ selectedDate=trim.substring(0,4)+"-0"+trim.substring(5,6)+"-"+list.get(position-(dayOfWeekInMonth-1)); } }else if(trim.length()==8){//双月 if(list.get(position - (dayOfWeekInMonth-1)).length()==1){ selectedDate=trim.substring(0,4)+"-"+trim.substring(5,7)+"-0"+list.get(position - (dayOfWeekInMonth-1)); }else{ selectedDate=trim.substring(0,4)+"-"+trim.substring(5,7)+"-"+list.get(position - (dayOfWeekInMonth-1)); } } calendarAdapter.setSelectedDay(Integer.valueOf(list.get(position - (dayOfWeekInMonth - 1)))); calendarAdapter.notifyDataSetChanged(); if(onDialogClick!=null){ onDialogClick.onClick(selectedDate); } new Handler().postDelayed(new Runnable() { @Override public void run() { dialog.dismiss(); } },300); }else{ return; } } } public interface OnDialogClick{ void onClick(String date); } } } 示例下载地址 http://download.csdn.net/detail/qq_26491053/9525462
-
从软件工程师到IT猎头:说说跳槽那点事
2010-03-02 08:46:00说说跳槽那点事对于高科技行业的工程师来说,技术的连续性是很重要的,中国的大学生在就业时并没有多少选择的余地,首先是毕业生找工作的时候,虽然头脑里有些大体的概念,但具体到了公司去做什么决定权不在他们手里...从软件工程师到IT猎头:说说跳槽那点事
对于高科技行业的工程师来说,技术的连续性是很重要的,中国的大学生在就业时并没有多少选择的余地,首先是毕业生找工作的时候,虽然头脑里有些大体的概念,但具体到了公司去做什么决定权不在他们手里,所以这么一干两三年,即使想在细分的小行业里去转,也不容易,比如你喜欢做一些更底层的开发,做软硬结合的那种开发,结果找到了一个通信公司被分去做网管,你做两三年之后,也只能找java相关的一些工作了,这种情况其实也挺难弄的,所以如果要想做自己更擅长的并以此发展下去就应该坚定信念,在毕业后的前三年内找准方向,否则三年之后,面临的矛盾和挣扎就会更多,当然也有一部分是属于干一行爱一行的,这也是华为企业文化的一句经典的话,这类人容易顺从,其实想通了,在技术这个行业,只要搞精了,不管做什么,你的未来肯定是越来越增值的,就像我们毕业时候都觉得测试在IT行业是被轻视的行业,其实后来你会发现,一个资深的测试工程师了解的东西更全面,而一个资深的测试工程师薪水也不会比同级别的开发工程师低。
一旦选定了技术的起始点,经验的积累就是最重要的了,像我觉得传统的技术行业,以及高科技行业里的大部分工程师,主要具备的素质还是基本功扎实+经验的积累,另外再加上可能具备的悟性,当然,悟性并非每个人都具备,其实中国的大学或者研究生毕业的人应该学习能力都是很强,但到一个公司的学习则要把自学放在第二位了,更重要的是要像有经验的人请教,学过一遍就要记录下来,就成了你自己的了。任何时候,主动都比被动的学习要对你有利,当然,悟性也十分重要,这个并非是可以培养的,因为基本功和经验,是每个人都可以通过努力得到的,而悟性则有点天生的能力,悟性可以理解为一点就透,再多一点,拥有好的悟性可以帮助你走的更远。其实对人情世故的把握也是非常重要的一个因素,这是职业发展中的软性因素,让你的上级看到你良好的沟通能力和工作激情,即使是有点刻意的表现,但也是有利的。
即使你跳槽比较频繁,也要尽量选择差不多的技术方向,但还是在选好一个公司的情况下,积累个3,4年,再做打算,跳槽一般可以获得薪水的增长,但职位的越级是比较难的,尤其是在同类型的公司领域,所以要想换一个发展方向,比如从开发换到系统,或者换到产品经理的方向,最好是在同一个公司内发展,发展的差不多,再进行跳槽的选择。
找一份好的工作就像找对象,也是要看缘分的,也少不了天时地利人和的配合,一份好的职业经历写在简历上是一个好的招牌,对于高科技的从业人员来说,可供选择的公司还是比较有限的,职业选择的道路也是较为单一的,尽管如此,我们还是希望在比较单一的道路上找到自己最合适的位置,并获得最大价值的体现。
职位信息的获取是很重要的,每个公司在不同的时候都会有不同的需求,因为公司人才都是在流动着的,对于每天在工作的人来说,如何来获取信息呢。
最传统的招聘方式就是网站招聘了,这个我不用多说了,搜索,投简历,但这里给大家提示一下,要注意的两点,1. 投简历不要直接在招聘网站上投,要直接把简历发到HR邮箱,并且用比较醒目的标题,比如硕士5年嵌入式开发应聘****岗位 2.投简历最好比较晚的夜里投,因为这样第二天你的邮件一定排在HR邮箱的第一个或者前几名,如果你实在要早睡,那就用邮箱的定时发送功能(呵呵,这个告诉大家,估计这个规则又要变了),仅供参考吧。
第二当然是最省心的方式,交给猎头来打理,认识行业内一到两个比较专业的猎头,保持长期联系,当然他们可能会整天骚扰你,给你提供这个信息那个信息,让你介绍这个那个朋友,但你有你的原则就行了,猎头也会适可而止。当然猎头搜寻人才还是会漏掉很,不一定能找到你,而且猎头提供的职位可能也未必就有适合你的,一个专业的猎头是应该把公司和职位的客观情况如实陈述给候选人,而不是遮遮掩掩,只说好的,其实讯息发达,很多东西在网上唾手可得,我们可以从多方面来了解这职位是否符合我们当下的选择。
但在猎头那边,还是会淘汰一些候选人,他们毕竟主要是面向客户的,未免对候选人是有点苛刻的,但有些猎头也未必判断的就准确,所以也会有漏网之鱼,猎头的工作其实很被动的,他们能做的努力是受限于客户的,当然一个好的猎头是不能首先给候选人希望,而最后却连简历都没发出去,最后连个反馈都没有。
第三是通过内部推荐,这个站在公司省钱的角度,他们最先选择的还是内部推荐,既能保证质量,也能节省下一大笔招聘费用,很多人也采用这个方式来找到了工作。
通过猎头的方式会比较透明一些,第三种也稍微好点,内部推荐人还是能拿到一些基本的反馈,第一种广播式的方式最不透明,但比较方便,可是在最重要的一个薪资的阶段,猎头的方式会更加有利一些,毕竟是通过第三方。
当然对公司的判断,一是可以通过介绍人或者网站论坛的一些评述,另外一点在你面试过程中,如果面试官给你提问的时间,你一定要提出自己关心的问题,但还没到谈薪水的时候,一定不要表现出急功近利,权力欲,钱等等这些看起来钻营的事情,会令对方反感,合理的获取信息是最重要的。
简历的问题我不用多说了,之前我也发过一个怎么写简历的帖子:从IT工程师到列头续:如何写简历。好的简历当然很重要。另外提示一点,如果你刚毕业后跳槽太过频繁,(比如半年就换一次的)但是做的工作又差不多类似,就可以合并成一个公司,因为招聘那一方,是不太关注你最初几个公司的,一部分公司做背景调查,也只是最近两个公司的一些调查。这样简历会好看一些。
面试过程中的硬件条件我不说了,那是候选人个人的能力和积累,但软性因素来说,我觉得在面试过程中要注意的:表达上的流畅,逻辑连贯,展示出好学态度,和对职业发展的明确性是面试官都愿意看到的,大多数的时候不要太强调自己个性,除非一些企业文化比较特殊的公司。说白了一点叫“吹的得体",尤其面试过程中眼神里传达的诚意,和那种机变的个性,比如问到个你不会的问题,如何对应?这都是你需要思考的问题。还有应对外企的英文面试以及英文电话面试,比如说你基础不错,但口语环境不好,开口难,就可以突击那么几天,但要有针对性的,因为面试不太可能问你专业以及职业发展以外的问题,他不会问你跟你父母的感情如何,法国牛排的英文怎么拼,所以你围绕你的项目经历,工作经历,个人发展这三个方面来准备,尤其是提前把简历上写的或者没有写出来的东西 用英文记录下来,拼命背几天,刺激一下,存在脑子里,若是面试给你发挥的时候,就把这些存放的释放出来,如果实在有的老外口音听不懂,你表示让他说慢点或者重复一遍或者在纸上写出来,都是可以实行的。
当然这些技巧只能适用于某些职位,提高自身的能力才是硬道理。
但其实能通过关卡,进入了一个好的环境,再学习就容易了,才能充分成长。
下面说一下最微妙的一个环节,就是薪水这个环节了,其实这不是一个两个人的问题,这是个辨证的问题,就是应聘者和招聘公司是一对矛盾共同体,每个公司都想以相对便宜的价格找到质量好的工程师和其他职员,因为人力成本是一个公司最大的一个开销了,所以在HR这一方,总是要尽可能的压低候选者的薪水,而站在候选人的角度呢,则肯定想多收入一些,所以在做猎头的时候,让候选人报期望薪资的时候,他们总是有点迟疑,问那这个职位能开多少?其实在招聘前,HR已经将这个职位的费用预算设定在一个范围内了,当然一个是在行业内的一个标准,一个是根据公司的情况。候选人怕报少了吃亏,公司则想低点价格找到最合适的人,所以这里面是要寻找一个平衡的,薪水是HR根据你面试的状况以及一系列公司标准来的,她们也挺迟疑的,一方面招个人不容易,花了钱,另一方面,她又想为公司省钱,提高自己的一些业绩,站在候选人这一方,就要看你自己设定的底线,以及你对这个职位的渴求度,还要结合你面试中的自我感觉来开薪水,如果你现在已经失业,急需一份工作,而面试的感觉又不甚理想,但你还想要这份工作,你可以在最后被询问薪水的时候要求低点,10-20%的涨幅即可,不过这时候,HR估计给你平薪可能你也去了;如果你觉得目前工作还可以,觉得这个机会就差不多稍好点,也并不是特别强的欲望,注重看薪水的涨幅,而且面试你又感觉极好,就可以开个30%左右的涨幅,如果公司这个职位又恰巧比较紧缺,你谈判的筹码就更大一些,30%-50%涨幅都是可能办到的,更厉害的还有double的,这种比较少了,不建议去冒险。其他的情况其实候选人可以依此类推。
当然这就跟考大学估分一样,还是有一定偏差的,薪水不要说死,HR那边大都会压一些薪水的,不会满足你的上限,公司跟公司,行业跟行业都是有差异的。这就要看个人的明辨了。
另外还要注意薪水的结构,其实现在基本都是论年薪制,年薪制当然是 固定薪水那块占的比例越大越好,所以很多人觉得,这薪水里没有bonus啊,其实bonus比例较少的薪水固定反而是好事,因为bonus一年发一次而且会有浮动,同样的pay,当然是固定越多越实惠,同时根据基数计算的公积金,保险什么的也会相应提高,另外有补充公积金,补贴这些非现金形式的福利,也是你在权衡薪水方面的一个重要因素,当然有些职位,比如销售类的职位,有一部分收入是根据业绩来决定的。
转载于:http://topic.csdn.net/u/20100227/18/1a3426d0-84dc-446b-86bf-05eeeaa52c44.html?40061
-
国旗下的讲话:小学生要养成的10个习惯.doc
2021-01-15 14:21:08今天陆老师要跟同学们一起来说说,作为小学生,我们要养成的可以决定一生10个良好习惯: 1、说了就要做 (诚实守信) 诚实守信是人的立身之本,是全部道德的基础。 2、耐心听别人讲话 (尊重别人) 尊重他人是最... -
整理UI设计的三个分类,入门前要清楚!
2019-08-22 18:13:49设计这行,做得越久,发现厉害的人越多,需要学习的地方越多,自己懂得太少,其实每个行业都如此,坚持学习才是这条道路上走的长久的一个重要因素。UI设计从工作内容上来说分为3个方向。它主要是由UI研究的3个因素... -
速卖通怎么做?速卖通好不好做?
2020-10-27 20:50:24装修店铺是非常重要的,打造一个风格清晰,结构明显的门面,店铺结构对于店铺而言非常重要,能让用户清晰、快速、准确地了解你的店铺。但首先还是需要明确一个概念,店铺结构并没有一个所谓的“标准唯一解”,而是... -
规划pk会,如何通过提问把和你抢资源的人干翻
2016-04-27 12:03:35先说说背景,规划pk会,通常在大公司里才有,因为人多事多,所以选择不做什么比决定做什么更重要,于是,每隔一段时间,也许是3个月,就会有一次产品/业务规划的pk会,参与宣讲... -
转:『IT视界』 [职场人生]从软件工程师到IT猎头:说说跳槽那点事
2010-08-09 16:09:00即使想在细分的小行业里去转,也不容易,比如你喜欢做一些更底层的开发,做软硬结合的那种开发,结果找到了一个通信公司被分去做网管,你做两三年之后,也只能找java相关的一些工作了,这种情况其实也挺难弄的,所以... -
开学第一课:“安全教育”主题班会教案.doc
2021-01-15 19:36:582、通过师生交流和讨论,明确数学的重要性,有意识地引导学生学好数学要关注数学学习的方法,明确怎样做一个会学习的人。 3、通过讲故事,让孩子们明白每天的学习每天的作业都是在为自己打基础,学习不是为他人,... -
2020班主任新学期开学第一课精选教案3篇.doc
2021-01-19 11:50:462、通过师生交流和讨论,明确数学的重要性,有意识地引导学生学好数学要关注数学学习的方法,明确怎样做一个会学习的人。 3、通过讲故事,让孩子们明白每天的学习每天的作业都是在为自己打基础,学习不是为他人,... -
职业生涯第二步----辞职(一)
2008-09-04 20:38:00那么辞职对于程序员来说是非常普通且重要的第二步 在考虑了很久以后我决定迈出这第二步----------辞职 先说说我的经历 我是学金融专业的,实在不太喜欢那种飘忽的,与人与事都要算计着过的生活,后来毅然决定转行来... -
对js中回调函数的一些看法
2020-11-23 03:25:40做了android之后更加感觉到手机端开发的重要性,现在做native App 和Web App是主流,也就是说现在各种基于浏览器的web app框架也会越来越火爆了,做js的也越来越有前途。我也决定从后端开发渐渐向前端开发和手机端... -
文档--项目中交流的利器还是项目的绊脚石
2015-11-22 12:34:28当然,我的目的不仅是做几个敏捷开发的项目,而是更期望组建一个敏捷开发团队。在此过程中,成员心理,现有流程,成果物定义,评审等等,理想与现实在不断地激情碰撞。 先来说说文档吧! 文档往往在项目中占据... -
PS大头照的背景
2013-08-29 09:09:10给同事ps一个大头照,只是修改一下背景颜色,以前没有做过,这次算是小练习了一把,与大家分享一下。修改大头照背景重要的是如何选中背景的区域,如果用魔棒选择的话,头与背景的边处理的不好,说说我的处理方法吧。... -
我的架构感悟:从美国宪法学习架构设计原则
2017-01-12 12:38:33为了让一个“系统”成功运行,架构师们需要考虑诸多重要的因素。在反复权衡之后,美国国父们设计出了一个稳定运行超过200年的架构,这其中的经验,对于我们在软件架构设计中应用的原则来说,有很多可供借鉴与印证之... -
超级有影响力霸气的Java面试题大全文档
2012-07-18 09:47:04SessionBean: Stateless Session Bean 的生命周期是由容器决定的,当客户机发出请求要建立一个Bean的实例时,EJB容器不一定要创建一个新的Bean的实例供客户机调用,而是随便找一个现有的实例提供给客户机。... -
大学怎样竞选团支书.doc
2021-01-15 10:46:272、讲一个和自己主题很贴切的故事,比如某一个精英竞选时的情景。3、适当的采用道具,比如一根筷子容易折,但是一捆筷子就很坚固等等(这个例子比较俗,我就是做个比方),让人觉得耳目一新”。其他的中间环节几乎所有... -
项目管理过程中,如何编制初步工作说明书
2013-07-17 22:50:00最近在做一个项目的时候,客户特别苛刻,在制定工作说明书的时候,费了很多周折,把很多以前做项目的时候都不怎么会专门考虑的细枝末节和例外情况都进行了详细说明和约定,但是在项目实施过程中,却发现这样的说明和... -
大学明明道
2016-02-15 14:05:27先问个问题,对创业团队的 CEO 来说,你最主要的工作是什么?...其实在这三要素外面,还有一个最为重要的环,叫做「企业文化」。 企业文化并不是简单的口号,当然成功的企业会被后人总结归纳出很多口号,真正形 -
数据仓库ETL工具箱——架构
2020-07-18 15:37:15本来这一篇是要详细写写ETL的需求部分,比如业务需求、合规需求等。但是码了一百多字发现大部分内容和上一篇都是重复的,因此决定本篇详细...数据前台中有一个非常重要的部分——数据集市。 数据集市是为了满足业务流. -
简单大方得体新娘致辞大全_精选.doc
2020-12-03 16:43:24简单大方得体新娘致辞大全 亲爱的丈夫,嫁给你,做你的妻子是我做过最重要最正确的决定,因为你给了我别人无法代替的爱。一想到以后在我们小窝里一起看看电视说说悄悄话,在困倦的时候躺在你怀里,有个休憩的港湾,... -
-2岁产品汪学习日记2/17
2020-02-17 23:40:11最近在看《人人都是产品经理》这本书,有的地方写的很好,但光看一遍又记不住,所以决定把自己认为重要的画在思维导图上面啦! 一个需求的奋斗史 说完BRD,来说说PRD 修改历史:写清楚每次修订的日期、版本号、... -
软件测试有感
2014-09-17 16:06:00从事了测试工作7,8年了,多少有些心得体会,一直想写些什么,但又好像无从下笔,直到今天看到一篇博文,同作者的观点甚为认同,于是也决定写下自己的感受。 先来说说原文的几个观点 1. 很多人认为最重要并且最不...