注:本文在转载基础上对代码以及注释进行了更新,确保代码正确可行
项目源码已经上传,欢迎下载~
1.项目整体架构
就建一个普通的java web项目就OK了,具体目录如下:
2.编码实现
2.1 自定义注解
(1).Controller注解
package com.heylink.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
String value() default "";
}
(2).Qualifier注解
package com.heylink.annotation;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Qualifier {
String value() default "";
}
(3).RequestMapping注解
package com.heylink.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
String value() default "";
}
(4).Service注解
package com.heylink.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
String value() default "";
}
2.2 Service模块
(1).MyService接口
package com.heylink.service;
import java.util.Map;
public interface MyService {
int insert(Map map);
int delete(Map map);
int update(Map map);
int select(Map map);
}
(2).MyServiceImpl类
package com.heylink.service.impl;
import com.heylink.annotation.Service;
import com.heylink.service.MyService;
import java.util.Map;
@Service("MyServiceImpl")
public class MyServiceImpl implements MyService {
public int insert(Map map) {
System.out.println("MyServiceImpl:" + "insert");
return 0;
}
public int delete(Map map) {
System.out.println("MyServiceImpl:" + "delete");
return 0;
}
public int update(Map map) {
System.out.println("MyServiceImpl:" + "update");
return 0;
}
public int select(Map map) {
System.out.println("MyServiceImpl:" + "select");
return 0;
}
}
(3).SpringmvcService接口
package com.heylink.service;
import java.util.Map;
public interface SpringmvcService {
int insert(Map map);
int delete(Map map);
int update(Map map);
int select(Map map);
}
(4).SpringmvcServiceImpl类
package com.heylink.service.impl;
import com.heylink.annotation.Service;
import com.heylink.service.SpringmvcService;
import java.util.Map;
/*
* introductions:
* created by Heylink on 2018/4/23 17:06
*/
@Service("SpringmvcServiceImpl")
public class SpringmvcServiceImpl implements SpringmvcService{
@Override
public int insert(Map map) {
System.out.println("SpringmvcServiceImpl:" + "insert");
return 0;
}
@Override
public int delete(Map map) {
System.out.println("SpringmvcServiceImpl:" + "delete");
return 0;
}
@Override
public int update(Map map) {
System.out.println("SpringmvcServiceImpl:" + "update");
return 0;
}
@Override
public int select(Map map) {
System.out.println("SpringmvcServiceImpl:" + "select");
return 0;
}
}
2.3 controller模块
(1).SpringmvcController类
package com.heylink.controller;
import com.heylink.annotation.Controller;
import com.heylink.annotation.Qualifier;
import com.heylink.annotation.RequestMapping;
import com.heylink.service.MyService;
import com.heylink.service.SpringmvcService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
* introductions:
* created by Heylink on 2018/4/23 17:24
*/
@Controller("test")
public class SpringmvcController {
@Qualifier("MyServiceImpl")
MyService myService;
@Qualifier("SpringmvcServiceImpl")
SpringmvcService springmvcService;
@RequestMapping("insert")
public String insert(HttpServletRequest request, HttpServletResponse response, String param) {
myService.insert(null);
springmvcService.insert(null);
return null;
}
@RequestMapping("delete")
public String delete(HttpServletRequest request, HttpServletResponse response, String param) {
myService.delete(null);
springmvcService.delete(null);
return null;
}
@RequestMapping("update")
public String update(HttpServletRequest request, HttpServletResponse response, String param) {
myService.update(null);
springmvcService.update(null);
return null;
}
@RequestMapping("select")
public String select(HttpServletRequest request, HttpServletResponse response, String param) {
myService.select(null);
springmvcService.select(null);
return null;
}
}
2.4 DispatcherServlet模块
package com.heylink.servlet;
import com.heylink.annotation.Controller;
import com.heylink.annotation.Qualifier;
import com.heylink.annotation.RequestMapping;
import com.heylink.annotation.Service;
import com.heylink.controller.SpringmvcController;
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.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;
/*
* introductions:
* created by Heylink on 2018/4/23 19:46
*/
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
List<String> packageNames = new ArrayList<>();
// 所有类的实例,key是注解的value,value是所有类的实例
Map<String, Object> instanceMap = new HashMap<>();
Map<String, Object> handlerMap = new HashMap<>();
public DispatcherServlet() {
super();
}
public void init(ServletConfig config) throws ServletException {
//包扫描,获取包中的文件
scanPackage("com.heylink");
//根据文件名查找controller与service的类,键为文件路径,值为类对象
try {
filterAndInstance();
} catch (Exception e) {
e.printStackTrace();
}
//对得到的controller键值对进行处理,键为方法路径,值为方法对象
handleMap();
//实现注入
ioc();
}
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
String url = req.getRequestURI();
/*
*获取当前项目根地址
* 比如你现在的url是127.0.0.1:8080/simplespringmvc/test/insert
* tomcat配置的当前项目访问地址是127.0.0.1:8080/simplespringmvc
* request.getContextPath()得到的就是127.0.0.1:8080/simplespringmvc
* */
String context = req.getContextPath();
//同上,类似于拿到test/insert 相对路径
String path = url.replace(context, "");
//根据 /test/insert 就能拿到方法对象(注意有两个斜杠)
Method method = (Method) handlerMap.get(path);
//拿到 test对应的对象——也就是我们@Controller注解值为test的SpringmvcController类
Object controllerObj = instanceMap.get(path.split("/")[1]);
//因为是简单模拟,我们也知道就一个SpringmvcController,所以可以强转
SpringmvcController controller = (SpringmvcController)controllerObj;
try {
method.invoke(controller, new Object[]{req, resp, null});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
//实现依赖注入,其实就是给注解过的属性进行注入
private void ioc() {
if (instanceMap.size() <= 0) {
return;
}
//遍历每一个键值对
for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
//对每一个实例都去遍历其属性 !!切记这里要用getDeclaredFields
for (Field field : entry.getValue().getClass().getDeclaredFields()) {
//设置,可访问私有属性
field.setAccessible(true);
//对属性进行判断,是否有注解Qualifier
if (field.isAnnotationPresent(Qualifier.class)){
Qualifier qualifier = field.getAnnotation(Qualifier.class);
//拿到注解的类名
String iocClazz = qualifier.value();
//将entry.getValue()这个对象中的field属性对象设为新值
try {
field.set(entry.getValue(), instanceMap.get(iocClazz));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
//???
SpringmvcController springmvcController = (SpringmvcController) instanceMap.get("test");
System.out.println(springmvcController);
}
//处理映射关系 controller/requestMapping将路径与对应的具体方法进行映射
private void handleMap() {
if (instanceMap.size() <= 0) {
return;
}
//遍历每一个键值对
for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
//寻找controller注解的类
if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
//拿到controller注解的value
Controller controller = entry.getValue().getClass().getAnnotation(Controller.class);
String firstPath = controller.value();
//拿到该类下所有requestMapping注解的方法与RequestMapping中的值
for (Method method : entry.getValue().getClass().getMethods()) {
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String secondPath = requestMapping.value();
//将相对路径作为键,将方法对象作为值添加到map中
handlerMap.put("/" + firstPath + "/" + secondPath, method);
}
}
}
}
}
//根据获得的文件进行过滤(选择Controller与Service注解的类)和实例化
private void filterAndInstance() throws Exception {
if (packageNames.size() <= 0) {
return;
}
for (String pName : packageNames) {
String cName = pName.replace(".class", "").trim();
Class<?> clazz = Class.forName(cName);
if (clazz.isAnnotationPresent(Controller.class)) {
//如果是被Controller注解
Object instance = clazz.newInstance();
Controller controller = clazz.getAnnotation(Controller.class);
String key = controller.value();
instanceMap.put(key, instance);
} else if (clazz.isAnnotationPresent(Service.class)) {
//如果是被Service注解
Object instance = clazz.newInstance();
Service service = clazz.getAnnotation(Service.class);
String key = service.value();
instanceMap.put(key, instance);
}
}
}
//扫描包
private void scanPackage(String packageName) {
URL url = this.getClass().getClassLoader().getResource("/" + replaceTo(packageName));
String filePath = url.getFile();
File file = new File(filePath);
String[] childrenFileList = file.list();
for (String path : childrenFileList) {
File childrenFile = new File(filePath + path);
if (childrenFile.isDirectory()) {
//递归查找所有子文件
scanPackage(packageName + "." + childrenFile.getName());
}else {
//将子文件添加到数组表中
packageNames.add(packageName + "." + childrenFile.getName());
}
}
}
// 将所有的.转义获取对应的路径
private String replaceTo(String path) {
//将.转为/
return path.replaceAll("\\.", "/");
}
}
2.5 web模块
(1).web.xml
<?xml version="1.0" encoding="UTF-8"?>
<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_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>simpleServlet</servlet-name>
<servlet-class>com.heylink.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>simpleServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
3.运行代码
3.1 配置Tomcat
3.2 运行项目
3.3 输入网址
在浏览器中输入以下网址
http://localhost:8080/test/insert
http://localhost:8080/test/delete
http://localhost:8080/test/update
http://localhost:8080/test/select
3.4 查看控制台输出
结果正确
4. 思路介绍
(1).首先我们需要扫描包中的所有文件
(DispatcherServlet->init(ServletConfig config)->scanPackage("com.heylink"))
也就是含有注解的文件。然后将该包下的所有文件都存入packageNames集合中。
(2).这时我们拿到了包下所有的文件,但我们只需要含有我们指定注解的那部分文件,因此需要过滤出我们想要的文件即可,并且在过滤的过程中,我们可以将过滤出来的类通过Class.forName来直接实例化并储存起来。存放到instanceMap集合中,并为其设置对应的key值,该key值就是类注解的value。
(3).然后遍历instanceMap集合中的所有对象,获取指定注解的对象,并通过反射获取该对象的所有的方法,遍历所有的方法,将指定注解的方法存入handerMap,key为拼接字符串("/" + 对象变量名 + "/" + 方法名),value为方法(Method)。
(4).然后我们可以遍历instanceMap集合中的所有对象,通过反射获取对象的所有属性值(字段)集合,然后遍历属性值集合,将属性值含有指定注解的,通过Field的set方法为该属性值赋值,这时就将对象注入了。(也就是往Controller中注入Service对象)
(5).最后就可以通过反射的invoke方法调用某个对象的某个方法。(此时对象存于instanceMap,而方法都存于handerMap)