树莓派官网有很多系统可以选择,我选了官方维护的 Raspbian 基于 Debian 的衍生版,主要是熟悉他的 APT 包管理,看评价三方维护的 Snappy Ubuntu Core 换用了其他的 snap 的管理,不是很了解,所以还是选择了 Raspbian。
官网提供的教程非常方便, 采用开源的镜像烧录工具 Etcher 非常方便的就可以在三大平台上完成镜像到 SD 的烧录。当然如果熟悉各个平台的工具也可以自己手动完成烧制。
在将系统写入 microSD 卡之后,将卡插入树莓派板子,启动树莓派,开机即可,可以用 HDMI 接口连接显示器,用一个外接键盘来输入。树莓派的默认用户名是: pi
,默认密码为: raspberry
。
使用如下命令给 root 账户设置密码并允许登录
sudo passwd root
# 然后输入密码
# 用同样的方式修改默认账户 pi 的密码
sudo passwd pi
raspbian 自带 SSH ,启动
sudo service ssh start
raspi-config
运行该命令可以看到一系列的选项,比如修改 Hostname ,修改密码等等选项,可以配置一下。
换用清华的源 : https://mirror.tuna.tsinghua.edu.cn/help/raspbian/
apt install vim
apt install samba samba-common-bin
apt install zsh
apt-get install nginx
sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
chsh -s /bin/zsh
apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
xz-utils tk-dev
curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
apt install mysql-server
apt-get install libmysqlclient-dev
Redis 在设计上,是用来被可信客户端访问的,也就意味着不适合暴露给外部环境非可信客户端访问。
最佳的实践方法是在 Redis 前增加一个访问控制层,用于校验用户请求。
Redis 本身提供了一些简单的配置以满足基本的安全控制。
requirepass
以明文的形式配置在 conf 文件里的,所以要尽可能得长和复杂,降低被破解的风险。因为 redis 非常快,外部环境可以在一秒内 150k 次暴力破解,所以配置密码一定要复杂。对于直接暴露在互联网的 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
命令是明文传输的,所以依然不能阻止那些获得网络访问权限的攻击者嗅探。
很久没有更新这个分类下的文章了,其实一直在体验不同的产品,只是真的很少有能拿出来讲一下的东西。不管是硬件还是软件,最近几年使用的东西越来越狭窄,越来越收缩,当然对于某一个特定的需求,总有一个产品能够占领绝大多数市场,而也有部分产品能够瓜分小众市场。这里要介绍的这个 NodeQuery 就不是一个大而全的产品,而是一个很精细的小众产品。我用它也一年多了,我的需求很简单,能够实时监控我的 VPS,能够在宕机或者高负载时报警。NodeQuery 完全能够满足我的需求。
用 NodeQuery 自己的话描述自己就是:”一个轻量、易用的 Linux 服务器监控服务”.
NodeQuery provides a lightweight and easy to use Linux server monitoring service.
NodeQuery 免费账户可以提供 10 台机器的监控,
官网地址: https://nodequery.com/
同样使用也非常方便,新建 Server,然后会给出一个一键脚本,在自己的 VPS 上一键安装就行,脚本同样是开源的托管在 GitHub 上。人人都可以审查。
2021 年 10月更新
很多年没有上 NodeQuery 查看,发现 NodeQuery 已经不更新了,这里记录一下移除 NodeQuery 的命令:
rm -R /etc/nodequery && (crontab -u nodequery -l | grep -v "/etc/nodequery/nq-agent.sh") | crontab -u nodequery - && userdel nodequery
这个网站也能够提供 API 支持,能够读取历史情况下 Server 的状态,目前写功能暂时还无法使用。
不过需要提醒的是,这个网站自从 2014 年起就再没有更新,不清楚背后发生了什么事情,但是也是感到非常的可惜。
许多使用静态语言比如 C、 C++ 或者 Java 的人,在转到 Python 的时候可能第一个会疑惑的就是 Python 不需要显示的指定类型,那么 Python 是怎么知道变量的类型呢?
在 Python 中,变量的创建遵循着一个非常合理的方式,以 a=3
来举例子:
变量创建
一个变量(名字)比如 a
,当第一次被赋值时被创建。
变量类型 Variable Types
一个变量永远不会有任何类型信息或者约束,类型的概念和 Object 关联,而不是变量名字。变量都是通用的(泛型),变量总是在特定时间指向一个特定的 Object 。
变量使用 Variable use
当变量出现在表达式中,他会立即使用当前指向的 Object 替换。因此,所有的变量在使用之前都必须显式的被赋值,当使用未被赋值的变量时会产生错误。
总结来说,变量会在赋值时被创建,并且能够指向任何类型的对象,在引用前必须已经被赋值。这就是动态类型模型和传统静态语言最显著的差别。
所以对于 a=3
Python 会执行三个完全不同的步骤来完成,下面的步骤显示了 Python 语言中所有赋值会执行的步骤:
a
a
连接到对象 3变量和对象会分别保存到不同的内存中。变量永远会指向 Objects,永远不会指向其他变量,但是大型的 Objects 可能会有指向其他 Objects 的链接(比如 list 对象就可能会有很多指向其他对象的链接)。
在 Python 中,这些从变量指向对象的链接被称为引用 references ,也就是说引用是一种关联,在内存中表现为指针。每当一个变量被使用, Python 会自动跟随着 variable to object 链接。
所以这些术语理解起来要简单得多,具体来讲:
概念上讲,每一次创建新的值, Python 都会创建新的对象(开辟内存空间)来表示值。内部来说,作为优化,Python 会使用缓存重用一些特定的不会改变的对象,比如比较小的 integers,strings,比如 0 并不会单独每一次都开辟内存空间,而是使用缓存。但是从逻辑上,每一个表达式的结果都会为不同的对象,每一个对象都有自己的内存空间。
每一个对象都有两个标准的 header fields:type designator
用来表明对象的类型,reference counter
来记录引用次数,何时对象应该被回收。
Types live with Objects, Not Variables
当对同一个变量重复赋值,那么变量之前指向的 Object 会发生什么?
a = 3
a = 'Spam'
当 a 被重新赋值为 Spam 时,对象 (3) 会发生什么。在 Python 中,当一个变量名被赋值到新对象时,之前对象的空间会被回收(也就是说当当一个对象不再被变量或者其他对象引用时会被回收)。这种自动回收对象空间的机制被称为 垃圾回收 (garbage collection)。
内部实现来说,Python 通过在每一个 Object 中存储一个 counter,来追踪当前对象被引用的次数来实现垃圾回收。一旦引用计数变为 0,对象的内存空间就会被自动回收。
上面的内容都是单一变量,如果出现变量赋值比如
a = 3
b = a
这个时候 Python 会怎么处理呢? 当变量 b 被创建时,会创建一个从 b 指向对象 3 的引用。这个有多个变量名字指向相同的对象的场景,被称为 shared reference
在上面的基础上,如果
a = 'spam'
a 被重新赋值 spam
这个时候不会影响 b 指向的对象 3.
如果
a = a + 2
同样不会影响 b 的值,对象 integer 5 会作为加号的结果放到新的内存空间,是一个新的对象,所以也不会改变 b 的值。事实上,也没有任何方法可以改变对象 3 的值,就像之前关于 Python 类型一文 中说的那样,integer 是不可变类型,因此不能原地修改其内容。
当使用可变对象,比如 list 时,如果存在共享引用,要特别注意,当修改其中一个引用的对象的值时,会影响其他指向这个对象的引用。
L1 = [2,3,4]
L2 = L1
L1[0] = 'spam'
这个时候 L1,L2 变量指向的对象值都被改变了。
如果需要深度拷贝 list 时,就需要特别注意,不要使用引用。
L1 = [2,3,4]
L2 = L1[:] # 创建了一个 L1 的拷贝
L1[0] = 'spam' # 此时再修改 L1 则不会对 L2 造成影响
对 L1 的修改不会影响 L2 ,因为 L2 指向和 L1 完全不同的内存区域。
import copy
X = copy.copy(Y) # make top-level "shadow" copy of any object Y
X = copy.deepcopy(Y) # make deep copy of any object Y: copy all nested parts
因为 Python 的引用模型,所有有两种完全不同的判断相等的方法
L = [1,2,3]
M = L
L == M # Same 这时候判断的是引用是否相同,判定值是否相同
L is M # Same is 操作符判断是指向的对象是否相同,更强的相等判断
更比如说
L = [1,2,3]
M = [1,2,3]
L == M # Same 相同的内容
L is M # False 不同的对象
再比如
X = 42
Y = 42 # 应该是两个不同的对象
X == Y # True
X is Y # 相同的对象,caching 在起作用
之前说过 Python 垃圾回收时,优化机制,导致 small integer 和 strings 会被缓存和重新使用,is
操作符告诉我们他们引用得是相同的对象。
事实上,如果想要知道对象的引用次数,可以使用 getrefcount
方法,在 sys
模块中。
import sys
sys.getrefcount(1)
检查对象 1 被引用的次数,常常会发现超过期待。因为 Integer 是不可变的,所以也就无所谓多少引用指向相同的对象了。
Python 很重要的一个概念 module,用来组织代码结构。
最终,这些路径都会存在 sys.path
中,是一个保存着一系列搜索路径的 list。
>>> import sys
>>> sys.path
一个目录的 Python code 被称为 package,这样的导入被成为 package import。
import dir1.dir2.mod
from dir1.dir2.mod import x
文件目录结构可能是
dir0\dir1\dir2\mod.py
每一个 package 被定义时都会产生一个 __init__.py
的文件,该文件可以像普通文件一样包含 Python 代码,也可以为空。
Package 初始化
当 Python 导入 Package 时,会自动跑 `__init__.py` 下的内容,所以 `__init__.py` 文件下是天然的存放初始化内容的地方,比如初始化数据库连接等等。
如果定义了 __all__
,在 from *
时导入,就会选择性导入指定内容
__all__ = ["Error", "encode", "decode"]
从 WizNote 中整理。
POJO, Plain Old java object, 最简单的 Java 对象
[[Dependency Injection]] 带来的最大好处,松耦合,如果一个对象只通过接口(而不是具体实现或初始化过程)来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。
AOP aspect-oriented programming, 面向切面编程允许将遍布应用各处的功能分离出来形成可重用的组件
依赖注入让互相协作的软件组件保持松散耦合,而 AOP 则是让遍布各处的功能分离出来形成可重用的组件。
Spring 框架提供约 20 个模块。
由核心,Bean,上下文,表达式语言模块
数据访问 / 集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成
还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块
DispatcherServlet 是前置控制器,配置在 web.xml 文件中的。拦截匹配的请求,Servlet 拦截匹配规则要自已定义,把拦截下来的请求,依据相应的规则分发到目标 Controller 来处理,是配置 spring MVC 的第一步。
视图名称解析器
@Controller
负责注册一个 bean 到 spring 上下文中,类名前加此注解,告知 Spring 容器这是一个控制器组件,负责注册一个 bean 到 spring 上下文中
Controller 注解示例
@Controller
@RequestMapping("/mvc")
public class mvcController {
@RequestMapping("/hello")
public String hello(){
return "hello";
}
}
@Controller
@RequestMapping("/rest")
public class RestController {
@RequestMapping(value="/user/{id}",method=RequestMethod.GET)
public String get(@PathVariable("id") Integer id){
System.out.println("get"+id);
return "/hello";
}
@RequestMapping(value="/user/{id}",method=RequestMethod.POST)
public String post(@PathVariable("id") Integer id){
System.out.println("post"+id);
return "/hello";
}
@RequestMapping(value="/user/{id}",method=RequestMethod.PUT)
public String put(@PathVariable("id") Integer id){
System.out.println("put"+id);
return "/hello";
}
@RequestMapping(value="/user/{id}",method=RequestMethod.DELETE)
public String delete(@PathVariable("id") Integer id){
System.out.println("delete"+id);
return "/hello";
}
}
@RequestMapping
类方法前加,注解为 Controller 指定可以处理哪些 URL 请求
@RequestMapping(value = "/register", method = RequestMethod.POST)
三个常用属性:value,params,method
value 必填属性,代表请求的 url,支持模糊配置。(value 字可以省略,但是属性值必须填)
@RequestMapping(value="/users/**") 匹配"/users/abc/abc";
@RequestMapping(value="/product?") 匹配"/product1"或"/producta",但不匹配"/product"或"/productaa";
@RequestMapping(value="/product*") 匹配“/productabc”或“/product”,但不匹配“/productabc/abc”;
@RequestMapping(value="/product/*") 匹配“/product/abc”,但不匹配“/productabc”;
params 可选属性,代表对请求参数进行过滤
@RequestMapping(value="/login.do",params="flag") 代表请求中必须要有名为 flag 的提交项
@RequestMapping(value="/login.do",params="!flag") 代表请求中不能有名为 flag 的提交项
@RequestMapping(value="/login.do",params="flag=hello") 代表请求中必须有名为 flag 的提交项,且值为 hello
@RequestMapping(value="/login.do",params="flag!=hello") 代表请求中如果有名为 flag 的提交项,其值不能为 hello
@RequestMapping(value="/login.do",params={"flag1","flag2=hello"}) 代表请求中必须有名为 flag1 的提交项,同时必须有名为 flag2 的提交项,且 flag2 的值必须为 hello
method 可选属性,代表请求方式
@RequestMapping(value="/login.do",method=RequestMethod.POST)
@RequestMapping(value="/login.do",method=RequestMethod.GET)
@RequestMapping(value="/login.do", method= {RequestMethod.POST, RequestMethod.GET}"
@RequestBody 该注解用于读取 Request 请求的 body 部分数据,使用系统默认配置的 HttpMessageConverter 进行解析,然后把相应的数据绑定到要返回的对象上 , 再把 HttpMessageConverter 返回的对象数据绑定到 Controller 中方法的参数上
@ResponseBody 该注解用于将 Controller 的方法返回的对象,通过适当的 HttpMessageConverter 转换为指定格式后,写入到 Response 对象的 body 数据区
@ModelAttribute 在方法定义上使用 @ModelAttribute 注解:Spring MVC 在调用目标处理方法前,会先逐个调用在方法级上标注了 @ModelAttribute 的方法
在方法的入参前使用 @ModelAttribute 注解:可以从隐含对象中获取隐含的模型数据中获取对象,再将请求参数 –绑定到对象中,再传入入参将方法入参对象添加到模型中
@RequestParam 在处理方法入参处使用 @RequestParam 可以把请求参数传递给请求方法
@RequestMapping(value = "/check", method = RequestMethod.GET)
public
@ResponseBody
String check(@RequestParam(value = "signature", required = true, defaultValue = "") String signature,
@RequestParam(value = "timestamp", required = true, defaultValue = "") String timestamp,
@RequestParam(value = "nonce", required = true, defaultValue = "") String nonce,
@RequestParam(value = "echostr", required = true, defaultValue = "") String echostr,
HttpServletRequest request,
HttpServletResponse response
) {
response.addHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
if (checkSignature(signature, timestamp, nonce)) {
return echostr;
}
return "";
}
@PathVariable
绑定 URL 占位符到参数
@ExceptionHandler
注解到方法上,出现异常时会执行该方法
@ControllerAdvice
使 Contoller 成为全局的异常处理类,类中用 @ExceptionHandler 方法注解的方法可以处理所有 Controller 中发生的异常
@ControllerAdvice
public class GlobalExceptionHandler {
private static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(value = Exception.class)
@ResponseBody
public void exceptionHandler(HttpServletRequest req, Exception e) throws Exception {
//todo add request info to log
logger.error("error: {}", e);
return;
}
}
自动装配主要使用 @ComponentScan、@Component 和 @Autowired。
@Autowired
@Resource
参数绑定注解
@PathVariable
当使用 @RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的 paramId 可通过 @Pathvariable 注解绑定它传过来的值到方法的参数上。
示例代码:
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping("/pets/{petId}")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
上面代码把 URI template 中变量 ownerId 的值和 petId 的值,绑定到方法的参数上。若方法参数名称和需要绑定的 uri template 中变量名称不一致,需要在 @PathVariable(“name”) 指定 uri template 中的名称。
@RequestHeader、@CookieValue
@RequestHeader 注解,可以把 Request 请求 header 部分的值绑定到方法的参数上。
示例代码:
这是一个 Request 的 header 部分:
Host
localhost:8080
Accept text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
代码:
@RequestMapping("/displayHeaderInfo.do")
public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Keep-Alive") long keepAlive) {
//...
}
上面的代码,把 request header 部分的 Accept-Encoding 的值,绑定到参数 encoding 上了, Keep-Alive header 的值绑定到参数 keepAlive 上。
@CookieValue 可以把 Request header 中关于 cookie 的值绑定到方法的参数上。
例如有如下 Cookie 值:
JSESSIONID=415A4AC17
参数绑定的代码:
@RequestMapping("/displayHeaderInfo.do") public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) { //... } 即把 JSESSIONID 的值绑定到参数 cookie 上。
@RequestParam, @RequestBody
@RequestParam
示例代码:
@Controller
@RequestMapping("/pets")
@SessionAttributes("pet")
public class EditPetForm {
@RequestMapping(method = RequestMethod.GET)
public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
}
@RequestBody
该注解常用来处理 Content-Type: 不是 application/x-www-form-urlencoded 编码的内容,例如 application/json, application/xml 等;
它是通过使用 HandlerAdapter 配置的 HttpMessageConverters 来解析 post data body,然后绑定到相应的 bean 上的。
因为配置有 FormHttpMessageConverter,所以也可以用来处理 application/x-www-form-urlencoded 的内容,处理完的结果放在一个 MultiValueMap<String, String>里,这种情况在某些特殊需求下使用,详情查看 FormHttpMessageConverter api;
示例代码:
@RequestMapping(value = "/something", method = RequestMethod.PUT) public void handle(@RequestBody String body, Writer writer) throws IOException { writer.write(body); }
4、@SessionAttributes, @ModelAttribute
@SessionAttributes:
该注解用来绑定 HttpSession 中的 attribute 对象的值,便于在方法中的参数里使用。
该注解有 value、types 两个属性,可以通过名字和类型指定要使用的 attribute 对象;
示例代码:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... }
@ModelAttribute
该注解有两个用法,一个是用于方法上,一个是用于参数上;
用于方法上时: 通常用来在处理 @RequestMapping 之前,为请求绑定需要从后台查询的 model;
用于参数上时: 用来通过名称对应,把相应名称的值绑定到注解的参数 bean 上;要绑定的值来源于:
A) @SessionAttributes 启用的 attribute 对象上;
B) @ModelAttribute 用于方法上时指定的 model 对象;
C) 上述两种情况都没有时,new 一个需要绑定的 bean 对象,然后把 request 中按名称对应的方式把值绑定到 bean 中。
用到方法上 @ModelAttribute 的示例代码:
// Add one attribute // The return value of the method is added to the model under the name "account" // You can customize the name via @ModelAttribute("myAccount") @ModelAttribute public Account addAccount(@RequestParam String number) { return accountManager.findAccount(number); }
这种方式实际的效果就是在调用 @RequestMapping 的方法之前,为 request 对象的 model 里 put(“account”, Account);
用在参数上的 @ModelAttribute 示例代码:
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute Pet pet) { }
首先查询 @SessionAttributes 有无绑定的 Pet 对象,若没有则查询 @ModelAttribute 方法层面上是否绑定了 Pet 对象,若没有则将 URI template 中的值按对应的名称绑定到 Pet 对象的各属性上。
补充讲解:
问题: 在不给定注解的情况下,参数是怎样绑定的?
通过分析 AnnotationMethodHandlerAdapter 和 RequestMappingHandlerAdapter 的源代码发现,方法的参数在不给定参数的情况下:
若要绑定的对象时简单类型: 调用 @RequestParam 来处理的。
若要绑定的对象时复杂类型: 调用 @ModelAttribute 来处理的。
这里的简单类型指 Java 的原始类型 (boolean, int 等)、原始类型对象(Boolean, Int 等)、String、Date 等 ConversionService 里可以直接 String 转换成目标对象的类型;
RequestMappingHandlerAdapter 中使用的参数绑定,代码稍微有些不同,有兴趣的可以分析下,最后处理的结果都是一样的。
示例:
@RequestMapping ({"/", "/home"}) public String showHomePage(String key){ logger.debug("key="+key);
return "home";
}
这种情况下,就调用默认的 @RequestParam 来处理。
@RequestMapping (method = RequestMethod.POST) public String doRegister(User user){ if(logger.isDebugEnabled()){ logger.debug("process url[/user], method[post] in "+getClass()); logger.debug(user); } return "user"; }
这种情况下,就调用 @ModelAttribute 来处理。
一般的注解,比如常见的 @Override
是 Java 从 1.5 版本开始引入,注解一般用来对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等等进行注解,他的作用一般分为如下四个方面:
一般的注解可以分为三类:
Spring 中的注解大概可以分为两大类:
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 中 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. 这是一个简单的使用 注解 和 过滤器 来进行权限处理的例子。扩展开来,那么我们就可以使用注解,来表示某方法或者类,只能被具有某种角色,或者具有某种权限的用户所访问,然后在过滤器中进行判断处理。
@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 中注解的处理基本都是通过实现接口 BeanPostProcessor 来进行的:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
相关的处理类有: AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor, RequiredAnnotationBeanPostProcessor
这些处理类,可以通过 <context:annotation-config/>
配置隐式的配置进 Spring 容器。这些都是依赖注入的处理,还有生产 bean 的注解 (@Component, @Controller, @Service, @Repository) 的处理:
<context:component-scan base-package="net.aazj.service,net.aazj.aop" />
这些都是通过指定扫描的基包路径来进行的,将他们扫描进 Spring 的 bean 容器。注意 context:component-scan 也会默认将 AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor 配置进来。所以
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 接口的接口
这篇文章总结一下 Python 的内置类型。
类型
Object type | Example literals/creation |
---|---|
Numbers | 1234 , 3.1415 , 3+4j , Decimal , Fraction |
Strings | ‘spam’ , “guido’s” , b’a\x01c’ |
Lists | [1, [2, ‘three’], 4] |
Dictionaries | {‘food’: ‘spam’, ‘taste’: ‘yum’} |
Tuples | (1, ‘spam’, 4, ‘U’) |
Files | myfile = open(‘eggs’, ‘r’) |
Sets | set(‘abc’), {‘a’, ‘b’, ‘c’} |
Other core types | Booleans, types, None |
Program unit types | Functions, modules, classes |
Implementation-related types | Compiled code, stack tracebacks |
可以表示整形,浮点数,分数等等,甚至可以用来表示非常大的数,比如 2 ** 10000
不同进制表示
0x1234 0X1234 # 16 进制 0x 后接 [0-9A-F]
0o177 0O177 # 8 进制 0o Zero 加大小写的 o 后接 [0-7]
0b101 0B101 # 2 进制 New in 2.6 > ,后接 [0-1]
内置的 hex(number)
, oct(number)
, bin(number)
将 int 转变为这三种进制的字符串。
字符串在 Python 中支持切片的操作,比如 S = 'Spam'
,这时 S[-1]
表示的是最后一个字符 m
。
S[1:3] # 'pa' 左边包括,右边不包括
S[1:] # 'pam' [1:len(S)]
S[:3] # 'Spa' 等效于 [0:3]
S[:-1] # 除去最后一个元素
S[:] # S 全部
字符串重复操作可以使用 S * 10
打印 10 遍 S.
字符串和 Java 一样是不可变对象,Numbers 和 Tuples 也是不可变的。
常用操作
S.find('pa') # 输出字串位置 1
S.replace('pa', 'xy') # 替换 pa 为 xy 输出到新的字符串,不改变原始字符串
line = 'hello world'
line.split(' ') # 空格分割,输出列表
line.upper() # 转为大写
line.isdigit() # 判断是否为数字,还有 isalpha() isnumeric() isspace() 等等
line.rstrip() # 移除行尾空白字符比如 `\n`
格式化字符串
'%s, eggs, and %s' % ('spam', 'SPAM!') # Python 格式化表达式
# or
'%s, eggs, and %s' % ('spam', 'SPAM!') # Python 2.6 and 3.0
对于任何一个对象,都可以调用内置方法 dir(S)
来查看相关属性和方法。dir 方法可以用来快速查看对象的可调用方法,所以记不住方法的名字也不需要担心,使用 dir() 方法即可。
对于任何方法的使用,可以用 help(S.replace)
查看。
说到字符串处理就不可避免的要谈到正则
>>> import re
>>> match = re.match('Hello[ \t]*(.*)world', 'Hello
>>> match.group(1)
'Python '
>>> match = re.match('/(.*)/(.*)/(.*)', '/usr/home/einverne')
>>> match.groups()
('usr', 'home', 'einverne')
基本操作,可 Strings 类似也都支持切片,下标索引等等
L = [123, 'spam', 1.23]
L[0]
L[:-1]
L + [4,5,6]
Lists 不同于其他熟悉的语言,可以承载不同的类型,比如上面的例子,L 中就有三种完全不同的类型,Lists 没有特定的大小,可以根据需求调整长度。
L.append('NI') # 添加
L.pop(2) # 删除 index 为 2 的元素,并返回
L.insert(1, 'xyz') # 在 index 之前插入 'xyz'
L.remove('xyz') # 按照值移除第一个找到的 item
另外 list 的 sort()
和 reverse()
方法分别为 list 排序,逆序,直接改动 list 自身。
循环嵌套,比如表示 3 × 3 矩阵
>>> M = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
>>> M
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
在获取值的时候可以
>>> M[1] # 获取第二行
>>> M[1][2] # 获取第二行第三列
可以使用 List comprehensions 来获取列
>>> col2 = [row[1] for row in M] # 获取第二列
这句话表达的意思就是,将矩阵 M 中每一行的 row[1]
第二个元素放到一个新的 list 中返回。这个表达式甚至可以更加复杂,比如在返回时每个元素乘以二。
>>> [row[1] * 2 for row in M]
或者加入判断,只有偶数才返回
>>> [row[1] for row in M if row[1] % 2 == 0]
List comprehensions 可以用在任何可以迭代的对象上,比如返回 M 矩阵对角线元素
>>> diag = [M[i][i] for i in range(3)]
>>> [c * 2 for c in 'spam'] # 输出一个列表 ['ss', 'pp', 'aa', 'mm']
在 Python 3.0 及以上,comprehension 语法也可以用来创建 set 或者 dict
{sum(row) for row in M} # {sum(row) for row in M} 创建一个每一行和的 set
{i : sum(M[i]) for i in range(3)} # {0: 6, 1: 15, 2: 24} dict
基本操作
D = {}
D['name'] = 'bob'
dict 也同样支持嵌套,value 值可以为不同类型。
>>> rec = {'name': {'first': 'Bob', 'last': 'Smith'},
'job': ['dev', 'mgr'],
'age': 40.5}
构造这样一个复杂结构的 dict 在 Python 中非常轻松,但是如果在 C 中将会需要非常多的 coding。在一个 lower-level 的语言中我们需要非常小心释放变量内存空间,但是在 Python 中当丢失对象的引用时,内存空间会自动被释放。
>>> rec = 0
Python 也有自己的垃圾回收机制,在 Python 中当对象的最后一个引用丢失时,空间会立即被回收。
使用 in
来检查 key 是否在 dict 中
if 'f' in D:
print D['f']
或者在 Python 3 可以使用 get() 方法,来避免获取一个不存在的 key 可能引发的错误
D.get('f', 0) # 如果 f 存在返回 D['f'],否则返回 0,等效于
D['f'] if 'f' in D else 0
元组,像 lists 一样的序列,但是像 string 一样不可变。支持任意的类型,任意的嵌套,和序列一样。
T = (1, 2, 3, 4)
T = ('spam', 3.0, [11, 22, 33])
文件类型是 Python 用来和外部文件访问的重要接口。使用内置的 open()
函数来创建文件对象。
f = open('data.txt', 'w')
f.write('Hello\n')
f.close()
f = open('data.txt') # 'r' 可以省略
text = f.read()
f.close()
在 Python 3 中区分了 text 和 binary data,Text files 代表字符串内容并且以 Unicode 形式可以 encode 和 decode。而 binary files 代表特殊的 bytes 并且允许用户直接访问无修改的文件内容。
集合
X = set('spam')
Y = {'h', 'a', 'm'}
X & Y # 交集
X | Y # 并集
X - Y # 差集
Decimal
import deciaml
d = decimal.Decimal('3.141')
>>> decimal.getcontext().prec = 2
>>> decimal.Decimal('1.00') / decimal.Decimal('3.00')
Decimal('0.33')
>>> from fractions import Fraction
>>> f = Fraction(2, 3)
>>> f + 1
Fraction(5, 3)
>>> f + Fraction(1, 2)
Fraction(7, 6)
if type(L) == type([]):
print('yes')
if type(L) == list:
print('yes')
if isinstance(L, list):
print('yes')
不过需要记住,代码少用这些类型检查。
In Python, we code to object interfaces (operations supported), not to types.
Python 中所有的一切都是 Object,所以上面提到的所有类型都是 Object。
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.
为了实现依赖注入 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 或者 constructor。为了防止在项目中实现同一个接口,或者一系列子类,可以使用 @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
寻找的优先顺序为
而 @Autowired
and @Inject
寻找的顺序为
@Resource 如果没有指定 name 属性,当注解标注在 field 上,默认取字段名称作为 bean 名称寻找依赖对象;当标注在属性 setter 方法上,默认取属性名作为 bean 名称寻找依赖。如果没有指定 name 属性,并且按照默认名称找不到依赖对象时,回退到类型装配。
https://github.com/google/guice/
Google 提供的轻量级依赖注入框架,支持 Java 6 及以上
http://square.github.io/dagger/
为 Android 和 Java 设计的 DI