Redis 安全性检查

Redis 在设计上,是用来被可信客户端访问的,也就意味着不适合暴露给外部环境非可信客户端访问。

最佳的实践方法是在 Redis 前增加一个访问控制层,用于校验用户请求。

基本配置

Redis 本身提供了一些简单的配置以满足基本的安全控制。

  • IP绑定。如果不需要直接对外提供服务,bind 127.0.0.1就行了,切忌bind 0.0.0.0
  • 端口设置。修改默认的6379,一定程度上避免被扫描。
  • 设置密码。Redis的密码是通过 requirepass 以明文的形式配置在conf文件里的,所以要尽可能得长和复杂,降低被破解的风险。因为redis非常快,外部环境可以在一秒内150k次暴力破解,所以配置密码一定要复杂。
  • 重命名或禁用某些高危操作命令。向config、flushall、flushdb这些操作都是很关键的,不小心就会导致数据库不可用。可以在配置文件中通过rename-command重命名或禁用这些命令。

网络配置

对于直接暴露在互联网的Redis,应该使用防火墙阻止外部访问 Redis 端口。客户端应该只通过回环接口访问 Redis。

在 redis.conf 文件添加

bind 127.0.0.1

由于Redis设计的初衷,如果不能成功阻止外部访问Redis端口,会有很大的安全影响。外部攻击者使用一个FLUSHALL命令就可以删除整个数据集。

使用密码

虽然 Redis 没有实现访问控制,但是提供了一个简单的身份验证功能。

在配置文件中修改:

requirepass mypassword

重启redis

sudo service redis-server restart

登录验证

./redis-cli -h 127.0.0.1 -p 6379 -a mypassword
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "mypassword"

如上输出,配置正确。也可以在连接之后使用 auth 验证

./redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> auth mypassword
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "mypassword"

通过修改 server 的 conf 文件方式需要重启 server, 也可以通过客户端来修改密码

127.0.0.1:6379> config set requirepass mypassword
OK

使用客户端设置的密码,Redis 重启之后还会使用 redis.conf 配置文件中的密码。

设置密码之后 Redis 集群中 slave 中也需要配置 和 master 一样的密码

masterauth master-password

身份验证是一个可选的冗余层,如果防火墙或者其他保护 Redis 安全的系统被攻破,对于不知道授权密码的情况,攻击者依然不能访问 Redis。

auth 命令是明文传输的,所以依然不能阻止那些获得网络访问权限的攻击者嗅探。

reference


2017-08-25 Redis , Database , NoSQL , security

Linux 主机在线监控

很久没有更新这个分类下的文章了,其实一直在体验不同的产品,只是真的很少有能拿出来讲一下的东西。不管是硬件还是软件,最近几年使用的东西越来越狭窄,越来越收缩,当然对于某一个特定的需求,总有一个产品能够占领绝大多数市场,而也有部分产品能够瓜分小众市场。这里要介绍的这个 NodeQuery 就不是一个大而全的产品,而是一个很精细的小众产品。我用它也一年多了,我的需求很简单,能够实时监控我的 VPS,能够在宕机或者高负载时报警。NodeQuery 完全能够满足我的需求。

用 NodeQuery 自己的话描述自己就是:”一个轻量、易用的Linux服务器监控服务”.

NodeQuery provides a lightweight and easy to use Linux server monitoring service.

NodeQuery 免费账户可以提供10台机器的监控,

官网地址: https://nodequery.com/

界面展示

index

details

使用

同样使用也非常方便,新建 Server,然后会给出一个一键脚本,在自己的 VPS 上一键安装就行,脚本同样是开源的托管在 GitHub 上。人人都可以审查。

API

这个网站也能够提供API支持,能够读取历史情况下 Server 的状态,目前写功能暂时还无法使用。

reminder

不过需要提醒的是,这个网站自从 2014年起就再没有更新,不清楚背后发生了什么事情,但是也是感到非常的可惜。


2017-08-23 Linux , VPS , monitor

Spring mvc 的注解

一般的注解,比如常见的 @Override 是 Java 从 1.5 版本开始引入,注解一般用来对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等等进行注解,他的作用一般分为如下四个方面:

  1. 生成文档
  2. 编译检查,通过注解让编译器在编译期间进行检查校验
  3. 编译时动态处理,编译时通过注解标示进行动态处理,比如生成代码
  4. 运行时动态处理,反射注入实例等等

一般的注解可以分为三类:

  1. Java 自带的注解,包括 @Override @Deprecated 等等
  2. 元注解,用于定义注解,包括 @Retention @Target @Inherited @Documented @Retention用于标明注解被保留的阶段,@Target用于标明注解使用的范围,@Inherited用于标明注解可继承,@Documented用于标明是否生成javadoc文档
  3. 自定义注解,根据需求使用元注解自定义注解

Spring 中的注解

