1. 程式人生 > >[筆記]架構探險-從零開始寫JavaWeb框架-1. 之搭建輕量級mvc框架

[筆記]架構探險-從零開始寫JavaWeb框架-1. 之搭建輕量級mvc框架

囉嗦一句: 看md語法寫的文章,注意檢視 上面 的目錄. 一般是很有節奏的導航.

ヽ(ˋ▽ˊ)ノヽ(ˋ▽ˊ)ノ 終於到了不會的地步了,該書的前面兩章節都是從零開始講解怎麼使用 idea搭建專案,從servlet開始講解怎麼使用. (idea的使用目錄)把思維過度到了 下面這樣一個局面:
最簡單的servlet 我們都知道是一個servlet處理一個種請求,那麼弊端大家都知道.
那麼這章要學習的應該就是 怎麼實現下面這樣的理想的狀態,處理customer的action 那麼相關的方法都放到一個類裡面,

@Controller
public class CustomerController
{
@Inject private CustomerService customerService; @Action("get:/customer") public View index(Param param){ List<Customer> customerList = customerService.getCustomerList(); return new View("customer.jsp").addModel("customerList",customerList); } @Action
("delete:/customer_deit") public Data delete(Param param){ long id = param.getLong("id"); boolean b = customerService.deleteCustomer(id); return new Data(b); } }
  1. @Controller 表明該類是一個控制器
  2. @Inject 注入,自動注入需要型別的例項
  3. @Action 標識為一個action,宣告該方法接收什麼訪問型別和路徑是什麼
  4. Param : 封裝請求的引數
  5. View : 封裝跳轉的頁面 和 需要的資料model
  6. Data : 封裝返回的json資料. 用於ajax

看了上面的介紹,我覺得 這個就是一個在模仿spring mvc的框架.spring mvc 和 spring一起使用的話,好像它就是有兩個bean容器.(不知道是不是這樣,如果確認的話,那麼按照這樣的思路來實現一個輕量級的mvc框架應該是對的)
我不會照搬書上的.文字和程式碼,只會把自己覺得不會的 和 自己理解的程式碼貼上來(大部分程式碼是書上的,因為我也不會這些,先學會,和理解).(對了.該書好像是實現一個 MVC的輕量級框架. c 是一個核心.)看書目錄,覺得這章應該學會:

  1. 如何載入並讀取配置檔案(忽略,就是使用各種解析方式,解析定義的屬性常量)
  2. 如何實現一個簡單的IOC容器
  3. 如何載入指定的類
  4. 如何初始化框架

如何載入指定基礎包名下的 類檔案

思路:

  1. 需要一個基礎限定基礎包名
  2. 通過基礎包名 獲取 絕對路徑,然後遞迴把該路徑下的所有jar包或則class檔案給裝載到記憶體中,並存起來供以後使用.

以下是用到的難點:

  1. 絕對路徑的獲取
  2. 包名的組裝
  3. 類載入

關於 類載入是否例項化的問題,百度了一下,總結如下:

  1. Class的裝載過程(也就是從位元組碼檔案到生成類的例項這個過程)分為三個階段,loading(裝載),linking(連線)和initializing(例項化)。
  2. forName(String name, boolean initialize,ClassLoader loader) : initialize : 值,被裝載後,是否例項化類,( 對於 例項化其實我不明白,通過實驗,例項化後,物件放在哪裡呢?)
  3. ClassLoader.loadClass(className) : 內部呼叫loadClass(name, false),jdk上說:裝載類,Java 虛擬機器呼叫它來分析類引用. 第二個引數 就是是否被linking.

好了,看了一圈也沒有明白 到底是個什麼意思, 總之,在這裡 initialize 如果為ture,那麼會出現某些類靜態程式碼塊中有呼叫了其他的靜態方法,這裡其他類的靜態屬性什麼的都不會執行. 繞來繞去,還是被搞蒙了. 記得 設定為false 就沒錯了.

