文章目录
前言
在Java框架中运用了很多的设计模式,常见的包括代理模式、责任链模式、观察者模式、普通工厂模式、抽象工厂模式和建造者模式等,那么下面让我们一起来了解一下。
1、什么是设计模式
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
2、Java反射技术
Java反射技术应用广泛,能够配置类的全限定名、方法和参数,完成对象的初始化等,增强了Java的可配置性,绝大数框架的基本原理也是基于这个技术。
我们简单了解一下反射的应用:
2.1、通过反射构建对象
(1)通过无参构造方式构建对象,代码如下:
public class ReflectServiceImpl {
public void sayHello(String name) {
System.err.println("Hello " + name);
}
public ReflectServiceImpl getInstance(){
ReflectServiceImpl object =null;
try {
//通过反射生成对象
object= (ReflectServiceImpl) Class.forName("com.ssm.learn.chapter2.reflect.ReflectServiceImpl").newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return object;
}
}
(2)通过有参构造方式构建对象,代码如下:
public class ReflectServiceImpl2 {
private String name;
public ReflectServiceImpl2(String name) {
this.name = name;
}
public void sayHello(String name) {
System.err.println("Hello " + name);
}
public ReflectServiceImpl2 getInstance(){
ReflectServiceImpl2 object =null;
try {
//通过反射生成对象
object= (ReflectServiceImpl2) Class.forName("com.ssm.learn.chapter2.reflect.ReflectServiceImpl2")
.getConstructor(String.class)
.newInstance("张三");
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return object;
}
}
-
反射的优点:只要配置就可以生成对象,可以解除程序的耦合度。
-
反射的缺点:运行较慢。
Spring IoC大部分情况下为了灵活度、降低耦合度,而使用反射是值得的。
2.2、反射方法
代码如下:
public Object reflectMethod() {
Object returnObj = null;
ReflectServiceImpl target = new ReflectServiceImpl();
try {
//Method method = ReflectServiceImpl.class.getMethod("sayHello", String.class);
//当有具体对象target但不知道它是哪个类的可以这样写
Method method = target.getClass().getMethod("sayHello", String.class);
//调用方法,相当于target.sayHello("张三")
returnObj = method.invoke(target, "张三");
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return returnObj;
}
2.3、实例
代码如下:
public static Object reflect() {
ReflectServiceImpl object = null;
try {
object = (ReflectServiceImpl) Class.forName("com.ssm.learn.chapter2.reflect.ReflectServiceImpl")
.newInstance();
Method method = object.getClass().getMethod("sayHello", String.class);
method.invoke(object,"张三");
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return object;
}
public static void main(String[] args) {
ReflectServiceImpl.reflect();
}
运行程序,得到以下结果:
Hello 张三
3、动态代理模式
动态代理的意义在于生成一个占位(又称为代理对象),来代理真实对象,从而控制真实对象的访问。
举个例子:客户来软件公司谈需求不会直接跟软件工程师谈,而是去找商务谈,客户认为商务代表了公司。如下图所示:
代理的作用:在真实对象访问之前或者之后加入对应的逻辑,或者根据其他规则控制是否使用真实对象。
代理分为两个步骤:
-
代理对象和真实对象之间建立代理关系
-
实现代理对象的代理逻辑方法
Java中最常用的动态代理技术有:
-
JDK动态代理。JDK自带功能,必须使用接口,比较复杂
-
CGLIB。第三方提供技术,不必使用接口,比较简单
3.1、JDK动态代理
首先定义HelloWorld
接口,代码如下:
public interface HelloWorld{
public void sayHelloWorld();
}
然后提供实现类HelloWorldImpl
来实现接口,代码如下:
public class HelloWorldImpl implements HelloWorld{
@Override
public void sayHelloWorld(){
System.out.println("Hello World");
}
}
动态代理绑定和代理逻辑实现,代码如下:
public class JdkProxyExample implements InvocationHandler {
//真实对象
private Object target = null;
/**
* 建立代理对象和真实对象的代理关系
*
* @param target 真实对象
* @return 代理对象
*/
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 代理方法逻辑
* @param proxy 代理对象
* @param method 当前调度方法
* @param args 当前方法参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理逻辑方法");
System.out.println("在调用真实对象之前的服务");
Object obj = method.invoke(target, args);//相当于调用sayHelloWord方法
System.out.println("在调用真实对象之后的服务");
return obj;
}
}
测试JDK动态代理,代码如下:
public class TestProxy {
@Test
public void testJdkProxy() {
JdkProxyExample jdkProxy = new JdkProxyExample();
//绑定关系,因为挂在接口HelloWorld下,所以声明代理对象HelloWorld proxy
HelloWorld proxy = (HelloWorld) jdkProxy.bind(new HelloWorldImpl());
//注意,此时proxy对象已经是一个代理对象,他会进入代理的逻辑方法invoke里
proxy.sayHelloWorld();
}
}
运行结果如下:
进入代理逻辑方法
在调度真实对象之前的服务
Hello World
在调用真实对象之后的服务
3.2、CGLIB动态代理
代码如下:
public class CglibProxyExample implements MethodInterceptor {
/**
* 生成CGLIB代理对象
* @param clazz Class类
* @return Class类的CGLIB对象
*/
public Object getProxy(Class clazz) {
//CGLIB enhancer增强类对象
Enhancer enhancer = new Enhancer();
//设置增强类型
enhancer.setSuperclass(clazz);
//定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor的方法
enhancer.setCallback(this);
//生成并返回代理对象
return enhancer.create();
}
/**
* 代理逻辑方法
* @param proxy 代理对象
* @param method 执行方法
* @param args 方法参数
* @param methodProxy 方法代理
* @return 代理逻辑返回
* @throws Throwable 异常
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("调用真实对象前");
Object result = methodProxy.invokeSuper(proxy, args);
System.out.println("调用真实对象后");
return result;
}
}
测试CGLIB动态代理,代码如下:
public class TestProxy {
@Test
public void testCGLIBProxy(){
CglibProxyExample cglibProxyExample = new CglibProxyExample();
ReflectServiceImpl obj = (ReflectServiceImpl) cglibProxyExample.getProxy(ReflectServiceImpl.class);
obj.sayHello("张三");
}
}
测试结果如下:
调用真实对象前
Hello 张三
调用真实对象后
3.3、拦截器
由于动态代理一般比较难理解,程序开发者会设计一个拦截器接口供开发者使用,开发者只需要知道拦截器接口的方法、含义和作用即可,无须知道动态代理是如何实现的。
3.4、实例
用JDK动态代理来实现一个拦截器的逻辑,为此先定义拦截器接口Interceptor
,代码如下:
public interface Interceptor {
/**
* 在真实对象前调用
* @param proxy 代理对象
* @param target 真实对象
* @param method 调用方法
* @param args 方法参数
* @return 当返回true时则反射真实对象的方法,当返回false时则调用around方法
* 当返回真实对象方法或者around方法执行后,调用after方法
*/
public boolean before(Object proxy, Object target, Method method,Object[] args);
public void around(Object proxy, Object target, Method method,Object[] args);
public void after(Object proxy, Object target, Method method,Object[] args);
}
实现这个接口,代码如下:
public class MyInterceptor implements Interceptor{
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法前逻辑");
return false;//不反射被代理对象原有方法
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法后逻辑");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("取代了被代理对象的方法");
}
}
在JDK动态代理中使用拦截器,代码如下:
public class InterceptorJdkProxy implements InvocationHandler {
//真实对象
private Object target = null;
//拦截器全限定名
private String interceptorClass = null;
public InterceptorJdkProxy(Object target, String interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
}
/**
* 绑定委托对象并返回一个[代理占位]
*
* @param target 真实对象
* @return 代理对象[占位]
*/
public static Object bind(Object target, String InterceptorClass) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InterceptorJdkProxy(target, InterceptorClass));
}
/**
* 通过代理对象调用方法,首先进入这个方法
*
* @param proxy 代理对象
* @param method 被调用方法
* @param args 方法参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (interceptorClass == null) {
//没有设置拦截器则直接反射原有方法
return method.invoke(target, args);
}
Object result = null;
//通过反射生成拦截器
Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();
//调用前置方法
if (interceptor.before(proxy, target, method, args)) {
//返回true反射原有对象方法
result = method.invoke(target, args);
} else {
//返回false执行around方法
interceptor.around(proxy, target, method, args);
}
//调用后置方法
interceptor.after(proxy, target, method, args);
return result;
}
}
代码的执行步骤:
- 在bind方法中用JDK动态代理绑定了一个对象,然后返回代理对象。
- 如果没有设置拦截器,则直接反射真实对象的方法,然后结束。否则,进行第三步。
- 通过反射生成拦截器,并准备使用它。
- 调用拦截器的
before
方法,如果返回true则反射原来的方法;否则运行拦截器的around
方法。 - 调用拦截器的
after
方法 - 返回结果
测试拦截器,代码如下:
public class TestInterceptor {
@Test
public void testInterceptor(){
HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),"com.ssm.learn.chapter2.interceptor.MyInterceptor");
proxy.sayHelloWorld();
}
}
测试结果如下:
反射方法前逻辑
反射方法后逻辑
取代了被代理对象的方法
4、责任链模式
举个例子,一个程序员需要请假一周。如果把请假申请单看成是一个对象,那么它需要经过项目经理、部门经理、人事经理等多个角色的审批,每个角色都有机会通过拦截这个申请单进行审批或者修改。这个时候就要考虑提供项目经理、部门经理和人事经理的处理逻辑了,所以需要提供3个拦截器,而传递的则是请假申请单,请假示例如图所示:
当一个对象在一条链上被多个拦截器拦截处理(拦截器也可以不拦截)时,我们把这样的设计模式称为责任链模式,它用于一个对象在多个角色中传递的场景。
回到刚才的例子,申请单来到项目经理那,经理可能把申请时间“一周”改为“5天”,从而影响到后面的审批,因为后面的审批会依据前面的结果进行。这时候考虑采用层层代理来实现,就是当申请单(target
)来到项目经理处时,使用第一个动态代理proxy1
,当他来到部门经理处时,部门经理会得到一个在项目经理的代理proxy1
基础上生成的第二个动态代理proxy2
来处理部门经理的逻辑,以此类推。
拦截逻辑如下图:
4.1、实例
定义三个拦截器,代码如下:
public class Interceptor1 implements Interceptor{
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器1】的before方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器1】的after方法");
}
}
/************************************拦截器2*************************************/
public class Interceptor2 implements Interceptor{
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器2】的before方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器2】的after方法");
}
}
/************************************拦截器3*************************************/
public class Interceptor3 implements Interceptor{
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器3】的before方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器3】的after方法");
}
}
测试责任链模式,代码如下:
@Test
public void testInterceptors() {
HelloWorld proxy1 = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(), "com.ssm.learn.chapter2.interceptor.Interceptor1");
HelloWorld proxy2 = (HelloWorld) InterceptorJdkProxy.bind(proxy1, "com.ssm.learn.chapter2.interceptor.Interceptor2");
HelloWorld proxy3 = (HelloWorld) InterceptorJdkProxy.bind(proxy2, "com.ssm.learn.chapter2.interceptor.Interceptor3");
proxy3.sayHelloWorld();
}
运行结果如下:
【拦截器3】的before方法
【拦截器2】的before方法
【拦截器1】的before方法
Hello World
【拦截器1】的after方法
【拦截器2】的after方法
【拦截器3】的after方法
由此可见,责任链模式的优点是我们可以在传递链上加入新的拦截器,增加拦截逻辑,其缺点是会增加代理和反射,而代理和反射的性能不高。
5、观察者(Observer)模式
观察者模式又称为发布订阅模式,它定义了一种一对多的依赖关系,让多个观察者同时监视着被观察者的状态,当观察者的状态发生改变时,会通知所有观察者,并让其自动更新自己。
例子1:微博推送机制。当作者发布一条新微博时,所有关注了该作者的粉丝都会收到推送。
例子2:一个商家有一些产品,他和一些电商合作,每当有新产品时,就会把这些产品推送到电商,现在只和淘宝、京东合作,于是有这样的伪代码:
if(产品库有新产品){
推送新产品到淘宝;
推送新产品到京东;
}
如果公司又和国美、苏宁、当当、唯品会签订合作协议,那么就需要时改变这段伪代码了:
if(产品库有新产品){
推送新产品到淘宝;
推送新产品到京东;
推送新产品到国美;
推送新产品到苏宁;
推送新产品到当当;
推送新产品到唯品会;
}
按照这种做法,如果还有其他电商合作,那么还要继续在if语句中增加逻辑。首先,如果电商越来越多,那么if语句的逻辑就越来越复杂。而且,如果推送商品给淘宝发生异常,需要捕捉异常,避免影响之后的电商接口,导致其不能往下进行,这样代码耦合就会增多。其次,if语句堆砌太多代码不利于维护和扩展。
**而观察者模式更利用扩展,责任也更加清晰。**首先,把每一个电商接口看成一个观察者,每一个观察者都能观察到产品列表(被监听对象)。当公司发布新产品时,就会发送到这个产品列表上,于是产品列表就发生了变化,这时就可以触发各个电商接口(观察者)发送新产品到对应的合作电商那里,观察者模式示例如图所示:
类似这样,一个对象(电商接口)会去监听另外一个对象(产品列表),当被监听对象(产品列表)发生变化时,对象(电商接口)就会触发一定的行为,以适合变化的逻辑模式,我们称为观察者模式。
这样的好处在于,程序不再出现if语句,观察者会根据被观察者对象的变化而做出对应的行为,无论是淘宝、京东或者其他电商团队只要维护自己的逻辑,而无须耦合在一起。同时责任是明确的,产品团队只需要维护产品列表,电商团队可以增加观察者去监听产品的电商接口。
5.1、实例
被观察者——产品列表,继承Observable
类,代码如下:
public class ProductList extends Observable {
//产品列表
private List<String> productList = null;
//类的唯一实例
private static ProductList instance;
//构造方法私有化
private ProductList() {
}
/**
* 取得唯一实例
*
* @return 产品列表唯一实例
*/
public static ProductList getInstance() {
if (instance == null) {
instance = new ProductList();
instance.productList = new ArrayList<>();
}
return instance;
}
/**
* 增加观察者(电商接口)
* @param observer 观察者
*/
public void addProductListObserver(Observer observer) {
this.addObserver(observer);
}
/**
* 新增产品
* @param newProduct 新产品
*/
public void addProduct(String newProduct){
productList.add(newProduct);
System.out.println("产品列表增加了新的产品 "+newProduct);
this.setChanged();//设置被观察对象发生变化
this.notifyObservers(newProduct);//通知观察者,并传递新产品
}
}
这里的使用了构造方法私有化,避免通过new方式创建对象,而是通过getInstance()
方法获得产品列表单例,这里使用了单例模式。
观察者——以淘宝和京东为例,实现他们的电商接口,作为观察者需要实现Observer
接口的update
方法,代码如下:
public class TaoBaoObserver implements Observer {
@Override
public void update(Observable o, Object product) {
String newProduct = (String) product;
System.out.println("发送新产品【" + newProduct + "】同步到淘宝商城");
}
}
public class JingDongObserver implements Observer {
@Override
public void update(Observable o, Object product) {
String newProduct = (String) product;
System.out.println("发送新产品【" + newProduct + "】同步到京东商城");
}
}
测试观察者模式,代码如下:
ublic class TestObserver {
@Test
public void testObserver() {
ProductList observable = ProductList.getInstance();
TaoBaoObserver taoBaoObserver = new TaoBaoObserver();
JingDongObserver jingDongObserver = new JingDongObserver();
observable.addObserver(taoBaoObserver);
observable.addObserver(jingDongObserver);
observable.addProduct("新增产品1");
}
}
运行结果如下:
产品列表增加了新的产品 新增产品1
发送新产品【新增产品1】同步到京东商城
发送新产品【新增产品1】同步到淘宝商城
6、工厂模式和抽象工厂模式
在大部分情况下,我们都是以new方式来创建对象。举个例子,现实中车子的种类可能很多,有大巴车、轿车、救护车、越野车、卡车等,每个种类下面还有具体的型号,一个工厂生产如此多的车会难以管理,所以往往要进一步拆分为各个分工厂:大巴车、轿车等分工厂。但是客户不需要知道工厂如何拆分,他只会告诉客服需要什么样的车,客服就会根据客户的要求找到对应的工厂去生产车。对客户而言,车厂只是抽象概念,他只是大概知道有这样的一个工厂能够满足他的需要。
6.1、普通工厂(Simple Factory)模式:
例如,有个IProduct
的产品接口,它下面有5个实现类Product1
、Product2
、Product3
、Product4
和Product5
。它们属于一个大类,可以通过产品工厂去管理它们的生成,但是由于类型不同,所以初始化有所不同。为了方便使用产品工厂(ProductFactory
)类来创建这些产品的对象,用户可以通过产品号来确定需要哪种产品,如图所示:
ProductFactory
的伪代码如下:
public class ProductFactory{
public static IProduct createProduct(String productNo){
switch(productNo){
case "1":return new Product1(XXX);
case "2":return new Product2(XXX);
case "3":return new Product3(XXX);
case "4":return new Product4(XXX);
case "5":return new Product5(XXX);
default:
throw new NotSupprotedException("未支持此编号产品生产。");
}
}
}
对于程序调用者而言,他只需要知道通过工厂的createProduct
方法,指定产品编号——productNo
可以得到对应的产品,而产品满足接口IProduct
的规范,所以初始化就简单了许多。对于产品对象的创建,可以把一些特有产品规则写入工厂类中。
6.2、抽象工厂(Abstract Factory)模式
抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。有时候对象很复杂,有几十种,又分为几个类别,如果使用简单工厂会使得这个工厂的逻辑过于复杂。所以把工厂分为好几个,这样便于工厂产品规则的维护。抽象工厂示意图如图所示:
为了统一,需要制定一个接口规范(IProductFactory
),所有的具体工厂和抽象工厂都要实现这一个接口,代码如下:
public interface IProductFactory {
public IProduct createProduct(String productNo);
}
现在再实现3个工厂类,代码如下:
public class ProductFactory1 implements IProductFactory {
@Override
public IProduct createProduct(String productNo) {
IProduct product = XXX;//工厂1生成产品对象规则,可以是一类产品的规则
return product;
}
}
public class ProductFactory2 implements IProductFactory {
@Override
public IProduct createProduct(String productNo) {
IProduct product = XXX;//工厂2生成产品对象规则,可以是一类产品的规则
return product;
}
}
public class ProductFactory3 implements IProductFactory {
@Override
public IProduct createProduct(String productNo) {
IProduct product = XXX;//工厂3生成产品对象规则,可以是一类产品的规则
return product;
}
}
使用一个公共的工厂,由它提供规则选择工厂,我们做如下业务约定:产品编号以x开头的用工厂ProductFactory
x创建对象。代码如下:
public class ProductFactory implements IProductFactory {
@Override
public IProduct createProduct(String productNo) {
char ch = productNo.charAt(0);
IProductFactory factory = null;
if (ch == '1') {
factory = new ProductFactory1();
} else if (ch == '2') {
factory = new ProductFactory2();
} else if (ch == '3') {
factory = new ProductFactory3();
}
if (factory != null) {
return factory.createProduct(productNo);
}
return null;
}
}
对于设计者而言,ProductFactory
就是一个抽象工厂,这样创建对象对调用者而言就简单多了。每一个工厂也只要维护其类型产品对象的生成,具体的工厂规则也不会特别复杂,难以维护。
7、建造者(Builder)模式
建造者模式可以将一个产品的内部表象(属性)与产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。
比如一些旅游套票可以分为:普通成年人、退休老年人、半票有座小孩、免费无座小孩、军人及其家属等,他们有不同的规定和优惠。如果通过new或者工厂模式来创建对象会造成不便,因为参数过多,对象也复杂。
Builder
模式是一种分步构建对象的模式。用一个配置类对各步进行统筹,然后将所有信息交由构造器来完成构造对象。
7.1、实例
创建一个配置类的对象TickerHelper
,它能够帮助我们一步步构建对象。代码如下所示:
public class TicketHelper {
public void buildAdult(String info) {
System.out.println("构建成年人票逻辑:" + info);
}
public void buildChildrenForSeat(String info) {
System.out.println("构建有座儿童票逻辑:" + info);
}
public void buildChildrenForNoSeat(String info) {
System.out.println("构建无座儿童票逻辑:" + info);
}
public void buildElderly(String info) {
System.out.println("构建老年人票逻辑:" + info);
}
public void buildSoldier(String info) {
System.out.println("构建军人及其家属票逻辑:" + info);
}
}
然后,需要一个构造类——TicketBuilder
,代码如下:
public class TicketBuilder {
public static Object builder(TicketHelper helper){
System.out.println("通过TicketHelper构建套票信息");
return null;
}
}
通过这两个类就可以构建出套票,代码如下:
public class TestBuilder {
@Test
public void testBuilder() {
TicketHelper helper = new TicketHelper();
helper.buildAdult("成人票");
helper.buildChildrenForSeat("有座儿童");
helper.buildChildrenForNoSeat("无座儿童");
helper.buildElderly("老年人票");
helper.buildSoldier("军人票");
Object ticket = TicketBuilder.builder(helper);
}
}
本文参考杨开振的《JavaEE互联网轻量级框架整合开发》