这是写给项目组小伙伴培训用的文档。
一、使用setXXX来配置Bean的问题
如果在spring中采用xml来配置,需要bean中提供setXXXX方法来做bean参数的注入,比如:
Class
Foo { Public
void
setName(string name) {}; Public
void
setThreadCount( int
count){}; } |
这方式带来的问题是,setXXX和真正开始执行业务逻辑的代码之间往往有调用先后的要求,比如 Foo会根据ThreadCount来设置要启动的Thread个数:
Foo::execute(){ 这里根据Threadcount
确定启动多少个线程; } |
考虑这个代码的执行:
Foo
foo = new
Foo(); foo.setName(xxxx); foo.setThreadCount( 5 ); foo.execute()
//启动了5个线程; foo.setThreadCount( 100 );
//这个方法调用后,会有什么副作用? |
所以,针对这个问题,现在一般使用builder模式,来将Bean初始化和Bean的业务逻辑执行分离。
二、使用Builder模式
Builder模式将类的配置和执行分离。对于只写的参数,放在builder中处理; 对于控制业务流程,并可以动态修改的参数,可以在原类中处理:
Class
Foo{ public
class
static
Builder{ private
String name; private
int
count; public
Builder name(String name){ this .name
= name; return
this ;}; public
Builder threadCount( int
count){ this .count
= count; return
this ;}; public
Foo build(){ return
new
Foo( this );} } private
Builder builder; public
static
newBuilder(){ return
new
Builder(); } private
Foo(Builder builder){ this .builder
= builder; } public
void
execute(){ ..... new
ThreadExecutor(builder.count); } } |
注意:
- Foo, Foo.Builder的构造函数不能是public的,避免被直接调用;
- Builder的构造函数也不能是public,避免直接调用;
- Builder的所有方法必须返回this,用来支持接连调用,如builder.name(xxx).threadcount(xxx).build();
- Foo中可以直接使用builder的属性;
三、 Builder如何支持类继承
如上所示,考虑如下类结构:
class
Bar extend Foo{ public
class
static
BarBuilder extend Builder{ public
BarBuilder age( int
age){ this .age=age;
return
this }; } public
Bar build(){ return
new
Bar( this );} } public
static
newBuilder(){ return
new
BarBuilder(); } } |
那么在构造Bar的时候,就出问题了:Bar.newBuilder().name(xxxx).age(12)
name()方法的返回值是Builder,不是BarBuilder, Builder是没有age方法的,所以调用Builder::age,就出现编译错误。
解决方法是使用模板类, Foo定义修改为:
Class
Foo{ public
class
static
Builder<T extends
Builder<T>>{ private
String name; private
int
count; public
T name(String name){ this .name
= name; return
(T) this ;}; public
T Builder threadCount( int
count){ this .count
= count; return
(T) this ;}; public
Foo build(){ return
new
Foo( this );} } private
Builder<?> builder; public
static
newBuilder(){ return
new
Builder(); } private
Foo(Builder<?> builder){ this .builder
= builder; } public
void
execute(){ ..... new
ThreadExecutor(builder.count); } } |
Bar定义修改为:
Class
Bar{ public
class
static
BarBuilder extends
Builder<BarBuilder>{ private
int
age; public
BarBuilder age( int
age){ this .age
= age; return
this ;}; public
Bar build(){ return
new
Bar( this );} } private
BarBuilder builder; |
这样,使用Bar.newBuilder(). name (xxxx).age(12) ,就没问题了。