public class ClassUtil {
    static {
        System.out.println("類被載入了");
    }
    private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);
    /** 獲取類載入器**/
    public static ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }

    /**
     * 載入類
     * @param className 所需類的完全限定名
     * @param isInitialized 是否執行類的靜態程式碼塊(api:引數為 true 且以前未被初始化時,才初始化該類,
     *                      作者說:設定為false可以提高效能.具體原因是什麼沒有說.我也不知道,這個引數確實  不知道是什麼意思)
     * @return
     */
    public static Class<?> loadClass(String className,boolean isInitialized){
        Class<?> aClass = null;
        try {
            aClass = Class.forName(className, isInitialized,getClassLoader());  // 該方法類載入器有值的時候呼叫的是原生方法
        } catch (ClassNotFoundException e) {
            LOGGER.error("load class failure",e);
            throw new RuntimeException(e);
        }
        return aClass;
    }

    /**
     * 獲取該包名下的所有類
     * @param packageName
     * @return
     */
    public static Set<Class<?>> getClassSet(String packageName){
        /**
         * 1. 獲取指定包名下的的所有類
         * 2. 根據包名將其轉換為檔案路徑
         * 3. 讀取class檔案或jar包
         * 4. 獲取指定的類名去載入類
         */
        Set<Class<?>> classSet = new HashSet<>();
        try {
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
            while (urls.hasMoreElements()){
                URL url = urls.nextElement();
                String protocol = url.getProtocol(); //獲取此 URL 的協議名稱。
                if(protocol.equals("file")){
                    // %20 表示file協議?
                    String packagePath = url.getPath().replaceAll("%20", "");
                    addClass(classSet,packagePath,packageName);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classSet;
    }

    /**
     * 如果是檔案,就根據包名 和 檔名 組成類的全限定名稱,然後 載入類
     * @param classSet
     * @param packagePath 檔案(夾)的絕對路徑
     * @param packageName 和當前檔案(夾) 對應的包名
     */
    public static  void addClass(Set<Class<?>> classSet,String packagePath,String packageName){

        File[] files = new File(packagePath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                // 只需要 檔案並且是.class的檔案,或則是目錄 都返回true
                return file.isFile() && file.getName().endsWith(".class") || file.isDirectory();
            }
        });

        for (File file : files) {
            String fileName = file.getName();
            if(file.isFile()){ // 是指定的檔案 就獲取到全限定類名 然後裝載它
                String className = fileName.substring(0, fileName.lastIndexOf(".")); // 把.class後最擷取掉
                if(StringUtils.isNotBlank(packageName)){
                    className = packageName + "." + className; // 根據包名 + 檔名 得到這個類的全限定名稱,
                }
                doAddClass(classSet,className);
            }else { // 是檔案 就遞迴自己. 獲取 資料夾的絕對路徑,和 當前資料夾對應的 限定包名.方便 檔案裡面直接使用

                String subPackagePath= fileName;
                if(StringUtils.isNotBlank(subPackagePath)){
                    subPackagePath = packagePath + "/" + subPackagePath; // 第一次:由基礎包名 得到絕對路徑,再加上當前資料夾名稱 = 當前資料夾的絕對路徑
                }
                subPackagePath = file.getAbsolutePath(); // 該方法獲得檔案的絕對路徑.和上面的程式碼效果是一致的
                String subPackageName = fileName;
                if(StringUtils.isNotBlank(subPackageName)){
                    subPackageName = packageName + "." + subPackageName; // 第一次: 基礎包名 加資料夾名稱 組合成 當前包名 +
                }
                addClass(classSet,subPackagePath,subPackageName);
            }
        }
    }

    /**
     * 載入類,並把該類物件 新增到集合中
     * @param classSet
     * @param className
     */
    public static void doAddClass(Set<Class<?>> classSet,String className){
        Class<?> cls = loadClass(className, false);
        classSet.add(cls);
    }

    public static void main(String[] args) {
        getClassSet("mr.code");
    }
}

實現一個簡單的bean容器

思路:

  1. 通過上面工具類 把所有的類載入之後,通過過濾自定義註解,得到自己需要管理的bean class物件集合.(其實我沒有明白:通過上面的類載入是把所有class都載入到了記憶體中麼?如果有靜態程式碼塊不就全都被載入了?)
  2. 通過class物件來獲取 建立的例項(因為是簡單的bean容器,很容易理解)
/**
 * Created by zhuqiang on 2015/10/19 0019.
 * bean 容器
 */
public class BeanHelper {
    /**
     * 1. 生成bean例項
     * 2. 獲取bean示例
     */

    /** key : class value:對應的例項 也就是隻能存在單例麼?*/
    private static final Map<Class<?>,Object> BEAN_MAP = new HashMap<>();