Spring中的注解大概可以分为两大类:

  • Spring的bean容器相关的注解,或者说bean工厂相关的注解
  • Springmvc相关的注解

Spring的bean容器相关的注解,先后有:@Required, @Autowired, @PostConstruct, @PreDestory,还有Spring3.0开始支持的JSR-330标准javax.inject.*中的注解(@Inject, @Named, @Qualifier, @Provider, @Scope, @Singleton).

Spring mvc相关的注解有:@Controller, @RequestMapping, @RequestParam, @ResponseBody等等。

要理解Spring中的注解,先要理解Java中的注解。

Java中的注解

Java中1.5中开始引入注解,最熟悉的应该是:@Override, 它的定义如下:

/**
 * Indicates that a method declaration is intended to override a
 * method declaration in a supertype. If a method is annotated with
 * this annotation type compilers are required to generate an error
 * message unless at least one of the following conditions hold:
 * The method does override or implement a method declared in a
 * supertype.
 * The method has a signature that is override-equivalent to that of
 * any public method declared in Object.
 *
 * @author  Peter von der Ahé
 * @author  Joshua Bloch
 * @jls 9.6.1.4 @Override
 * @since 1.5
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

从注释,我们可以看出,@Override的作用是,提示编译器,使用了@Override注解的方法必须override父类或者java.lang.Object中的一个同名方法。我们看到@Override的定义中使用到了 @Target, @Retention,它们就是所谓的“元注解”——就是定义注解的注解,或者说注解注解的注解。我们看下 @Retention

/**
 * Indicates how long annotations with the annotated type are to
 * be retained.  If no Retention annotation is present on
 * an annotation type declaration, the retention policy defaults to
 * RetentionPolicy.CLASS.
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
	/**
	 * Returns the retention policy.
	 * @return the retention policy
	 */
	RetentionPolicy value();
}

@Retention用于提示注解被保留多长时间,有三种取值:

public enum RetentionPolicy {
	/**
	 * Annotations are to be discarded by the compiler.
	 */
	SOURCE,
	/**
	 * Annotations are to be recorded in the class file by the compiler
	 * but need not be retained by the VM at run time.  This is the default
	 * behavior.
	 */
	CLASS,
	/**
	 * Annotations are to be recorded in the class file by the compiler and
	 * retained by the VM at run time, so they may be read reflectively.
	 *
	 * @see java.lang.reflect.AnnotatedElement
	 */
	RUNTIME
}

RetentionPolicy.SOURCE 保留在源码级别,被编译器抛弃(@Override就是此类); RetentionPolicy.CLASS 被编译器保留在编译后的类文件级别,但是被虚拟机丢弃; RetentionPolicy.RUNTIME保留至运行时,可以被反射读取。

再看 @Target:

package java.lang.annotation;

/**
 * Indicates the contexts in which an annotation type is applicable. The
 * declaration contexts and type contexts in which an annotation type may be
 * applicable are specified in JLS 9.6.4.1, and denoted in source code by enum
 * constants of java.lang.annotation.ElementType
 * @since 1.5
 * @jls 9.6.4.1 @Target
 * @jls 9.7.4 Where Annotations May Appear
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
	/**
	 * Returns an array of the kinds of elements an annotation type
	 * can be applied to.
	 * @return an array of the kinds of elements an annotation type
	 * can be applied to
	 */
	ElementType[] value();
}

@Target 用于提示该注解使用的地方,取值有:

public enum ElementType {
	/** Class, interface (including annotation type), or enum declaration */
	TYPE,
	/** Field declaration (includes enum constants) */
	FIELD,
	/** Method declaration */
	METHOD,
	/** Formal parameter declaration */
	PARAMETER,
	/** Constructor declaration */
	CONSTRUCTOR,
	/** Local variable declaration */
	LOCAL_VARIABLE,
	/** Annotation type declaration */
	ANNOTATION_TYPE,
	/** Package declaration */
	PACKAGE,
	/**
	 * Type parameter declaration
	 * @since 1.8
	 */
	TYPE_PARAMETER,
	/**
	 * Use of a type
	 * @since 1.8
	 */
	TYPE_USE
}

分别表示该注解可以被使用的地方:1)TYPE 用于类,接口(包括注解),enum 定义; 2) FIELD 属性域;3)METHOD 方法;4)PARAMETER 参数;5)CONSTRUCTOR 构造函数;6)LOCAL_VARIABLE 局部变量;7)ANNOTATION_TYPE 注解类型;8)PACKAGE 包

所以:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

表示 @Override 只能使用在方法上,保留在源码级别,被编译器处理,然后抛弃掉。

还有一个经常使用的元注解 @Documented :

