SSM相關原理概述

目錄

SpringIOC&AOP

MyBatis

SpringMVC


SpringIOC&AOP

源碼
new ClassPathXmlApplicationContext(String[] configLocations,boolean refresh,ApplicationContext   parent){
    //...;
    refresh();//創建主流程
    //...;
}

AbstractApplicationContext#refresh(){
    //...;
    // 創建BeanFactory,(DefaultListableBeanFactory)
    // 並解析spring配置文件,構建一個BeanDefinitionMap集合,Key是id,value是BeanDefinition(所有類的類名)
    //將結果給到finishBeanFactoryInitialization,創建單例bean 
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    //...;
    finishBeanFactoryInitialization(beanFactory);//創建所有單例的bean
    //...;
}

AbstractApplicationContext#finishBeanFactoryInitialization(){
	//...;
	beanFactory.preInstantiateSingletons();//創建所有單例的bean
	//...;
}
DefaultListableBeanFactory#preInstantiateSingletons(){
    for (String beanName : beanNames) {//遍歷所有的beanName,挨個創建對象
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {//非抽象+單例+非延遲,則創建
            if (isFactoryBean(beanName)) {//判斷是否爲工廠bean
                FactoryBean  fb = getBean("&"+beanName);//獲得工廠Bean對象本身								
                //....
                getBean(beanName);//創建實際對象,此中會通過工廠Bean獲得
            }else{
                getBean(beanName);//普通bean,直接創建對象
            }
        }
    }
}
AbstractBeanFactory#getBean(){
	return doGetBean(...);
}

AbstractBeanFactory#doGetBean(){
    //....
    if (mbd.isSingleton()) {
        //注意:此處有回調 ObjectFactory#getObject()
        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {//獲得或創建單例的bean
            @Override
            public Object getObject() throws BeansException {
                try {
                    return createBean(beanName, mbd, args);//回調方法,創建bean
                }
                //...
            }
        }
    }
DefaultSingletonBeanRegistry#getSingleton(beanName,singletonFactory){
    synchronized (this.singletonObjects) {//枷鎖,防止重複創建
        Object singletonObject = this.singletonObjects.get(beanName);//嘗試獲取,如果有則不再創建
        //....
        singletonObject = singletonFactory.getObject();//回調doGetBean中內部類的方法,創建單例bean
        //....
        addSingleton(beanName, singletonObject);//記錄已創建的單例bean,下次不再創建
    }
}
//接:回調doGetBean中內部類“ObjectFactory”的getObject方法,創建單例bean
AbstractAutowireCapableBeanFactory#createBean(){
    //.....
    doCreateBean(beanName, mbdToUse, args);//創建單例bean
}

這裏先執行createBeanInstance,得到bean 原始對象 然後將bean原始對象給到populateBean中,然後進行初始化操作 在初始化過程中,會判斷是否有AOP,從而創建出對應的代理bean對象返回

AbstractAutowireCapableBeanFactory#doCreateBean(){
    //創建bean對象
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    ...
    try {
       	//完成注入,前面獲取的instanceWrapper是無參bean,這裏可以將配置文件中有的參數注入進去
        populateBean(beanName, mbd, instanceWrapper);
        if (exposedObject != null) {
            //執行初始化過程
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }
}
AbstractAutowireCapableBeanFactory#createBeanInstance(){
    //.....
    return instantiateBean(beanName, mbd);//使用無參構造創建bean
}

AbstractAutowireCapableBeanFactory#instantiateBean(){
    //.....
    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}

SimpleInstantiationStrategy#instantiate(){
    //....
    constructorToUse =  clazz.getDeclaredConstructor((Class[]) null);//獲得構造方法對象
    //....
    return BeanUtils.instantiateClass(constructorToUse);//反射構建無參bean
}
AbstractAutowireCapableBeanFactory#initializeBean(){
    //....
    if (mbd == null || !mbd.isSynthetic()) {
        // 後處理器的 前置過程
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        // 調用初始化方法
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }

    if (mbd == null || !mbd.isSynthetic()) {
        // 調用後處理器的 後置過程,這裏創建代理類對象,而不再是原始bean對象
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
}

AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization(){
    //....
    // 遍歷所有後處理器,對bean做後處理
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
}

在後處理器後置過程中,會進行springAOP的相關操作。

// 工廠啓動時,會註冊一些 後處理器,其中就包含 AspectJAwareAdvisorAutoProxyCreator
AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator{
    ...
}
AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
    ...
}
// AbstractAutoProxyCreator是 AspectJAwareAdvisorAutoProxyCreator的父類
// 該父類中定義了 後處理器的功能實現,有父類發起動態代理的定製
AbstractAutoProxyCreator#postProcessAfterInitialization(Object bean, String beanName){
    if (!this.earlyProxyReferences.contains(cacheKey)) {
        // 開始動態定製代理
        return wrapIfNecessary(bean, beanName, cacheKey);
   	}
}
AbstractAutoProxyCreator#wrapIfNecessary(){
    ....
    Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
}
AbstractAutoProxyCreator#createProxy(){
    ....
    return proxyFactory.getProxy(getProxyClassLoader());
}
============================================================================================
ProxyFactory#getProxy(ClassLoader classLoader) {
    // 最後調用:JdkDynamicAopProxy.getProxy();或 ObjenesisCglibAopProxy.getProxy()
    // 獲取代理對象
    return createAopProxy().getProxy(classLoader);
}
ProxyCreatorSupport#createAopProxy() {
    ...
	// DefaultAopProxyFactory.createAopProxy(this)
	// 獲取創建代理對象的 對象 ,返回JdkDynamicAopProxy 或 ObjenesisCglibAopProxy
	return getAopProxyFactory().createAopProxy(this);
}
========================================================================================
DefaultAopProxyFactory#createAopProxy(){
    // 判斷決定使用 jdk代理 還是 cglib代理,
    // 會最終返回 JdkDynamicAopProxy 或 ObjenesisCglibAopProxy
    // JdkDynamicAopProxy 或 ObjenesisCglibAopProxy的 getProxy方法最終返回代理對象
    // 如果需要優化,如果設置了proxy-target-class="true" 如果目標類沒有接口
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("...");
        }
        // 如果目標是接口,如果目標已經是一個代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}
==============================================================================================
JdkDynamicAopProxy#getProxy(ClassLoader classLoader) {
    ...
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
FactoryBean的處理

如果是工廠bean,同樣的原理,會根據xml文件解析並無參構造出一個Bean,但外部真正拿到的並不是該Bean,而是會調用該Bean自己的方法從而生產出一個複雜類對象返回,供外部使用。

繼承web後的spring工廠啓動

在web.xml文件中有【ContextLoaderListener】類,但其實是父類【ContextLoader】啓動工廠,並將工廠放入【servletContext】全局作用域中,供全局使用。
在這裏插入圖片描述

返回目錄


MyBatis

以一個實例一步步解析,如下:

public class AppTest {
    //測試查詢所有
    @Test
    public void testFindAll() throws IOException {
        //讀取資源文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        //創建工廠
        SqlsessionFactory sqlsessionFactory = new SqlSessionFactoryBuilder().build(is);
        //獲取SqlSession
        SqlSession sqlSession = sqlsessionFactory.openSession();
        //動態代理獲取UserDao的代理對象
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        //執行查詢所有方法
        List<User> userList = userDao.findAll();
        for (User user : userList) {
            System.out.println(user);
        }
        //釋放資源
        sqlSession.close();
    }
}
1、讀取資源文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

//Resources類的getResourceAsStream方法將資源文件讀取爲二進制流
//使用的是類加載器轉換
public class Resources {
    public static InputStream getResourceAsStream(String filePath){
        //讀取配置文件  使用類加載器加載配置文件  返回一個流
        return Resources.class.getClassLoader().getResourceAsStream(filePath);
    }
}
2、創建工廠
SqlsessionFactory sqlsessionFactory = new SqlSessionFactoryBuilder().build(is);

