目錄
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);
}