Netty之HTTP+XML编解码框架开发

来源:互联网 发布:html5tooltips.js 编辑:程序博客网 时间:2024/06/06 05:34

Netty之HTTP+XML编解码框架开发

一.HTTP+XML请求消息编码类

    对于上层业务,构造订购请求消息后,以HTTP+XML协议将消息发送给服务端,如果要实现对业务零侵入或者尽可能少的侵入看,协议层和应用层应该解耦。

    考虑到HTTP+XML协议栈需要一定的定制扩展能力,例如通过HTTP消息头携带业务自定义字段,应该允许业务利用Netty的HTTP协议栈接口自行构造私有的HTTP消息头。

    HTTP+XML的协议编码仍然采用ChannelPipeline中增加对应的编码handler类实现。

1.1 HttpXmlRequestEncode实现

  packagejibx;

 

import java.net.InetAddress;

import java.util.List;

 

importio.netty.channel.ChannelHandlerContext;

importio.netty.handler.codec.http.DefaultFullHttpRequest;

importio.netty.handler.codec.http.FullHttpRequest;

importio.netty.handler.codec.http.HttpHeaders;

/*

 *HTTP+XML HTTP请求消息编码类

 *

 */

public class HttpXmlRequestEncoder

     extends AbstarctHttpXmlEncoder<HttpXmlRequest>{

        

   @Override

   protected void encode(ChannelHandlerContext ctx,HttpXmlRequestmsg,List<Object> out)

   throws Exception{

             // 调用父类的encode0,将业务需要发送的POJO对象Order实例通过jibx序列化为XML字符串

             // 随后将它封装成netty的bytebuf

             ByteBufbody=encode0(ctx,msg.getBody());

             FullHttpRequestrequest=msg.getRequest();

             // 对消息头进行判断,如果业务侧自定义和定制了消息头,则使用业务侧设置的HTTP消息头

             // 如果业务侧没有设置,则构造新的HTTP消息头

             if(request==null){

                       // 构造和设置默认的HTTP消息头,由于通常情况下HTTP通信双方更关注消息体本身,

                       // 所以这里采用了硬编码方式,如果要产品化,可以做成XML配置文件,允许业务自定义配置,

                       // 以提升定制的灵活性

                       request=newDefaultFullHttpRequest(HttpVersion.HTTP_1_1,HttpMethod.GET,"/do",body);

                       HttpHeadersheaders=request.headers();

                       headers.set(HttpHeaders.Names.HOST,InetAddress.getLocalHost().getHostAddress());

                       headers.set(HttpHeaders.Names.CONNECTION,HttpHeaders.Value.CLOSE);

                       headers.set(HttpHeaders.Names.ACCEPT_ENCODING,HttpHeaders.Value.GZIP.toString()+','+

                                         HttpHeaders.Value.DEFLATE.toString());

                       headers.set(HttpHeaders.Names.ACCEPT_CHARSET,"ISO-8859,utf-8,q=0.7,*;q=0.7");

                       headers.set(HttpHeaders.Names.USER_AGENT,"Nettyxml Http Client side");

                       headers.set(HttpHeaders.Names.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8");

             }

             // 由于请求消息消息体不为空,也没有chunk方式,所以在HTTP消息头中设置消息体的长度conent-length

             // 完成消息体的XML序列化后将重新构造的HTTP请求消息加入到out中

             // 由后续netty的http请求编码器继续对HTTP请求消息进行编码

             HttpHeaders.setContentLength(request,body.eradableBytes());

             out.add(request);

}

}

1.2 AbstrctHttpXmlEncoder实现

package jibx;

 

import java.io.StringWriter;

import io.netty.buffer.ByteBuf;

import io.netty.buffer.Unpooled;

importio.netty.channel.ChannelHandlerContext;

importio.netty.handler.codec.MessageToMessageEncoder;

/*

 *HTTP+XML HTTP请求消息编码基类

 */

public classAbstractHttpXmlEncoder<T>

 extends MessageToMessageEncoder<T>{

         IBindingFactoryfactory=null;

         StringWriterwriter=null;

         finalstatic String CHARSET_NAME="UTF-8";

         finalstatic Charset UTF-8=

                            Charset.forName(CHARSET_NAME);

         //将业务的Order实例序列化为XML字符串

         protectedByteBuf encode0(ChannelHandlerContext ctx,Object body) throws Exception{

                   factory=BindingDirectory.getFactory(body.getClass());

                   writer=newStringWriter();

                   IMarshallingContextmctx=factory.createMarshallingContext();

                   mctx.setIndent(2);

                   mctx.marshalDocument(body,CHARSET_NAME,null,writer);

                   StringxmlStr=writer.toString();

                   writer.close();

                   writer=null;

                   //将XML字符串包装成netty的ByteBuf并返回,实现了HTTP请求消息的XML编码

                   ByteBufencodeBuf=Unpooled.copiedBuffer(xmlStr,UTF-8);

                   returnencodeBuf;

         }

        

         @Override

         publicvoid exceptionCaught(ChannelHandlerContext ctx,Throwable cause) throwsException{

                   //释放资源

                   if(writer!=null){

                            writer.close();

                            writer=null;

                           

                   }

         }

}

1.3 HttpXmlRequest实现

package jibx;

/*

 *HTTP+XML请求消息  用于实现和协议栈之间的解耦

 */

importio.netty.handler.codec.http.FullHttpRequest;

 

public class HttpXmlRequest {

         privateFullHttpRequest request;

         privateObject body;

        

         publicHttpXmlRequest(FullHttpRequest request,Object body){

                   this.request=request;

                   this.body=body;

                  

         }

        

         publicfinal FullHttpRequest getRequest(){

                   returnrequest;

         }

        

         publicfinal void setRequest(FullHttpRequest request){

                   this.request=request;

         }

        

         publicfinal Object getBody(){

                   returnbody;

         }

        

         publicfinal void setBody(Object body){

                   this.body=body;

         }

 

}

    它包含2个成员变量FullHttpRequest和编码对象Object,用于实现和协议栈之间的解耦。

二.HTTP+XML请求消息解码类

   Http服务端接收到HTTP+XML请求消息后,需要从http消息体重获取请求码流,通过jibx框架对它进行反序列化,得到请求POJO对象,然后对结果进行封装,回调到业务handler对象,业务得到的就是解码后的POJO对象和HTTP消息头。

2.1 HttpXmlRequestDecoder实现

package jibx;

 

import io.netty.buffer.Unpooled;

importio.netty.channel.ChannelFutureListener;

importio.netty.handler.codec.http.HttpResponseStatus;

/*

 *HTTP+XML http请求消息解码类

 */

public class HttpXmlRequestDecoder

  extends AbstactHttpXmlDecode<FullHttpRequest>{

        

         publicHttpXmlRequestDecoder(Class<?> clazz) {

                   this(clazz,false);

         }

         //HttpXmlRequestDecoder有2个参数,分别为需要解码的对象的类型信息和

         //是否打印HTTP消息体码流的码流开关,码流开关默认关闭

         publicHttpXmlRequestDecoder(Class<?> clazz,boolean isPrint){

                   super(clazz,isPrint);

         }

  

         @Override

         protectedvoid decode(ChannelHandlerContext arg0,FullHttpRequest arg1,

                            List<Object>arg2) throws Exception{

                   //对HTTP请求消息本身的解码结果进行判断,如果已经失败,二次解码就无意义

                   if(!arg1.getDecodeResult().isSuccess()){

                            sendError(arg0,BAD_REQUEST);

                            return;

                   }

                   //通过HttpXmlRequest和反序列化后的Order对象构造HttpXmlRequest实例,

                   //最后将它添加到解码结果list列表中

                   HttpXmlRequestrequest=new HttpXmlRequest(arg1,decode0(arg0,arg1.content()));

                   arg2.add(request);

         }

        

         /*

          * 如果HTTP消息本身解码失败,则构造处理结果异常的HTTP应答消息返回给客户端。

          * 作为演示程序,本例子没有考虑XML消息解码失败后的异常封装和处理,

          * 在商用项目中需要统一的异常处理机制,提升协议栈的健壮性和可靠性

          */

         privatestatic void sentError(ChannelHandlerContext ctx,HttpResponseStatus status){

                   FullHttpResponseresponse=new DefaultFullHttpResponse(HTTP_1_1,status,

                                     Unpooled.copiedBuffer("Failure:"+status.toString()+"\r\n",CharsetUtil.UTF_8));

                   reponse.headers().set(CONTENT_TYPE,"text/plain,charset-UTF-8");

                   ctx.writeAndFlush(reponse).addListener(ChannelFutureListener.CLOSE);

         }

}

2.2 AbstractHttpXmlDecoder实现

package jibx;

 

import io.netty.buffer.ByteBuf;

import io.netty.handler.codec.MessageToMessageDecoder;

 

import java.io.StringReader;

import java.nio.charset.Charset;

/*

 *HTTP+XML HTTP请求消息解码类

 */

public classAbstarctHttpXmlDecoder<T> extends

  MessageToMessageDecoder<T>{

        

         privateIBindingFactory factory;

         privateStringReader reader;

         privateClass<?> clazz;

         privateboolean isPrint;

         privatefinal static String CHARSET_NAME="UTF-8";

         privatefinal static Charset UTF-8=Charset.forName(CHARSET_NAME);

        

         protectedAbstarctHttpXmlDecoder(Class<?> clazz){

                   this(clazz,false);

         }

        

         protectedAbstrctHttpXMlDecoder(Class<?> clazz,boolean isPrint){

                   this.clazz=clazz;

                   this.isPrint=isPrint;

         }

        

         protectedObject decode0(ChannelHandlerContext arg0,ByteBuf body) throws Exception{

                   //从HTTP的消息体中获取请求码流,然后通过jibx类库将XML转换成POJO对象

                   factory=BindingDirectory.getFactory(clazz);

                   Stringcontent=body.toString(UTF-8);

                   //根据码流开关决定是否打印消息体码流

                   //增加码流开关往往是为了方便问题定位,在实际项目中,需要打印到日志中

                   if(isPrint)

                            System.out.println("Thebody is:"+content);

                   reader=newStringReader(content);

                   IUnmarshllingContextuctx=factory.createUnmarshallingContext();

                   Objectresult=uctx.unmarshalDocument(reader);

                   reader.close();

                   reader=null;

                   returnresult;

         }

        

         @Override

         publicvoid exceptionCaught(ChannelHandlerContext ctx,Throwable cause) throwsException{

                   //释放资源

                   //如果解码发生异常,要判断StringReader是否已经关闭

                   //如果没有关闭,则关闭输入流并通知JVM对其进行垃圾回收

                   if(reader!=null){

                            reader.close();

                            reader=null;

                   }

         }

}

2.3 HttpXmlResponse实现

package jibx;

 

importio.netty.handler.codec.http.FullHttpResponse;

 

/*

 *HTTP+XML 响应消息编码类

 */

public class HttpXmlResponse {

   

         privateFullHttpResponse httpResponse;

         privateObject result;

        

         publicHttpXmlResponse(FullHttpResponse httpResponse,Object result){

                   this.httpResponse=httpResponse;

                   this.result=result;

         }

        

         publicfinal FullHttpResponse getHttpResponse(){

                   returnhttpResponse;

         }

        

         publicfinal void setHttpResponse(FullHttpResponse httpResponse){

                   this.httpResponse=httpResponse;

         }

        

         publicfinal Object getResult(){

                   returnresult;

         }

        

         publicfinal void setResult(Object result){

                   this.result=result;

         }

}

2.4 HttpXmlResponseEncoder实现

package jibx;

 

import java.util.List;

 

import io.netty.buffer.ByteBuf;

importio.netty.channel.ChannelHandlerContext;

importio.netty.handler.codec.http.DefaultFullHttpResponse;

importio.netty.handler.codec.http.FullHttpResponse;

/*

 *HTTP+XML 应答消息编码类

 */

public class HttpXmlResponseEncoder

   extends AbstractHttpXmlEncoder<HttpXmlResponse>{

     

         protectedvoid encode(ChannelHandlerContext ctx,

                            HttpXmlResponsemsg,List<Object> out

                            )throws Exception{

                   ByteBufbody=encode0(ctx,msg.getResult());

                   FullHttpResponseresponse=msg.getHttpResponse();

                   //对应答消息进行判断,如果业务侧已经构造了HTTP应答消息

                   //则利用业务已有应答消息重新复制一个新的HTTP应答消息

                   if(response==null){

                            response=newDefaultFullHttpResponse(HTTP_1_1,OK,body);

                   }else{

                            response=newDefaultFullHttpResponse(msg.getHttpResponse()

                                               .getProtocolVersion(),msg.getHttpResponse().getStatus(),body);

                   }

                   //设置消息体的内容格式为"text/xml",然后在消息头中设置消息体的长度

                   response.headers().set(CONTENT_TYPE,"text/xml");

                   setContentLength(response,body,readableBytes);

                   //把编码后的DefaultFullHttpResponse对象添加到编码结果列表中

                   //由后续Netty的HTTP编码类进行二次编码

                   out.add(response);

         }

         }

2.5 HttpXmlResponseDecoder实现

package jibx;

 

import java.util.List;

 

importio.netty.channel.ChannelHandlerContext;

import io.netty.handler.codec.http.DefaultFullHttpResponse;

 

public class HttpXmlResponseDecoder extends

 AbstarctHttpXmlDecoder<DefaultFullHttpResponse>{

        

         publicHttpXmlResponseDecoder(Class<?> clazz){

                   this(clazz,false);

         }

        

         publicHttpXmlResponseDecoder(Class<?> clazz,boolean isPrintLog){

                   super(clazz,isPrintLog);

         }

  

         @Override

         protectedvoid decode(ChannelHandlerContext ctx,DefaultFullHttpResponse msg,

                            List<Object>out) throws Exception{

                   //通过DefaultFullHttpResponse和HTTP应答消息反序列化后的POJO对象构造HttpXmlResponse

                   //并将其添加到解码结果列表中

                   HttpXmlResponseresHttpXmlResponse=new HttpXmlResponse

                                     (msg,decode0(ctx,msg.content()));

                   out.add(resHttpXmlResponse);

         }

}

三.HTTP+XML客户端开发

客户端功能:

1.发起HTTP连接请求

2.构造订购请求消息,将其编码成XML,通过HTTP协议发送给服务端

3.接收HTTP服务端的应答消息,将XML应答消息反序列化为订购消息POJO对象

4.关闭HTTP连接

3.1 HttpXmlClient实现

package jibx;

 

import java.net.InetSocketAddress;

 

import client.TimeClient;

import io.netty.bootstrap.Bootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioSocketChannel;

importio.netty.handler.codec.http.HttpResponseDecoder;

 

public class HttpXmlClient {

        

         publicvoid connect(int port) throws Exception{

                   //配置客户端NIO线程组

                   EventLoopGroupgroup =new NioEventLoopGroup();

                   try{

                            Bootstrapb=new Bootstrap();

                            b.group(group).channel(NioSocketChannel.class)

                            .option(ChannelOption.TCP_NODELAY,true)

                            .handler(newChannelInitializer<SocketChannel>(){

                                     @Override

                                     publicvoid initChannel(SocketChannel ch) throws Exception{

                                               //将二进制码流解码成为HTTP的应答消息

                                               ch.pipeline().addLast("http-decoder",newHttpResponseDecoder());

                                               //负责将1个HTTP请求消息的多个部分合并成一条完整的HTTP消息

                                               ch.pipeline().addLast("http-aggregator",newHttpObjectAggregator(65536));

                                               //XML解码器

                                               //将前面开发的XML解码器HttpXmlResponseDecoder添加到ChannelPipeline中

                                               //它有2个参数,分别是解码对象的类型信息和码流开关,这样就实现了HTTP+XML应答消息的自动解码

                                               ch.pipeline().addLast(

                                                                 "xml-decoder",

                                                                 newHttpXmlResponseDecoder(Order.class,true);

                                               //将HttpRequestEncoder解码器添加到ChannelPipeline中时,需要注意顺序,

                                        // 编码的时候是按照从尾到头的顺序调度执行的,它后面放的是我们自定义开发的

                                               //HTTP+XML请求消息解码器HttpXmlRequestEncoder

                                               ch.pipeline().addLast("http-encoder",newHttpRequestEncoder());

                                               ch.pipeline().addLast("xml-encoder,newHttpXmlRequestEncoder());

                                               ch.pipeline().addLast("xmlClientHandler",newHttpXmlClientHandler());

                                                                

                                     }

                                     });

                            //发起异步连接操作

                                               ChannelFuturef=b.connect(new InetSocketAddress(port),sync());

                            //等待客户端链路关闭

                                               f.channel().closeFuture().sync();

                   }catch (Exception e) {

                            //TODO: handle exception

                   }finally{

                            //优雅退出,释放NIO线程组

                            group.shutdownGracefully();

                   }

         }

        

         publicstatic void main(String[] args) throws Exception{

                   intport=8080;

                   if(args!=null&&args.length>0){

                            try{

                                     port=Integer.valueOf(args[0]);

                                    

                            }catch (NumberFormatException e) {

                                     //TODO: handle exception

                                    

                            }

                            newHttpXmlClient().connect(port);

                   }

         }

 

}

3.2业务逻辑编排类HttpXmlClientHandler实现

package jibx;

 

import io.netty.channel.ChannelHandlerContext;

importio.netty.channel.SimpleChannelInboundHandler;

 

public class HttpXmlClientHandler extends

    SimpleChannelInboundHandler<HttpXmlResponse>{

  

         @Override

         publicvoid channelActive(ChannelHandlerContext ctx){

                    HttpXmlRequest request=newHttpXmlRequest(null,OrderFactory.create(123));

                    ctx.writeAndFlush(request);

   

}

          @Override

          public voidexceptionCaught(ChannelHandlerContext ctx,Throwable cause){

                    cause.printStackTrace();

                    ctx.close();

          }

          

          @Override

          protected voidmessageReceived(ChannelHandlerContext ctx,HttpXmlResponse msge)

            throws Exception{

                    System.out.println("The client receiveresponse of http header is:"+msg.getHttpResponse().headers().names());

                    System.out.println("The client receiveresponse of http body is:"+msg.getResult());

          }

 

}

3.3 订购对象工厂类OrderFactory实现

public class OrderFactory {

         publicstatic Order create(long orderID){

                   Orderorder=new order();

                   order.setOrderNumber(orderID);

                   order.setTotal(9999.999f);

                   Addressaddress=new Address();

                   address.setCity("武汉市");

                   address.setCountry("中国");

                   address.setPostCode("11");

                   address.setState("湖北省");

                   address.setStreet1("龙阳大道");

                   order.setBillTo(address);

                   Customercustomer=new Customer();

                   customer.setCustomerNumber(orderID);

                   customer.setFirstName("郑");

                   customer.setLastName("广");

                   order.setCustomer(customer);

                   order.setShipping(Shipping.INTERNATIONAL_MAIL);

                   order.setShipTo(address);

                   returnorder;

         }

}

四.HTTP+XML服务端开发

HTTP服务端的功能:

1.接收HTTP客户端连接

2.接收HTTP客户端的XML请求消息,并将其解码为POJO对象

3.对POJO对象进行业务处理,构造应答消息返回

4.通过HTTP+XML的格式返回应答消息

5.主动关闭HTTP连接

4.1 服务端主程序HttpXmlServer实现

package jibx;

 

import java.net.InetSocketAddress;

 

import client.TimeClient;

import io.netty.bootstrap.ServerBootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

importio.netty.channel.socket.nio.NioServerSocketChannel;

importio.netty.handler.codec.http.HttpObjectAggregator;

import io.netty.handler.codec.http.HttpRequestDecoder;

/*

 *HTTP+XML 服务端

 */

public class HttpXmlServer {

         publicvoid run(final int port) throws Exception{

                   EventLoopGroupbossGroup=new NioEventLoopGroup();

                   EventLoopGroupworkerGroup=new NioEventLoopGroup();

                   try{

                            ServerBootstrapb=new ServerBootstrap();

                            b.group(bossGroup,workerGroup)

                            .channel(NioServerSocketChannel.class)

                            .childHandler(newChannelInitializer<SocketChannel>() {

                                     @Override

                                     protectedvoid initChannel(SocketChannel ch) throws Exception{

                                               //用于绑定HTTP请求消息解码器

                                               ch.pipeline().addLast("http-decoder",newHttpRequestDecoder());

                                               ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65536));

                                    

                                               ch.pipeline().addLast(

                                                                 "xml-decoder",

                                                                 newHttpXmlRequestDecoder(

                                                                                    Order.class,true));

                                               ch.pipeline().addLast("http-encoder",newHttpResponseEncoder());

                                               //添加自定义的HttpXmlResponseEncoder编码器用于响应消息的编码

                                               ch.pipeline().addLast("xml-encoder",newHttpXmlResponseEncoder());

                                               ch.pipeline().addLast("xmlServerHandler",newHttpXmlServerHandler());

                                     }

                                     });

                            ChannelFuturefuture=b.bind(new InetSocketAddress(port)).sync();

                            System.out.println("HTTP订购服务器启动,网址是"+http://localhost:"+port);

                                     future.channel().closeFuture().sync();

                   }catch (Exception e) {

                            //TODO: handle exception

                   }  finally {

                            bossGroup.shutdownGracefully();

                            workerGroup.shutdownGracefully();

                   }

         }

        

         publicstatic void main(String[] args) throws Exception{

                   intport=8080;

                   if(args!=null&&args.length>0){

                            try{

                                     port=Integer.valueOf(args[0]);

                                    

                            }catch (NumberFormatException e) {

                                     //TODO: handle exception

                                     e.printStackTrace();

                            }

                            newHttpXmlServer().run(port);

                   }

         }

}

4.2 服务端处理类HttpXmlServerHandler实现

package jibx;

 

import com.sun.jndi.cosnaming.IiopUrl.Address;

 

import io.netty.buffer.Unpooled;

import io.netty.channel.ChannelFuture;

importio.netty.channel.ChannelHandlerContext;

importio.netty.handler.codec.http.FullHttpResponse;

importio.netty.handler.codec.http.HttpRequest;

import io.netty.handler.codec.http.HttpResponseStatus;

 

public class HttpXmlServerHandler extends

  SimpleChannelInboundHandler<HttpXmlRequest> {

 

 

    @Override

    public void messgeReceived(final ChannelHandlerContextctx,HttpXmlRequest xmlRequest)

     throws Exception{

              HttpRequest request=xmlRequest.getRequest();

              Order order=(Order)xmlRequest.getBody();

              System.out.println("Http server receiverequest:"+order);

              dobusiness(order);

              ChannelFuture future=ctx.writeAndFlush(newHttpXmlResponse(null,order));

              if(!isKeepAlive(request)) {

                        future.addListener(newGenericFutureListener<Future<? super void >> () {

                                 public void operationComplete(Future future)throws Exception{

                                          ctx.close();

                                 }

                        }};

              }

        }

 

      private void dobusiness(Order order){

        order.getCustomer().setFirstName("郑");

        order.getCustomer.setLastName("广");

        List<String> midNames=new ArrayList<String>();

        midName.add("李元芳");

        order.getCustomer().setMiddleNames(midNames);

        Address address=order.getBillTo();

        address.setCity("武汉");

        address.setCountry("大道");

        address.setState("东阳大道");

        address.setPostCode("123");

        order.setBillTo(address);

        order.setShipTo(address);

 

      }

      

      @Override

      public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause)throws Exception{

                

                cause.printStackTrace();

                if(ctx.channel().isActive()){

                          sendError(ctx,INTERNAL_SERVER_ERROR);

      }

      }

      

      private static void sendError(ChannelHandler ctx,HttpResponseStatusstatus){

                FullHttpResponse response=newDefaultFullHttpResponse

                                   (HTTP_1_1,status,Unpooled.copiedBuffer("失败:"+status.toString()+"\r\n",charsetUtil.UTF-8));

                response.headers().set(CONTENT_TYPE,"text/plain";charset=UTF-8);

                ctx.writeAndFlush(response).addListener(ChannelFutureListener).CLOSE;

      }

}

   上面例子是一个高性能,通用的协议栈,但是忽略了一些异常场景的处理,可扩展性API和一些配置能力,如果要在商用项目中使用,需要做一些产品化的完善工作。