/**
 * Indicates that annotations with a type are to be documented by javadoc
 * and similar tools by default.  This type should be used to annotate the
 * declarations of types whose annotations affect the use of annotated
 * elements by their clients.  If a type declaration is annotated with
 * Documented, its annotations become part of the public API
 * of the annotated elements.
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

表示注解是否能被 javadoc 处理并保留在文档中。

使用 元注解 来自定义注解 和 处理自定义注解

有了元注解,那么我就可以使用它来自定义我们需要的注解。结合自定义注解和AOP或者过滤器,是一种十分强大的武器。比如可以使用注解来实现权限的细粒度的控制——在类或者方法上使用权限注解,然后在AOP或者过滤器中进行拦截处理。下面是一个关于登录的权限的注解的实现:

/**
 * 不需要登录注解
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoLogin {
}

我们自定义了一个注解 @NoLogin, 可以被用于 方法 和 类 上,注解一直保留到运行期,可以被反射读取到。该注解的含义是:被 @NoLogin 注解的类或者方法,即使用户没有登录,也是可以访问的。下面就是对注解进行处理了:

/**
 * 检查登录拦截器
 * 如不需要检查登录可在方法或者controller上加上@NoLogin
 */
public class CheckLoginInterceptor implements HandlerInterceptor {
	private static final Logger logger = Logger.getLogger(CheckLoginInterceptor.class);

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
							 Object handler) throws Exception {
		if (!(handler instanceof HandlerMethod)) {
			logger.warn("当前操作handler不为HandlerMethod=" + handler.getClass().getName() + ",req="
						+ request.getQueryString());
			return true;
		}
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		String methodName = handlerMethod.getMethod().getName();
		// 判断是否需要检查登录
		NoLogin noLogin = handlerMethod.getMethod().getAnnotation(NoLogin.class);
		if (null != noLogin) {
			if (logger.isDebugEnabled()) {
				logger.debug("当前操作methodName=" + methodName + "不需要检查登录情况");
			}
			return true;
		}
		noLogin = handlerMethod.getMethod().getDeclaringClass().getAnnotation(NoLogin.class);
		if (null != noLogin) {
			if (logger.isDebugEnabled()) {
				logger.debug("当前操作methodName=" + methodName + "不需要检查登录情况");
			}
			return true;
		}
		if (null == request.getSession().getAttribute(CommonConstants.SESSION_KEY_USER)) {
			logger.warn("当前操作" + methodName + "用户未登录,ip=" + request.getRemoteAddr());
			response.getWriter().write(JsonConvertor.convertFailResult(ErrorCodeEnum.NOT_LOGIN).toString()); // 返回错误信息
			return false;
		}
		return true;
	}
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response,
						   Object handler, ModelAndView modelAndView) throws Exception {
	}
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
								Object handler, Exception ex) throws Exception {
	}
}

上面我们定义了一个登录拦截器,首先使用反射来判断方法上是否被 @NoLogin 注解:

NoLogin noLogin = handlerMethod.getMethod().getAnnotation(NoLogin.class);

然后判断类是否被 @NoLogin 注解:

noLogin = handlerMethod.getMethod().getDeclaringClass().getAnnotation(NoLogin.class); 

如果被注解了,就返回 true,如果没有被注解,就判断是否已经登录,没有登录则返回错误信息给前台和false. 这是一个简单的使用 注解 和 过滤器 来进行权限处理的例子。扩展开来,那么我们就可以使用注解,来表示某方法或者类,只能被具有某种角色,或者具有某种权限的用户所访问,然后在过滤器中进行判断处理。

