实现功能
开发环境配置好,那么我们可以编写代码了
由于BasicMVC框架的对象交由BasicIOC容器托管,所以启动JavaWeb项目时,将程序所有的对象使用Ioc内核的机制加载到容器池里面。
基于这个原因:我们实现DispacherServlet控制器跳转必须首先完成以下三个动作
1.我们必须在Web启动是创建一个ApplicationContext容器操作对象;
2.我们必须需要一个配置类,来获得创建容器的信息
3.根据请求得路径跳转到对应Controller的对应映射路径的方法
实现步骤
第一步:我们必须需要一个配置类,来获得创建容器的信息
1.实现Web程序启动BasicIoc容器
--在用于测试的web项目,创建一个配置类IocCofing,并在web.xml作为DispacherServlet参数配置,以及编写一个用于测试是否启动了Ioc容器的UserController类
--配置类IocConfig代码--
1 package org.webmvc.cofig; 2 3 import ioc.core.annotation.ComponentScan; 4 import ioc.core.annotation.Configuration; 5 6 //使用定义@Configuration定义该类是一个配置类 7 @Configuration 8 //使用ComponentScan设置扫描包的路径 9 @ComponentScan(basePackages={"org.webmvc.controller"}) 10 public class IocConfig { 11 12 }
--web.xml代码--
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> 3 <display-name>webmvc-demo-test-01</display-name> 4 <servlet> 5 <servlet-name>dispacherServlet</servlet-name> 6 <servlet-class>ioc.webmvc.DispacherServlet</servlet-class> 7 <init-param> 8 <!-- 设置配置类的类全名 --> 9 <param-name>cofig</param-name> 10 <param-value>org.webmvc.cofig.IocConfig</param-value> 11 </init-param> 12 </servlet> 13 <servlet-mapping> 14 <servlet-name>dispacherServlet</servlet-name> 15 <url-pattern>*.do</url-pattern> 16 </servlet-mapping> 17 </web-app>
-编写一个用于测试的UserController类--
1 package org.webmvc.controller; 2 3 import ioc.core.annotation.stereotype.Controller; 4 5 @Controller 6 public class UserController { 7 8 public void login(){ 9 System.out.println("-登录Controller-"); 10 } 11 }
2.编写BasicMVCMVC框架的DispacherServlet类的init方法。获得配置参数,以及启动BaiscIoC容器。
代码如下:
1 package ioc.webmvc; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletConfig; 6 import javax.servlet.ServletException; 7 import javax.servlet.http.HttpServlet; 8 import javax.servlet.http.HttpServletRequest; 9 import javax.servlet.http.HttpServletResponse; 10 11 import ioc.core.impl.AnntationApplicationContext; 12 13 /** 14 * 核心控制,用于拦截所有的请求,MVC框架的入口。 15 * @author ranger 16 * @date 2017-11-08 17 * 18 */ 19 public class DispacherServlet extends HttpServlet { 20 21 private static final long serialVersionUID = -5969172674760020122L; 22 private AnntationApplicationContext contextApplication=null; 23 24 25 /** 26 * 启动Web程序时,获得配置的参数 27 */ 28 @Override 29 public void init(ServletConfig config) throws ServletException { 30 //1.配置参数是配置类的类全名 31 String parameter = config.getInitParameter("config"); 32 //2.查看是否有参数了 33 System.out.println(parameter); 34 try { 35 //3.将字符串使用反射技术,转成一个Class类 36 Class<?> classType = Class.forName(parameter); 37 //4.将对象加载到容器里面 38 this.contextApplication=new AnntationApplicationContext(classType); 39 } catch (ClassNotFoundException e) { 40 e.printStackTrace(); 41 } 42 } 43 44 45 46 @Override 47 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 48 System.out.println("-测试成功--"); 49 super.service(request, response); 50 } 51 }
3.启动index.jsp,点击请求。容器打印出加载的对象内存地址。说明成功。
第二步:根据请求得路径跳转到对应Controller的对应映射路径的方法
确认通过web程序启动就可以使用BaiscIoc容器托管所有对象后。
我们需要理解请求可以找到对应的执行方法这个问题?
要解决这个问题,必须要有一个地方标识请求路径和方法的关系。这里我通过一个@RequestMapping注解来标识请求路径和执行方法的关系。
实现思路:
(1)在业务控制器设置标识方法对应请求路径的注解(@RequestMapping),必须指定映射路径
(2)请求到核心控制器是扫描所有业务控制器。获得对应的方法。
实现步骤:
1. 定义一个注解RequestMapping来声明路径与方法的关系
1 package ioc.webmvc.annotation; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.ElementType; 5 import java.lang.annotation.Retention; 6 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.Target; 8 9 @Retention(RetentionPolicy.RUNTIME) 10 /* 11 * 12 * 指定可以在方法以及类上面使用面使用 13 * 1.在方法上面,指定请求路径 14 * 2.在类上面用于指定命名空间 15 */ 16 17 @Target(value={ElementType.METHOD,ElementType.TYPE}) 18 @Documented 19 public @interface RequestMapping { 20 /** 21 * 没有默认值,表示必须要填写 22 * @return 23 */ 24 String value(); 25 26 }
2.获得所有Controller类型的对象。用于判断路径对应的方法。
在容器的实现类ContextImpl里面增加一个检索业务控制器对象的方法。
实现方式分为两步:标红的代码
(1)在容器定义接口中定义一个方法
package ioc.core; import java.util.Map; /** * Ioc框架的容器接口 * @author ranger * */ public interface Context { /** * 用于获得容器中的所有对象 * @return */ Map<String,Object> getObjects(); /** * 用于获得容器中的所有业务控制器对象 * @return */ Map<String,Object> getControllerObjects(); /** * 用于增加容器中的对象 * @param key * @param value */ void addObject(String key, Object value); /** * 根据类的类型以及设置的对象名返回容器对象 * 如果传入的类型容器中有对应key的对象,而且返回类型是兼容的,直接返回对应的对象。 * 如果传入的类型容器中有没有对应key的对象,那么判断传入的类型是否和容器的对象的找到唯一配置的。 * 如果传入类型唯一匹配,返回对象。如果没有或者配配多个对象,都报一个RuntimeException异常 * @param classType * @return */ Object getObject(Class<?> classType,String key); }
(2)在实现类ContextImpl实现该方法,先增加一个getObjectsByComponentType方法编写通过组件注解类型返回对象,再实现getControllerObjects()方法。
1 /** 2 * 用于通过传入标识组件类型的注解,返回对应类型的注解 注解类型分别为: Controller Service Repository 3 * Component 4 * 5 * @param componentsType 6 * @return 7 */ 8 public Map<String, Object> getObjectsByComponentType(Class<? extends Annotation> componentsType) { 9 // 1.创建一个存储指定组件类型的Map 10 Map<String, Object> componentsObjects = new HashMap<String, Object>(); 11 // 2.获得所有的容器对象 12 Map<String, Object> objects = this.getObjects(); 13 // 3.获得Map中所有的对象的Set集合 14 Set<Entry<String, Object>> entrySet = objects.entrySet(); 15 // 4.获得Set集合迭代器 16 Iterator<Entry<String, Object>> iterator = entrySet.iterator(); 17 // 5.循环判断 18 while (iterator.hasNext()) { 19 Entry<String, Object> entry = iterator.next(); 20 // 6.获得当前对象的类类型,用于获得该类的类结构中的组件注解 21 Class<?> classType = entry.getValue().getClass(); 22 // 7.容器里的对象是否是指定注解类型的,如果是加入到componentsObjects中 23 Annotation annotation = classType.getDeclaredAnnotation(componentsType); 24 if (annotation != null) { 25 componentsObjects.put(entry.getKey(), entry.getValue()); 26 } 27 28 } 29 // 8.返回指定组件类型的对象 30 return componentsObjects; 31 } 32 33 /** 34 * 返回业务控制器Controller注解类型的所有对象 35 */ 36 @Override 37 public Map<String, Object> getControllerObjects() { 38 return this.getObjectsByComponentType(Controller.class); 39 }
3.测试代码获得的getObjectsByComponentType方法是否成功,输出指定的类型对象,说明成功。
1 package ioc.core.test; 2 3 import java.util.Map; 4 5 import org.junit.Test; 6 7 import ioc.core.annotation.stereotype.Service; 8 import ioc.core.impl.AnntationApplicationContext; 9 import ioc.core.impl.ContextImpl; 10 import ioc.core.test.config.Config; 11 12 public class ContextImplTest { 13 14 15 @Test 16 public void getObjectsByComponentsType(){ 17 AnntationApplicationContext applicationContext=new AnntationApplicationContext(Config.class); 18 ContextImpl context = (ContextImpl) applicationContext.getContext(); 19 Map<String, Object> componentObjects = context.getObjectsByComponentType(Service.class); 20 System.out.println(componentObjects+"=========================="); 21 22 } 23 24 }
--测试结果,输出的就是指定类型的对象
3.获得页面的请求路径匹配Controller对象中的方法的路径。
--创建一个ControllerRelolver类编写,检索方法的@RequestMapping注解
1 package ioc.webmvc.impl; 2 3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5 import java.util.Collection; 6 import java.util.Iterator; 7 import java.util.Map; 8 9 import javax.servlet.http.HttpServletRequest; 10 import javax.servlet.http.HttpServletResponse; 11 12 import ioc.core.Context; 13 import ioc.webmvc.annotation.RequestMapping; 14 15 public class ControllerRelolver { 16 17 /** 18 * 通过传入的参数,执行路径对应的业务控制器(Controller)的方法 19 * @param request 20 * @param response 21 * @param context 22 * @return 23 * @throws IllegalAccessException 24 * @throws IllegalArgumentException 25 * @throws InvocationTargetException 26 */ 27 public String execute(HttpServletRequest request,HttpServletResponse response,Context context) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{ 28 //1.获得请求过来的路径 29 String uri = request.getRequestURI(); 30 //2.根据路径规则,获得映射路径 31 String path = this.pathRule(uri); 32 //3.通过路径获得容器中对应的业务控制器的对象和执行方法 33 MappingEnttiy mappingEnttiy = this.getRequestMethod(path, context); 34 //4.获得执行方法 35 Method method = mappingEnttiy.getMethod(); 36 //5.获得路径对应的业务控制器 37 Object controller = mappingEnttiy.getController(); 38 //6.执行方法,执行方法必须有request,response两个参数 39 Object resultObject = method.invoke(controller, request,response); 40 if(resultObject!=null){ 41 //7.返回执行方法返回的映射字符串 42 return (String) resultObject; 43 } 44 return null; 45 } 46 /** 47 * 设置路径的规则 48 * 路径规则为,保留/,去掉后缀。 49 * 如: 50 * 请求路径:http://localhost:8080/webmvc-demo-test-01/test.do 51 * 合法的映射路径为:/test 52 * 53 * @param url 传入的为请求路径 54 * @return 符合规则的路径字符串 55 * 56 */ 57 private String pathRule(String url){ 58 //1.创建一个String构建字符串 59 StringBuilder sb=new StringBuilder(url); 60 System.out.println(url); 61 //2.删除路径最后一个/之前的所有字符 62 sb.delete(0, url.lastIndexOf("/")); 63 System.out.println(sb.toString()+"删除后的字符串长度:"+sb.length()); 64 //3.删除(.)后面的后缀 65 sb.delete(sb.lastIndexOf("."),sb.length()); 66 return sb.toString(); 67 } 68 69 /** 70 * 通过路径获得对应的业务控制器对象和执行方法 71 * @param path 72 * @param context 73 * @return 74 */ 75 private MappingEnttiy getRequestMethod(String path,Context context){ 76 //1.获得Controller所有的Controller对象 77 Map<String, Object> controllerObjects = context.getControllerObjects(); 78 System.out.println("-getRequestMethod-业务控制器对象池:"+controllerObjects); 79 //2.获得业务控制器池里面的所有值 80 Collection<Object> values = controllerObjects.values(); 81 //3.遍历 82 Iterator<Object> iterator = values.iterator(); 83 while (iterator.hasNext()) { 84 //4.获得业务控制器池中当前对象 85 Object object = iterator.next(); 86 //5.获得当前对象的类类类型 87 Class<? extends Object> classType = object.getClass(); 88 //6.通过对象的类类型,获得对象的方法列表 89 Method[] methods = classType.getMethods(); 90 //7.循环判断方法是否有RequestMapping注解 91 for(Method method:methods){ 92 RequestMapping mapping = method.getDeclaredAnnotation(RequestMapping.class); 93 //8.RequestMapping注解存在,而且等于映射路径,返回该方法的方法和当前对象 94 if(mapping!=null&&mapping.value().equals(path)){ 95 //9.声明内部类MappingEnttiy来封装映射的方法和对象。创建这个类的对象 96 MappingEnttiy entity=new MappingEnttiy(); 97 entity.setController(object); 98 entity.setMethod(method); 99 return entity; 100 } 101 } 102 103 } 104 105 return null; 106 } 107 /** 108 * 声明一个私有的内部类对象,用于存储执行检索业务控制器时返回的数据 109 * @author ranger 110 * 111 */ 112 private class MappingEnttiy{ 113 //1.当前映射路径对应的对象 114 private Object controller; 115 //2.当前映射路径对应的方法 116 private Method method; 117 118 public Object getController() { 119 return controller; 120 } 121 public void setController(Object controller) { 122 this.controller = controller; 123 } 124 public Method getMethod() { 125 return method; 126 } 127 public void setMethod(Method method) { 128 this.method = method; 129 } 130 131 } 132 }
4.核心控制器(DispacherServlet)调用这个业务控制解释器(ControllerRelolver),标红处
1 package ioc.webmvc; 2 3 import java.io.IOException; 4 import java.lang.reflect.InvocationTargetException; 5 6 import javax.servlet.ServletConfig; 7 import javax.servlet.ServletException; 8 import javax.servlet.http.HttpServlet; 9 import javax.servlet.http.HttpServletRequest; 10 import javax.servlet.http.HttpServletResponse; 11 12 import ioc.core.impl.AnntationApplicationContext; 13 import ioc.webmvc.impl.ControllerRelolver; 14 15 /** 16 * 核心控制,用于拦截所有的请求,MVC框架的入口。 17 * 18 * @author ranger 19 * @date 2017-11-08 20 * 21 */ 22 public class DispacherServlet extends HttpServlet { 23 24 private static final long serialVersionUID = -5969172674760020122L; 25 private AnntationApplicationContext contextApplication = null; 26 27 /** 28 * 启动Web程序时,获得配置的参数 29 */ 30 @Override 31 public void init(ServletConfig config) throws ServletException { 32 // 1.配置参数是配置类的类全名 33 String parameter = config.getInitParameter("config"); 34 // 2.查看是否有参数了 35 System.out.println(parameter); 36 try { 37 // 3.将字符串使用反射技术,转成一个Class类 38 Class<?> classType = Class.forName(parameter); 39 // 4.将对象加载到容器里面 40 this.contextApplication = new AnntationApplicationContext(classType); 41 42 } catch (ClassNotFoundException e) { 43 e.printStackTrace(); 44 } 45 } 46 47 @Override 48 protected void service(HttpServletRequest request, HttpServletResponse response) 49 throws ServletException, IOException { 50 51 52 ControllerRelolver rs = new ControllerRelolver(); 53 try { 54 String result= rs.execute(request, response, contextApplication.getContext()); 55 if(result!=null){ 56 //返回执行方法 57 request.getRequestDispatcher(result).forward(request, response); 58 } 59 } catch (IllegalAccessException e) { 60 // TODO Auto-generated catch block 61 e.printStackTrace(); 62 } catch (IllegalArgumentException e) { 63 // TODO Auto-generated catch block 64 e.printStackTrace(); 65 } catch (InvocationTargetException e) { 66 // TODO Auto-generated catch block 67 e.printStackTrace(); 68 } 69 } 70 }
测试代码
1.测试的Web项目代码目录
2.代码如下
--IocConfig--
1 package org.webmvc.cofig; 2 3 import ioc.core.annotation.ComponentScan; 4 import ioc.core.annotation.Configuration; 5 6 //使用定义@Configuration定义该类是一个配置类 7 @Configuration 8 //使用ComponentScan设置扫描包的路径 9 @ComponentScan(basePackages={"org.webmvc.controller"}) 10 public class IocConfig { 11 12 }
--UserController--
1 package org.webmvc.controller; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import ioc.core.annotation.stereotype.Controller; 7 import ioc.webmvc.annotation.RequestMapping; 8 9 @Controller 10 public class UserController { 11 12 /** 13 * 路径规则: 14 * 1.必须包括有/开头 15 * 2.后缀必须忽略不写 16 * 如:http://localhost:8080/webmvc-demo-test-01/test.do 17 * 对应的映射路径为:/test 18 * 19 */ 20 @RequestMapping(value = "/login") 21 public String login(HttpServletRequest request,HttpServletResponse response){ 22 System.out.println("-登录Controller-"); 23 return "/login.jsp"; 24 25 } 26 27 }
---请求页面index.jsp--
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <a href="${pageContext.request.contextPath }/login.do">test</a> </body> </html>
---返回页面login.jsp--
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <title>Insert title here</title> 8 </head> 9 <body> 10 登录成功! 11 </body> 12 </html>
2.测试结