PicoContainer学习手册
2005-08-12 09:57
281 查看
PicoContainer学习手册
Author:Kongxx
当前PicoContainer版本为V1.1,可以通过 http://picocontainer.codehaus.org/地址来访问并下载。
Ø PicoContainer是一种非侵入式的组件框架,用户的组件不需要实现任何特殊的API,甚至通常我们可以使用普通的POJO对象;
Ø 内建的生命周期管理,可以方便的管理组件的生命周期,通常我们的组件只需要实现Startable接口即可以实现由容器管理的生命周期管理;
Ø 非常灵活的设计,允许对核心功能进行任何形式的扩展;
Ø 是代码更简洁,提高代码的可测试性和可配置性;
具体程序代码如下:
User.java
UserDAO.java
UserDAOImpl.java
UserService.java
Test.java
运行Test,输出如下
1. 创建一个PicoContainer容器对象,
MutablePicoContainer pico = new DefaultPicoContainer();
在此方法中以后的操作均是针对此容器实例;
2. 创建一个User Bean实例;
3. 向容器中注册组件,
pico.registerComponentImplementation(UserDAOImpl.class);
pico.registerComponentImplementation(UserService.class);
此处注册的两个组件为UserDAOImpl和UserService,这两个类均是具体的类,UserDAOImpl类是UserDAO接口的实现,并且在UserService的构造函数中需要使用UserDAO接口,即UserServer组件依赖UserDAO;
4. 从容器中获取一个UserService组件的对象实例,
UserService us = (UserService) pico.getComponentInstance(UserService.class);,其中UserService对象具体是怎样构造的将由PicoContainer容器全权负责,容器负责在容器内部查找UserDAO接口的实现,然后传递给UserService的构造函数来构造对象实例;
5. 调用UserService对象的具体方法。从运行输出的结果来看,容器确实创建了UserService对象实例,并且执行的其相关的方法。
A.java
B.java
AB.java
Test.java
A.java
B.java
AB.java
Test.java
Ø 一个具体的实现类,如:registerComponentImplementation(Class c);
Ø 一个对象实例,如:registerComponentInstance(Object o);
Ø 一个ComponentAdapter对象实例,如:registerComponent(ComponentAdapter ca)。
A.java
B.java
AB.java
Test.java
运行Test.java,输出如下:
此处A,B是两个简单的类,AB类是一个依赖于A,B的类,Test是一个测试类,通过运行结果可以得出以下结论:
Ø 容器可以实现继承,容器可以无限级的向下继承;
Ø 子容器中的组件可以依赖于父容器中的组件;
Ø 父容器中的组件不可以依赖子容器中的组件;
Ø 两个子容器中的组件不可以互相依赖;
IService.java提供服务规约,只有一个方法service():
AbstractService.java是一个抽象类,实现了IService接口,其中包含了一个成员变量Configuration对象,用来访问系统配置信息:
Service1.java是实现了AbstractService抽象类的具体类,其中实现了service()方法,仅仅向控制台输出简单信息,同时每个Service服务提供一个构造函数,用来初始化内部的Configuration对象,即实现了依赖注入:
Service2.java
Configuration.java系统配置接口:
DefaultConfiguration.java提供对Configuration接口的默认实现:
Test.java测试类:
在Test类中有一个私有MutablePicoContainer容器parent,然后在构造函数初始化并将DefaultConfiguration注入到parent中,然后在testService1和testService2中分别定义了一个新的继承自parent的MutablePicoContainer,并将Service1和Service2分别注入到各自的容器中,然后从各自的容器中获取实例并调用相应的service()方法。此处Service1和Service2都依赖于Configuration接口。
运行Test,输出如下:
PicoContainer是典型的访问者模式的应用,详细见GoF的Visitor模式。以下是PicoContainer中Visitor模式中用到的两个主要的接口和类。
PicoVisitor.java
LifecycleVisitor.java
Ø DefaultPicoContainer.start()将调用LifecycleVisitor.start(Object node)方法,此方法是一个静态方法,实际的执行情况是,构造了一个LifecycleVisitor对象,然后又调用LifecycleVisitor对象的traverse(Object node)方法,由此方法执行具体的操作;
Ø traverse(Object node)方法执行以下两个步操做,首先调用父类AbstractPicoVisitor.traverse(Object node)方法,此方法通过反射调用容器的accept(PicoVisitor visitor)方法,此时accept将遍历当前容器中相关的组件和子容器,然后调用其相关的accept(PicoVisitor visitor)方法,这是使用一个递归的方法注入PicoVisitor对象;然后遍历所有组件的start()方法,开始组件的生命周期。
Configuration.java和DefaultConfiguration.java内如不变,其它类代码如下:
IService.java接口增加了对Startable接口的继承,如下:
AbstractService.java
Service1.java增加了对start()和stop()方法的实现,这里仅仅是调用了原来的service()方法:
Service2.java增加了对start()和stop()方法的实现,这里仅仅是调用了原来的service()方法:
Test.java修改了testService1()和testService2 ()方法的实现,将调用改为由容器管理的生命周期方法:
运行Test,输出结果如下:
Author:Kongxx
1. PicoContainer介绍
1.1. 简介
PicoContainer是codehaus开源组织的一个子项目。它是一个轻量级的DI(Dependency Injection)组件容器。当前PicoContainer版本为V1.1,可以通过 http://picocontainer.codehaus.org/地址来访问并下载。
1.2. 功能特性
Ø 依赖注射模式的应用,他可以很好的管理组件与组件之间的依赖关联。像SpringFramework一样,它也支持Constructor Injection(Type3)和Setter Injection(Type2)两种注入方式,但PicoContainer内部默认使用Constructor Injection(Type3)注入方式;Ø PicoContainer是一种非侵入式的组件框架,用户的组件不需要实现任何特殊的API,甚至通常我们可以使用普通的POJO对象;
Ø 内建的生命周期管理,可以方便的管理组件的生命周期,通常我们的组件只需要实现Startable接口即可以实现由容器管理的生命周期管理;
Ø 非常灵活的设计,允许对核心功能进行任何形式的扩展;
Ø 是代码更简洁,提高代码的可测试性和可配置性;
2. 开始
2.1. 简介
以下是一个简单的小例子,用来说明PicoContainer的简单应用。它包括以下几个类(或接口):类名[/b] | 说明[/b] |
example1.bean.User | 一个普通的用户Bean。 |
example1.dao.UserDAO.java | 一个关于User的数据访问接口,定义了几个常见的用户操作。 |
example1.dao.UserDAOImpl.java | 一个关于User的数据访问接口的实现类,具体实现比较简单。 |
example1.service.UserService.java | 用户服务类,向上层服务提供接口。 |
example1.Test | 测试类 |
User.java
package example1.bean; public class User { private String userid ; private String username ; private String password ; public String getUserid() { return userid; } public void setUserid(String userid) { this.userid = userid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } |
package example1.dao; import java.util.List; import example1.bean.User; /** * 用户数据访问接口 */ public interface UserDAO { /** * 根据用户名查询一个用户对象。 * @param username 要查询的用户名 * @return 返回查询到的用户对象,如果未查到返回null */ public User findUser(String username) ; /** * 添加(持久化)一个用户。 * @param user 要添加的用户对象 * @return 返回持久化后的用户对象 */ public User addUser(User user); /** * 更新一个持久化的用户对象 * @param user 要更新的用户对象 * @return 返回更新后的用户对象 */ public User updateUser(User user) ; /** * 删除一个持久化的用户对象。 * @param user 要删除的用户对象 * @return true-成功 false-失败 */ public boolean removeUser(User user) ; /** * 列出所有用户对象。 * @return 返回所有用户对象 */ public List listUsers() ; } |
package example1.dao; import java.util.ArrayList; import java.util.List; import example1.bean.User; /** * 用户数据访问接口实现。 * 只做测试使用 */ public class UserDAOImpl implements UserDAO { public User findUser(String username) { System.out.println("find User " + username + " ..."); if(username.equals("kongxx")) { User user = new User(); user.setUserid("10000"); user.setUsername("kongxx"); user.setPassword("kongxx"); return user ; } return null; } public User addUser(User user) { System.out.println("add User ..."); return null; } public User updateUser(User user) { System.out.println("update User ..."); return null; } public boolean removeUser(User user) { System.out.println("remove User ..."); return false; } public List listUsers() { System.out.println("list Users ..."); return new ArrayList(); } } |
package example1.service; import java.util.List; import example1.bean.User; import example1.dao.UserDAO; public class UserService { private UserDAO dao ; public UserService(UserDAO dao) { this.dao = dao ; } public User findUser(String username) { return this.dao.findUser(username); } public User addUser(User user) { return this.dao.addUser(user); } public User updateUser(User user) { return this.dao.updateUser(user); } public boolean removeUser(User user) { return this.dao.removeUser(user); } public List listUsers() { return this.dao.listUsers() ; } } |
package example1; import org.picocontainer.ComponentAdapter; import org.picocontainer.MutablePicoContainer; import org.picocontainer.defaults.DefaultPicoContainer; import example1.bean.User; import example1.dao.UserDAOImpl; import example1.dao.UserDAOImpl2; import example1.service.UserService; public class Test { public static void main(String[] args) { test(); } public static void test() { //定义个PicoContainer容器,这里使用默认实现 MutablePicoContainer pico = new DefaultPicoContainer(); //创建一个User Bean实例 User user = new User(); user.setUserid("1"); user.setUsername("kongxx"); user.setPassword("kongxx"); //向容器中注册组件 ComponentAdapter ca1; ComponentAdapter ca2; ca1 = pico.registerComponentImplementation(UserDAOImpl.class); ca2 = pico.registerComponentImplementation(UserService.class); //从容器中获取一个UserService组件对象实例 UserService us = (UserService) pico.getComponentInstance(UserService.class); //调用UserService对象实例的方法 us.findUser("kongxx"); us.findUser("tom"); us.addUser(user); us.updateUser(user); us.removeUser(user); us.listUsers(); } } |
find User kongxx ... find User tom ... add User ... update User ... remove User ... list Users ... |
2.2. 说明
在Test类的test()方法中,执行了一下操作:1. 创建一个PicoContainer容器对象,
MutablePicoContainer pico = new DefaultPicoContainer();
在此方法中以后的操作均是针对此容器实例;
2. 创建一个User Bean实例;
3. 向容器中注册组件,
pico.registerComponentImplementation(UserDAOImpl.class);
pico.registerComponentImplementation(UserService.class);
此处注册的两个组件为UserDAOImpl和UserService,这两个类均是具体的类,UserDAOImpl类是UserDAO接口的实现,并且在UserService的构造函数中需要使用UserDAO接口,即UserServer组件依赖UserDAO;
4. 从容器中获取一个UserService组件的对象实例,
UserService us = (UserService) pico.getComponentInstance(UserService.class);,其中UserService对象具体是怎样构造的将由PicoContainer容器全权负责,容器负责在容器内部查找UserDAO接口的实现,然后传递给UserService的构造函数来构造对象实例;
5. 调用UserService对象的具体方法。从运行输出的结果来看,容器确实创建了UserService对象实例,并且执行的其相关的方法。
2.3. Constructor Injection
在PicoContainer框架中默认使用的是Constructor Injection方式进行依赖注入,如下,AB中通过构造函数注入A,B。A.java
package example3; public class A { } |
package example3; public class B { } |
package example3; import org.picocontainer.Startable; public class AB { private A a ; private B b ; public AB(A a ,B b) { this.a = a ; this.b = b ; } public A getA() { return a; } public void setA(A a) { this.a = a; } public B getB() { return b; } public void setB(B b) { this.b = b; } public String toString() { return "This is AB."; } } |
package example3; import org.picocontainer.MutablePicoContainer; import org.picocontainer.defaults.DefaultPicoContainer; public class Test { public static void main(String[]args) { MutablePicoContainer pico = new DefaultPicoContainer(); pico.registerComponentImplementation(A.class); pico.registerComponentImplementation(B.class); pico.registerComponentImplementation(AB.class); AB ab = (AB)pico.getComponentInstance(AB.class); System.out.println(ab); } } |
2.4. Setter Injection
PicoContainer容器也支持Setter Injection方式的依赖注入,如下AB中通过对属下的Setter方法进行注入:A.java
package example3; public class A { } |
package example3; public class B { } |
package example3; import org.picocontainer.Startable; public class AB { private A a ; private B b ; public AB() { } public A getA() { return a; } public void setA(A a) { this.a = a; } public B getB() { return b; } public void setB(B b) { this.b = b; } public String toString() { return "This is AB."; } } |
package example3; import org.picocontainer.MutablePicoContainer; import org.picocontainer.defaults.DefaultPicoContainer; import org.picocontainer.defaults.SetterInjectionComponentAdapterFactory; public class Test { public static void main(String[]args) { MutablePicoContainer pico = new DefaultPicoContainer(new SetterInjectionComponentAdapterFactory()); pico.registerComponentImplementation(A.class); pico.registerComponentImplementation(B.class); pico.registerComponentImplementation(AB.class); AB ab = (AB)pico.getComponentInstance(AB.class); System.out.println(ab); } } |
3. 接口说明
3.1. PicoContainer
此接口全名为:org.picocontainer.PicoContainer,是PicoContainer框架的核心接口,并且继承自Disposable和Startable接口,主要提供的功能是从容器中获取已注册的组件实例,而对于怎么注册组件则使用org.picocontainer.MutablePicoContainer接口。3.2. MutablePicoContainer
此接口全名为:org.picocontainer.MutablePicoContainer,继承自PicoContainer接口,是PicoContainer框架中注册组件的核心接口。通常它可以通过以下三种方式注册组件:Ø 一个具体的实现类,如:registerComponentImplementation(Class c);
Ø 一个对象实例,如:registerComponentInstance(Object o);
Ø 一个ComponentAdapter对象实例,如:registerComponent(ComponentAdapter ca)。
3.3. ComponentAdapter
此接口全名为:org.picocontainer.ComponentAdapter,组件适配器主要用来负责提供符合一种规范的组件实例。对于每一个在PicoContainer容器中注册的组件都将产生一个组件适配器对象实例,并且每一个组件适配器实例必须有一个在一个容器中唯一的key值。key值可以是一个类的类型也可以是一个唯一标识(比如一个对象实例的地址)。具体情况更具在注册时使用注册方法来定。比如:当使用registerComponentImplementation(Class c)方法注册时,使用的是类的类型做key值;而在使用registerComponentInstance(Object o);方法注册时使用的唯一标识做key值。3.4. Startable
此接口全名为:org.picocontainer.Startable,主要用来提供PicoContainer容器的生命周期管理,如果我们的类实现了Startable接口,就可以通过一个简单的方法控制我们的对象的生命周期。容器可以按照正确的顺序调用start()/stop()方法来管理所有对象。start()方法必须在组件的生命周期开始时被调用,并且可以被再次调用(必须在stop()方法调用以后)。stop()方法必须在组件的生命周期结束时被调用,当前必须在start()方法调用之后。如果组件实现了Disposable接口,stop()方法应该在Disposable.dispose()之前被调用调用。3.5. Parameter
此接口全名为:org.picocontainer.Parameter,主要用来处理在构造对象实例时传递给构造函数的参数。3.6. PicoVisitor
此接口全名为:org.picocontainer.PicoVisitor,此接口是使用GoF Visitor(访问者)模式的实现,访问者可以访问容器及其子容器和在容器中注册的ComponetAdapter组件。他的两个主要实现类是LifecycleVisitor和VerifyingVisitor,LifecycleVisitor负责组件的生命周期管理,VerifyingVisitor负责容器中组件的的验证。4. 容器继承
4.1. 简介
在PicoContainer中容器可以使用继承管理来管理组件,如以下例子所示:A.java
package example3; public class A { } |
package example3; public class B { } |
package example3; import org.picocontainer.Startable; public class AB { private A a ; private B b ; public AB(A a ,B b) { this.a = a ; this.b = b ; } public A getA() { return a; } public void setA(A a) { this.a = a; } public B getB() { return b; } public void setB(B b) { this.b = b; } public String toString() { return "This is AB."; } } |
package example3; import org.picocontainer.MutablePicoContainer; import org.picocontainer.Startable; import org.picocontainer.defaults.DefaultPicoContainer; public class Test { public static void main(String[]args) { try { test1(); } catch(Exception ex) { System.out.println("test1 Exception:" + ex); } try { test2(); } catch(Exception ex) { System.out.println("test2 Exception:" + ex); } try { test2(); } catch(Exception ex) { System.out.println("test3 Exception:" + ex); } } public static void test1() { //定义个PicoContainer容器,这里使用默认实现 MutablePicoContainer parent = new DefaultPicoContainer(); MutablePicoContainer child = new DefaultPicoContainer(parent); //向容器中注册组件 parent.registerComponentImplementation(A.class); parent.registerComponentImplementation(B.class); child.registerComponentImplementation(AB.class); //从容器中获取组件实例 AB ab = (AB)child.getComponentInstance(AB.class); System.out.println(ab); } //This is an error example. public static void test2() { //定义个PicoContainer容器,这里使用默认实现 MutablePicoContainer parent = new DefaultPicoContainer(); MutablePicoContainer child = new DefaultPicoContainer(parent); //向容器中注册组件 parent.registerComponentImplementation(AB.class); child.registerComponentImplementation(A.class); child.registerComponentImplementation(B.class); //从容器中获取组件实例 AB ab = (AB)parent.getComponentInstance(AB.class); System.out.println(ab); } //This is an error example. public static void test3() { //定义个PicoContainer容器,这里使用默认实现 MutablePicoContainer parent = new DefaultPicoContainer(); MutablePicoContainer child1 = new DefaultPicoContainer(parent); MutablePicoContainer child2 = new DefaultPicoContainer(parent); //向容器中注册组件 child1.registerComponentImplementation(A.class); child1.registerComponentImplementation(B.class); child2.registerComponentImplementation(AB.class); //从容器中获取组件实例 AB ab = (AB)child2.getComponentInstance(AB.class); System.out.println(ab); } } |
This is AB. test2 Exception: org.picocontainer.defaults.UnsatisfiableDependenciesException: example3.AB has unsatisfiable dependencies: [[class example3.A, class example3.B]] test3 Exception: org.picocontainer.defaults.UnsatisfiableDependenciesException: example3.AB has unsatisfiable dependencies: [[class example3.A, class example3.B]] |
Ø 容器可以实现继承,容器可以无限级的向下继承;
Ø 子容器中的组件可以依赖于父容器中的组件;
Ø 父容器中的组件不可以依赖子容器中的组件;
Ø 两个子容器中的组件不可以互相依赖;
4.2. 应用
在现实应用中我们可以通过PicoContainer容器提供的容器继承关系来简化我们的应用开发,使我们的开发结构更清晰,比如有以下应用:系统需要提供N个组件服务service1,service2 …ServiceN,每个service依赖于一个Configuration接口来获取系统配置信息,具体实现如下:IService.java提供服务规约,只有一个方法service():
package example4; public interface IService { public void service() ; } |
package example4; public abstract class AbstractService implements IService { protected Configuration conf ; public abstract void service() ; } |
package example4; public class Service1 extends AbstractService { public Service1(Configuration conf ) { this.conf = conf ; } public void service() { System.out.println("Service1.service()"); } } |
package example4; public class Service2 extends AbstractService { public Service2 (Configuration conf ) { this.conf = conf ; } public void service() { System.out.println("Service2.service()"); } } |
package example4; public interface Configuration { public String getValue(String key) ; } |
package example4; public class DefaultConfiguration implements Configuration { static { //从系统配置文件中获取配置信息 //TODO } public String getValue(String key) { return null ; } } |
package example4; import org.picocontainer.MutablePicoContainer; import org.picocontainer.defaults.DefaultPicoContainer; public class Test { private MutablePicoContainer parent ; public static void main(String[]args) { Test t = new Test(); t.testService1(); t.testService2(); } public Test() { this.parent = new DefaultPicoContainer(); this.parent.registerComponentImplementation(DefaultConfiguration.class); } public void testService1() { MutablePicoContainer child = new DefaultPicoContainer(parent); child.registerComponentImplementation(Service1.class); Service1 service1 = (Service1)child.getComponentInstance(Service1.class); service1.service(); } public void testService2() { MutablePicoContainer child = new DefaultPicoContainer(parent); child.registerComponentImplementation(Service2.class); Service2 service2 = (Service2)child.getComponentInstance(Service2.class); service2.service(); } } |
运行Test,输出如下:
Service1.service() Service2.service() |
5. 生命周期管理
5.1. 简介
PicoContainer容器支持生命周期管理,只要我们的组件实现Startable接口即可,此时我们的组件需要实现start()和stop()方法,从而使对生命周期的管理变为由容器控制的对两个方法的简单调用。PicoContainer是典型的访问者模式的应用,详细见GoF的Visitor模式。以下是PicoContainer中Visitor模式中用到的两个主要的接口和类。
PicoVisitor.java
package org.picocontainer; public interface PicoVisitor { Object traverse(Object node); void visitContainer(PicoContainer pico); void visitComponentAdapter(ComponentAdapter componentAdapter); void visitParameter(Parameter parameter); } |
//负责生命周期管理 public class LifecycleVisitor extends AbstractPicoVisitor { public Object traverse(Object node) {...} public void visitContainer(PicoContainer pico) {...} public void visitComponentAdapter(ComponentAdapter componentAdapter) {...} public void visitParameter(Parameter parameter) {...} public static void start(Object node) {...} public static void stop(Object node) {...} public static void dispose(Object node) {...} } |
5.2. 开始生命周期
Ø 调用PicoContainer容器的start()方法,这里默认为DefaultPicoContainer.start()方法;Ø DefaultPicoContainer.start()将调用LifecycleVisitor.start(Object node)方法,此方法是一个静态方法,实际的执行情况是,构造了一个LifecycleVisitor对象,然后又调用LifecycleVisitor对象的traverse(Object node)方法,由此方法执行具体的操作;
Ø traverse(Object node)方法执行以下两个步操做,首先调用父类AbstractPicoVisitor.traverse(Object node)方法,此方法通过反射调用容器的accept(PicoVisitor visitor)方法,此时accept将遍历当前容器中相关的组件和子容器,然后调用其相关的accept(PicoVisitor visitor)方法,这是使用一个递归的方法注入PicoVisitor对象;然后遍历所有组件的start()方法,开始组件的生命周期。
5.3. 结束生命周期
同开始生命周期。5.4. 应用
仍然以上一章最后一个应用为例,说明生命周期的具体应用。Configuration.java和DefaultConfiguration.java内如不变,其它类代码如下:
IService.java接口增加了对Startable接口的继承,如下:
package example5; import org.picocontainer.Startable; public interface IService extends Startable{ public void service() ; } |
package example5; public abstract class AbstractService implements IService { protected Configuration conf ; public abstract void service() ; public abstract void start() ; public abstract void stop() ; } |
package example5; public class Service1 extends AbstractService { public Service1 (Configuration conf ) { this.conf = conf ; } public void service() { System.out.println("Service1.service()"); } public void start() { service(); } public void stop() { //TODO } } |
package example5; public class Service2 extends AbstractService { public Service2 (Configuration conf ) { this.conf = conf ; } public void service() { System.out.println("Service2.service()"); } public void start() { service(); } public void stop() { //TODO } } |
package example5; import org.picocontainer.MutablePicoContainer; import org.picocontainer.defaults.DefaultPicoContainer; public class Test { private MutablePicoContainer parent ; public static void main(String[]args) { Test t = new Test(); t.testService1(); t.testService2(); } public Test() { this.parent = new DefaultPicoContainer(); this.parent.registerComponentImplementation(DefaultConfiguration.class); } public void testService1() { MutablePicoContainer child = new DefaultPicoContainer(parent); child.registerComponentImplementation(Service1.class); child.start(); child.stop(); } public void testService2() { MutablePicoContainer child = new DefaultPicoContainer(parent); child.registerComponentImplementation(Service2.class); child.start(); child.stop(); } } |
Service1.service() Service2.service() |
相关文章推荐
- PicoContainer学习手册
- PicoContainer学习手册
- PicoContainer学习手册
- PicoContainer学习手册
- perl手册学习摘记3
- Redis学习手册(开篇)
- MySql官方手册学习笔记2 MySql的模糊查询和正则表达式
- Enterprise Library – Data Access Application Block 学习手册(最新版)- Part 3
- Enterprise Library - Validation Application Block 学习手册(最新版) Part 4
- PostgreSQL学习手册
- 嵌入式学习手册-4412开发板使用手册
- Prototype使用学习手册指南之ajax.js
- XML轻松学习手册(1-3)XML快速入门
- JSP&Servlet学习手册
- Genesis-3D学习手册——28.材质与着色器
- 酷宇通讯SDK学习手册
- Linux命令学习手册-tty命令
- Iptables学习笔记和快速入门手册
- Google Interview University 一套完整的学习手册帮助自己准备 Google 的面试
- PostgreSQL学习手册(常用数据类型)