一、数据库连接池
- 通过 java 程序操作数据库,必须首先要得到与数据库的连接对象,java 程序与数据库是两个进程,为了能够对数据库中的数据进行各种操作,两者之间首先要建立通讯连接(两端都要维护一个 Socket 套接字),另外要通过套接字向对方发送请求和数据,因此这种操作很耗资源,对两边来讲都是负担,因此连接对象的数量越少越好,尽量少的创建连接对象,
- 在一个项目中要经常对数据库进行操作,如果每次都建立连接就会造成资源消耗越来越大,为了解决以上问题,出现了数据库连接池。
-
什么是数据库连接池?
- javax.sql Interface DataSource 接口,它用来表示数据库连接池对象,如果一个类实现了该接口,该类就是数据库连接池对象。
- 它的一个主要方法就是:Connection getConnection() throws SQLException,作用是从连接池中得到一个连接对象。
-
连接池的基本原理
- 内部维护着一个队列,初始创建的连接对象会在此队列中进行排队,等待分配给某个线程使用。这种队列是有界队列,其中的对象是有最大数量限制的,不会无限增加。多数的连接池都有三个基本参数,第一是 initSize 表示连接池中最初的连接数量,minIdleSize 表示可保持的最小的空闲连接的对象,maxActivedSize 表示池中最多的连接数量,该值不能超过,如果需要连接的线程数量大于最大的连接数量,线程池也需要排队等待分配连接。
- 当连接池刚创建时,内部的连接数量是0,如果有线程请求连接对象,此时才会创建连接对象,使用完后会归还到池中等待下一次使用。如果池中有空闲的连接对象,就直接把该对象分配给线程。
- 如果空闲的对象也分配完了,还会创建新的对象,直到达到最多的连接数量后就不在创建了。
- 如果线程的数量减少,多创建出来的连接对象才会被销毁。
-
连接池的使用方式
- 在一个项目中只使用一个连接池,所以连接池对象应该是单例的。
- 当线程需要连接对象时,就要调用连接池对象的 getConnection() 方法来获得连接并使用。
- 当线程使用完连接对象后,必须释放并关闭该对象,还要保证连接对象被归还到池中。
-
常用的连接池
- 在传统中,c3p0 连接池使用的非常多,它的速度比较快,但是当系统规模达到一定规模,它在连接的创建和归还方面会出错。阿里自研的连接池是 druid 连接池,它号称全世界速度最快的连接池,另外它是国产,因此它是首选。
- 另外,日本也有一种连接池,被 springboot 默认使用,据说速度也非常快, 叫 HikariCP。号称性能最好。
-
如何创建 druid 连接池
-
在项目中引用 druid 连接池的 jar 包。
-
创建单例的连接池对象供项目使用。
-
driverName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/students?serverTimezone=UTC&characterEncoding=utf-8&useSSL=false user=huayunliufeng password=********* MaxActive=10 MinIdle=3 InitialSize=3
-
package util; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidPooledConnection; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.util.Properties; /** * // .::::. * // .::::::::. * // ::::::::::: * // ..:::::::::::' * // '::::::::::::' * // .:::::::::: * // '::::::::::::::.. * // ..::::::::::::. * // ``:::::::::::::::: * // ::::``:::::::::' .:::. * // ::::' ':::::' .::::::::. * // .::::' :::: .:::::::'::::. * // .:::' ::::: .:::::::::' ':::::. * // .::' :::::.:::::::::' ':::::. * // .::' ::::::::::::::' ``::::. * // ...::: ::::::::::::' ``::. * // ````':. ':::::::::' ::::.. * // '.:::::' ':'````.. * * @author 华韵流风 * @ClassName DruidPool * @Description TODO * @Date 2021/5/7 14:46 * @packageName util */ public class DruidPool { //饿汉式,单例模式 private static DruidDataSource pool = new DruidDataSource(); //静态代码快,保证连接池对象在类加载过程中就完成初始化 static { //对象连接池进行初始化,设置一些必须的参数,这些参数从外部文件中获取 Properties pt = new Properties();//创建属性集合对象 //把外部参数加载到该对象中 InputStream inputStream = DruidPool.class.getClassLoader().getResourceAsStream("db.properties"); try { pt.load(inputStream);//利用字节输入流对象完成参数的加载 //从集合中取出各参数,并设置连接池 pool.setDriverClassName(pt.getProperty("driverName")); pool.setUrl(pt.getProperty("url")); pool.setUsername(pt.getProperty("user")); pool.setPassword(pt.getProperty("password")); //以上四个为必须要有的 pool.setInitialSize(Integer.parseInt(pt.getProperty("InitialSize"))); pool.setMinIdle(Integer.parseInt(pt.getProperty("MinIdle"))); pool.setMaxActive(Integer.parseInt(pt.getProperty("MaxActive"))); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { try { DruidPooledConnection connection = pool.getConnection(); System.out.println(connection); } catch (SQLException sqlException) { sqlException.printStackTrace(); } } }
-
改进:
package util; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidPooledConnection; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; /** * @author 华韵流风 * @ClassName DruidPool * @Description TODO * @Date 2021/5/7 14:46 * @packageName util */ public class DruidPool { private static ThreadLocal<Connection> tl = new ThreadLocal<>(); //饿汉式,单例模式 private static final DruidDataSource pool = new DruidDataSource(); //静态代码快,保证连接池对象在类加载过程中就完成初始化 static { //对象连接池进行初始化,设置一些必须的参数,这些参数从外部文件中获取 Properties pt = new Properties();//创建属性集合对象 //把外部参数加载到该对象中 InputStream inputStream = DruidPool.class.getClassLoader().getResourceAsStream("db.properties"); try { pt.load(inputStream);//利用字节输入流对象完成参数的加载 //从集合中取出各参数,并设置连接池 pool.setDriverClassName(pt.getProperty("driverName")); pool.setUrl(pt.getProperty("url")); pool.setUsername(pt.getProperty("user")); pool.setPassword(pt.getProperty("password")); //以上四个为必须要有的 pool.setInitialSize(Integer.parseInt(pt.getProperty("InitialSize"))); pool.setMinIdle(Integer.parseInt(pt.getProperty("MinIdle"))); pool.setMaxActive(Integer.parseInt(pt.getProperty("MaxActive"))); } catch (IOException e) { e.printStackTrace(); } } //该方法返回一个连接对象给线程,并保证同一线程中只有一个连接对象 public static Connection getConnection() throws SQLException { DruidPooledConnection connection = null; System.out.println(Thread.currentThread().getName() + " 当前的连接数量:" + pool.getActiveCount() + " 当前空闲的连接数量:" + pool.getPoolingCount()); if (tl.get() == null) { try { connection = pool.getConnection(); //把连接对象放到ThreadLocal变量中,保证线程在各个方法中都可以使用 tl.set(connection); } catch (SQLException sqlException) { sqlException.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 当前的连接数量:" + pool.getActiveCount() + " 当前空闲的连接数量:" + pool.getPoolingCount()); return connection; } else { throw new SQLException("本线程已有连接对象!"); } } //释放连接 public static void releaseConnection() { if (tl.get() != null) { try { tl.get().close(); tl.remove(); } catch (SQLException sqlException) { sqlException.printStackTrace(); } } } }
-
二、ThreadLocal 类
-
java.lang Class ThreadLocal<T>,它是一个基础 API,这个类提供线程局部变量,这种变量与一般的其它类的变量不同,对于创建的每一个 ThreadLocal 变量,在多线程环境下,每一个线程都可以得到该变量的副本,该副本不是简单的复制,而是每个线程都会维护着 ThreadLocal 类型的不同的一个变量值。
-
在线程中,给变量赋值使用 set() 方法,取变量值使用 get() 方法。在不同的线程中,无论它们各自给什么值,相互之间不会影响。
-
package com.zhong.test; import java.util.Random; /** * @author 华韵流风 * @ClassName TestDemo * @Description TODO * @Date 2021/5/7 15:29 * @packageName com.zhong.test */ public class TestDemo { private static final ThreadLocal<Integer> tl = new ThreadLocal<>(); public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(() -> { //在本线程内,给变量指定值,此值一旦指定,在本线程的所有方法中都可以使用 tl.set(new Random().nextInt(100)); outI(); }).start(); } } private static void outI(){ //在本线程内,从ThreadLocal对象中得到该值 System.out.println(Thread.currentThread().getName()+" "+tl.get()); } }
三、项目阶段的学习方式
- 学习的路径从点–>线–>面–>体。
- 点:以前学习过的所有知识点。
- 线:在 web 项目中,当请求发出到接收到响应整个过程中所使用的技术及相关的执行原理和步骤。在整个项目中的运行过程中,就是由以上的各种线来实现各种功能。
- 面:项目中的一个模块,比如用户管理,商品管理等。对特定的表示所执行的各种功能往往会把它们归纳到同一模块中,此处与项目的功能有紧密关系。
- 体:由各个模块所组成的项目内容,要关注模块间的相互作用关系。
- 在项目学习开发中,主要关注设计项目的过程,及其中个功能所实现的过程。
- 通过项目的设计过程,进一步把 java 的基础作更深入的学习和巩固。
- 项目的选择,尽量选择具有代表性的。
四、MVC 设计模式
-
它是 javaEE 中的特定的模式。
-
它属于结构型的模式。
-
MVC 设计模式的结构和作用
- M = model(模型)
- V = view(视图)
- C = controller(控制器)
- 现代的 web 应用程序基本上都采用 mvc 设计模式。
- 传统的设计采用 model1 模式,它是基于 jsp 来设计系统,把所有的代码都写在页面中, 缺点很多,包括难维护,结构混乱,代码混乱,可读性差,严重耦合。
- MVC 模式按照个功能的实现,划分成三个部分,好处在于降低耦合,各个部分实现自己应该实现的功能(高内聚),因此易于维护,结构清晰等。
-
如何基于 mvc 的要求,划分项目中的包结构?
-
任何一个类或接口在全世界都是唯一的。
-
包的划分要么依据内容所属层次,要么依据功能模块或者两者结合在一起。
-
分包的方式:
- 组织名称(邮箱)
- 项目名称
- 模块名称
- MVC 的层次名(dao,service,controller)
- 接口的实现类
- MVC 的层次名(dao,service,controller)
- 模块名称
- 项目名称
以上划分方式有利于各种类及接口的分类管理,也有利于类和接口的划分。
- 组织名称(邮箱)
-
五、Dao 的底层实现
-
通过自己手工写代码去实现底层的功能,如果大量的去书写底层代码,问题在于会用到 jdbc 底层的一些接口和类,第二会出现很多重复的代码,第三对于常见的操作比如 CRUD,代码的书写步骤也大同小异,基于以上情况,在项目中不宜反复的去写这种没有技术含量的代码,不建议在项目中这样做。
-
使用别人已经写好的底层代码去实现底层功能,对于不同功能的实现只需要调用不同的方法并接收所需要的结果就可以了。这是推荐的方式。
-
建议使用 apache 的 common-dbutils 作为底层的实现,它提供了完整的 jar 包,可以复制到项目中直接使用。
-
了解 dbutils 提供的 api。
- public class QueryRunner 该类提供了实现 CRUD 以及批量更新的主要方法,各方法都有不同的重载形式,首先方法的返回类有区别,方法是否需要创建连接,方法是否需要传递执行参数。
- update(参数列表…),此类方法的作用,实现增删改的功能,具体的功能由 sql 命令决定。
- query(参数列表…),实现查询,查询的结果类型有多种,所以实现不同的查询,选择的方法是不同的。
- batch(参数列表…),参数包含一个 sql 命令和一份二维数组(多组参数),可以针对指定的 sql 命令,给它多组参数,分别执行,调用此方法就可以把同一 sql 结合不同的参数组合反复执行,执行次数由二维数组的行数决定。
- 以上三种有一个共同的特点:方法分为是否传递连接对象。
-
测试 update 方法
- 增删改都可以实现 update 方法,是否创建连接对象由自己决定。
-
测试查询
-
实现查询的前提之一是需要针对表单在项目中建立对应的 po(持久化对象),它就是一个 JavaBean。
-
在查询方法中有一个接口参数 interface ResultSetHandler<T>,接口的作用可以把查询到的结果进行转换,转换成指定的结果种类(包括单一 Bean,标量值(原始数据类型),Map,List,List<Map>等多种),该接口有很多实现类,不同的实现类的对象就表示一种特定的结果种类。
-
BeanHandler,就是单一 Bean,MapHandler,返回 Map,ScalarHandler 返回标量值,BeanListHandler 返回 List<Bean>,MapListHandler 返回 List<Map>。注意选择它们时要结合 sql 命令的返回结果及查询条件。
package com.zhong.test; import cn.softeem.test.po.User; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.MapHandler; import org.junit.Test; import util.DruidPool; import javax.sound.midi.Soundbank; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author 华韵流风 * @ClassName DbUtilsTest * @Description TODO * @Date 2021/5/8 16:41 * @packageName com.zhong.test */ public class DbUtilsTest { private final QueryRunner qr = new QueryRunner(DruidPool.getPool()); @Test public void testUpdate() throws SQLException { String sql = "delete from user where username=?"; Connection connection = DruidPool.getConnection(); int count = qr.update(connection, sql, "sss"); System.out.println(count); DruidPool.releaseConnection(); } @Test public void testQuery1() throws SQLException { String sql = "select * from user where username=?"; Connection connection = DruidPool.getConnection(); User user = qr.query(connection, sql, new BeanHandler<>(User.class), "root"); System.out.println(user); DruidPool.releaseConnection(); } @Test public void testQuery2() throws SQLException { String sql = "select * from user where password=?"; Connection connection = DruidPool.getConnection(); List<User> users = qr.query(connection, sql, new BeanListHandler<>(User.class), "1234"); for (User user : users) { System.out.println(user); } DruidPool.releaseConnection(); } @Test public void testQuery3() throws SQLException { String sql = "select * from user where username=?"; Connection connection = DruidPool.getConnection(); Map user = qr.query(connection, sql, new MapHandler(), "root"); for (Object o : user.entrySet()) { System.out.println(((Map.Entry)o).getKey()+" "+((Map.Entry)o).getValue()); } DruidPool.releaseConnection(); } }
-
-
如何用好 QueryRunner
- 在实现的项目设计中,一定要考虑事务的处理,它是业务逻辑实现必须要注意的问题。
- QueryRunner 的底层实现有两个问题,它与实现的项目设计有一段距离,第一个就是事务的处理,提供的事务方法要求外部传入连接对象,可是在 MVC 这种模式要求下,事务的控制是在 service 层来完成,如果在 service 中使用它的事务方法那么在 service 中就是用到连接对象,而连接对象是属于 Dao 层的对象,这样影响到结构的低耦合要求。第二,它未考虑到同一事务中包含多个增删改的操作。
- 基于第二点,项目中不能直接使用 QueryRunner 类,需要对它进行改造,
-
对连接池工具进行改造,要考虑事务处理的过程,另外再新增一个类,对 QueryRunner 进行封装。
-
在封装QueryRunner的代码中,batch方法一定是针对增删改的方法,操作完成后要释放连接,本操作是事务中的操作,连接对象的释放应该由提交或回滚事务的方法来完成,要保证 releaseConnection() 方法在事务持续期间不能关闭当前的连接,否则事务会出错。
-
package cn.softeem.taobao.utils; import com.alibaba.druid.pool.DruidDataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; /** * @author 华韵流风 * @ClassName DruidPool * @Description TODO * @Date 2021/5/7 14:46 * @packageName util */ public class DruidPool { private static final ThreadLocal<Connection> tl = new ThreadLocal<>(); /** * 连接池,饿汉式,单例模式 */ private static final DruidDataSource pool = new DruidDataSource(); //静态代码快,保证连接池对象在类加载过程中就完成初始化 static { //对象连接池进行初始化,设置一些必须的参数,这些参数从外部文件中获取 //创建属性集合对象 Properties pt = new Properties(); //把外部参数加载到该对象中 InputStream inputStream = DruidPool.class.getClassLoader().getResourceAsStream("db.properties"); try { //利用字节输入流对象完成参数的加载 pt.load(inputStream); //从集合中取出各参数,并设置连接池 pool.setDriverClassName(pt.getProperty("driverName")); pool.setUrl(pt.getProperty("url")); pool.setUsername(pt.getProperty("user")); pool.setPassword(pt.getProperty("password")); //以上四个为必须要有的 pool.setInitialSize(Integer.parseInt(pt.getProperty("InitialSize"))); pool.setMinIdle(Integer.parseInt(pt.getProperty("MinIdle"))); pool.setMaxActive(Integer.parseInt(pt.getProperty("MaxActive"))); } catch (IOException e) { e.printStackTrace(); } } /** * 该方法返回一个连接对象给线程,并保证同一线程中只有一个连接对象 * * @return Connection * @throws SQLException SQL */ public static Connection getConnection() throws SQLException { if (tl.get() == null) { //当前线程有可能拿到一个不存在于tl中的连接对象 return pool.getConnection(); } return tl.get(); } public static void beginTransaction() throws SQLException { Connection connection = tl.get(); if (connection != null) { throw new SQLException("当前已有事务!不能开始"); } connection = pool.getConnection(); //设置为手动提交 connection.setAutoCommit(false); tl.set(connection); } public static void commitTransaction() throws SQLException { Connection connection = tl.get(); if (connection == null) { throw new SQLException("当前没有事务!不能提交"); } //提交事务 connection.commit(); //释放连接 connection.close(); tl.remove(); } public static void rollbackTransaction() throws SQLException { Connection connection = tl.get(); if (connection == null) { throw new SQLException("当前没有事务"); } //回滚事务 connection.rollback(); //释放连接 connection.close(); tl.remove(); } //关闭连接 public static void releaseConnection(Connection connection) throws SQLException { Connection threadCon = tl.get(); if (threadCon == null) { if (connection != null) { connection.close(); } } } }
-
package util; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; import util.DruidPool; import java.sql.Connection; import java.sql.SQLException; /** * @author 华韵流风 * @ClassName TxQueryRunner * @Description TODO * @Date 2021/5/8 18:00 * @packageName com.zhong.test */ public class TxQueryRunner extends QueryRunner { @Override public int[] batch(String sql, Object[][] params) throws SQLException { Connection connection = DruidPool.getConnection(); int[] result = super.batch(connection, sql, params); DruidPool.releaseConnection(connection); return result; } @Override public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException { Connection connection = DruidPool.getConnection(); T result = super.query(connection, sql, rsh, params); DruidPool.releaseConnection(connection); return result; } @Override public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException { Connection connection = DruidPool.getConnection(); T result = super.query(connection, sql, rsh); DruidPool.releaseConnection(connection); return result; } @Override public int update(String sql) throws SQLException { Connection connection = DruidPool.getConnection(); int result = super.update(connection, sql); DruidPool.releaseConnection(connection); return result; } @Override public int update(String sql, Object param) throws SQLException { Connection connection = DruidPool.getConnection(); int result = super.update(connection, sql,param); DruidPool.releaseConnection(connection); return result; } @Override public int update(String sql, Object... params) throws SQLException { Connection connection = DruidPool.getConnection(); int result = super.update(connection, sql,params); DruidPool.releaseConnection(connection); return result; } }
-
package com.zhong.test; import cn.softeem.test.po.User; import org.apache.commons.dbutils.handlers.BeanHandler; import org.junit.Test; import util.DruidPool; import util.TxQueryRunner; import java.sql.SQLException; import java.util.Arrays; /** * @author 华韵流风 * @ClassName DbUtilsTest * @Description TODO * @Date 2021/5/8 16:41 * @packageName com.zhong.test */ public class DbUtilsTest { private TxQueryRunner tqr = new TxQueryRunner(); @Test public void testUpdate() throws SQLException { String sql = "insert into user values (?,?)"; String[][] params = {{"aaa", "1234"}, {"bbb", "1234"}}; DruidPool.beginTransaction(); try{ int[] result = tqr.batch(sql, params); DruidPool.commitTransaction(); System.out.println(Arrays.toString(result)); }catch (Exception e){ e.printStackTrace(); DruidPool.rollbackTransaction(); } } @Test public void testUpdate1() throws SQLException { String sql = "update user set password=? where username=?"; String[][] params = {{"1234", "aaa"}}; DruidPool.beginTransaction(); try{ int[] result = tqr.batch(sql, params); System.out.println(Arrays.toString(result)); DruidPool.commitTransaction(); }catch (Exception e){ e.printStackTrace(); DruidPool.rollbackTransaction(); } } @Test public void testQuery() throws SQLException { String sql = "select * from user where username=?"; User result = tqr.query(sql, new BeanHandler<>(User.class),"root"); System.out.println(result); } }
-
六、项目
- 新增商品
<input type="file" name="pimg" class="test"/><!--文件上传的控件-->
-
实现一个通用的 Servlet,项目中其它的 servet 都去继承它,好处在于,对于不同的请求可以定义不同的处理方法,可以统一向客户端发出请求的编码集,对于常用的操作比如请求重定向、请求转发可以用更简单的方式去实现。
-
package cn.softeem.taobao.utils; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Method; /** * @author 华韵流风 * @ClassName ${NAME} * @Description TODO * @Date 2021/5/11 16:27 * @packageName ${PACKAGE_NAME} * 在整个后台,主要功能是商品管理,对商品的所有操作统一使用同一个Servlet */ @WebServlet(name = "BaseServlet") public class BaseServlet extends HttpServlet { /** * 以下方法可以对所有请求进行一般化处理,以达到三个目的 * * @param req req * @param res res * @throws ServletException servlet * @throws IOException io */ @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { //转换类型 HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; //1、对客户端的输出统一为utf-8的编码 res.setContentType("text/html;charset=utf-8"); //2、得到method的参数值 String method = req.getParameter("method"); try { //3、判断当前servlet中是否有method方法,如果有就调用该方法,用到了反射技术 Method processMethod = this.getClass().getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class); //4、方法存在,也就是子类中添加的处理请求的方法,接收方法的返回值 try { String result = (String) processMethod.invoke(this, req, res); //判断是重定向还是转发 int pos = result.lastIndexOf(':'); //得到冒号前的字符 char ch = result.charAt(0); String path = result.substring(pos + 1); //转发 if (ch == 'f') { req.getRequestDispatcher(path).forward(req, res); } else { //重定向,用绝对路径 response.sendRedirect(request.getContextPath() + path); } } catch (Exception e) { e.printStackTrace(); } } catch (NoSuchMethodException e) { throw new RuntimeException("指定的方法不存在!"); } } }
-
enctype=“application/x-www-form-urlencoded” 取值决定了表单提交传递数据的方式,application/x-www-form-urlencoded 表示采用默认方式,multipart/from-data,表示采用流的方式传送,如果是文件上传则使用这个。
-
如果添加失败,异常会在 service 中被捕获并处理,客户端不知道添加失败,情况属于异常被闷死,不应该这样做,需要把异常抛到控制器,进行统一异常处理。
-
表单的 enctype=“multipart/form-data”,表单的普通控件以及文件的数据都会在一个流中向服务器传递,在服务器程序中接收流,对流进行解析,把普通的控件数据与文件的数据拆出来,分别进行处理。