    static {
        Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();
        for (Class<?> c : beanClassSet) {
            BEAN_MAP.put(c, ReflectionUtil.newInstance(c));  //建立所有class的例項(自己定義的註解類)
        }
    }

    /** bean 容器**/
    public static Map<Class<?>, Object> getBeanMap() {
        return BEAN_MAP;
    }

    /** 獲取bean**/
    public static <T> T getBean(Class<T> cls){
        if(!BEAN_MAP.containsKey(cls)){
            throw new RuntimeException("can not get bean by class : " + cls);
        }
        return (T) BEAN_MAP.get(cls);
    }
}

實現依賴入住功能(IOC)

依賴注入功能,在spring中最爽了,通過@Autowired等註解來實現注入的功能,那麼就離不開了ioc和di
ioc(Inversion of control): 控制反轉,控制不是由開發者決定的,而是反轉給框架來控制了.
di(Dependency Injection): 依賴注入,將某個類需要依賴的成員注入到這個類中

這樣一來,ioc和di其實就是一個東西了, 你要注入的話.那麼你就得有例項物件,要有例項物件,就得你來管理了.

實現思路:

  1. 在BeanHelper中,已經把我們需要管理的bean對應關係拿到了,
  2. 遍歷判斷這些類中的屬性成員是否有我們指定的註解,有則把所需要的成員給通過反射的方法注入

其實我們能感受到,在實際的專案開發中,注入的場景其實是多樣的,構造\屬性\靜態等 要實現這樣的 就很難了,靜態的就涉及到時間先後順序,在類載入的時候 就得先例項化了.某些場景下. 還好本章講的是最簡單的一種方式.直接通過反射set注入.也因為目前實現的都是單例的.

/**
 * Created by zhuqiang on 2015/10/19 0019.
 * ioc 依賴注入
 */
public class IocHelper {
    static{
        Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap(); //框架需要管理的bean對映
        if(MapUtils.isNotEmpty(beanMap)){
            for(Map.Entry<Class<?>, Object> ent : beanMap.entrySet()){
                Class<?> beanCls = ent.getKey();
                Object beanInstance = ent.getValue();
                // 獲取該class所有的成員屬性,包括公共、保護、預設(包)訪問和私有欄位,但不包括繼承的欄位
                Field[] beanFields = beanCls.getDeclaredFields();
                for (Field beanField : beanFields) {
                    //判斷 該欄位是否 包含 inject註解
                    if(beanField.isAnnotationPresent(Inject.class)){
                        Class<?> beanFieldType = beanField.getType(); //宣告型別 成員變數的Class
                        Object beanFieldInstance = beanMap.get(beanFieldType); // 獲取該型別的例項
                        if(beanFieldInstance != null){
                            ReflectionUtil.setField(beanInstance,beanField,beanFieldInstance); // 把對應的成員變數屬性 賦值
                        }
                    }
                }
            }
        }
    }
}

實現Controller對映類

所有我們需要管理的物件 都有了例項,而且也通過依賴注入使例項完整了, 那麼 接下來,就是 Controller註解的處理了.

仔細想一想,在spring mvc中,我們在頁面中發起一個請求,dispatcherServlet接收到之後,會做的處理,簡單的說,根據請求路徑等特徵 去 和 我們在RequestMapping中設定的 路徑等資訊 去匹配,然後定位到 哪一個類的哪一個方法來處理這個請求,最後獲取結果,返回給前臺?(這裡只是我的 思路.至於找到對應的方法之後,在處理的時候需要做些什麼東西,目前我也想不到,只能拭目以待了.)

實現思路:

  1. 獲取@action配置的資訊,封裝成一個request請求物件
  2. 封裝該@action對應的方法和class物件 ,封裝成一個hander處理物件
  3. 然後 把 request 和 hander 對映存起來,以供我們可以通過 頁面發出的請求 封裝成 request物件 找到對應的 hander 處理方法.
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

/**
 * Created by zhuqiang on 2015/10/20 0020.
 * 請求物件
 */
public class Request {
    // 請求方法
    private String requestMethod;
    //請求路徑
    private String requestPath;

    public Request(String requestMethod, String requestPath) {
        this.requestMethod = requestMethod;
        this.requestPath = requestPath;
    }

