什么是模板模式
模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。
模板模式的作用
复用以及扩展
标准实现
public abstract class AbstractClass { public final void templateMethod() { ....处理一些初始化操作 或者公用逻辑 //... method1(); //... method2(); //... } //定义好的模板方法1 protected abstract void method1(); //定义好的模板方法2 protected abstract void method2(); } //实现1 public class ConcreteClass1 extends AbstractClass { //父类初始化了 专注自己的处理逻辑 @Override protected void method1() { //... } @Override protected void method2() { //... } } //实现2 public class ConcreteClass2 extends AbstractClass { //父类初始化了 专注自己的处理逻辑 @Override protected void method1() { //... } @Override protected void method2() { //... } } AbstractClass demo = ConcreteClass1(); demo.templateMethod();
源码中的使用
DispatcherServlet
org.springframework.web.servlet.FrameworkServlet做一些初始化操作
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; /** * <31>获取当前线程 国际化上下文 从里面会从2个ThreadLocal获取 * org.springframework.web.filter.RequestContextFilter#initContextHolders * 如果配置了这个filter这里每次请求已经初始化 spring boot 验证 */ LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); //构建当前线程当本次请求的国际化上下文 LocaleContext localeContext = this.buildLocaleContext(request); /** * <32>获取当前线程请求的RequestAttribute 里面会从2个ThreadLocal获取 * org.springframework.web.filter.RequestContextFilter#initContextHolders已经初始化 spring boot验证 */ RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); //<4>如果线程缓存没有构建过,表示之前没初始化过 ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes); /** *会从requsetAttribute里面获取获取不到初始化一个再set进去 key=WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE * 表示我们后去都可以根据reqeust.getAttribute(WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE)获取 * 用于给管理异步请求 */ WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor()); /** * <5>这里面会存入根据当前对象成语变量区分放到哪个线程缓存里面threadContextInheritable 默认为false * 我们可以利用前面的BeanWapper进行修改 * 表示我们后续都可以根据LocaleContextHolder和RequestContextHolder获取 * */ this.initContextHolders(request, localeContext, requestAttributes); try { //<6>抽象方法 由子类实现 如:DispatcherServlet实现 this.doService(request, response); } catch (ServletException var17) { failureCause = var17; throw var17; } catch (IOException var18) { failureCause = var18; throw var18; } catch (Throwable var19) { failureCause = var19; throw new NestedServletException("Request processing failed", var19); } finally { /** * <7>默认previousLocaleContext previousAttributes 是null * 这里主要是为了将线程缓存的previousLocaleContext previousAttributes清空 */ this.resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } if (this.logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", (Throwable)failureCause); } else if (asyncManager.isConcurrentHandlingStarted()) { this.logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } //spring事件机制 发布ServletRequestHandlerEvent消息,这个请求是否执行成功都会发布消息的 this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause); } }
//抽象方法 protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
org.springframework.web.servlet.DispatcherServlet#doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (this.logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } //如果是include请求,保存request attribute快照数据,并在finally中进行还原 Map<String, Object> attributesSnapshot = null; /** * request.getAttribute("javax.servlet.include.request_uri")!=null * <jsp:incluede page="xxx.jsp"/> * jsp里面嵌套上面这个标签 编译器后也是一个servlet来调用 通过这个属性判断是否是include标签 */ if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap(); Enumeration attrNames = request.getAttributeNames(); label108: while(true) { String attrName; do { if (!attrNames.hasMoreElements()) { break label108; } attrName = (String)attrNames.nextElement(); } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet")); attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } //保存ApplicationContext 到request request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext()); //保存localeResolver 到request request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); //保存themeResolver 到request request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); //保存gThemeSource 到request request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource()); //貌似是为了解决重定向302 带参数 长度问题 先保存到服务器 然后302重定向只需要带过来一个对应的key FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } //保存一个空的FlashMap request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); //保存flashMapManager request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try { //<7>执行处理请求 this.doDispatch(request, response); } finally { //还原快照数据 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) { this.restoreAttributesAfterInclude(request, attributesSnapshot); } } }
摘自:《SpringMVC源码阅读-一个请求主要处理流程DispatcherServlet(四)》
模板模式和回调
回调标准实现
//回调接口实现 public interface ICallback { void methodToCallback(); } public class BClass { public void process(ICallback callback) { ...执行一系列逻辑 //...调用回调 callback.methodToCallback(); //... } } public class AClass { public static void main(String[] args) { BClass b = new BClass(); //调用处理方法 传入回调 b.process(new ICallback() { //回调对象 @Override public void methodToCallback() { System.out.println("Call back me."); } }); } }
JdbcTemplate
传统通过jdbc实现访问数据库 我们可以发现大量的模板代码 如果 1 2 4 5 6 7 这些步骤几乎每次访问都需要
public class JdbcDemo { public User queryUser(long id) { Connection conn = null; Statement stmt = null; try { //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo", "xzg", "xzg"); //2.创建statement类对象,用来执行SQL语句 stmt = conn.createStatement(); //3.ResultSet类,用来存放获取的结果集 String sql = "select * from user where id=" + id; ResultSet resultSet = stmt.executeQuery(sql); String eid = null, ename = null, price = null; //4.封装结果 while (resultSet.next()) { User user = new User(); user.setId(resultSet.getLong("id")); user.setName(resultSet.getString("name")); user.setTelephone(resultSet.getString("telephone")); return user; } } catch (ClassNotFoundException e) { // 5.TODO: log... 异常处理 } catch (SQLException e) { // 6.TODO: log... 异常处理 } finally { //7.释放连接 if (conn != null) try { conn.close(); } catch (SQLException e) { // TODO: log... } if (stmt != null) try { stmt.close(); } catch (SQLException e) { // TODO: log... } } return null; } }
jdbctemlate使用
public class JdbcTemplateDemo { private JdbcTemplate jdbcTemplate; public User queryUser(long id) { String sql = "select * from user where id="+id; //通过回调传入Mapper 我们只需要关心 封装结果集 和执行sql return jdbcTemplate.query(sql, new UserRowMapper()).get(0); } class UserRowMapper implements RowMapper<User> { public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getLong("id")); user.setName(rs.getString("name")); user.setTelephone(rs.getString("telephone")); return user; } } }
setClickListener
set存入成员变量
Button button = (Button) findViewById(R.id.button); //注册点击事件 button.setOnClickListener(new OnClickListener() { @Override public void onClick (View v){ System.out.println("I am clicked."); } });
2者区别
模板模式:通过再父类定义好算法骨架,将具体实现下放到子类 实现复用和扩展 本质通过继承
回调:定义好骨架 通过处理函数传入定制化的处理逻辑执行 或者提前拿通过成员变量注入,在骨架特定地点调用 实现不同需求的定制化逻辑,实现复用,本质是通过 实时传入回调或者组合的方式提前注入 实现代码复用