Spring的bean容器相关的注解

  • @Autowired 是我们使用得最多的注解,其实就是 autowire=byType 就是根据类型的自动注入依赖(基于注解的依赖注入),可以被使用再属性域,方法,构造函数上。

  • @Qualifier 就是 autowire=byName, @Autowired注解判断多个bean类型相同时,就需要使用 @Qualifier(“xxBean”) 来指定依赖的bean的id:

      @Controller
      @RequestMapping("/user")
      public class HelloController {
          @Autowired
          @Qualifier("userService")
          private UserService userService;
    
  • @Resource 属于JSR250标准,用于属性域和方法上。也是 byName 类型的依赖注入。使用方式:@Resource(name=”xxBean”). 不带参数的 @Resource 默认值类名首字母小写。关于 Autowired 和 @Resouece 的区别可以参考这篇

  • JSR-330 标准 javax.inject.* 中的注解(@Inject, @Named, @Qualifier, @Provider, @Scope, @Singleton)。@Inject就相当于@Autowired, @Named 就相当于 @Qualifier, 另外 @Named 用在类上还有 @Component的功能。

  • @Component, @Controller, @Service, @Repository, 这几个注解不同于上面的注解,上面的注解都是将被依赖的bean注入进入,而这几个注解的作用都是生产bean, 这些注解都是注解在类上,将类注解成Spring的bean工厂中一个一个的bean。@Controller, @Service, @Repository基本就是语义更加细化的@Component。关于这几个注解可以参考这篇文章

  • @PostConstruct 和 @PreDestroy 不是用于依赖注入,而是bean 的生命周期。类似于 init-method(InitializeingBean) destory-method(DisposableBean)

Spring中注解的处理

Spring中注解的处理基本都是通过实现接口 BeanPostProcessor 来进行的:

public interface BeanPostProcessor {
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

相关的处理类有: AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor, RequiredAnnotationBeanPostProcessor

这些处理类,可以通过 配置隐式的配置进Spring容器。这些都是依赖注入的处理,还有生产bean的注解(@Component, @Controller, @Service, @Repository)的处理:

<context:component-scan base-package="net.aazj.service,net.aazj.aop" />

这些都是通过指定扫描的基包路径来进行的,将他们扫描进Spring的bean容器。注意 context:component-scan 也会默认将 AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor 配置进来。所以是可以省略的。另外context:component-scan也可以扫描@Aspect风格的AOP注解,但是需要在配置文件中加入 进行配合。

Spring注解和JSR-330标准注解的区别:

Spring javax.inject.* javax.inject restrictions/ comments
@Autowired @Inject @Inject 没有 required 属性
@Component @Named  
@Scope(“singletion”) @Singleton JSR-330 默认 scope 和 Spring prototype 类似,为了和 Spring 中默认保持一直, JSR-330 bean 使用 singleton 作为默认值。
@Qualifier @Named -
@Value - no equivalent
@Required - no equivalent
@Lazy - no equivalent

注解的原理

注解被编译后本质上就是一个继承 Annotation 接口的接口

reference


2017-08-18 Spring , Java , Web , 注解 , 拦截器 , 反射

google foobar page

https://www.google.com/foobar/?eid=sfeTWdGPBIac8QXcxpXgAQ&usg=AG3vBD082_C21k4vEcaG4KspC-1eGqU7KA

Minion Labor Shifts
===================

Commander Lambda's minions are upset! They're given the worst jobs on the whole space station, and some of them are starting to complain that even those worst jobs are being allocated unfairly. If you can fix this problem, it'll prove your chops to Commander Lambda so you can get promoted!

Minions' tasks are assigned by putting their ID numbers into a list, one time for each day they'll work that task. As shifts are planned well in advance, the lists for each task will contain up to 99 integers. When a minion is scheduled for the same task too many times, they'll complain about it until they're taken off the task completely. Some tasks are worse than others, so the number of scheduled assignments before a minion will refuse to do a task varies depending on the task.  You figure you can speed things up by automating the removal of the minions who have been assigned a task too many times before they even get a chance to start complaining.

Write a function called answer(data, n) that takes in a list of less than 100 integers and a number n, and returns that same list but with all of the numbers that occur more than n times removed entirely. The returned list should retain the same ordering as the original list - you don't want to mix up those carefully-planned shift rotations! For instance, if data was [5, 10, 15, 10, 7] and n was 1, answer(data, n) would return the list [5, 15, 7] because 10 occurs twice, and thus was removed from the list entirely.

Languages
=========

To provide a Python solution, edit solution.py
To provide a Java solution, edit solution.java

Test cases
==========

Inputs:
	(int list) data = [1, 2, 3]
	(int) n = 0
Output:
	(int list) []

Inputs:
	(int list) data = [1, 2, 2, 3, 3, 3, 4, 5, 5]
	(int) n = 1
Output:
	(int list) [1, 4]

Inputs:
	(int list) data = [1, 2, 3]
	(int) n = 6
Output:
	(int list) [1, 2, 3]

Use verify [file] to test your solution and see how it does. When you are finished editing your code, use submit [file] to submit your answer. If your solution passes the test cases, it will be removed from your home folder.

2017-08-16

@Autowired vs @Resource vs @Inject 的区别

为了实现依赖注入 DI 而引入,Java 提供 javax.annotation.Resource , javax.inject.Inject 注解,Spring 框架提供了 org.springframework.beans.factory.annotation.Autowired 。依赖注入(Denpendency Injection,DI), 控制反转(Inversion of Control, IoC),主要的目的是去除代码耦合。具体可参考其他资料。

使用

Spring 注入的方式有多种,可以写在field上,可以写在setter方法上,可以写在constructor上。

// field
@Autowired
private UserDao userDao;

// constructor
@Autowired
public UserService(UserDao userDao) {
	this.userDao = userDao;
}


@Resource
private UserDao userDao;

配置

<context:annotation-config/>

or

<context:component-scan base-package="需要自动扫描的包" />

具体解释

Annotation Package Source
@Autowired org.springframework.beans.factory.annotation.Autowire Spring
@Resource javax.annotation.Resource Java
@Inject javax.inject.Inject Java 需额外依赖

@Autowired: Spring 特有的注解,@Autowired 通过类型来注入,比如通过类的类型,或者类的接口来注解 field 或者 constractor。为了防止在项目中实现同一个接口,或者一系列子类,可以使用 @Qualifier 注解来避免歧义。默认情况下 bean 的名字就是 qualifier 的值。 尽管你可以按照约定通过名字来使用 @Autowired 注解,@Autowired 根本上还是类型驱动的注入,并且附带可选的语义上的 qualifiers.

@Inject: 该注解基于 JSR-330, @Inject 注解是 Spring @Autowired 注解的代替品。所以使用 Spring 独有的 @Autowired 注解时,可以考虑选择使用 @Inject. @Autowired 和 @Inject 的不同之处在于是否有 required 属性,@Inject 没有 required 属性,因此在找不到合适的依赖对象时 inject 会失败,而 @Autowired 可以使用 required=false 来允许 null 注入。

使用 @Inject 需要添加如下依赖:

<dependency>
	<groupId>javax.inject</groupId>
	<artifactId>javax.inject</artifactId>
	<version>1</version>
</dependency>

Advantage of @Inject annotation is that rather than inject a reference directly, you could ask @Inject to inject a Provider. The Provider interface enables, among other things, lazy injection of bean references and injection of multiple instances of a bean. In case we have few implementation of an interface or a subclass we can narrow down the selection using the @Named annotation to avoid ambiguity. @Named annotation works much like Spring’s @Qualifier

@Resource: JDK 1.6 支持注解,JSR-250 引入. @Resource 和 @Autowired @Inject 类似, 最主要的区别在于寻找存在的Bean注入的路径不同。@Resource 寻找的优先顺序为 1) 优先通过名字(by name) 2)其次是类型(by type) 3)再次是qualifier(by qualifier) 。而 @Autowired and @Inject 寻找的顺序为 1) 通过类型寻找 2)通过 qualifier 3) 最后通过名字寻找 。