    public String getRequestMethod() {
        return requestMethod;
    }

    public String getRequestPath() {
        return requestPath;
    }

    // 重寫這兩個方法 就是為了 在 map中以 request作為key的時候,避免我們自己手動的來重寫 hashCode 和 equaks 方法
    // 因為我們認為.相同的 requestMethod 和 requestPath 就是匹配的. 需要轉發到 對應的 hander中去

    @Override
    public int hashCode() {
        //通過反射欄位獲取code
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public boolean equals(Object obj) {
        //如果兩個物件相等(是同一種class物件)當且僅當每個屬性值都相等
        return EqualsBuilder.reflectionEquals(this,obj);
    }
}

import java.lang.reflect.Method;

/**
 * Created by zhuqiang on 2015/10/20 0020.
 * 處理物件 class 與 對應的 處理方法
 */
public class Hander {
    private Class<?> controllerClass; //controller類
    private Method actionMethod; //方法

    public Hander(Class<?> controllerClass, Method actionMethod) {
        this.controllerClass = controllerClass;
        this.actionMethod = actionMethod;
    }

    public Class<?> getControllerClass() {
        return controllerClass;
    }

    public Method getActionMethod() {
        return actionMethod;
    }
}

初始化框架

關於這節,完全是沒有弄明白,裝載的類都使用的是false,static程式碼塊並沒有被執行,為什麼還要裝載呢?

import mr.code.utils.ClassUtil;

/**
 * Created by zhuqiang on 2015/10/20 0020.
 * 載入對應的 classHelper
 */
public final class HelperLoader {
    public static void init(){
        Class<?>[] list ={
                ClassHelper.class,
                BeanHelper.class,
                IocHelper.class,
                ControllerHelper.class
        };

        for (Class<?> cls : list) {
            ClassUtil.loadClass(cls.getName(),false);
        }
    }

    public static void main(String[] args) throws ClassNotFoundException {
        init();
    }
}

mvc核心編寫 DispatcherServlet 編寫

首先要實現一個DispatcherServlet的大體思路:

