月度归档:2015年06月

使用jQuery的clone+模板的形式动态生成页面

公司的官网改造,其中有些地方要获取服务器返回的json数据,并动态生成列表。

原来的代码是用字符串的形式拼接html标签内容来动态生成的。这样虽然是没什么问题,但是我觉得代码看起来不够整齐,并且后面为何还得在一串String中找到要修改的地方,实在够难受。所以想着能不能在html先写好一个模板,然后在js中复制这个模板,然后修改需要变动的地方后,再嵌入到页面中呢?

有了这个想法,就到网上各种搜,后来发现使用jQuery选择出来的元素对象,有个clone方法,刚好适用。有了可行性后就着手开始尝试了。

1、添加模板

<div id=”news_list”></div>
<div id=”news_templete” style=”display:none;”>
<div class=”divider”></div>
<div class=”media”>
<div class=”blink-dot-top”></div>
<div class=”blink-dot-bottom”></div>
<div class=”blink-dot-right”></div>
<div class=”blink-dot-left”></div>
<div class=”media-left”>
<!– <a href=”#”> –>
<img class=”news_thumd_img media-object” src=”” alt=”…”>
<!– </a> –>
</div>
<div class=”media-body”>
<h4 class=”media-heading”><a class=”news_title_link” href=”#” data-toggle=”modal” data-target=”.bs-example-modal-lg”></a></h4>
<h4 class=”news_time media-heading”>
<small>2015/05/15</small>
</h4>
<div class=”news_body”>
</div>

</div>
</div>
</div>

例如这里的一个id=”news_templete”的div包裹的元素,就可以作为一个模板,用于在页面展示一条新闻。

2、使用js动态复制模板

