Spring 源碼筆記
1. Spring MVC初始化流程
1.1 加載配置文件 doLoadConfig(config.getInitParameter(“contextConfigLocation”))
如果是加載Properties相對簡單一點,XML要解析,複雜一點
Properties properties = new Properties();
// 根據web.xml中配置的application.properties路徑名,加載到輸入流
InputStream is = this.getClass().getClassLoader().getResourceAsStream(String location);
properties.load(is);
//此時,配置文件已經加載到properties中
1.2 根據配置文件掃描所有的相關類 doScanner(p.getProperty(“scanPackage”));
List classNames = new ArrayList<>();
//進行遞歸掃描
URL url = this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.","/"));
File classDir = new File(url.getFile());
for(File file : classDir.listFiles()){
if(file.isDirectory()){
doScanner(packageName + "." + file.getName());
}else{
String className = packageName + "." + file.getName().replace(".class","");
classNames.add(className);
}
}
1.3 初始化相關類的實例,並將其放入IOC容器中,也就是MAP中 doInstance()
private Map<String, Object> ioc = new HashMap<String, Object>();
//IOC容器
if(classNames.isEmpty()){
return;
}
for(String className : classNames){
Class<?> clazz = class.forName(className);
}
/**
*初始化IOC容器 添加註解的才實例化
*IOC容器規則
*1.key默認用類名首字母小寫
*2.如果用戶自定義,優先選擇用戶自定義名字
*3.如果是接口的話,我們可以巧妙用接口類型作爲key
*/
if(clazz.isAnnotationPresent(Controller.class)){
//simpleName爲類名,不含路徑信息。方法讓首字母+32得到小寫
String beanName = lowerFirstCase(clazz.getSimpleName());
}else if(clazz.isAnnotationPresent(Service.class)){
//2.如果用戶自定義,優先選擇用戶自定義名字
Service service = clazz.getAnnotation(Service.class);
String beanName = service.value();
if("".equals(beanName.trim())){
beanName = lowerFirstCase(clazz.getSimpleName());
}
Object instance = clazz.newInstance();
ioc.put(beanName,instance);
//3.如果是接口的話,我們可以巧妙用接口類型作爲key
Class<?>[] interfaces = clazz.getInterfaces();
for(Class<?> i : interfaces){
ioc.put(i.getName(),instance);
}
}else{
continue;
}
1.4 實現自動依賴注入 doAutowired()
if(ioc.isEmpty()){
return;
}
for(Map.Entry<String,Object> entry : ioc.entrySet()){
//獲取所有字段field 無論private protected default都強制注入
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for(Field field : fields){
if(!field.isAnnotationPresent(Autowried.class)){continue;};
Autowried autowried = field.getAnnotation(Autowried.class);
String beanName = autowried.value().trim();
if("".equals(beanName)){
beanName = field.getType().getName();
}
//想訪問私有的,受保護的 要強制授權訪問
field.setAccessible(true);
//疑問:Object instance = entry.getValue(); field爲什麼set兩個對象實例?
//其實是給對象屬性賦值,第一個參數相當於new出來一個對象,第二個參數是具體值。
field.set(entry.getValue(),ioc.get(beanName));
}
}
1.5 初始化HandlerMapping initHanlerMapping()
// private Map<String, Method> hanlderMapping = new HashMap<String,Method>();
//改造版:
private List<Handler> handlerMapping = new ArrayList<Handler>();
Handler中含有Method, pattern(正則),paramOrder,controller
if(ioc.isEmpty()){return;}
for(Map.Entry<String,Object> entry : ioc.entrySet()){
Class<?> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(Controller.class)){
continue;
}
String baseUrl = "";
if(clazz.isAnnotationPresent(Controller.class)){
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
baseUrl = requestMapping.value();
}
Method[] method = clazz.getMethod();
for(Method method : methods){
if(!method.isAnnotationPresent(RequestMapping.class)){
continue;
}
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
/*
//多輸入的/替換爲一個
String url = (baseUrl + requestMapping.value()).replaceAll("/+","/");
handlerMapping.put(url,method);
logger.info("Mapping:{},{}",url,method);
*/
String regex = ("/"+baseUrl+requestMapping.value()).replaceAll("/+","");
Pattern pattern = Pattern.compile(regex);
handlerMapping.add(new Handler(pattern,entry.getVallue(),method));
logger.info("mapping:{},{}",regex,method);
}
}
1.6 doPost()
//運行階段所執行的方法
/*
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath,"").replaceAll("/+","");
if(!handlerMapping.containsKey(url)){
resp.getWriter().writer("404 Not Found!");
}
Method m = handlerMapping.get(url);
*/
//改造後:
Handler handler = getHandler(req);
if(handler == null){
resp.getWriter().writer("404 Not Found!");
return;
}
//獲取方法參數列表
Class<?> [] paramTypes = handler.method.getParameterTypes();
//保存所有需要自動賦值的參數值
Object[] paramValues = new Object[paramTypes.length];
Map<String,String[]> params = req.getParameterMap();
for(Map.Entry<String,String[]> param : params.entrySet()){
//第二個參數待確認
String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]","/");
//如果找到匹配的對象,則填充參數值
if(!handler.paramIndexMapping.containsKey(param.getKey())){continue;}
int index = handler.paramIndexMapping.get(param.getKey());
paramValues[index] = convert(paramTypes[index], value);
}
//設置方法中的request 和 response對象
int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
paramValue[reqIndex] = req;
int respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
paramValue[respIndex] = req;
private Handler getHandler(HttpServletRequest req){
if(handlerMapping.isEmpty()){return null;}
String url = url.replace(contextPath,"").replaceAll("/+","");
for(Handler handler : handlerMapping){
Matcher matcher = handler.pattern.matcher(url);
if(!matcher.matches()){continue;}
return handler;
}
}