  1. 框架得有一個入口,這裡使用 servlet 作為攔截所有路徑的入口
  2. 攔截到所有的請求之後,需要 根據 請求 的特徵資訊,去匹配 對應的hander處理物件,
  3. 找到hander物件之後, 需要處理客戶端傳遞的引數資訊
  4. 根據action註解對應的hander配置資訊,把處理好的引數等傳遞給hander
  5. 呼叫hander得到結果後,需要分析處理返回的結果,到底是返回到頁面?還是 json資料

總結下: reqest請求 –> DispatcherServlet –> 尋找hander –> 處理對應的引數 –> 呼叫hander –> 獲取結果 –> 處理返回的結果 –> 響應客戶端;

好把.大體的程式碼如下, 不過很遺憾的是,這個小框架寫到這裡 能解決最開始的局面了. 有了 ioc和di,有了DispatcherServlet 用幾個小注解 就能達到處理簡單的應用了. 太遺憾的是,經過測試. 請求靜態圖片什麼的.不知道為什麼沒有任何顯示. 估計是 還需要 處理吧. 好把,短暫的測試了下.還是不能顯示其他的.只能處理自己對映的方法. 繼續學習吧

import com.fasterxml.jackson.databind.ObjectMapper;
import mr.code.bean.Data;
import mr.code.bean.Hander;
import mr.code.bean.Param;
import mr.code.bean.View;
import mr.code.utils.CodecUtil;
import mr.code.utils.ReflectionUtil;
import mr.code.utils.StreameUtil;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
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.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by zhuqiang on 2015/10/21 0021.
 * mvc 框架的核心
 */
@WebServlet(urlPatterns = "/*",loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet{
    @Override
    public void init(ServletConfig config) throws ServletException {
        HelperLoader.init();  // 就是載入之前封裝好的 Helper等class
        ServletContext servletContext = config.getServletContext();

        // 查看了下 返回的是 org.apache.jasper.servlet.JspServlet 說明是框架中的一個servlet
        ServletRegistration jspServlet = servletContext.getServletRegistration("jsp"); //獲取註冊的 js servlet物件
        jspServlet.addMapping(ConfigHelper.getJspPath() + "*");

        ServletRegistration aDefault = servletContext.getServletRegistration("default"); //處理靜態資源
        aDefault.addMapping(ConfigHelper.getAssetPath() + "*");

        // 然後這個是所有的servlet . 裡面有我們自己的 和 框架註冊的
        Map<String, ? extends ServletRegistration> servletRegistrations = servletContext.getServletRegistrations();
    }

    // 基礎知識: 先進入service 在service方法中判斷 然後呼叫不同的get啊post方法的
    @Override
    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String reqMethod = req.getMethod().toLowerCase();
        String reqPath = req.getPathInfo(); // 獲取請求路徑
        Hander hander = ControllerHelper.getHander(reqMethod, reqPath); // 獲取對應的處理類
        Map<String,Object> paramMap = new HashMap<>();
        if(hander != null){ //找到了對應的處理器
            Class<?> controllerClass = hander.getControllerClass();
            Object bean = BeanHelper.getBean(controllerClass); // 處理器例項

            // 獲取請求的引數,然後封裝到 param裡面
            Enumeration<String> parameterNames = req.getParameterNames();
            while (parameterNames.hasMoreElements()){
                String paramName = parameterNames.nextElement();
                String paramValue = req.getParameter(paramName);
                paramMap.put(paramName,paramValue);
            }

            //這裡還不知道 在什麼情況下 流裡面會有值.難道是圖片上傳?/ 下面這個工具類 就是把流裡面的字串給讀取出來
            String body = CodecUtil.decodeURL(StreameUtil.getString(req.getInputStream()));
            if(StringUtils.isNotBlank(body)){
                String[] params = StringUtils.split(body, "&");
                for (String param : params) {
                    String[] array = StringUtils.split(param, "=");
                    String paramName = array[0];
                    String paramValue = array[1];
                    paramMap.put(paramName,paramValue);
                }
            }

            // 把處理好的 請求引數 封裝成 param 然後呼叫 hander
            Param param = new Param(paramMap);
            Method actionMethod = hander.getActionMethod();
            Object result = ReflectionUtil.invokeMethod(bean, actionMethod, param);

            // 處理 返回值, 需要區別對待 是 view 還是 data json資料
            if(result instanceof View){
                View view = (View)result;
                String path = view.getPath();
                if(StringUtils.isNotBlank(path)){
                    if(path.startsWith("/")){
                        resp.sendRedirect(req.getContextPath() + path); //
                    }else { // 返回到頁面的
                        Map<String, Object> model = view.getModel();
                        for (Map.Entry<String,Object> ent:model.entrySet()){
                            req.setAttribute(ent.getKey(),ent.getValue());
                        }
                        req.getRequestDispatcher(ConfigHelper.getJspPath() + path).forward(req,resp);
                    }
                }
            }else if(result instanceof Data){
                Data data = (Data) result;
                Object model = data.getModel();
                if(model != null){
                    resp.setContentType("application/json");
                    resp.setCharacterEncoding("UTF-8");
                    PrintWriter writer = resp.getWriter();
                    writer.write(new ObjectMapper().writeValueAsString(data));
                    writer.flush();
                    writer.close();
                }
            }
        }

    }
}


/**
 * Created by zhuqiang on 2015/10/20 0020.
 * 請求物件
 */
public class Request {
    // 請求方法
    private String requestMethod;
    //請求路徑
    private String requestPath;

    public Request(String requestMethod, String requestPath) {
        this.requestMethod = requestMethod;
        this.requestPath = requestPath;
    }

    public String getRequestMethod() {
        return requestMethod;
    }

    public String getRequestPath() {
        return requestPath;
    }

    // 重寫這兩個方法 就是為了 在 map中以 request作為key的時候,避免我們自己手動的來重寫 hashCode 和 equaks 方法
    // 因為我們認為.相同的 requestMethod 和 requestPath 就是匹配的. 需要轉發到 對應的 hander中去

    @Override
    public int hashCode() {
        //通過反射欄位獲取code
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public boolean equals(Object obj) {
        //如果兩個物件相等(是同一種class物件)當且僅當每個屬性值都相等
        return EqualsBuilder.reflectionEquals(this,obj);
    }
}

/**
 * Created by zhuqiang on 2015/10/20 0020.
 * 處理物件 class 與 對應的 處理方法
 */
public class Hander {
    static{
        System.out.println("是否被載入了兩次");
    }
    private Class<?> controllerClass; //controller類
    private Method actionMethod; //方法

