手写 spring ioc
一、实现自己的servlet,模拟DispatcherServlet
package com.sl; import com.sl.mvcframework.annotation.SLAutowired; import com.sl.mvcframework.annotation.SLController; import com.sl.mvcframework.annotation.SLRequestMapping; import com.sl.mvcframework.annotation.SLService; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.*; /** * Author: sl * Date: 2020/8/21-20:31 * Description:描述信息 */ public class HandSpringServlet extends HttpServlet { private static final String location="contextConfigLocation"; private Properties properties=new Properties(); private List<String> classNames=new ArrayList<String>(); private Map<String,Object> ioc=new HashMap<String,Object>(); private Map<String,Method> handerMapping=new HashMap<String,Method>(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doDispach(req,resp); } private void doDispach(HttpServletRequest req, HttpServletResponse resp) { if(this.handerMapping.isEmpty()){ return; } String url=req.getRequestURI(); String contextPath=req.getContextPath(); url=url.replace(contextPath,"").replaceAll("/+","/"); if(!this.handerMapping.containsKey(url)){ try { resp.getWriter().write("404 页面找不到了"); return; } catch (IOException e) { e.printStackTrace(); } } Map<String,String[]> parameterMap=req.getParameterMap(); Method method=this.handerMapping.get(url); Class<?> []methodParameterTypes=method.getParameterTypes(); Object []paramValue=new Object[methodParameterTypes.length]; for(int i=0;i<methodParameterTypes.length;i++){ Class type=methodParameterTypes[i]; if(type==HttpServletRequest.class){ paramValue[i]=req; continue; }else if(type==HttpServletResponse.class){ paramValue[i]=resp; continue; }else if(type==String.class){ for(Map.Entry<String,String[]> entry:parameterMap.entrySet()){ String value=Arrays.toString(entry.getValue()).replaceAll("\\[|\\]","").replaceAll("\\s",","); paramValue[i]=value; } }else if(type==Integer.class){ for(Map.Entry<String,String[]> entry:parameterMap.entrySet()){ Integer value=Integer.parseInt(Arrays.toString(entry.getValue()).replaceAll("\\[|\\]","").replaceAll("\\s",","));; paramValue[i]=value; } } } String beanName=method.getDeclaringClass().getSimpleName(); try { method.invoke(this.ioc.get(beanName),paramValue); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } @Override public void init(ServletConfig config) throws ServletException { //1.加载配置文件 doLoadConfig(config.getInitParameter(location)); //2.扫描所有相关的类 doScanner(properties.getProperty("package")); //3.初始化所有类相关的实例,并保存到ioc容器中 doInstance(); //4.依赖注入 doAurowaired(); //5.构造handerMapping doHanderMapping(); System.out.println("SL mvcframework 初始化完成"); } private void doHanderMapping() { if(ioc.isEmpty()){ return; } for(Map.Entry<String,Object> entry:ioc.entrySet()){ Class<?> clazz=entry.getValue().getClass(); if(!clazz.isAnnotationPresent(SLController.class)){ continue; } String baseUrl=""; if(clazz.isAnnotationPresent(SLRequestMapping.class)){ SLRequestMapping requestMapping=clazz.getAnnotation(SLRequestMapping.class); baseUrl=requestMapping.value(); } Method []methods=clazz.getMethods(); for(Method method:methods){ if(!method.isAnnotationPresent(SLRequestMapping.class)){ continue; } SLRequestMapping requestMappingMethod=method.getAnnotation(SLRequestMapping.class); String url=("/"+baseUrl+"/"+requestMappingMethod.value()).replaceAll("/+","/"); handerMapping.put(url,method); System.out.println(url+":"+method); } } } private void doAurowaired() { 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(SLAutowired.class)){ return; } SLAutowired autowired=field.getAnnotation(SLAutowired.class); String beanName=autowired.value(); if("".equals(beanName)){ beanName=field.getType().getSimpleName(); } field.setAccessible(true); try { field.set(entry.getValue(),ioc.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } private void doInstance() { if(classNames.size()==0){ return; } try { for(String className:classNames){ Class<?> clazz=Class.forName(className); if(clazz.isAnnotationPresent(SLController.class)){ String beanName=clazz.getSimpleName(); ioc.put(beanName,clazz.newInstance()); }else if(clazz.isAnnotationPresent(SLService.class)){ SLService service=clazz.getAnnotation(SLService.class); String beanName=service.value(); beanName=clazz.getSimpleName(); if(!"".equals(beanName.trim())){ ioc.put(beanName,clazz.newInstance()); continue; } /*Class<?> [] interfaces=clazz.getInterfaces(); for(Class<?> cz:interfaces){ ioc.put(beanName,cz.newInstance()); }*/ }else{ continue; } } } catch (Exception e) { e.printStackTrace(); } } private void doScanner(String aPackage) { URL url= this.getClass().getClassLoader().getResource("/"+aPackage.replaceAll("\\.","/")); File dir=new File(url.getFile()); for(File file:dir.listFiles()){ if(file.isDirectory()){ doScanner(aPackage+"."+file.getName()); }else{ classNames.add(aPackage+"."+file.getName().replaceAll(".class","")); } } } private void doLoadConfig(String initParameter) { InputStream ins=this.getClass().getClassLoader().getResourceAsStream(initParameter); try { properties.load(ins); } catch (IOException e) { e.printStackTrace(); }finally { if(ins !=null){ try { ins.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Override public void destroy() { super.destroy(); } }
二、实现自己的注解类
@Target({ElementType.FIELD})//注解用于什么地方 @Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。 @Documented public @interface SLAutowired { String value() default ""; } @Target({ElementType.TYPE})//注解用于什么地方 @Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。 @Documented public @interface SLController { String value() default ""; } @Target({ElementType.TYPE,ElementType.METHOD})//注解用于什么地方 @Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。 @Documented public @interface SLRequestMapping { String value() default ""; } @Target({ElementType.PARAMETER})//注解用于什么地方 @Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。 @Documented public @interface SLRequestParam { String value() default ""; } @Target({ElementType.TYPE})//注解用于什么地方 @Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。 @Documented public @interface SLService { String value() default ""; }
三、实现测试crontoller 和service
@SLController @SLRequestMapping("/demo") public class DemoController { @SLAutowired DemoService demoService; @SLRequestMapping("/query") public void query(HttpServletRequest request, HttpServletResponse response,@SLRequestParam("name") String name) throws IOException { String result=demoService.getName(name); response.getWriter().write(result); } @SLRequestMapping("/add") public void add(HttpServletRequest request, HttpServletResponse response,@SLRequestParam("a") Integer a,@SLRequestParam("b")Integer b) throws IOException { response.getWriter().write("a+b="+(a+b)); } @SLRequestMapping("/remove") public void remove(HttpServletRequest request, HttpServletResponse response,@SLRequestParam("name") String name) throws IOException { response.getWriter().write("已经删除了,success"); } } @SLService public class TestService { public String getName(String name){ return "您的名字是"+name; } }
四、总结
1.实现spring iot 其核心其实就是servlet 的实现,在web.xml中配置了servlet 的启动参数后,程序按照servlet 中的init 方法加载ioc过程
2、当servlet 启动后,首先会去加载web.xml init-parpameter 中配置的配置文件,按照路径扫描包下的class 文件,并按照反射实现类的初始化并将其存入map中
3、上一步其实就是初始化bean 容器,当bean 完成初始化后,紧接着就是完成依赖注入,就是将类中标注了@Autowaire 注解的累变量给其赋值,相当于new对象,换言之就是给cronller中的service 变量赋值
4、当完成对象注入后,接着就是url 映射,也就是handermapping,意思就是给crontroller 中加了requestMapping 注解的方法增加地址映射,如可以通过request 请求中的url /test/getName 找到目标方法
5、以上就完成了spring iot 最基本的bean 初始化、依赖注入、地址映射,最后就是实现doget dopost ,按照request 中的请求地址以及参数,通过反射调用目标方法
6、需要注意的地方:实现注解一定要加runtime 也就是运行时有效,不然无法确定是否加了注解 ,其次就是用到了大量的反射及代理知识,需要同学们巩固巩固。