//將流導入,這裏會進行SAX的dom4j解析
public class SqlSessionFactoryBuilder {
    //這個類中有一個build 方法接收 InputStream 返回一個SqlSessionFactory
    public static SqlsessionFactory build(InputStream config){
        //調用解析xml文件的工具類解析 返回一個Configuration對象
        Configuration cfg = XMLConfigBuilder.loadConfiguration(config);
        //把配置文件傳給工廠
        return new DefaultSqlSessionFactory(cfg);
    }
}

//XMLConfigBuilder下的loadConfiguration方法解析文件
/**
 *  用於解析配置文件
 */
public class XMLConfigBuilder {

    /**
     * 解析主配置文件,把裏面的內容填充到DefaultSqlSession所需要的地方
     * 使用的技術:
     *      dom4j+xpath
     */
    public static Configuration loadConfiguration(InputStream config){
        try{
            //定義封裝連接信息的配置對象(mybatis的配置對象)
            Configuration cfg = new Configuration();

            //1.獲取SAXReader對象
            SAXReader reader = new SAXReader();
            //2.根據字節輸入流獲取Document對象
            Document document = reader.read(config);
            //3.獲取根節點
            Element root = document.getRootElement();
            //4.使用xpath中選擇指定節點的方式,獲取所有property節點
            List<Element> propertyElements = root.selectNodes("//property");
            //5.遍歷節點
            for(Element propertyElement : propertyElements){
                //判斷節點是連接數據庫的哪部分信息
                //取出name屬性的值
                String name = propertyElement.attributeValue("name");
                if("driver".equals(name)){
                    //表示驅動
                    //獲取property標籤value屬性的值
                    String driver = propertyElement.attributeValue("value");
                    cfg.setDriver(driver);///////
                }
                if("url".equals(name)){
                    //表示連接字符串
                    //獲取property標籤value屬性的值
                    String url = propertyElement.attributeValue("value");
                    cfg.setUrl(url);////////
                }
                if("username".equals(name)){
                    //表示用戶名
                    //獲取property標籤value屬性的值
                    String username = propertyElement.attributeValue("value");
                    cfg.setUsername(username);////////
                }
                if("password".equals(name)){
                    //表示密碼
                    //獲取property標籤value屬性的值
                    String password = propertyElement.attributeValue("value");
                    cfg.setPassword(password);////////
                }
            }
            //取出mappers中的所有mapper標籤,判斷他們使用了resource還是class屬性
            List<Element> mapperElements = root.selectNodes("//mappers/mapper");
            //遍歷集合
            for(Element mapperElement : mapperElements){
                //判斷mapperElement使用的是哪個屬性
                Attribute attribute = mapperElement.attribute("resource");
                if(attribute != null){
                    System.out.println("使用的是XML");
                    //表示有resource屬性,用的是XML
                    //取出屬性的值
                    String mapperPath = attribute.getValue();//獲取屬性的值"com/qf/dao/UserDao.xml"
                    //把映射配置文件的內容獲取出來,封裝成一個map
                    //System.out.println(mapperPath);
                     //把 usermapper.xml的路徑傳過去
                    Map<String, Mapper> mappers = loadMapperConfiguration(mapperPath);
                    //給configuration中的mappers賦值
                    cfg.setMappers(mappers);//////////////
                }
            }
            //返回Configuration
            return cfg;
        }catch(Exception e){
            throw new RuntimeException(e);
        }finally{
            try {
                config.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }

    }

    /**
     * 根據傳入的參數,解析XML,並且封裝到Map中
     * @param mapperPath    映射配置文件的位置
     * @return  map中包含了獲取的唯一標識(key是由dao的全限定類名和方法名組成)
     *          以及執行所需的必要信息(value是一個Mapper對象,裏面存放的是執行的SQL語句和要封裝的實體類全限定類名)
     */
    private static Map<String,Mapper> loadMapperConfiguration(String mapperPath)throws IOException {
        InputStream in = null;
        try{
            //定義返回值對象
            Map<String,Mapper> mappers = new HashMap<String,Mapper>();
            //1.根據路徑獲取字節輸入流
            in = Resources.getResourceAsStream(mapperPath);
            //2.根據字節輸入流獲取Document對象
            SAXReader reader = new SAXReader();
            Document document = reader.read(in);
            //3.獲取根節點
            Element root = document.getRootElement();
            //4.獲取根節點的namespace屬性取值
            String namespace = root.attributeValue("namespace");//是組成map中key的部分
            System.out.println(namespace);  //執行了
            //5.獲取所有的select節點
            List<Element> selectElements = root.selectNodes("//select");
            //6.遍歷select節點集合
            for(Element selectElement : selectElements){
                //取出id屬性的值      組成map中key的部分
                String id = selectElement.attributeValue("id");
                //取出resultType屬性的值  組成map中value的部分
                String resultType = selectElement.attributeValue("resultType");
                //取出文本內容            組成map中value的部分
                System.out.println(id+resultType);  //執行了
                String queryString = selectElement.getText();
                //創建Key
                String key = namespace+"."+id;  //mapper文件上的namespace+"."+id
                //創建Value
                System.out.println(key);  //執行了
                Mapper mapper = new Mapper();
                mapper.setQueryString(queryString);/////////////sql語句
                mapper.setResultType(resultType);////////////返回值的類型
                //把key和value存入mappers中
                mappers.put(key,mapper);
            }
            return mappers;
        }catch(Exception e){
            throw new RuntimeException(e);
        }finally{
            in.close();
        }
    }
}

經過這個方法或,在cfg這個對象中就存儲瞭如下的內容:
在這裏插入圖片描述

3、獲取sqlSession並獲取對象
//獲取session
SqlSession sqlSession = sqlsessionFactory.openSession();
//動態代理獲取UserDao的代理對象
UserDao userDao = sqlSession.getMapper(UserDao.class);

//這個過程就是創建了連接connection並創建了對象
//sqlsessionFactory的實現類
public class DefaultSqlSessionFactory implements SqlsessionFactory {
    private Configuration cfg;
    //接受cfg  cfg中包含  mappers<string mapper>(sql語句  返回值的全限定類名)  數據庫連接信息
    public DefaultSqlSessionFactory(Configuration cfg) {
        this.cfg = cfg;
    }