    public Hander(Class<?> controllerClass, Method actionMethod) {
        this.controllerClass = controllerClass;
        this.actionMethod = actionMethod;
    }

    public Class<?> getControllerClass() {
        return controllerClass;
    }

    public Method getActionMethod() {
        return actionMethod;
    }
}

/**
 * Created by zhuqiang on 2015/10/21 0021.
 * 封裝請求引數
 */
public class Param{
    private Map<String,Object> paramMap = new HashMap<>();

    public Param(Map<String, Object> paramMap) {
        this.paramMap = paramMap;
    }

    public Map<String, Object> getParamMap() {
        return paramMap;
    }
}

/**
 * Created by zhuqiang on 2015/10/21 0021.
 * 返回檢視物件
 */
public class View {
    private String path;//路徑名稱
    private Map<String,Object> model;  //返回的資料模型

    public View(String path) {
        this.path = path;
        model = new HashMap<>();
    }

    public View addModel(String key,Object value){
        model.put(key,value);
        return this;
    }

    public String getPath() {
        return path;
    }

    public Map<String, Object> getModel() {
        return model;
    }
}

/**
 * Created by zhuqiang on 2015/10/21 0021.
 * 封裝返回的json資料
 */
public class Data {
    private Object model;

    public Data(Object model) {
        this.model = model;
    }

    public Object getModel() {
        return model;
    }
}

相關推薦

[筆記]架構探險-開始JavaWeb框架-1. 搭建輕量級mvc框架

囉嗦一句: 看md語法寫的文章,注意檢視 上面 的目錄. 一般是很有節奏的導航. ヽ(ˋ▽ˊ)ノヽ(ˋ▽ˊ)ノ 終於到了不會的地步了,該書的前面兩章節都是從零開始講解怎麼使用 idea搭建專案,從servlet開始講解怎麼使用. (idea的使用目錄)

[筆記]架構探險-開始JavaWeb框架-2.1. 使框架具有aop特性-aop框架載入與切面執行流程分析

囉嗦一句:本筆記只是自己在學習過程中的一些分析和理解,看的人不一定能看懂.如果有興趣還是去買這本書看.筆記就當是另外一種解說好了 在本章節中會學習到如下的技術: 如何理解並使用代理技術 如何使用Spring提供的AOP技術(忽略,太多知識) 如何使

架構探險-開始Javaweb框架讀書筆記(5)

AOP實現 AOP(Aspect Oriented Programming,面向切面程式設計);用來不改變程式碼的情況下在方法前後加入效能監控,日誌列印等等。 依照慣例,有時spring aop的實現過程 advice 直譯為通知 黃勇老師

架構探險——開始Java Web框架》 讀書筆記

由於囤書,近期終於把很早之前買的《架構探險——從零開始寫Java Web框架》讀完,記錄下筆記,把一些有意思並且需要鞏固的,拿出來分享給大家。 涉及內容:開發思路、IoC與DI、AOP、事務的4種隔離級別、框架推薦(Apache Shiro、Apache CX

架構探險——開始Java Web框架》 試讀——感想

《架構探險——從零開始寫Java Web框架》 試讀感想          宣告:         本篇文章在iteye上發表的也是我本人的號。所以不存在未經作者允許非法轉載的事。為了證明,我擷取登

架構探險開始Java Web框架》讀後感

講真,該書利用一個小專案引導讀者從零開始手把手的搭建了一個Java Web框架,一個類Spring框架,重複造了輪子;但能幫助初學的讀者瞭解常用框架的底層原理。書中堆了很多程式碼,html程式碼也有,

開始javaweb框架筆記9-細節完善與程式碼優化-完善控制器層

    在這一小節中,我們開始寫jsp頁面  開啟/WEB-INF/view/customer.jsp檔案,完成如下程式碼: <%-- Created by IntelliJ IDEA. User: jack Date: 2015/12/5 Time:

開始JavaWeb框架(第二章節)

oca ext span logs http ioe 請求方法 servlet 類型 這一章太多了。。。好累,不想寫那麽細了,就做一點總結吧。 package org.smart4j.chapter2.controller; import java.io.IOExcep

開始Python爬蟲 --- 1.1 requests庫的安裝與使用

從零開始寫Python爬蟲 --- 1.1 requests庫的安裝與使用     先來說說爬蟲的 原理:爬蟲本質上是模擬人瀏覽資訊的過程,只不過他通過計算機來達到快速抓取篩選資訊的目的。所以我們想要寫一個爬蟲,最基本的就是要將我們需要抓取資訊的網頁原

python開始爬蟲(1)-- 開發環境搭建

我是一點都不會python,但為了寫爬蟲,所以就硬幹了。。。 1.windows下搭建python環境直接參考這個就行了安裝指南 2.透過pip安裝套件:1)pip install requests;2)pip install BeautifulSoup4      具體操

開始Python爬蟲 --- 1.6 爬蟲實踐: DOTA'菠菜'結果查詢

說起來目錄裡面本來是準備雙色球資訊查詢的,但是我一點都不懂這個啊,恰好身邊有個老賭棍,沉迷Dota飾品交易,俗稱 “菠菜”。老賭棍啊,老賭棍,能不能不要每天我說天台見。。。 這次的爬蟲功能十分的簡答,主要目的是延展一下bs4庫的使用。 目標分析: 看一看網站裡的資訊是怎麼排列的: 和上一次一樣 我們

讀書雜談-《架構探險開始Java Web框架

