/* 自定义的controller注解类 */ //target的作用是声明该注解可以标记类还是方法还是方法还是属性的,Type代表该注解作用在类上面 //Retention标记该注解什么时候生效,runtime代表运行时生效 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface HainaController { //这其实相当于是一个value属性,用来给controller类设置url String value() default ""; }
可以通过自己设定的注解,来设置controller类,实现在项目中通过url能够访问到这个类
//可以用在类上,也可以用在方法上 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface HainaRequestMapping { String value() default ""; }
通过自己设定的注解,这个注解注释的方法,能够在项目中通过url直接调用该方法
/* *视图解析器 */ public class HainaViewResolver {</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> String prefix; </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> String suffix; </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String jspMapping (String value){ </span><span style="color: #008000;">//</span><span style="color: #008000;">给视图拼接前缀和后缀</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span>.prefix + value + <span style="color: #0000ff;">this</span><span style="color: #000000;">.suffix; } </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String getPrefix() { </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> prefix; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setPrefix(String prefix) { </span><span style="color: #0000ff;">this</span>.prefix =<span style="color: #000000;"> prefix; } </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String getSuffix() { </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> suffix; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setSuffix(String suffix) { </span><span style="color: #0000ff;">this</span>.suffix =<span style="color: #000000;"> suffix; }
}
将url中的方法字段传入视图解析器,就可以调用相应的方法
/** * 自定义的分发器 */ public class HainaDispatcherServlet extends HttpServlet { //用来保存url和controller对象的集合 private Map<String, Object> controllerMap = new HashMap<>(); //用来保存url和方法之间的映射 private Map<String, Method> requestMappingMap = new HashMap<>();</span><span style="color: #0000ff;">private</span> HainaViewResolver resolver = <span style="color: #0000ff;">new</span><span style="color: #000000;"> HainaViewResolver(); @Override </span><span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">void</span> doGet(HttpServletRequest req, HttpServletResponse resp) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> ServletException, IOException { doPost(req, resp); } @Override </span><span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">void</span> doPost(HttpServletRequest req, HttpServletResponse resp) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> ServletException, IOException { String uri </span>= req.getRequestURI();<span style="color: #008000;">//</span><span style="color: #008000;">获取uri</span><span style="color: #000000;"> String controllerUri </span>= uri.split("/")[2];<span style="color: #008000;">//</span><span style="color: #008000;">获取controller</span><span style="color: #000000;"> String methodUri </span>= uri.split("/")[3];<span style="color: #008000;">//</span><span style="color: #008000;">获取方法名</span><span style="color: #008000;">//</span><span style="color: #008000;">通过路径找到对应的controller对象和方法对象</span> Object obj =<span style="color: #000000;"> controllerMap.get(controllerUri); Method method </span>=<span style="color: #000000;"> requestMappingMap.get(methodUri); </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { </span><span style="color: #008000;">//</span><span style="color: #008000;">使用反射,调用对应的方法,返回值实际就是页面的名字</span> String result =<span style="color: #000000;"> (String) method.invoke(obj); </span><span style="color: #008000;">//</span><span style="color: #008000;">把视图名拼接完整</span> String url =<span style="color: #000000;"> resolver.jspMapping(result); </span><span style="color: #008000;">//</span><span style="color: #008000;">使用请求转发的形式跳转到对应的视图上</span>
req.getRequestDispatcher(url).forward(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}</span><span style="color: #008000;">//</span><span style="color: #008000;">初始化</span>
@Override
public void init(ServletConfig config) throws ServletException {
//1.扫描controller,创建对应的实例对象,存入自定义容器中
scanController(config);
//2.初始化url和方法映射的容器
initRequestMapping();resolver.setPrefix(</span>"/"<span style="color: #000000;">);<span style="color: #008000;">//设置前缀</span> resolver.setSuffix(</span>".jsp"<span style="color: #000000;">);<span style="color: #008000;">//设置后缀</span> } </span><span style="color: #008000;">//</span><span style="color: #008000;">解析springmvc.xml文件,扫描文件中配置的包名中的使用了HainaController注解标注的类</span> <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> scanController(ServletConfig config) { SAXReader reader </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> SAXReader(); </span><span style="color: #008000;">//</span><span style="color: #008000;">这里其实是在读取web.xml文件的配置,通过web.xml文件,读取到对应springmvc </span><span style="color: #008000;">//</span><span style="color: #008000;">文件的名称,然后拼接好完整路径</span> String path = config.getServletContext().getRealPath(""<span style="color: #000000;">) </span>+ "\\WEB-INF\\classes\\" + config.getInitParameter("contextConfigLocation"<span style="color: #000000;">); System.out.println(</span>"path:" +<span style="color: #000000;"> path); </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { Document root </span>=<span style="color: #000000;"> reader.read(path); Element rootElement </span>=<span style="color: #000000;"> root.getRootElement(); Iterator it </span>=<span style="color: #000000;"> rootElement.elementIterator(); </span><span style="color: #0000ff;">while</span><span style="color: #000000;"> (it.hasNext()) { Element next </span>=<span style="color: #000000;"> (Element) it.next(); </span><span style="color: #0000ff;">if</span> ("component-scan"<span style="color: #000000;">.equals(next.getName())) { </span><span style="color: #008000;">//</span><span style="color: #008000;">获取要扫描的包名</span> String packageName = next.attributeValue("base-package"<span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">有了包名之后,我们就需要根据包名,找到该包下的所有类名,然后根据类名 </span><span style="color: #008000;">//</span><span style="color: #008000;">创建出对应的class对象</span> List<String> list =<span style="color: #000000;"> getClassNameList(packageName); </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (String str : list) { </span><span style="color: #008000;">//</span><span style="color: #008000;">使用类反射,将类名加载进内存,转换成class类型对象</span> Class clazz =<span style="color: #000000;"> Class.forName(str); </span><span style="color: #008000;">//</span><span style="color: #008000;">判断是否使用了HainaController注解标注</span> <span style="color: #0000ff;">if</span> (clazz.isAnnotationPresent(HainaController.<span style="color: #0000ff;">class</span><span style="color: #000000;">)) { </span><span style="color: #008000;">//</span><span style="color: #008000;">获取当前类上标注的注解,获取到之后,我们可以获取该注解中配置的路径</span> HainaController annotation = (HainaController) clazz.getAnnotation(HainaController.<span style="color: #0000ff;">class</span><span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">路径都用/开头,所以在获取的时候,我们把斜杠过滤掉</span> String value = annotation.value().substring(1<span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">这样,路径以及它对应的类就都有了,我们可以把路径作为key,类的对象作为value,存入map中 </span><span style="color: #008000;">//</span><span style="color: #008000;">方便之后我们使用url来找对应的类和方法</span>
controllerMap.put(value, clazz.newInstance());
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}</span><span style="color: #008000;">//</span><span style="color: #008000;">获取所有类名</span> <span style="color: #0000ff;">public</span> List<String><span style="color: #000000;"> getClassNameList(String packageName) { String packagePath </span>= packageName.replace(".", "/"<span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">获取类加载器,通过类加载器去找文件路径</span> ClassLoader loader =<span style="color: #000000;"> Thread.currentThread().getContextClassLoader(); URL url </span>= loader.getResource(packagePath);<span style="color: #008000;">//</span><span style="color: #008000;">获取文件的路径</span> List<String> result = <span style="color: #0000ff;">new</span> ArrayList<><span style="color: #000000;">(); </span><span style="color: #0000ff;">if</span> (url != <span style="color: #0000ff;">null</span><span style="color: #000000;">) { File file </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> File(url.getPath()); </span><span style="color: #008000;">//</span><span style="color: #008000;">获取当前包下的所有文件</span> File[] files =<span style="color: #000000;"> file.listFiles(); </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (File child : files) { </span><span style="color: #008000;">//</span><span style="color: #008000;">拼接完整的包名加类名</span> String className = packageName + "." +<span style="color: #000000;"> child.getName() .replace(</span>".class", ""<span style="color: #000000;">); result.add(className); } } </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> result; } </span><span style="color: #008000;">//</span><span style="color: #008000;">初始化url和方法的映射关系</span> <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> initRequestMapping() { </span><span style="color: #008000;">//</span><span style="color: #008000;">遍历controllerMap,获取里面的所有key</span> <span style="color: #0000ff;">for</span><span style="color: #000000;"> (String str : controllerMap.keySet()) { </span><span style="color: #008000;">//</span><span style="color: #008000;">通过key,取出对应的值,然后调用getclass方法,把对象转换成class类型 </span><span style="color: #008000;">//</span><span style="color: #008000;">因为只有class类型的对象,才可以获取该类里的所有方法</span> Class<?> clazz =<span style="color: #000000;"> controllerMap.get(str).getClass(); Method[] methods </span>=<span style="color: #000000;"> clazz.getMethods(); </span><span style="color: #008000;">//</span><span style="color: #008000;">遍历每个方法,看看方法有没有被requestmapping注解标注</span> <span style="color: #0000ff;">for</span><span style="color: #000000;"> (Method method : methods) { </span><span style="color: #0000ff;">if</span> (method.isAnnotationPresent(HainaRequestMapping.<span style="color: #0000ff;">class</span><span style="color: #000000;">)) { </span><span style="color: #008000;">//</span><span style="color: #008000;">获取当前方法的requestMapping注解对象</span> HainaRequestMapping annotation = method.getAnnotation(HainaRequestMapping.<span style="color: #0000ff;">class</span><span style="color: #000000;">);</span><span style="color: #000000;"> String value </span>= annotation.value().substring(1<span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">把url和方法对象,添加到map中</span>
requestMappingMap.put(value, method);
}
}
}
}
}
springmvc本质上就是通过一个servlet类来接收所有的请求,并且将这些请求分发到指定注解的controller类中,再调用controller类中指定注解的方法。
<?xml version="1.0" encoding="UTF-8" ?> <beans> <component-scan base-package="com.haina.springmvc"></component-scan><bean <span style="color: #0000ff;">class</span>="com.haina.springmvc.HainaViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"><servlet> <servlet-name>myDispatcher</servlet-name> <servlet-<span style="color: #0000ff;">class</span>>com.haina.springmvc.HainaDispatcherServlet</servlet-<span style="color: #0000ff;">class</span>> <init-param> <param-name>contextConfigLocation</param-name> <param-value>springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>myDispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
</web-app>
@HainaController("/haina") public class TestController {@HainaRequestMapping(</span>"/test"<span style="color: #000000;">) </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String abc(){ System.out.println(</span>"come in controller"<span style="color: #000000;">); </span><span style="color: #0000ff;">return</span> "test"<span style="color: #000000;">; }
}
成功访问controller类,并且调用了abc方法
如何自己构建一个类似springmvc的框架(springmvc原理理解)
原文:https://www.cnblogs.com/TidalCoast1034/p/14539767.html