    //用於創建一個新的操作數據庫對象
    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(cfg);
    }
}

//sqlsession的實現類DefaultSqlSession
public class DefaultSqlSession implements SqlSession {
    private Configuration cfg;
    private Connection connection;
    
    //接受cfg  cfg中包含  mappers<string mapper>(sql語句  返回值的全限定類名)  數據庫連接信息
    public DefaultSqlSession(Configuration cfg) {
        this.cfg = cfg;
        connection = DataSourceUtils.getConnection(cfg);
    }

    //當調用【sqlSession.getMapper(UserDao.class)】時才調用該方法並創建代理類對象
    @Override
    public <T> T getMapper(Class<T> UserMapperClass) {
        return (T) Proxy.newProxyInstance(UserMapperClass.getClassLoader(),
                new Class[]{UserMapperClass},
                new MapperProxy(cfg.getMappers(),connection));
    }

    //用於釋放資源
    @Override
    public void close() {
        if (connection!=null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
4、調用findAll方法,查詢所有
List<User> userList = userDao.findAll();

當調用該方法的時候會執行上面第3條的動態代理中的【new MapperProxy(cfg.getMappers(),connection)】中的【invoke】方法,反射獲取

public class MapperProxy implements InvocationHandler {
    // key 全限定類名加方法名
    private Map<String, Mapper> mappers;
    private Connection connection;

    public MapperProxy(Map<String, Mapper> mappers, Connection connection) {
        this.mappers = mappers;
        this.connection = connection;
    }

    //用於對方法進行增強 我們的增強就是調用select方法
    // method 是 創建代理對象時 第一個參數中的一個方法 也就是getMapper方法
    // getMapper接口中的方法名getName()  finall()
    // method.getDeclaringClass().getName();  dao接口的實現類的全限定類名com.qf.mapper.UserMapper
    // 拼接 key 根據key去mappers中去查詢 能得到對應的 Mapper對象(stringsql  返回值的全限定類名)
    //判斷mapper是否爲空 如果不爲空 調用工具類執行傳入參數 Mapper對象  數據庫連接 返回一個list集合
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //獲取方法名
        String methodName = method.getName();
        //獲取方法所在類的名稱  就是返回值的類名
        String className = method.getDeclaringClass().getName();
        //3組合key
        String key = className+"."+methodName;

        //獲取mappers 中的mapper對象  (sql  返回值類型)
        Mapper mapper = mappers.get(key);
        //判斷是否有mapper
        if (mapper==null){
            throw new IllegalArgumentException("傳入參數有誤");
        }
        //調用工具類 執行查詢所有
        return new Executor().selectList(mapper, connection);
    }
}

/**
 * 負責執行SQL語句,並且封裝結果集,這時cfg的mappers中的value裏的查詢結果就不是Null了
 */
public class Executor {
    //反射內省 執行查詢語句
    public <E> List<E> selectList(Mapper mapper, Connection conn) {
        PreparedStatement pstm = null;
        ResultSet rs = null;
        try {
            //1.取出mapper中的數據
            String queryString = mapper.getQueryString();//select * from user
            String resultType = mapper.getResultType();//com.itheima.domain.User
            Class domainClass = Class.forName(resultType);
            //2.獲取PreparedStatement對象
            System.out.println("--------------------");
            System.out.println(queryString);
            System.out.println(resultType);
            pstm = conn.prepareStatement(queryString);
            //3.執行SQL語句,獲取結果集
            rs = pstm.executeQuery();
            //4.封裝結果集
            List<E> list = new ArrayList<>();//定義返回值
            while(rs.next()) {
                //實例化要封裝的實體類對象
                E obj = (E)domainClass.newInstance();

                //取出結果集的元信息:ResultSetMetaData
                ResultSetMetaData rsmd = rs.getMetaData();
                //取出總列數
                int columnCount = rsmd.getColumnCount();
                //遍歷總列數
                for (int i = 1; i <= columnCount; i++) {
                    //獲取每列的名稱,列名的序號是從1開始的
                    String columnName = rsmd.getColumnName(i);
                    //根據得到列名,獲取每列的值
                    Object columnValue = rs.getObject(columnName);
                    //給obj賦值:使用Java內省機制(藉助PropertyDescriptor實現屬性的封裝)
                    PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:實體類的屬性和數據庫表的列名保持一種
                    //獲取它的寫入方法
                    Method writeMethod = pd.getWriteMethod();
                    //把獲取的列的值,給對象賦值
                    writeMethod.invoke(obj,columnValue);
                }
                //把賦好值的對象加入到集合中
                list.add(obj);
                System.out.println(obj.toString());
            }
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            release(pstm,rs);
        }
    }

    private void release(PreparedStatement pstm,ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }

        if(pstm != null){
            try {
                pstm.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

返回目錄


SpringMVC

1、 運行主流程

在這裏插入圖片描述

DispatcherServlet#doService(){
	//....
    doDispatch();//主體流程
    //....
}

DispatcherServlet#doDispatch(){
	//....
    //獲取請求中指向的 Handler,返回 HandlerExecutionChain
    //HandlerExecutionChain中主要兩個屬性,【HandlerMapping對當前handler封裝對象】+【當前Handler的攔截器數組】
    mappedHandler = getHandler(processedRequest);
    //....
    // 爲當前Handler找一個合適的 HandlerAdapter,來執行此Handler鏈
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    //....
}
DispatcherServlet#getHandler() {
    //遍歷工廠中的 HandlerMapping, HandlerMapping中記錄一個Map集合,key是訪問路徑,value是handler鏈
    //handler鏈包含的是【攔截器數組(鏈)+handler】
    //但只有認識@RequestMapping註解的HandlerMapping,才真正記錄着各個handler的訪問地址
    for (HandlerMapping hm : this.handlerMappings) {
        if (logger.isTraceEnabled()) {
            logger.trace(
                "Testing handler map ...);
        }
        // 從HandlerMapping中獲取 請求路徑對應的Handler鏈
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {//如果當前HandlerMapping可以識別當前請求,則返回
            return handler;
        }
    }
    return null;
}
                
DispatcherServlet#getHandlerAdapter(){
    for (HandlerAdapter ha : this.handlerAdapters) {
        // ....
        if (ha.supports(handler)) {// 遍歷多個Adapter,選擇一個適合當前Handler鏈的Adapter
            return ha;
        }
    }
}
DispatcherServlet#doDispatch(){
	//....
    //獲取請求中指向的 Handler,返回 HandlerExecutionChain
    //HandlerExecutionChain中主要兩個屬性,【HandlerMapping對當前handler封裝對象】+【當前Handler的攔截器數組】
    mappedHandler = getHandler(processedRequest);
    //....
    // 爲當前Handler找一個合適的 HandlerAdapter,來執行此Handler
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    //....
    //執行攔截器的前置邏輯,執行攔截器部分
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }
    //緊接着,執行handler,返回一個ModelAndView
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    //.....
    //執行攔截器的後置邏輯,執行攔截器部分
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    //.....
    //視圖渲染,在此方法內部解析完視圖渲染後,會調用:
    // mappedHandler.triggerAfterCompletion(request, response, null);//攔截器的最終邏輯
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// processDispatchResult會調用此方法,進行視圖渲染
DispatcherServlet#render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response){
    // ....
    // 解析出一個View對象,其實此View已經在ModelAndView中,在ha執行完handler後就存在了
    View view = resolveViewName(...);
    // ....
    // 調用View的渲染方法,內部將跳轉視圖,並響應請求
    view.render(....);
    // 常見的View類型:InternalResourceView用戶處理轉發跳轉,內部使用 request.getRequestDispatcher().forward()
    //               RedirectView 用於處理重定向,內部使用 response.sendRedirect()
}
2、 Json處理