        愛買書,雖然讀書懶惰的很,但最近還是陸陸續續的買了五六本書的樣子,包括:核心技術、深入虛擬機器、併發程式設計等,這些書中我能讀的進去,且通俗易懂的當屬這本黃勇寫的《架構探險:從零開始寫Java Web框架》。         這本書的特點是大量的程式碼,通俗的

[Golang] 開始Socket Server(3): 對長、短連接的處理策略(模擬心跳)

microsoft ted 每次 range 點擊 關閉 ade 而在 href 通過前兩章,我們成功是寫出了一套湊合能用的Server和Client,並在二者之間實現了通過協議交流。這麽一來,一個簡易的socket通訊框架已經初具雛形了,那麽我們接下來做的

開始STL-容器-雙端隊列

這一 偏移 nis log index end ref 分配 locate 從零開始寫STL-容器-雙端隊列 什麽是雙端隊列?在介紹vector源碼,我們發現在vector前端插入元素往往會引起大量元素的重新分配,雙端隊列(deque)就是為了解決這一問題,雙端隊列中在首

開始STL—functional

binder 保存 函數調用 mark 獲取 AR ref 返回 log function C++11 將任意類型的可調用(Callable)對象與函數調用的特征封裝到一起。 這裏的類是對函數策略的封裝,將函數的性質抽象成組件,便於和algorithm庫配合使用 基本運

開始C# MVC框架--- 配置log4日誌

寫入 出錯 fill 文件 幫助 fontsize att 日誌處理 引用 在框架中配置日誌分2步,一個是在幫助項目Zy.Utilities--Zy.Utility.Core中新建log類,封裝寫入日誌方法,還需要在Zy.Utility.Core添加 log4net 的引用

一起學習造輪子(三):開始一個React-Redux

導致 href dispatch 判斷 som render connect mis 回調 本文是一起學習造輪子系列的第三篇,本篇我們將從零開始寫一個React-Redux,本系列文章將會選取一些前端比較經典的輪子進行源碼分析,並且從零開始逐步實現,本系列將會學習Prom

開始bootloader

inf boot bsp 開始 src 技術 text tex gif 從零開始寫bootloader

[Golang] 開始Socket Server(6)【完結】:日誌模組的設計與定時任務模組模組

好久沒寫文章啦。。。今天把golang挖的這個坑給補完吧~ 作為一個Server,日誌(Log)功能是必不可少的,一個設計良好的日誌模組,不論是開發Server時的除錯,還是執行時候的維護,都是非常有幫助的。 因為這裡寫的是一個比較簡化的Server框架,因此我選擇對Golang本

[Golang] 開始Socket Server(5):Server的解耦—通過Router+Controller實現邏輯分發

       在實際的系統專案工程中中,我們在寫程式碼的時候要儘量避免不必要的耦合,否則你以後在更新和維護程式碼的時候會發現如同深陷泥潭,隨便改點東西整個系統都要變動的酸爽會讓你深切後悔自己當初為什麼非要把東西都寫到一塊去(我不會說我剛實習的時候就是這麼幹