@Resource 如果没有指定 name 属性,当注解标注在 field 上,默认取字段名称作为 bean 名称寻找依赖对象;当标注在属性 setter 方法上,默认取属性名作为 bean 名称寻找依赖。如果没有指定 name 属性,并且按照默认名称找不到依赖对象时,回退到类型装配。

扩展

https://github.com/google/guice/

Google 提供的轻量级依赖注入框架,支持 Java 6 及以上

http://square.github.io/dagger/

为 Android 和 Java 设计的 DI

reference


2017-08-15 Spring , Java , Web , DesignPattern

Spring Interceptor vs Filter 拦截器和过滤器区别

Spring的Interceptor(拦截器)与Servlet的Filter有相似之处,都能实现权限检查、日志记录等。不同的是:

Filter Interceptor Summary
Filter 接口定义在 javax.servlet 包中 接口 HandlerInterceptor 定义在org.springframework.web.servlet 包中  
Filter 定义在 web.xml 中    
Filter在只在 Servlet 前后起作用。Filters 通常将 请求和响应(request/response) 当做黑盒子,Filter 通常不考虑servlet 的实现。 拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。允许用户介入(hook into)请求的生命周期,在请求过程中获取信息,Interceptor 通常和请求更加耦合。 在Spring构架的程序中,要优先使用拦截器。几乎所有 Filter 能够做的事情, interceptor 都能够轻松的实现1
Filter 是 Servlet 规范规定的。 而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。 使用范围不同
Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的。 而拦截器是在 Spring容器内的,是Spring框架支持的。 规范不同
Filter 不能够使用 Spring 容器资源 拦截器是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可 Spring 中使用 interceptor 更容易
Filter 是被 Server(like Tomcat) 调用 Interceptor 是被 Spring 调用 因此 Filter 总是优先于 Interceptor 执行

interceptor 使用

interceptor 的执行顺序大致为:

  1. 请求到达 DispatcherServlet
  2. DispatcherServlet 发送至 Interceptor ,执行 preHandle
  3. 请求达到 Controller
  4. 请求结束后,postHandle 执行

Spring 中主要通过 HandlerInterceptor 接口来实现请求的拦截,实现 HandlerInterceptor 接口需要实现下面三个方法:

  • preHandle() – 在handler执行之前,返回 boolean 值,true 表示继续执行,false 为停止执行并返回。
  • postHandle() – 在handler执行之后, 可以在返回之前对返回的结果进行修改
  • afterCompletion() – 在请求完全结束后调用,可以用来统计请求耗时等等

统计请求耗时

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter{

	private static final Logger logger = Logger.getLogger(ExecuteTimeInterceptor.class);

	//before the actual handler will be executed
	public boolean preHandle(HttpServletRequest request,
		HttpServletResponse response, Object handler)
		throws Exception {

		long startTime = System.currentTimeMillis();
		request.setAttribute("startTime", startTime);

		return true;
	}

	//after the handler is executed
	public void postHandle(
		HttpServletRequest request, HttpServletResponse response,
		Object handler, ModelAndView modelAndView)
		throws Exception {

		long startTime = (Long)request.getAttribute("startTime");

		long endTime = System.currentTimeMillis();

		long executeTime = endTime - startTime;

		//modified the exisitng modelAndView
		modelAndView.addObject("executeTime",executeTime);

		//log it
		if(logger.isDebugEnabled()){
		   logger.debug("[" + handler + "] executeTime : " + executeTime + "ms");
		}
	}
}