在這裏插入圖片描述

響應

View InternalResourceView(JSTlView) RedirectView FastJsonView xxxJacksonView

Json響應是靠 HttpMessageConverter,而不是View!!

// 使用了@ResponseBoy 後,Handler執行後不再返回一個ModelAndView,
// 而是在 前端控制器觸發Handler執行時,handler執行的最後一步就已經響應了。所以沒有後續的View過程。

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//從此處開始執行Handler

FastJsonHttpMessageConverter#writeInternal(){
    ByteArrayOutputStream outnew = new ByteArrayOutputStream();//字節輸出流
    //......
    //將轉換後的json,存入outnew
    int len = JSON.writeJSONString(outnew, //
                    fastJsonConfig.getCharset(), //
                    value, //
                    fastJsonConfig.getSerializeConfig(), //
                    //fastJsonConfig.getSerializeFilters(), //
                    allFilters.toArray(new SerializeFilter[allFilters.size()]),
                    fastJsonConfig.getDateFormat(), //
                    JSON.DEFAULT_GENERATE_FEATURE, //
                    fastJsonConfig.getSerializerFeatures());
    // ....
    // outnew將內容輸出給 response.getOutputStream()
    outnew.writeTo(outputMessage.getBody());// outputMessage是response對象,getBody中 		
                                            // response.getOutputStream()
}

