手写SpringMVC首先理清思路,具体要做什么 我分为以下几个步骤
1.扫描包下带有@Controller @Service注解的类
2.实例化:将扫描的类通过反射实例化并保存到ioc容器中
3.依赖注入:将存在依赖的bean进行注入:列如被@Autowired修饰的属性进行注入
4.urlMapping:http请求路径与方法建立映射关系
理清思路我们就开始快乐之旅吧~~~~~~
1.新建一个web工程添加servlet jar包
4.0.0 com.work StudySpring 1.0-SNAPSHOT 1.8 1.8 javax.servlet javax.servlet-api 3.1.0 provided
1.1 web.xml配置
DisparcherServlet com.work.servlet.DispatcherServlet 0 DisparcherServlet *.do
2 .新建注解(@Controller,@Awtowired,@RequestMapping,@Service,@RequestParams)(为了区分Spring框架我这里注解前缀都会有Zp)
@Target(ElementType.TYPE) //表示只能作用在类上 @Retention(RetentionPolicy.RUNTIME) //表示可以在运行时通过反射获取 @Documented //javadoc public @interface ZpController { String value() default ""; }
@Target(ElementType.FIELD) //表示只能作用在属性上 @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ZpAutowired { String value() default ""; }
@Target({ElementType.TYPE,ElementType.METHOD}) //表示可以作用在类上和方法上 @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ZpRequestMapping { String value() default ""; }
@Target(ElementType.TYPE) //表示可以作用在类上 @Retention(RetentionPolicy.RUNTIME) //表示可以在运行时通过反射获取 @Documented public @interface ZpService { String value() default ""; }
@Target(ElementType.PARAMETER) //表示只能作用在参数上 @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ZpRequestParams { String value() default ""; }
3.新建接口类
public interface StudentBiz { String helloSpring(); }
4.接口实现类
@ZpService("studentBizImpl") public class StudentBizImpl implements StudentBiz{ public String helloSpring() { return "hello Spring"; } }
5.新建controller控制层
@ZpController @ZpRequestMapping("/student") public class StudentController { @ZpAutowired("studentBizImpl") public StudentBiz studentBiz; @ZpRequestMapping("/query.do") public void queryInfo(HttpServletRequest req, HttpServletResponse resp,@ZpRequestParams("name") String name){ try { resp.setContentType("application/json;charset=utf-8"); PrintWriter pw=resp.getWriter(); //调用业务层方法 String str = studentBiz.helloSpring(); pw.print(str); } catch (IOException e) { e.printStackTrace(); } } }
6.新建DispatcherServlet处理器
package com.work.servlet; import com.work.annotation.*; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; 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.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @ProjectName: SpringPrinciple * @Package: PACKAGE_NAME * @ClassName: DispatcherServlet * @Description: java类作用描述: 模仿springMVC处理器 * @Author: 祝平 * @CreateDate: 2019-02-26 17:21 * @UpdateUser: 更新者 * @UpdateDate: 2019-02-26 17:21 * @UpdateRemark: 更新说明 * @Version: 1.0 */ public class DispatcherServlet extends HttpServlet{ //保存class文件地址 ListclassPaths=new ArrayList (); //IOC容器 Map beans=new HashMap (); //存储方法的容器 Map handlerMap=new HashMap (); @Override public void init() throws ServletException { //扫描所有类 doScanPackage("com.work"); //实例化 doInstance(); //依赖注入 doAutowired(); //请求地址和方法映射 urlMapping(); } /** * 扫描所有带有@ZpController,@ZpService的类进行装载 * @param basePackage 类的路径 */ public void doScanPackage(String basePackage){ //获取com.work下所有的类文件地址 //对于java来说只认识com/work格式,所以需要把.转换成/ URL url=this.getClass().getClassLoader().getResource("/"+basePackage.replaceAll("\\.","/")); String fileStr=url.getFile(); //相当于获取到com/work.. File file=new File(fileStr); //根据获取到的地址转成文件 String[] filesStr=file.list(); //获取所有文件 //遍历所有文件判断是否是.class结尾的 for(String path:filesStr){ File filePath=new File(fileStr+path); //判断是否是路径,如果是就递归,直至拿到class文件 if(filePath.isDirectory()){ doScanPackage(basePackage+"."+path); }else{ // .class文件 classPaths.add(basePackage+"."+path); //其实就是com.work.controller...下的类路径 } } } /** * 进行实例化 */ public void doInstance(){ //遍历所有地址进行类加载,判断是否被@ZpController和@ZpService注解修饰的类 for (String classPath:classPaths){ try { //拿到的地址是com.work.controller.类.class所以需要把.class转换一下 Class> clazz=Class.forName(classPath.replace(".class","")); //判断是否被@ZpController修饰 if (clazz.isAnnotationPresent(ZpController.class)){ //说明是控制类 beans.put(lowerFirstChar(clazz.getSimpleName()),clazz.newInstance()); }else if (clazz.isAnnotationPresent(ZpService.class)){ //说明是Service ZpService zpService=clazz.getAnnotation(ZpService.class); String key=zpService.value(); if(!"".equals(key)){ beans.put(key,clazz.newInstance()); continue; } //如果@ZpService注解没设置值就用接口的名字接口的名字首字母小写 Class[] inters=clazz.getInterfaces(); //此处简单处理了,假定ServiceImpl只实现了一个接口 for (Class c : inters) { //举例 modifyService->new ModifyServiceImpl() beans.put(lowerFirstChar(c.getSimpleName()), clazz.newInstance()); break; } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } } /** * 依赖注入 */ public void doAutowired(){ for (Map.Entry bean:beans.entrySet()){ Class> clazz=bean.getValue().getClass(); if(clazz.isAnnotationPresent(ZpController.class)){ //获取private修饰的属性 Field[] fields=clazz.getDeclaredFields(); //遍历所有属性是否被@ZpAutowired修饰 for(Field field:fields){ if(!field.isAnnotationPresent(ZpAutowired.class)){ continue; } //获取@ZpAutowired的值进行匹配注入 ZpAutowired zpAutowired=field.getAnnotation(ZpAutowired.class); String beanName; if("".equals(zpAutowired.value())){ beanName = lowerFirstChar(field.getType().getSimpleName()); }else{ beanName = zpAutowired.value(); } //因为是private修饰 需要开启权限才能注入实例 field.setAccessible(true); if(beans.get(beanName) != null){ try { //注入实例 field.set(bean.getValue(),beans.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } } /** * 进行请求地址和方法映射 */ public void urlMapping(){ for(Map.Entry entry:beans.entrySet()){ Class> clazz=entry.getValue().getClass(); //判断是否是控制类 if(clazz.isAnnotationPresent(ZpController.class)){ //获取ZpController注解上的值 ZpRequestMapping zrm=clazz.getAnnotation(ZpRequestMapping.class); String classPath=zrm.value(); //获取方法上ZpRequestMapping注解的值 Method[] methods=clazz.getDeclaredMethods();//获取所有方法 //遍历方法 for (Method method:methods){ if(method.isAnnotationPresent(ZpRequestMapping.class)){ ZpRequestMapping zpRequestMapping=method.getAnnotation(ZpRequestMapping.class); String methodPath=zpRequestMapping.value(); //进行拼接 handlerMap.put(classPath+methodPath,method); }else{ continue; } } } } } /** * tomcat启动完成后做得处理 * @param req * @param resp * @throws ServletException * @throws IOException */ @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 { //获取请求的地址 String url=req.getRequestURI(); int index=url.indexOf("/",1); String controllerURL=url.substring(0,index);//截取到/hello Method method=(Method) handlerMap.get(url); for(Map.Entry bean:beans.entrySet()){ Class> clazz=bean.getValue().getClass(); if(clazz.isAnnotationPresent(ZpController.class)){ ZpRequestMapping zrm=clazz.getAnnotation(ZpRequestMapping.class); String zcValue=zrm.value(); if(controllerURL.equals(zcValue)){ try { Object[] args=getArgs(req,resp,method); method.invoke(bean.getValue(),args); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } } /** * 找出方法所需要的方法参数 * @param req * @param resp * @param method * @return */ private static Object[] getArgs(HttpServletRequest req, HttpServletResponse resp,Method method){ //拿到当前类待执行的方法参数 Class>[] clazzParams=method.getParameterTypes(); //根据参数的个数,new一个参数的数组,将方法里的所有参数赋值到args里 Object [] args=new Object[clazzParams.length]; int args_i=0; int index=0; for(Class> clazz:clazzParams){ //判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口 if(ServletRequest.class.isAssignableFrom(clazz)){ args[args_i++]=req; } if(ServletResponse.class.isAssignableFrom(clazz)){ args[args_i++]=resp; } //判断有没有@ZpRequestParams注解 Annotation[] pramsAns=method.getParameterAnnotations()[index]; if(pramsAns.length>0){ for(Annotation paramAn:pramsAns){ if(ZpRequestParams.class.isAssignableFrom(paramAn.getClass())){ ZpRequestParams zrp=(ZpRequestParams)paramAn; //找到注解的name args[args_i++]=req.getParameter(zrp.value()); } } } index++; } return args; } /*** * 转小写方法 * @param className * @return */ private String lowerFirstChar(String className) { char[] chars = className.toCharArray(); chars[0] += 32; return String.valueOf(chars); } }
至此就已经完成我们的功能了