例子来源 mkyong

使用mvc:interceptors标签来声明需要加入到SpringMVC拦截器链中的拦截器

<mvc:interceptors>  
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->  
<bean class="com.company.app.web.interceptor.AllInterceptor"/>  
	<mvc:interceptor>  
		 <mvc:mapping path="/**"/>  
		 <mvc:exclude-mapping path="/parent/**"/>  
		 <bean class="com.company.authorization.interceptor.SecurityInterceptor" />  
	</mvc:interceptor>  
	<mvc:interceptor>  
		 <mvc:mapping path="/parent/**"/>  
		 <bean class="com.company.authorization.interceptor.SecuritySystemInterceptor" />  
	</mvc:interceptor>  
</mvc:interceptors>  

可以利用mvc:interceptors标签声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法和afterCompletion方法却会后执行。

在mvc:interceptors标签下声明interceptor主要有两种方式:

  • 直接定义一个Interceptor实现类的bean对象。使用这种方式声明的Interceptor拦截器将会对所有的请求进行拦截。
  • 使用mvc:interceptor标签进行声明。使用这种方式进行声明的Interceptor可以通过mvc:mapping子标签来定义需要进行拦截的请求路径。

经过上述两步之后,定义的拦截器就会发生作用对特定的请求进行拦截了。

Filter 使用

Servlet 的 Filter 接口需要实现如下方法:

  • void init(FilterConfig paramFilterConfig) – 当容器初始化 Filter 时调用,该方法在 Filter 的生命周期只会被调用一次,一般在该方法中初始化一些资源,FilterConfig 是容器提供给 Filter 的初始化参数,在该方法中可以抛出 ServletException 。init 方法必须执行成功,否则 Filter 可能不起作用,出现以下两种情况时,web 容器中 Filter 可能无效: 1)抛出 ServletException 2)超过 web 容器定义的执行时间。
  • doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain) – Web 容器每一次请求都会调用该方法。该方法将容器的请求和响应作为参数传递进来, FilterChain 用来调用下一个 Filter。
  • void destroy() – 当容器销毁 Filter 实例时调用该方法,可以在方法中销毁资源,该方法在 Filter 的生命周期只会被调用一次。

    FrequencyLimitFilter com.company.filter.FrequencyLimitFilter FrequencyLimitFilter /login/*

Filter 和 Interceptor 的一些用途

  • Authentication Filters
  • Logging and Auditing Filters
  • Image conversion Filters
  • Data compression Filters
  • Encryption Filters
  • Tokenizing Filters
  • Filters that trigger resource access events
  • XSL/T filters
  • Mime-type chain Filter

Request Filters 可以:

  • 执行安全检查 perform security checks
  • 格式化请求头和主体 reformat request headers or bodies
  • 审查或者记录日志 audit or log requests
  • 根据请求内容授权或者限制用户访问 Authentication-Blocking requests based on user identity.
  • 根据请求频率限制用户访问

Response Filters 可以:

  • 压缩响应内容,比如让下载的内容更小 Compress the response stream
  • 追加或者修改响应 append or alter the response stream
  • 创建或者整体修改响应 create a different response altogether
  • 根据地方不同修改响应内容 Localization-Targeting the request and response to a particular locale.

reference

  1. https://stackoverflow.com/a/8006315/1820217 


2017-08-14 Spring , Java , Web

Spring @Component vs @Service vs @Controller vs @Repository

@Component, @Service, @Controller@Repository 四个注解在 Spring 中等同于在XML中定义 <bean> 标签,他们注解的对象都是 Spring 的 Bean。@Service@Controller@Repository 本质上就是 @Component@Controller@Service@Repository 他们在功能上几乎相同,主要的功能是用来给应用分层。

  • @Controller: 处理对应的请求,对应表现层(控制层),使用 @RequestMapping 注解来定义请求 Path,在该层中做请求分发,转发,调用Service方法等
  • @Service: 所有业务逻辑放在 Service 中,对应业务层,包括数值计算,业务逻辑,在该层中直接调用持久层的方法
  • @Repository: 持久层,访问数据,保存数据,DAO(Data Access Objects),比如所有数据库相关的操作
  • @Component: 通用的Spring 组件,generic stereotype for any Spring-managed component,当组件不好归类时可以使用该注解

在配置文件中定义

<context:component-scan base-package="com.mycompany.mypackage" />

Spring 就会自动扫描package下所有类,将带有@Component@Repository@Service@Controller 标签的类自动注册到Spring容器。 简而言之,注解的方式省去了过去需要在 XML 中定义 <bean class="..."> 的繁重工作。component-scan 包含了annotation-config标签的作用。 @Repository@Service@Controller 这三个注解除了作用于不同的软件层面外,其他使用方式和 @Repository 几乎一致。

除了上面的四个注解外,用户同样也可以创建自定义的注解,然后在注解上标注 @Component,那么,该自定义注解便具有了与所 @Component 相同的功能。

当一个 Bean 被自动检测到时,会根据那个扫描器的 BeanNameGenerator 策略生成它的 bean 名称。默认情况下,对于包含 name 属性的 @Component@Repository@Service@Controller,会把 name 取值作为 Bean 的名字。如果这个注解不包含 name 值或是其他被自定义过滤器发现的组件,默认 Bean 名称会是小写开头的非限定类名。如果你不想使用默认 bean 命名策略,可以提供一个自定义的命名策略。首先实现 BeanNameGenerator 接口,确认包含了一个默认的无参数构造方法。然后在配置扫描器时提供一个全限定类名,如下所示:

<beans ...> 
<context:component-scan 
	base-package="a.b" name-generator="a.SimpleNameGenerator"/> 
</beans> 

与通过 XML 配置的 Spring Bean 一样,通过上述注解标识的 Bean,其默认作用域是”singleton”,为了配合这四个注解,在标注 Bean 的同时能够指定 Bean 的作用域,Spring 2.5 引入了 @Scope 注解。 可以在定义Component 的时候指定 @Scope("prototype”) 来改变。

@Component
@Scope("prototype")
public class UserService {
	private int counter;

}

通过名字获取Bean

在一些特殊情况下当我们无法使用注解直接使用 Spring Bean 时,比如在 Filter 中,有一些教程提示我们可以直接使用 ApplicationContext.getBean() 来后去 Bean,但这样的方式不优雅 ,我们可以考虑实现 org.springframework.context.ApplicationContextAware 接口,来动态的根据名字来获取 Bean。文档上

Interface to be implemented by any object that wishes to be notified of the ApplicationContext that it runs in. Implementing this interface makes sense for example when an object requires access to a set of collaborating beans. Note that configuration via bean references is preferable to implementing this interface just for bean lookup purposes.

具体实现如下:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
@Component
public class ContextProvider implements ApplicationContextAware {
 
	private static ApplicationContext CONTEXT;
 
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		CONTEXT = applicationContext;
	}
 
	/**
	 * Get a Spring bean by type.
	 **/
	public static <T> T getBean(Class<T> beanClass) {
		return CONTEXT.getBean(beanClass);
	}
 
	/**
	 * Get a Spring bean by name.
	 **/
	public static Object getBean(String beanName) {
		return CONTEXT.getBean(beanName);
	}
}

