在手写spring之前我们可以从下图大致了解一个简陋版spring运行的流程。需要注意的是这个简陋版本没有包含AOP功能。
项目搭建直接创建一个Maven项目,然后逐步添加代码。以下为我的工程目录。
包含 jetty插件,帮助我们直接运行程序。webdefault.xml为jetty所需要的文件。
4.0.0 org.example spring-source-1 1.0-SNAPSHOT 8 8 2.4 javax.servlet servlet-api ${servlet.api.version} provided ${artifactId} ${basedir}/src/main/resources ***.java ***.* true org.mortbay.jetty maven-jetty-plugin 6.1.26 src/main/resources/webdefault.xml / 8081 0 src/main/webapp ***.properties javax.xml.parsers.documentBuilderFactory com.sun.org.apache.xerces.internal.jaxp.documentBuilderFactoryImpl javax.xml.parsers.SAXParserFactory com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl javax.xml.transform.TransformerFactory com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl org.eclipse.jetty.util.URI.charset UTF-8 org.apache.maven.plugins maven-war-plugin 2.2 false src/main/resources/ WEB-INF/classes **local **/test **/product -->true src/main/resources WEB-INF/classes true org.zeroturnaround javarebel-maven-plugin generate-rebel-xml process-resources generate 1.0.5 org.eclipse.m2e lifecycle-mapping 1.0.0 org.zeroturnaround javarebel-maven-plugin [1.0.5,) generate
相关配置文件Default web.xml file. This file is applied to a Web application before it's own WEB_INF/web.xml file org.mortbay.jetty.webapp.NoTLDJarPattern start.jar|ant-.*.jar|dojo-.*.jar|jetty-.*.jar|jsp-api-.*.jar|junit-.*.jar|servlet-api-.*.jar|dnsns.jar|rt.jar|jsse.jar|tools.jar|sunpkcs11.jar|sunjce_provider.jar|xerces.*.jar default org.mortbay.jetty.servlet.DefaultServlet acceptRanges true dirAllowed true welcomeServlets false redirectWelcome false maxCacheSize 256000000 maxCachedFileSize 10000000 maxCachedFiles 1000 cacheType both gzip true useFileMappedBuffer false 0 default / jsp org.apache.jasper.servlet.JspServlet logVerbosityLevel DEBUG fork false xpoweredBy false 0 jsp *.jsp *.jspf *.jspx *.xsp *.JSP *.JSPF *.JSPX *.XSP 30 index.html index.htm index.jsp ar ISO-8859-6 be ISO-8859-5 bg ISO-8859-5 ca ISO-8859-1 cs ISO-8859-2 da ISO-8859-1 de ISO-8859-1 el ISO-8859-7 en ISO-8859-1 es ISO-8859-1 et ISO-8859-1 fi ISO-8859-1 fr ISO-8859-1 hr ISO-8859-2 hu ISO-8859-2 is ISO-8859-1 it ISO-8859-1 iw ISO-8859-8 ja Shift_JIS ko EUC-KR lt ISO-8859-2 lv ISO-8859-2 mk ISO-8859-5 nl ISO-8859-1 no ISO-8859-1 pl ISO-8859-2 pt ISO-8859-1 ro ISO-8859-2 ru ISO-8859-5 sh ISO-8859-5 sk ISO-8859-2 sl ISO-8859-2 sq ISO-8859-2 sr ISO-8859-5 sv ISO-8859-1 tr ISO-8859-9 uk ISO-8859-5 zh GB2312 zh_TW Big5 Disable TRACE / TRACE
application
scanPackage=com.spring.test.demo
web.xml
web容器的项目都是从读取web.xml开始;其中DemoDispatchServlet是我们模拟spring的核心功能类。
Web Application gpmvc com.spring.test.mvc.servlet.v1.DemoDispatchServlet contextConfigLocation application.properties 1 gpmvc /*
自定义注解
模拟spring的常用注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface DemoAutowired {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface DemoController {
String value() default "";
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface DemoRequestMapping {
String value() default "";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface DemoRequestParam {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface DemoService {
String value() default "";
}
业务逻辑类
就是常规的spring操作,直接复制即可。
public interface IDemoService {
String get(String name);
}
//我这边是类名和注解名重复了,可以自行替换
@com.spring.test.mvc.annotation.DemoService
public class DemoService implements IDemoService {
public String get(String name) {
return "My name is " + name + ",from service.";
}
}
@DemoController
@DemoRequestMapping("/demo")
public class DemoAction {
@DemoAutowired
private IDemoService demoService;
@DemoRequestMapping("/query")
public void query(HttpServletRequest req, HttpServletResponse resp,
@DemoRequestParam("name") String name) {
// String result = demoService.get(name);
String result = "My name is " + name;
try {
resp.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
@DemoRequestMapping("/add")
public void add(HttpServletRequest req, HttpServletResponse resp,
@DemoRequestParam("a") Integer a, @DemoRequestParam("b") Integer b) {
try {
resp.getWriter().write(a + "+" + b + "=" + (a + b));
} catch (IOException e) {
e.printStackTrace();
}
}
@DemoRequestMapping("/sub")
public void sub(HttpServletRequest req, HttpServletResponse resp,
@DemoRequestParam("a") Double a, @DemoRequestParam("b") Double b) {
try {
resp.getWriter().write(a + "-" + b + "=" + (a - b));
} catch (IOException e) {
e.printStackTrace();
}
}
@DemoRequestMapping("/remove")
public String remove(@DemoRequestParam("id") Integer id) {
return "" + id;
}
}
编写核心业务
当我们构建完项目之后,我们便可以开始编写我们的核心servlet了。
初始类我们首先创建一个初始类,根据流程将大致框架搭好,然后开始编写代码。
public class DemoDispatchServlet extends HttpServlet {
//保存用户篇配置好的配置文件
private Properties contextConfig = new Properties();
//缓存从包路径下扫描的全类名
private List classNames = new ArrayList();
//保存所有扫描的类的实例
private Map ioc = new HashMap();
//保存controller 中 URL和Method的对应关系
private Map handlerMapping = new HashMap();
@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 {
//6.根据url委派给具体的调用方法
}
@Override
public void init(ServletConfig config) throws ServletException {
//1.加载配置文件
//参数为web.xml中定义的键值名
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2.扫描相关的类
doScanner(contextConfig.getProperty("scanPackage"));
//============IOC功能===============
//3.初始IOC容器,将扫描到的类进行实例化,缓存到IOC容器中
doInstance();
//============DI功能===============
//4.完成依赖注入
doAutowired();
//============MVC功能===============
//5.初始化HandleMapping
doInitHandleMapping();
System.out.println("Spring framework is init.");
}
private void doInitHandleMapping() {
}
private void doAutowired() {
}
private void doInstance() {
}
// 扫描 classpath下符合包路劲规则所有的class文件
private void doScanner(String scanPackage) {
}
//根据contextConfigLocation的路径去classpath下找到的对应的文件
private void doLoadConfig(String contextConfigLocation) {
}
}
加载配置文件
在初始化过程中,我们的第一步就是获取配置文件;
//根据contextConfigLocation的路径去classpath下找到的对应的文件
private void doLoadConfig(String contextConfigLocation) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
扫描相关的类
扫描配置类中路径下的所有文件;首先需要注意配置类中的文件路径和我们正常的路径不同,需要将其 * . * 替换为/。
如果扫描到文件夹则继续往该路径下扫描;
如果不是class文件则跳过;
将符合规则的文件添加到集合中。
// 扫描 classpath下符合包路劲规则所有的class文件
private void doScanner(String scanPackage) {
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\.", "/"));
File classPath = new File(url.getFile());
for (File file : classPath.listFiles()) {
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
// 不是class文件则跳过
if (!file.getName().endsWith(".class")) {
continue;
}
// 包名.类型 比如: com.demo.DemoAction
String className = (scanPackage + "." + file.getName().replace(".class", ""));
// 实例化 需要用到class.forName(className);
classNames.add(className);
}
}
}
将扫描的类放入IOC容器中
扫描到文件之后,我们就需要将其实例化并存至我们的IOC容器中。
流程:
-
controller注解的类:
1.判断这个类有没有使用controller注解
2.将类名的首字母小写,通过位操作实现
3.将该类实例化后添加至IOC容器中
-
service注解的类:
1.判断这个类有没有使用service注解
2.将类名的首字母小写,通过位操作实现
3.获取别名,因为注解具有默认值,所以不需要进行判空操作
4.实例化类并存入IOC容器中
5.如果是接口则初始化他的实现类,多个实现类则报错提示。
-
其他:跳过
private void doInstance() {
if (classNames.isEmpty()) {
return;
}
try {
for (String className : classNames) {
Class> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(DemoController.class)) {
String beanName = toLowerFirstCase(clazz.getSimpleName());
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
} else if (clazz.isAnnotationPresent(DemoService.class)) {
// 1.默认类名首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
// 2.如果多个包下出现了相同的类名,优先使用别名(自定义命名)
DemoService demoService = clazz.getAnnotation(DemoService.class);
if (!"".equals(demoService.value())) {
beanName = demoService.value();
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
// 3.如果是接口 ,只能初始化他的实现类
for (Class> i : clazz.getInterfaces()) {
if (ioc.containsKey(i.getName())) {
throw new Exception("The" + i.getName() + "is exists! please use aliens");
}
ioc.put(i.getName(), instance);
}
} else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32; //利用ascll码,大小写字母之间相差32
return String.valueOf(chars);
}
依赖注入
1.遍历IOC容器中的每个实例
2.通过反射获取类中的所有字段
3.判断该字段有无使用auto wired注解
4.如果使用了则获取该注解的值,如果为默认值,则获取该字段的类型名使用
5.将IOC容器中对应该beanName的实例注入到该字段中
private void doAutowired() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry entry : ioc.entrySet()) {
//忽略字段的修饰符
for (Field field : entry.getValue().getClass().getDeclaredFields()) {
if (!field.isAnnotationPresent(DemoAutowired.class)) {
continue;
}
DemoAutowired autowired = field.getAnnotation(DemoAutowired.class);
String beanName = autowired.value().trim();
if ("".equals(beanName)) {
beanName = field.getType().getName();
}
//强制访问
field.setAccessible(true);
try {
// 相当于 demoAction.demoService = ioc.get("com.demo.service.IDemoService");
field.set(entry.getValue(), ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
初始化HandlerMapping
该部分主要是对url路径及相对应的方法进行映射。
首先遍历IOC容器,没有使用controller的则跳过。
然后处理controller中添加的requestmapping,把其作为baseUrl。
然后开始处理类中的publi方法,如果没有使用requestMapping注解则跳过;
使用该注解的则对url地址进行相关处理后添加进Map中以供使用。
private void doInitHandleMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry entry : ioc.entrySet()) {
Class> clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(DemoController.class)) {
continue;
}
//处理controller类添加地址注解
String baseUrl = "";
if (clazz.isAnnotationPresent(DemoRequestMapping.class)) {
DemoRequestMapping requestMapping = clazz.getAnnotation(DemoRequestMapping.class);
baseUrl = requestMapping.value();
}
//只迭代public方法
for (Method method : clazz.getMethods()) {
if (!method.isAnnotationPresent(DemoRequestMapping.class)) {
continue;
}
DemoRequestMapping requestMapping = method.getAnnotation(DemoRequestMapping.class);
// /demo/query 使用正则表达处理 多个斜杠问题
String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
handlerMapping.put(url, method);
System.out.println("Mapped : " + url + "--->" + method);
}
}
}
根据url委派给具体的调用方法
这一步就是我们通过地址访问后触发的方法。
1.首先获取URL地址;这边是将地址中的根地址去除,方便根据映射找到对应的方法。
2.首先判断映射关系中是否包含该路径
3.然后将形参的位置和参数名字建立映射关系,缓存下来
4.根据参数位置匹配参数名字,从url中取到参数名字对应的值
5.以上两步都需要对request及response两个类进行相关的处理
6.使用反射调用相关方法
@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 {
//6.根据url委派给具体的调用方法
try {
doDispatcher(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 Exception,Detail: " + Arrays.toString(e.getStackTrace()));
}
}
private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!this.handlerMapping.containsKey(url)) {
resp.getWriter().write("404 Not Found!");
return;
}
Method method = this.handlerMapping.get(url);
// 1.先把形参的位置和参数名字建立映射关系,缓存下来
Map paramIndexMapping = new HashMap();
// 多个参数, 每个参数后面有多个值
Annotation[][] pa = method.getParameterAnnotations();
for (int i = 0; i < pa.length; i++) {
for (Annotation annotation : pa[i]) {
if (annotation instanceof DemoRequestParam) {
String paramName = ((DemoRequestParam) annotation).value();
if (!"".equals(paramName.trim())) {
paramIndexMapping.put(paramName, i);
}
}
}
}
Class>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
Class> type = paramTypes[i];
if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
paramIndexMapping.put(type.getName(), i);
}
}
// 2.根据参数位置匹配参数名字,从url中取到参数名字对应的值
Object[] paramValues = new Object[paramTypes.length];
//http://localhost/demo/query?name=Tom&name=Tomcat&name=Mic
Map params = req.getParameterMap();
for (Map.Entry param : params.entrySet()) {
String value = Arrays.toString(param.getValue())
.replaceAll("\[|\]", "")
.replaceAll("\s", "");
if (!paramIndexMapping.containsKey(param.getKey())) {
continue;
}
int index = paramIndexMapping.get(param.getKey());
//设计到类型强制转换
paramValues[index] = value;
}
if(paramIndexMapping.containsKey(HttpServletRequest.class.getName())){
int index = paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[index] = req;
}
if(paramIndexMapping.containsKey(HttpServletResponse.class.getName())){
int index = paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[index] = resp;
}
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
// 3.组成动态参数列表 ,传给反射调用。
method.invoke(ioc.get(beanName), paramValues);
//method.invoke(ioc.get(beanName), new Object[]{req, resp, params.get("name")[0]});
}
运行测试
这边是使用的jeety直接启动的项目。
按照如下方式运行即可。
打开浏览器访问控制器中的路径即可发现成功。
优化以上便是一个简陋版Spring的编写过程,基本功能已经实现,但代码的优雅程度还不人意,在处理URL及方法映射的时候还可以继续优化。以下直接放优化后的全部代码,有相关注解可以自行查看。
public class DemoDispatcherServlet extends HttpServlet {
//保存application.properties配置文件中的内容
private Properties contextConfig = new Properties();
//保存扫描的所有的类名
private List classNames = new ArrayList();
//为了简化程序,暂时不考虑ConcurrentHashMap
private Map ioc = new HashMap();
//保存url和Method的对应关系
// private Map handlerMapping = new HashMap();
//用Map的话,key,只能是url
//Handler 本身的功能就是把url和method对应关系,已经具备了Map的功能
//根据设计原则:单一职责,最少知道原则,帮助我们更好的理解
private List handlerMapping = new ArrayList();
@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 {
//6、调用,运行阶段
try {
doDispatch(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 Exection,Detail : " + Arrays.toString(e.getStackTrace()));
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
Handler handler = getHandler(req);
if (handler == null) {
// if(!this.handlerMapping.containsKey(url)){
resp.getWriter().write("404 Not Found!!!");
return;
}
//获得方法的形参列表
Class>[] paramTypes = handler.getParamTypes();
Object[] paramValues = new Object[paramTypes.length];
Map params = req.getParameterMap();
for (Map.Entry parm : params.entrySet()) {
String value = Arrays.toString(parm.getValue()).replaceAll("\[|\]", "")
.replaceAll("\s", ",");
if (!handler.paramIndexMapping.containsKey(parm.getKey())) {
continue;
}
int index = handler.paramIndexMapping.get(parm.getKey());
paramValues[index] = convert(paramTypes[index], value);
}
if (handler.paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex] = req;
}
if (handler.paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
int respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[respIndex] = resp;
}
Object returnValue = handler.method.invoke(handler.controller, paramValues);
if (returnValue == null || returnValue instanceof Void) {
return;
}
resp.getWriter().write(returnValue.toString());
}
private Handler getHandler(HttpServletRequest req) {
if (handlerMapping.isEmpty()) {
return null;
}
//绝对路径
String url = req.getRequestURI();
//处理成相对路径
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
for (Handler handler : this.handlerMapping) {
Matcher matcher = handler.getPattern().matcher(url);
if (!matcher.matches()) {
continue;
}
return handler;
}
return null;
}
//url传过来的参数都是String类型的,HTTP是基于字符串协议
//只需要把String转换为任意类型就好
private Object convert(Class> type, String value) {
//如果是int
if (Integer.class == type) {
return Integer.valueOf(value);
} else if (Double.class == type) {
return Double.valueOf(value);
}
//如果还有double或者其他类型,继续加if
//这时候,我们应该想到策略模式了
//在这里暂时不实现,希望小伙伴自己来实现
return value;
}
//初始化阶段
@Override
public void init(ServletConfig config) throws ServletException {
//1、加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2、扫描相关的类
doScanner(contextConfig.getProperty("scanPackage"));
//3、初始化扫描到的类,并且将它们放入到ICO容器之中
doInstance();
//4、完成依赖注入
doAutowired();
//5、初始化HandlerMapping
initHandlerMapping();
System.out.println("Spring framework is init.");
}
//初始化url和Method的一对一对应关系
private void initHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry entry : ioc.entrySet()) {
Class> clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(DemoController.class)) {
continue;
}
//保存写在类上面的@GPRequestMapping("/demo")
String baseUrl = "";
if (clazz.isAnnotationPresent(DemoRequestMapping.class)) {
DemoRequestMapping requestMapping = clazz.getAnnotation(DemoRequestMapping.class);
baseUrl = requestMapping.value();
}
//默认获取所有的public方法
for (Method method : clazz.getMethods()) {
if (!method.isAnnotationPresent(DemoRequestMapping.class)) {
continue;
}
DemoRequestMapping requestMapping = method.getAnnotation(DemoRequestMapping.class);
//优化
// //demo///query
String regex = ("/" + baseUrl + "/" + requestMapping.value())
.replaceAll("/+", "/");
Pattern pattern = Pattern.compile(regex);
this.handlerMapping.add(new Handler(pattern, entry.getValue(), method));
// handlerMapping.put(url,method);
System.out.println("Mapped :" + pattern + "," + method);
}
}
}
//自动依赖注入
private void doAutowired() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry entry : ioc.entrySet()) {
//Declared 所有的,特定的 字段,包括private/protected/default
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(DemoAutowired.class)) {
continue;
}
DemoAutowired autowired = field.getAnnotation(DemoAutowired.class);
//如果用户没有自定义beanName,默认就根据类型注入
//这个地方省去了对类名首字母小写的情况的判断
String beanName = autowired.value().trim();
if ("".equals(beanName)) {
//获得接口的类型,作为key待会拿这个key到ioc容器中去取值
beanName = field.getType().getName();
}
field.setAccessible(true);
try {
//用反射机制,动态给字段赋值
field.set(entry.getValue(), ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
private void doInstance() {
//初始化,为DI做准备
if (classNames.isEmpty()) {
return;
}
try {
for (String className : classNames) {
Class> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(DemoController.class)) {
Object instance = clazz.newInstance();
//Spring默认类名首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
ioc.put(beanName, instance);
} else if (clazz.isAnnotationPresent(DemoService.class)) {
//1、自定义的beanName
DemoService service = clazz.getAnnotation(DemoService.class);
String beanName = service.value();
//2、默认类名首字母小写
if ("".equals(beanName.trim())) {
beanName = toLowerFirstCase(clazz.getSimpleName());
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
//3、根据类型自动赋值,投机取巧的方式
for (Class> i : clazz.getInterfaces()) {
if (ioc.containsKey(i.getName())) {
throw new Exception("The “" + i.getName() + "” is exists!!");
}
//把接口的类型直接当成key了
ioc.put(i.getName(), instance);
}
} else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
//扫描出相关的类
private void doScanner(String scanPackage) {
//转换为文件路径,实际上就是把.替换为/就OK了
//classpath
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\.", "/"));
File classPath = new File(url.getFile());
for (File file : classPath.listFiles()) {
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
if (!file.getName().endsWith(".class")) {
continue;
}
String className = (scanPackage + "." + file.getName().replace(".class", ""));
classNames.add(className);
}
}
}
//加载配置文件
private void doLoadConfig(String contextConfigLocation) {
InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(fis);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != fis) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//保存一个url和一个Method的关系
public class Handler {
//必须把url放到HandlerMapping才好理解
private Pattern pattern; //正则
private Method method;
private Object controller;
private Class>[] paramTypes;
public Pattern getPattern() {
return pattern;
}
public Method getMethod() {
return method;
}
public Object getController() {
return controller;
}
public Class>[] getParamTypes() {
return paramTypes;
}
//形参列表
//参数的名字作为key,参数的顺序,位置作为值
private Map paramIndexMapping;
public Handler(Pattern pattern, Object controller, Method method) {
this.pattern = pattern;
this.method = method;
this.controller = controller;
paramTypes = method.getParameterTypes();
paramIndexMapping = new HashMap();
putParamIndexMapping(method);
}
private void putParamIndexMapping(Method method) {
//提取方法中加了注解的参数
//把方法上的注解拿到,得到的是一个二维数组
//因为一个参数可以有多个注解,而一个方法又有多个参数
Annotation[][] pa = method.getParameterAnnotations();
for (int i = 0; i < pa.length; i++) {
for (Annotation a : pa[i]) {
if (a instanceof DemoRequestParam) {
String paramName = ((DemoRequestParam) a).value();
if (!"".equals(paramName.trim())) {
paramIndexMapping.put(paramName, i);
}
}
}
}
//提取方法中的request和response参数
Class>[] paramsTypes = method.getParameterTypes();
for (int i = 0; i < paramsTypes.length; i++) {
Class> type = paramsTypes[i];
if (type == HttpServletRequest.class ||
type == HttpServletResponse.class) {
paramIndexMapping.put(type.getName(), i);
}
}
}
}
}