AbstractJackson2HttpMessageConverter#writeInternal(){
    // ....
    // 獲得一個可以響應json的generator                                    response.getOutputStream()
    JsonGenerator gene = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
    // ....
    // 將json響應到客戶端
    objectWriter.writeValue(generator, value);
}
收參

@RequestBody收參時,會調用如下核心方法,實現從json到java對象的轉換

AbstractJackson2HttpMessageConverter#read(){//將json轉換爲java對象
    //....
}


FastJsonHttpMessageConverter#read(){//將json轉換爲java對象
    //....
}
@RequestMapping(value="/users",method = RequestMethod.PUT)
@ResponseBody
public MyRequestStatus updateUser(@RequestBody User user) {
    System.out.println("update One user:"+user);
    MyRequestStatus status = new MyRequestStatus("update", "ok");
    return status;
}
3、 啓動細節

第一次訪問時,DispatcherServlet啓動,啓動中會初始化很多,系統組件( HandlerMapping, HandlerAdapter … ),以及所有Controller。

ComponentScanBeanDefinitionParser 負責解析配置中的 context:component-scan,並掃描Controller,並創建
AnnotationDrivenBeanDefinitionParser 負責解析配置中的 mvc:annotation-driven,並創建對應組件

4、 HandlerMapping 解析請求細節