上面的类实现,定义了一个简单的 @Component 实现了 ApplicationContextAware 接口,当 Spring Context 被创建时会被通知到。当被通知后 Spring context 会被放到唯一的静态变量 CONTEXT 中,因此静态方法能够通过 getBean 方法找到相应的 Bean 实例。

getBean 的默认名称是类名(头字母小写),如果想简单自定义Bean名字,可以@Service("serviceNewName") 这样来指定。

使用 @PostConstruct 和 @PreDestroy 指定生命周期回调方法

可以使用以下方式指定初始化方法和销毁方法:

@PostConstruct
public void init() { 

} 

@PreDestroy
public void destory() { 

}

Spring Bean 是受 Spring IoC 容器管理,由容器进行初始化和销毁的(prototype 类型由容器初始化之后便不受容器管理),通常我们不需要关注容器对 Bean 的初始化和销毁操作,由 Spring 经过构造函数或者工厂方法创建的 Bean 就是已经初始化完成并立即可用的。然而在某些情况下,可能需要我们手工做一些额外的初始化或者销毁操作,这通常是针对一些资源的获取和释放操作。

Spring 2.5 在保留以上两种方式的基础上,提供了对 JSR-250 的支持。JSR-250 规范定义了两个用于指定声明周期方法的注解:@PostConstruct 和 @PreDestroy。这两个注解使用非常简单,只需分别将他们标注于初始化之后执行的回调方法或者销毁之前执行的回调方法上。由于使用了注解,因此需要配置相应的 Bean 后处理器,亦即在 XML 中增加如下一行:

比较上述三种指定生命周期回调方法的方式,第一种是不建议使用的,不但其用法不如后两种方式灵活,而且无形中增加了代码与框架的耦合度。后面两种方式开发者可以根据使用习惯选择其中一种,但是最好不要混合使用,以免增加维护的难度。

reference


2017-08-13 Spring , Bean , Java , DI , IoC

Java enum 相等比较 == or equal

能否使用 == 来针对 enum 来比较?

答案是:YES, 枚举谨慎的实例化管理允许使用 == 来进行比较,JLS 8.9 Enums 中有Java 语言的规范定义:

枚举类型除了定义时的枚举常量外没有其他实例