for(var i = 0 ; i < newsJson.length; i++){

var newTitle = newsJson[i].title;
var newContent = newsJson[i].content;
var newPicUrl=newsJson[i].picUrl;
if(null==newPicUrl||””==newPicUrl){
newPicUrl=”images/123.JPG”;
}
newsListContent.push(newContent);//储存起来,点展开时显示内容
var newContentFormatted = formatContent(newContent);
if(newContent.length>150){
newContentFormatted+=’…’;
}
var newTime=””;
if(newsJson[i].time != null){
newTime = dateTotime(newsJson[i].time);
}else{
newTime = “”;
}
var new_temp_div=$(“#news_templete”);
var newDiv = new_temp_div.clone(true);
newDiv.removeAttr(“id”);
newDiv.find(“.news_thumd_img”).attr(“src”,newPicUrl);
newDiv.find(“.news_title_link”).attr(“onclick”,”javascript:showNewsDetail(“+i+”);”);
newDiv.find(“.news_title_link”).html(newTitle);
newDiv.find(“.news_time”).html(“<small>”+newTime+”<small>”);
newDiv.find(“.news_body”).html(newContentFormatted);
newDiv.show();//取消从模板中带过来的“隐藏”设置
$(“#news_list”).append(newDiv);
}

根据服务器返回的json对象列表,循环生成需要展示新闻列表。

蓝色字体部分,就是使用clone方法复制news_templete模板后,将json的数据填充到这个新div中。这个newDiv就是一条新闻,最后使用append的方式添加到页面中($(“#news_list”).append(newDiv);,其中$(“#news_list”)即为你想要这些内容出现的地方,如第一点中的蓝字html标签)。

这样代码看起来就整齐多了,后面修改也比较容易。如果只是需要修改页面布局,则只需要修改页面中的模板即可。

后来发现公司有用一个react.js这套界面构造框架,用来动态构建界面,感觉还是比较方便。嗯,它是Facebook推出的。

reactjs官网

 

Spring 使用@AspectJ实现aop

《Spring参考手册》中定义了以下几个AOP的重要概念:

  • 切面(Aspect) :官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”,在本例中,“切面”就是类TestAspect所关注的具体行为,例如,AServiceImpl.barA()的调用就是切面TestAspect所关注的行为之一。“切面”在ApplicationContext中<aop:aspect>来配置。
  • 连接点(Joinpoint) :程序执行过程中的某一行为,例如,AServiceImpl.barA()的调用或者BServiceImpl.barB(String _msg, int _type)抛出异常等行为。
  • 通知(Advice) :“切面”对于某个“连接点”所产生的动作,例如,TestAspect中对com.spring.service包下所有类的方法进行日志记录的动作就是一个Advice。其中,一个“切面”可以包含多个“Advice”,例如TestAspect
  • 切入点(Pointcut) :匹配连接点的断言,在AOP中通知和一个切入点表达式关联。例如,TestAspect中的所有通知所关注的连接点,都由切入点表达式execution(* com.spring.service.*.*(..))来决定
  • 目标对象(Target Object) :被一个或者多个切面所通知的对象。例如,AServcieImpl和BServiceImpl,当然在实际运行时,Spring AOP采用代理实现,实际AOP操作的是TargetObject的代理对象。
  • AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,TargetObject实现了接口时,则采用JDK动态代理,例如,AServiceImpl;反之,采用CGLIB代理,例如,BServiceImpl。强制使用CGLIB代理需要将 <aop:config>proxy-target-class 属性设为true

通知(Advice)类型

  • 前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。例如,TestAspect中的doBefore方法
  • 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。例如,TestAspect中的doAfter方法,所以AOPTest中调用BServiceImpl.barB抛出异常时,doAfter方法仍然执行
  • 返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
  • 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。例如,TestAspect中的doAround方法。
  • 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。 ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。例如,TestAspect中的doThrowing方法。

好,进入主题。原先在项目中,在controller返回的状态码,是写在返回的json体中的,故无论业务逻辑是否成功执行,在返回到客户端时,Response的status都是正常(即http返回码是200)。现要改成根据controller返回的状态码,设置Response的status。由于已经存在很多controller方法,就不想一个个地找到每个方法的返回的地方来增加处理逻辑,所以决定用AOP来实现。

而我使用的@AspectJ的方式来实现。

首先要引入cglib-nodep.jar这个包,因为要使controller也能凑效,需要使用CGLIB代理。我使用的是3.1版本。

1、先定义切点

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseStatusPC {

}

2、定义切面,由于这里是controller返回后处理的,所以实现“返回后通知(After return advice)”。

import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
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;

import com.kainaodong.xsst.common.api.constant.CommConstant;
import com.kainaodong.xsst.common.api.domain.ServiceRes;

@Aspect
@Component
public class HttpResponseStatusAspect {
final static Logger log = LoggerFactory.getLogger(HttpResponseStatusAspect.class);
//Controller层切点
@Pointcut(“@annotation(com.kainaodong.xsst.common.api.util.aop.ResponseStatusPC)”)
public void controllerAspect() {
//System.out.println(“controllerAspect”);
}

//Service层切点
@Pointcut(“@annotation(com.kainaodong.xsst.common.api.util.aop.ResponseStatusPC)”)
public void serviceAspect() {
}

@AfterReturning(pointcut=”controllerAspect()”,returning=”res”)
public void doAfter(JoinPoint jp,ServiceRes res){
int reCode=res.getReCode();
if(reCode!=CommConstant.RECODE_SUCCESS){
log.info(“根据controller的返回值设置HttpServletResponse的status:”+reCode);
setResponseStatus(jp.getArgs(), reCode);

}

}

public void setResponseStatus(Object[] args,int status){
for(Object arg:args){
if(arg instanceof HttpServletResponse){
//System.out.println(“HttpServletResponse”);
HttpServletResponse response=(HttpServletResponse) arg;
response.setStatus(status);
break;
}
}
}
}

ServiceRes是封装controller返回值的类。

3、将切点添加到目标对象中:

@ResponseStatusPC
public ServiceRes getUserInfor(HttpServletRequest request,
HttpServletResponse response,Integer userId,Integer loginUserId)

在controller中需要织入通知的方法上,添加@ResponseStatusPC切点的注解即可。

4、修改配置(一般是在applicationContext.xml中配置):

  • 增加切面的bean定义:<bean class=”com.aop.HttpResponseStatusAspect”></bean>  (我开发时,一开始忘了添加,写好切面,添加了切点,却怎样都没有代理成功)
  • 增加自动代理:<aop:aspectj-autoproxy proxy-target-class=”true”/>,如果是代理service,proxy-target-class=”true”可加可不加,而代理controller,则必须要加。

启动项目,即可发现添加了切点的method每次执行完返回后,都会调用HttpResponseStatusAspect.doAfter()。

maven nexus+nginx配置

由于项目中需要用到第三方的推送sdk,而这些sdk都是只提供jar包下载,而我们的项目中是使用maven进行jar包管理。所以只能在自己的服务器上搭建一个maven私服,将这些第三方sdk放到这个maven私服里面进行管理了。

在网上搜了一圈,发现最简单的搭建方法就是使用nexus(不是谷歌亲儿子nexus哦)这个软件进行搭建了。

Nexus官网:http://www.sonatype.org/nexus/

在linux上安装nexus也比较简单,在百度上一搜一大把。以下的步骤引用自:http://blog.163.com/sz2273_pr/blog/static/4126429620135811573231/

nexus安装步骤开始
 1, Nexus 下载

Nexus 官方下载, 最新版下载: nexus-2.4.0-09-bundle.tar.gz

Nexus war下载: nexus-2.4.0-09.war

nexus有两种安装实现方式,一种是war包的方式,将它直接放在例如tomcat下就可以启用的,还有一种源码的方式,做为一个运维还是喜欢源码方式的说哈。开始吧。。。

2, Nexus 安装

解压tar.gz文件:

#cd /home/tools

#tar zxvf nexus-2.4.0-09-bundle.tar.gz

移动目录

#mv nexus-2.4.0-09 /usr/local/nexus

3, Nexus 启动

nexus启动是在bin目录下,首先看一下启动/关闭/重启等命令, 输入命令:

#cd /usr/local/nexus/bin

#./nexus

出现如下选项:

[root@test01 bin]# ./nexus

Usage: ./nexus { console | start | stop | restart | status | dump }

启动nexus:

./nexus  start

关闭nexus:

./nexus  stop

4, Nexus 验证

启动nexus后,在本机浏览器输入地址: http://localhost:8081/nexus

出现上述页面,说明配置nexus成功!

点击右上角“Log in”, 输入用户名和密码(默认用户名:admin      密码: admin123)登录

5.设置

1.点击左侧 Repositories将所有Type 是 proxy 的 configuration配置选项中的 Download Remote Index 配置改为 True

如果需要代理将配置最下面的Override Http Proxy Setting 勾上并填写代理服务器地址和端口,最后保存设置

2.然后在列表中分别右键点击 ReIndex

3、添加自己的jar到nexus

选中 3rd party , Artifact Upload 标签

GAV Definition 选择GAV Parameters

填写 Group Artifact version packaging

然后选择jar包 上传提交即可。

nexus安装步骤结束

另外:在解压出来的nexus目录中,{nexus}/conf/ 下有个名为nexus.properties配置文件,在这里可以配置nexus的端口,仓库的文件的路径等等

其中有一项nexus-webapp-context-path=/nexus的配置,这是指定url中ip:port/XXX中的XXX的,所以如果配置成/nexus的话,你要访问nexus,则要输入ip:port/nexus。

如果要配合nginx使用,则这一项要改成nexus-webapp-context-path=/,即配制成使用ip:port就能访问到nexus。

然后就是配置,nginx了。

我的配置如下:

server {
listen 80;
server_name nexus.kainaodong.com;
location / {
expires -1;
index /;
proxy_pass http://127.0.0.1:9001;
}
include /alidata/server/nginx/conf/rewrite/default.conf;
access_log /alidata/log/nginx/access/default.log;
}

proxy_pass就是配置将对nexus.kainaodong.com子域名的访问转到http://127.0.0.1:9001,也即是我的服务器的nexus上。

一开始,我的nexus的nexus-webapp-context-path这一下没有配置成“/”,而是将nginx的proxy_pass配置成http://127.0.0.1:9001/nexus,这样虽然能访问到nexus,但是里面的仓库内容时访问不到的,也不能进行登录。

然后试着在nexus的server配置中,将base url强制为子域名,这时仓库的内容可以访问了,但是还是登录不了。

 

 

 

QQ截图20150606204706

 

最后在网上又搜了一圈,发现nexus的nexus-webapp-context-path这个属性是可以配置访问路径,所以直接将它改为”/”,nginx中的proxy_pass配置成http://127.0.0.1:9001。

这样,nexus终于能使用子域名来访问了。

 

 

Interpolator – Android动画插值器

好的动画可以很大程度的提高App交互体验,Android API提供的几种基本类型动画,加上插值器可以做出来大部分比较满意的效果。

一、插值器是什么

简单来讲,插值器就是一个函数,将时间t经过一个函数的变换映射到t’。

二、插值器为什么

自定义Animation我再赘述一下,比较简单的自定义Animation基本上重写下面的方法就够了:

protected void applyTransformation(float interpolatedTime, Transformation t) {

}

interpolatedTime就是动画过程中的t(0<=t<=1),根据当前的时间进行相应的canvas变换,以得到动画的效果。

再看方法的调用:

final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
applyTransformation(interpolatedTime, outTransformation);

所以,传进去的t是经过插值器mInterpolator映射后的t’,影响动画的运动轨迹和速度变化,即: t->t’->s 。

如果我们不给动画设置插值器得到的是什么?答案是匀速,即映射关系 t → t’: t’ = t 。

三、插值器怎么做

实现android.view.animation.Interpolator接口,实现方法:float getInterpolation(float input); 如:

public class LinerInterpolater implements Interpolator {

    @Override
    public float getInterpolation(float input) {
        return input;
    }

}

这便是一个默认的匀速插值,映射其实就是y=x,即t’=t。

四、21分钟精通插值器

t’=f(t) ,f(t)求导便是动画的速度,所以接下来我们可以根据需求进行插值器的定制。

如:

  • 加速(匀变速),这是一个二次插值,f(t) = t * t

image

  • 对应还可以得到减速(匀变速),也是一个二次插值, f(t) = 1 – ( 1 – t ) * ( 1 – t )

image

在一般情况下,映射后的t’也是在0~1范围内;如果我们需要过冲的效果,这个时候就会出现t’大于1的情况,最终回到1位置。(终点不是一定要在1)。

1. 0~1范围映射

只需经过简单的函数变换,就可以得到任意我们想要的变速效果。以上面列举到的二次插值y = x / x*为例:

这种情况下t在0~1上映射到t’也是0~1,可以直接使用。

如果我们想要减速的效果,只需经过一些简单变换。

  1. 先将函数图像上下翻转,y = – x * x
  2. 再将函数图像在x轴上右移一个单位,y = – ( 1 – x ) * ( 1 – x )
  3. 再将行数图像在y轴上上移一个单位,y = 1 – ( 1 – x ) * ( 1 – x )*

即可得到匀减速插值器,t->t’在0~1上映射过去也是0~1。

所以我们可以使用任何一个函数制作需要的插值器,只需要找到一个满意的变速区域(x,x+1),经过一些列函数变换,最终满足x、y均在0~1范围内(再来一遍:y不是一定要在0~1范围)。

2. 过冲的情况

对于t’不是全程都在0~1范围内的情况,比如我们需要平移动画有一个过冲(当然可以通过分段函数实现),抛体运动其实是一个不错的选择,其实就是上面说到的二次函数插值。

我们甚至可以在y=1-(1-x)*(1-x)的基础上继续变换。

1.将函数图像在y轴上放大为原图的m(m>1)倍,可以求出来y=1的时候方程的两个根x1和x2,x2 > x1。

image

y = ( 1 – ( 1 – x ) * ( 1 – x ) ) * m

2.将函数图像在x轴上压缩为原图的1/x2,得到:

image

y = ( 1 – ( 1 – x * x2 ) * ( 1 – x * x2 ) ) * m

如此,便得到了过冲为m-1、峰值为m的插值,且合物理运动规律,比较自然。

我们制作插值的时候,可以尽量模拟物理运动,这样动画看起来会比较自然、舒服。

我们制作插值的时候,可以尽量模拟物理运动,这样动画看起来会比较自然、舒服。

我们制作插值的时候,可以尽量模拟物理运动,这样动画看起来会比较自然、舒服。

重要的事情说三遍。

五、我要干货

以上只是带大家复习了一下中学数学,下面给大家提供一些实用的插值器:

1. x^n 插值

加速:y = x^n

减速:y = 1-(1-x)^n

先加速后减速:课后作业,自己练习

求导可知,次数越高,速度的变化速度越快。

2. a^x 指数插值

加速:a^(1-t) 其中a大于0小于1,下同

减速:1-a^t

先加速后减速:课后作业,自己练习。有兴趣的还可以自己变换得到上面的两个函数。

这个插值器的速度变化非常快。减速效果可以得到一种类似于物体在粘性液体中受阻力的运动效果,在终点位置可以非常缓慢逼近,以前做产品的时候,用户的描述是:黄油一般的动画效果……当然动画时间会变的比较长,不然体现不出效果。

减速指数插值终点位置不是1,而是无限逼近1,需要做处理。

这也是要注意的:当t>=1的时候,请直接返回1。不然动画貌似无法停止(懒了,不想查看源码了,改天回来贴源码)。

3、过冲插值或者终点位置回弹的插值

过冲效果的插值最简单可以使用抛体运动的运动方程,上面已述。

另外可以使用简谐运动,即三角函数。下面是一个我写的阻尼运动插值器,模拟的是不断衰减的简谐运动。可以用于一次过冲,也可以模拟在终点位置不断回弹的效果。

import android.view.animation.Interpolator;

/**
 *
 * 
 类描述:阻尼运动插值器
 * 
 * 功能详细描述:
 *
 * @author mengsifan
 * @date [2013-12-27]
 */
public class DampingInterpolator implements Interpolator {

    /** 过冲部分比例,0.0~1.0之间,默认0.5 */
    float mOvershootPercent = 0.5f;
    /** 用于计算阻力的系数,由mOvershootPercent和mRegion求得 */
    float mOvershootModulus;
    /** 回弹次数,最少回弹一次 */
    int mCount = 1;
    /** 整个运动范围,由mCount决定 */
    float mRegion;

    /**
     * <默认构造函数> 默认过冲比例是0.5,回弹次数是1
     */
    public DampingInterpolator() {
        this(1, 0.5f);
    }

    /**
     * @param count
     *            回弹次数
     * @param overshoot
     *            过冲比例,0.0~1.0之间
     */
    public DampingInterpolator(int count, float overshoot) {
        setOverShootCount(count);
        setOverShootPercent(overshoot);
    }

    public void setOverShootCount(int count) {
        mCount = Math.max(1, count);
        mRegion = (float) (Math.PI * 2 * (mCount - 1) + Math.PI / 2 * 3);
        mOvershootModulus = (float) Math.pow(mOvershootPercent, mRegion
                / Math.PI);
    }

    public void setOverShootPercent(float overshoot) {
        mOvershootPercent = Math.max(0, Math.min(1, overshoot));
        /*
         * 当 t * mRegion = Math.PI 的时候,达到第一次过冲的峰值, 则 t = Math.PI / mRegion 。 且此时
         * mOvershootModulus^t = mOvershootPercent , 所以 mOvershootModulus =
         * Math.pow(mOvershootPercent, 1 / t) , 即 mOvershootModulus =
         * Math.pow(mOvershootPercent, mRegion / Math.PI) 。
         */
        mOvershootModulus = (float) Math.pow(mOvershootPercent, mRegion
                / Math.PI);
    }

    public int getOverShootCount() {
        return mCount;
    }

    public float getOverShootPercent() {
        return mOvershootPercent;
    }

    @Override
    public float getInterpolation(float t) {
        if (t <= 0) {
            return 0;
        }
        if (t >= 1) {
            return 1;
        }
        return (float) (1 - Math.pow(mOvershootModulus, t)
                * Math.cos(mRegion * t));
    }
}

get方式乱码解决

一开始并没有发现是get方式引起的问题,当中的幸酸不讲了,直接说结果:

  1. post方式将参数放在数据包的消息体中,get方式的参数放在URL中,而request.setCharacterEncoding(“UTF-8”)只对消息体起作用。

  2. 在struts2中,即时设置了<constant name=”struts.i18n.encoding” value=”UTF-8″></constant>,对于get方式,依旧不起作用。

  3. 使用过滤器,通过重写HttpServletRequest的getParameter方法,将返回的字符串重新编码,可以处理部分get方式乱码。

  4. 而struts2并不直接通过getParameter或getAttribute获取参数,因此,上面的方式不管用,所以只能考虑,将保存在HttpServletRequest中的参数值直接改为UTF-8编码,因此过滤器如下:

request.setCharacterEncoding(“UTF-8”);
Iterator iter = request.getParameterMap().values().iterator();
while (iter.hasNext()) {
String[] parames = (String[]) iter.next();
for (int i = 0; i < parames.length; i++) {
parames[i] = new String(parames[i].getBytes(“iso8859-1”), “utf-8”);
}
}

  1. 当然,也可以考虑使用拦截器来解决,对于struts2,如下:

@Override
public String intercept(ActionInvocation arg0) throws Exception {
// TODO Auto-generated method stub
ActionContext actionContext = arg0.getInvocationContext();
HttpServletRequest request= (HttpServletRequest)                  actionContext.get(StrutsStatics.HTTP_REQUEST);
// 以下代码同上面的过滤器
return arg0.invoke();
}