每種HandlerMapping都有一方法getHandlerInternal(request)爲當前請求匹配並返回一個Handler鏈,

但返回類型不一。所謂返回類型不一,是指如果找到匹配的Handler鏈,封裝的方法不同,即封裝後的對象類型不同。

比如RequestMappingHandlerMapping的返回的是一個HandlerMethod

比如BeanNameUrlHandlerMapping的返回直接是一個Controller對象

細節:

RequestMappingHandlerMapping中的getHandler方法,會調用getHandlerInternal()返回一個 HandlerMethod; 在getHandler方法中再封裝進一個HandlerExecutionChain中。

BeanNameUrlHandlerMapping中的getHandler方法,會調用getHandlerInternal()方法,方法中會先找到對應的Controller類,然後直接封裝爲HandlerExecutionChain,並返回給getHandler方法。

HandlerExecutionChain 中主要封裝兩類信息:當前handler 、handler的攔截器數組。

@Controller("/abc")//不能定義多方法,且 "/abc"既是beanId,更是訪問路徑。BeanNameUrlHandlerMapping可以解析
public class CaptchaController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) 
        throws Exception {
        ModelAndView mav = new ModelAndView("index");
        return mav;
    }
}
5、 HandlerAdapter解析Handler細節

HandlerExecutionChain在得到後,會遍歷所有HandlerAdapter,通過調用HandlerAdapter中的supports(),判斷哪個HandlerAdapter可以解析當前HandlerExecutionChain中持有的handler。

RequestMappingHandlerAdapter 可以識別 HandlerMethod,所以和RequestMappingHandlerMapping是一個組合

SimpleControllerHandlerAdapter 可以識別Controller類,所以和 BeanNameUrlHandlerMapping是一個組合

綜上,HandlerMapping和HandlerAdapter是配套使用的。

ops:可以斷點在DispatcherServlet的doDispatch方法中,

然後查看DispatcherServlet的 handlerMappings 和 handlerAdapters屬性

6、 SpringMVC工廠啓動
DispatcherServlet父類的父類:
HttpServletBean#init(){
	//....
	// Let subclasses do whatever initialization they like.
	initServletBean();//初始化
	//....
}
FrameworkServlet#initServletBean(){
    //....
    this.webApplicationContext = initWebApplicationContext();//初始化工廠
    //....
}

FrameworkServlet#initFrameworkServlet(){
    //獲取Spring工廠,準備將其作爲父容器
    WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    //....
    if (wac == null) {
       // No context instance is defined for this servlet -> create a local one
       wac = createWebApplicationContext(rootContext);
    }
    //....
}

FrameworkServlet#createWebApplicationContext(){
   return createWebApplicationContext((ApplicationContext) parent);
}

FrameworkServlet#createWebApplicationContext(xx){
    //....
    // 創建springMVC工廠對象:XmlWebApplicationContext
    ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    wac.setEnvironment(getEnvironment());//設置環境參數
    wac.setParent(parent);//將spring容器,作爲自己的父容器
    wac.setConfigLocation(getContextConfigLocation());//設置配置文件位置

    configureAndRefreshWebApplicationContext(wac);//啓動SpringMVC工廠,創建其中的bean

    return wac;
}
//如上的啓動過程中 configureAndRefreshWebApplicationContext(wac),會執行到:
DispatcherServlet#initStrategies(ApplicationContext context) {
    initMultipartResolver(context);//初始化上傳解析器
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);//初始化 HandlerMappings
    initHandlerAdapters(context);//初始化 HandlerAdapters
    initHandlerExceptionResolvers(context);//初始化 異常解析器
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

返回目錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章