总览
- 引入servlet-api 依赖
- 在web.xml中的配置servlet,将对应请求都转发到自写的DispatcherServlet类(并在init-param参数中设置配置文件参数)
- DispatcherServlet类继承HttpServlet接口,并实现对应的方法。(get,post,init)
- init():完成IOC,DI,handlerMapping等的初始化
- get/post():根据用户的请求uri,从handlerMapping中获取到对应的调用方法和参数,使用反射实现调用
DispatcherServlet
field:
Properties contextConfig; //配置信息
List<String> classNames = new ArrayList<>(); // 扫描根目录下的所有的类文件名
Map<String,Object> ioc = new HashMap<>(); // ioc容器,存储已经实例化和依赖注入的对象(控制反转、依赖注入,单例)
Map<String,Method> handlerMapping = new HashMap<>(); // url映射表,每个url对应一个method方法
init(ServletConfig config):
-
doLoadContextConfig()
从servlet参数中获取配置文件路径 - 并将配置读取到contextConfig中
-
doScanner()
从config中获取到扫描根目录,扫描其下的所有.class文件,保存到classNames数组中
-
doInstance()
遍历扫描到的所有类,如果类存在相关的注解,则初始化bean,并注册到ioc容器
-
doAutowired()
扫描ioc容器中的所有实例的属性,如果存在@Autowired注解,则从ioc中找到对应的实例进行注入(私有属性需要开启访问权限)
-
doInitHandlerMapping()
对ioc中所有具有@Controller标签的类,读取其方法,解析出对应的请求uri,以<uri,method>的方式存放到map中
doDipatch(req,resp) <- doGet(),doPost()
- 获取用户请求uri
- 组装请求实参列表
- method.invoke(instance,params)反射调用
代码详解
init(ServletConfig config)
@Override
public void init(ServletConfig config){
// 1. 从servlet参数中获取配置文件路径 - 并将配置读取到contextConfig中
doLoadContextConfig(config.getInitParameter("contextConfigLocation"));
// 2. 从config中获取到扫描根目录,扫描其下的所有.class文件,保存到classNames数组中
doScanner(contextConfig.getProperty("scan-package"));
// 3. 遍历扫描到的所有类,如果类存在相关的注解,则初始化bean,并注册到ioc容器
doInstance();
// 4. 扫描ioc容器中的所有实例的属性,如果存在@Autowired注解,则从ioc中找到对应的实例进行注入
doAutowired();
// 5. 对ioc中所有具有@Controller标签的类,读取其方法,解析出对应的请求uri,以<uri,method>的方式存放到map中
doInitHandlerMapping();
logger.info("哦豁,初始化完成了!");
}
配置加载和读取
doLoadContextConfig(String contextConfigLocation)
private void doLoadContextConfig(String contextConfigLocation) {
InputStream fis = null;
contextConfig = new Properties();
try {
fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
contextConfig.load(fis);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
IOC
doScanner(String packageName)
private void doScanner(String packageName) {
URL url = this.getClass().getClassLoader().getResource("/" + packageName.replace(‘.‘,‘/‘));
File fileDir = new File(url.getFile());
for(File file : fileDir.listFiles()){
if(file.isDirectory()){
doScanner(packageName + "." + file.getName());
continue;
}
if(file.getName().endsWith(".class")){
classNames.add(packageName + "." + file.getName().replace(".class","").trim());
}
}
}
doInstance()
private void doInstance() {
if(classNames.isEmpty()){return;}
for(String className : classNames){
try {
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(GPController.class)) {
String beanName = toLowerFirstCase(clazz.getSimpleName());
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
} else if (clazz.isAnnotationPresent(GPService.class)) {
Object instance = clazz.newInstance();
// 1. 根据对象名或Autowired参数作为beanName注册到ioc容器
GPService service = clazz.getAnnotation(GPService.class);
String beanName = service.value().trim();
if("".equals(service.value())){
beanName = toLowerFirstCase(clazz.getSimpleName());
}
ioc.put(beanName,instance);
// 2. 将对象的所有实现接口,以接口的全类名作为beanName注册到ioc容器
for(Class<?> interfaceClass : clazz.getInterfaces() ) {
beanName = interfaceClass.getName();
if(ioc.containsKey(beanName)){
logger.info("The bean " + interfaceClass.getName() + " is already exists!");
continue;
}
ioc.put(beanName,instance);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
DI
doAutowired()
/**
* 反射中Filed对象,Method等对象,均只与来源类有关,与来源类的实例无关联,
* 当需要使用对应的filed对象或method对象去调用来源类的具体实例时,需要将具体的实例作为参数传递给filed对象或method对象
*/
private void doAutowired() {
if(ioc.isEmpty()){return;}
for(Map.Entry<String,Object> entry : ioc.entrySet()){
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for(Field field : fields){
if(!field.isAnnotationPresent(GPAutowired.class)){continue;}
GPAutowired autowired = field.getAnnotation(GPAutowired.class);
String beanName = autowired.value().trim();
if("".equals(beanName)){
beanName = field.getType().getName();
}
field.setAccessible(true);
//field 相当于@GPAutowired private IDemoService demoService;
//entry.getValue() 相当于DemoAction的实例
//ioc.get(beanName)相当于 ioc.get("com.gupaoedu.demo.service.IDemoService");
try {
field.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
MVC
doInitHandlerMapping()
private void doInitHandlerMapping() {
if(ioc.isEmpty()){return;}
for(Map.Entry<String,Object> entry : ioc.entrySet()){
Class<?> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(GPController.class)){continue;}
// 获取controller上的base路径
String baseUrl = "";
if(clazz.isAnnotationPresent(GPRequestMapping.class)){
GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
baseUrl = requestMapping.value().trim();
}
// 依次扫描所有的方法,对带有GPRequestMapping注解的方法解析相对路径拼接上base路径,保存到HandlerMapping
for(Method method : clazz.getMethods()){
if(!method.isAnnotationPresent(GPRequestMapping.class)){continue;}
GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
String handlerUrl = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
handlerMapping.put(handlerUrl,method);
logger.info("handler mapping init:" + handlerUrl);
}
}
}
dispatch
doDispatch(req,resp)
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String uri = req.getRequestURI();
String contextPath = req.getContextPath();
uri = uri.replace(contextPath,"/").replaceAll("/+","/");
Method method = handlerMapping.get(uri);
if(method == null){
resp.getWriter().write("404 Not Found!");
return;
}
// 方法的形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
// 方法的实参列表
Object[] parameters = new Object[parameterTypes.length];
for(int i = 0; i < parameterTypes.length; i++){
if(parameterTypes[i] == HttpServletRequest.class){
parameters[i] = req;
}else if(parameterTypes[i] == HttpServletResponse.class){
parameters[i] = resp;
}else{
Annotation[][] annotations = method.getParameterAnnotations();
for(Annotation annotation : annotations[i]){
if(annotation instanceof GPRequestParam){
String parameterName = ((GPRequestParam) annotation).value();
parameters[i] = req.getParameter(parameterName);
}
}
}
}
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
method.invoke(ioc.get(beanName),parameters);
}
手写Spring实现1.0 - 单个类实现spring的ioc,Di和HandlerMapping