如果显示的实例化枚举类型,会产生编译时异常。final clone 方法保证了 Enum 变量不会被 clone, 序列化的机制也保证了重复的实例在反序列化时不会创建额外的枚举变量。通过反射实例化 Enum 类型是被禁止的。所有这四种方式确保了 enum 类型不存在额外的实例,除了定义时的常量

因为每一个枚举常量只有一个实例,因此使用 == 来代替 equals 方法来比较两个枚举是被允许的.

== 和 equals 的区别

使用 == 来代替 equals 也存在两点需要注意的问题。

== 不会抛出空指针异常

enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);      // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException

不过 == 有编译时类型检查,这一点还是很不错的

enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };

if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine
if (Color.BLACK == Chiral.LEFT);      // DOESN'T COMPILE!!! Incompatible types!

总结

对于不可变类,并且该类能够有效的控制实例数量, == 就是可用的。

在 Effective Java 一书中指出:

考虑静态工厂方法代替构造器,它使得不可变类可以确保不存在两个相等的实例,即当且仅当 a==b 时才有 a.equals(b) 成立,如果类能够保证这一点,它的客户端可以使用 == 来代替 equals 方法,这样可以提升性能,而枚举类型可以保证这一点。

所以在枚举比较中可以使用 ==

  1. 可以正常工作
  2. 更快
  3. 运行时安全
  4. 编译期也是安全的。

reference


2017-08-10 Java , enum

在 Ubuntu 下安装并使用 Cinnamon

Ubuntu 16.04 LTS 或者 Ubuntu 17.04 下可以通过 PPA 来安装 Cinnamon,感谢维护者

命令如下:

sudo add-apt-repository ppa:embrosyn/cinnamon
sudo apt update && sudo apt install cinnamon

当安装完成之后,Log out 或者 重启,在登录界面选择 Cinnamon 来使用。

我在使用一段时间之后才发现没有安装 Nemo 的插件,以至于右击都没有压缩的选项,通过一下步骤安装 Nemo 以及相关套件。

安装 Nemo

sudo add-apt-repository ppa:noobslab/mint
sudo apt-get update
sudo apt-get install nemo

安装插件

sudo apt-get install nemo-compare nemo-dropbox nemo-fileroller nemo-pastebin nemo-seahorse nemo-share nemo-preview nemo-rabbitvcs

安装完成之后退出 nemo :

nemo -q

然后重启 nemo 即可。

reference


2017-08-07 Ubuntu , Linux , Cinnamon , LinuxMint

git common alias

之前有写过 Git alias 的文章, 不过过去很多时间,现在对Git命令越来越熟悉就希望更快的提高输入效率,也越来越感受到 alias 的重要性,不管是直接在bash中的 alias 还是 Git 的alias。所以准备找一些合适的 alias 添加到自己的 gitconfig 文件中长期使用。

添加 alias

使用命令的方式添加

git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.br branch
git config --global alias.hist "log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short"
git config --global alias.type 'cat-file -t'
git config --global alias.dump 'cat-file -p'

git status, git add, git commit, git checkoutgit branch 是最常见的 git 命令,给他们设置 alias 能提高不少效率。使用以上命令添加 alias ,其实作用等同于直接编辑 HOME 目录下的 gitconfig 文件, vim ~/.gitconfig:

[alias]
  co = checkout
  ci = commit
  st = status
  br = branch
  hist = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short
  type = cat-file -t
  dump = cat-file -p

如果这样设置之后就可以使用 git co <branch> 来切换分支了。

bash alias

可以在 ~/.bashrc 或者 ~/.zshrc 中设置 alias g=git 这样就可以使用 g checkout 来代替 git checkout 了。

reference


2017-08-04

Google+

最近文章

  • 使用mutt在Bash中发送邮件及附件 在编写定时备份脚本时遇到一个需求,就是在 Bash 脚本中发送带附件的邮件。于是找到了 mutt。
  • 在 Spring Boot 中使用 Swagger 在使用 Spring Boot 构建一套 RESTful 接口的时候经常需要手工维护一份接口文档以提供给不同的客户端使用,有的时候手工维护成本太高,今天发现了一套自动化生成 RESTful 接口文档的工具 Swagger 。
  • Linux 查看磁盘信息命令 di 平时在 Linux 上查看磁盘信息都使用 df -lh , -l 显示本地文件系统, -h 来表示 human readable 。虽然 df 在一定程度上能够满足查询磁盘剩余空间的需求,但是这里要介绍一款强大于 df 的命令 —- di 。
  • Maven 介绍 Maven 是一个项目管理工具,主要用于项目构建,依赖管理,项目信息管理。自动化构建过程,从清理、编译、测试和生成报告、再到打包和部署。Maven 通过一小段描述信息来管理项目。
  • IntelliJ IDEA 中使用 Resin 调试 平时开发环境使用的是 jetty,而 Java Web 有一个更好更快的服务器 Resin,这篇文章就来说一下什么是 Resin,以及在 Debug 中如何使用。