自定义几个注解 手写一个DispatcherServlet,代码结构如下:
基本思路流程:
一、 读取配置文件
1.类加载器获取initParam中定义的资源文件
2.利用Properties的load方法加载io对象,映射key value
3.获取配置文件里的包路径
二、初始化ioc容器
定义一个map集合
三、扫描包下的class
1.类加载器根据包路径得到url.getFile全路径
2.利用File遍历出该路径下所有文件和文件夹
3.如果是文件夹则拼接路径递归遍历,如果是文件则判断是否以.class结尾,是则将全类名放入list集合中
四、实例化对象并放入ioc
1.遍历list集合,Class.forName("全类名"),通过反射获取其类名和实例对象
2.判断是否加了@YService、@YController注解,如果注解加在了接口上则默认无效,
3.如果定义了别名则获取,没有则默认类名首字母小写,并且遍历当前类所实现的所有接口,将子类对象值赋予存入ioc
五、扫描放入ioc中的对象,初始化其属性
1.遍历ioc集合,获取声明的所有属性
2.反射判断是否加入@YAutowired注解,是则获取当前属性名,将ioc中存入的值赋予属性
六、初始化handleMapping,将url和menthod一一对应
1.定义一个handleMapping的map集合
2.遍历ioc集合,根据对象的class对象判断是否有@YController注解
3.判断是否有@YRequestMapping注解,有url值的话则获取baseUrl
4.反射获取对象中所有方法,判断方法上是否有@YRequestMapping注解,获取其url值,和baseUrl一起拼接成一个uri,将其和method一起放入handleMapping集合
------------------至此init初始化阶段完成,进入service方法----------
1.获取请求的url和项目名称,得到uri
2.判断handle集合中是否有这个uri,没有就404,有则取出其method
3.根据method获得对象名,获取请求的参数执行invoke方法
大体代码逻辑如下:
完整代码我发布在github:https://github.com/yangzhenyan/spring.git
@YController
@YRequestMapping("item")
public class ItemController {
@YAutowired
private ItemService itemService;
private ItemService itemService1;
//查询
@YRequestMapping("query")
public void query(HttpServletRequest request, HttpServletResponse response,
@YRequestParam("name") String name, @YRequestParam("id") String id) {
try {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("Amos的名字叫做:" + name + " id为:" + id);
} catch (IOException e) {
e.getCause().getMessage();
}
}
}
public class YzyDispatcherServlet extends HttpServlet {
private Properties properties = new Properties();
//2、初始化ioc容器
private Map<String, Object> iocMap = new HashMap<>();
//存放全类名
private List<String> classNameList = new ArrayList<>();
//handleMapping集合
private Map<String, Method> handleMap = new HashMap<>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//7、执行相关方法
try {
String requestURI = req.getRequestURI();
//获取项目名 "/项目名"
String contextPath = req.getContextPath();
//获取请求的uri
String uri = requestURI.replace(contextPath, "");
//如果请求的uri不包含在handleMapping集合
if (!handleMap.containsKey(uri)) {
resp.getWriter().write("404 Not Found !!");
return;
}
Method method = handleMap.get(uri);
Map<String, String[]> params = req.getParameterMap();
String beanName = toLowerLetters(method.getDeclaringClass().getSimpleName());
method.invoke(iocMap.get(beanName),new Object[]{req,resp,params.get("name")[0],params.get("id")[0]});
} catch (Exception e) {
resp.getWriter().write("500 Internal Server Exception !!");
e.printStackTrace();
}
}
@Override
public void init(ServletConfig config) throws ServletException {
//1、读取配置文件
doReadConfig(config.getInitParameter("myConfigLocation"));
//3、扫描包路径下的类 将.替换成/
String scanPackage = properties.getProperty("scanPackage").trim();
doScanner(scanPackage.replaceAll("\\.", "/"));
//4、创建实例化对象将其注入到ioc容器中
doInstance();
//5、DI 扫描容器中对象 对其属性赋值
doAutowired();
//6、初始化handleMapping 将url和method一一映射
doHandleMapping();
System.out.println("Spring Framework init......");
}
//1、读取配置文件
private void doReadConfig(String configLocation) {
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(configLocation);
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//3、扫描包路径下的类
private void doScanner(String scanPackage) {
// scanPackage = scanPackage.replace(".", "/");
URL url = getClass().getClassLoader().getResource("/" + scanPackage);
//url.getFile() 获取此文件名URL
File filePath = new File(url.getFile());
File[] files = filePath.listFiles();
for (File file : files) {
if (file.isDirectory()) {
//如果是文件夹 则在该文件夹的路径下递归扫描
String s = scanPackage + "/" + file.getName();
doScanner(s);
}
//再判断是不是以.class结尾的文件
if (file.getName().endsWith(".class")) {
//拼接全类名 去除.class后缀
String className = scanPackage.replace("/", ".") + "." + file.getName().replace(".class", "");
classNameList.add(className);
}
}
}
//4、实例化对象并注入ioc中
private void doInstance() {
if (classNameList.isEmpty()) {
return;
}
//遍历全类名list
for (String name : classNameList) {
try {
//根据全类名获取对应字节码对象
Class<?> aClass = Class.forName(name);
//判断是不是加了@YService或@YController注解
if (!aClass.isAnnotationPresent(YService.class) & !aClass.isAnnotationPresent(YController.class)) {
continue;
}
//如果是接口上加的注解 则不放入ioc
if (aClass.isInterface()) {
continue;
}
//将对象放入ioc中 ioc中组件名默认首字母小写
//如果不同包下有相同类名 自定义类名
String beanName = null;
if (aClass.isAnnotationPresent(YService.class)) {
beanName = toLowerLetters(aClass.getSimpleName());
if (!"".equals(aClass.getAnnotation(YService.class).value())) {
beanName = aClass.getAnnotation(YService.class).value();
}
} else if (aClass.isAnnotationPresent(YController.class)) {
beanName = toLowerLetters(aClass.getSimpleName());
if (!"".equals(aClass.getAnnotation(YController.class).value())) {
beanName = aClass.getAnnotation(YController.class).value();
}
}
//获取对象
Object instance = aClass.newInstance();
iocMap.put(beanName, instance);
//获得这个对象实现的所有接口 将其接口也放入ioc 接口的值为子类值
for (Class<?> inter : aClass.getInterfaces()) {
beanName = toLowerLetters(inter.getSimpleName());
iocMap.put(beanName, instance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//5、DI 扫描容器中对象 对其属性赋值
private void doAutowired() {
if (iocMap.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
//获取对象中的属性
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
//如果加了@YAutowired注解就给属性赋值
if (field.isAnnotationPresent(YAutowired.class)) {
//暴力访问
field.setAccessible(true);
try {
//获取该属性beanName
String beanName = toLowerLetters(field.getType().getSimpleName());
field.set(entry.getValue(), iocMap.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
continue;
}
}
}
}
}
//6、初始化handleMapping 将url和method一一映射
private void doHandleMapping() {
if (iocMap.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
Class<?> aClass = entry.getValue().getClass();
if (!aClass.isAnnotationPresent(YController.class)) {
continue;
}
//判断类路径上是否有@YRequestMapping注解 有就获取
String baseUrl = null;
if (aClass.isAnnotationPresent(YRequestMapping.class)) {
baseUrl = aClass.getAnnotation(YRequestMapping.class).value();
}
//判断方法上@YRequestMapping注解
Method[] methods = aClass.getMethods();
String methodUrl = null;
for (Method method : methods) {
if (method.isAnnotationPresent(YRequestMapping.class)) {
methodUrl = method.getAnnotation(YRequestMapping.class).value();
String uri = "/" + baseUrl + "/" + methodUrl;
//url method放入集合
handleMap.put(uri, method);
}
}
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
//获取对象名并把首字母转为小写
private String toLowerLetters(String simpleName) {
String letters = simpleName.substring(0, 1).toLowerCase();
return letters + simpleName.substring(1);
}
}
配置tomcat启动访问http://localhost:8080/spring_war_exploded/item/query?name=zhangsan&id