要实现的功能:
初始化时,将注解的类实例化到容器中进行管理,访问时可以根据url找到指定的方法进行调用,对于Autowired注解的属性,要将容器中管理的bean注入进来,实现springmvc的简易功能。
具体思路:
实现中要注意的细节:
项目结构目录如下:
这里我们先实现这些注解的功能,其余的可自行补充实现
@Autowired : required参数的作用为自动注入时bean不存在是否忽略,默认true,不忽略
package com.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.Documented;
import java.lang.annotation.RetentionPolicy;
/**
* @Autowired注解:注入bean实例
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* Defaults to {@code true}.
*/
boolean required() default true;
}
@Controller注解:标记bean为controller层
package com.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Documented;
/**
* @Controller注解:标记bean为controller层
*/
@Target({ElementType.TYPE}) //注解可以在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
String value() default "";
}
@RequestMapping注解:映射请求地址
package com.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @RequestMapping注解:映射请求地址
*/
@Target({ElementType.TYPE,ElementType.METHOD}) //注解可以在类或者方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
String value();
}
@RequestParam注解:参数绑定
package com.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @RequestParam注解:参数绑定
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
String value();
}
@Service注解:标记bean为Service层
package com.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Documented;
/**
* @Service注解:标记bean为Service层
*/
@Target({ElementType.TYPE}) //注解可以在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
String value() default "";
}
在实现前端控制器之前,我们要配置一下web.xml,将需要加载的DispatcherServlet路径以及配置文件路径指定
SpringMVC
com.servlet.DispatcherServlet
contextConfigLocation
application.properties
1
SpringMVC
/*
application.properties配置扫描路径:
scanPackage=com.web
csdnUrl=手写springmvc框架启动成功,更多技术文档请访问:https://blog.csdn.net/dwhdome
还需要一个servlet的pom依赖,我们引入一下:
UTF-8
1.8
javax.servlet
javax.servlet-api
3.0.1
provided
具体实现:
详细步骤都在代码中有详细说明,继承HttpServlet类,重写父类的init,get,post方法。在init中写我们的实现逻辑
package com.servlet;
import com.annotation.*;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DispatcherServlet extends HttpServlet{
//spring配置文件里的对象
private Properties properties = new Properties();
// handlerMapping对象,存储url和方法的映射关系
private Map handlerMapping = new HashMap<>();
//扫描路径下的类名列表
private List classNames = new ArrayList<>();
//IOC容器对象,保存注入的实例对象
private Map ioc = new HashMap<>();
//保存url和注入的bean
private Map controllerMap =new HashMap<>();
//重写父类doGet方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
//重写父类doPost方法
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//处理请求
doDispatch(this, req,resp);
} catch (Exception e) {
resp.getWriter().write("500!! Server Exception");
}
}
//重写父类init方法
@Override
public void init(ServletConfig config) throws ServletException {
//1、加载spring的配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2、初始化所有相关联的类,扫描用户设定的包下面所有的类
doScanner(properties.getProperty("scanPackage"));
//3、拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中beanName默认是首字母小写
doInstance();
//4.属性注入,将@Autowired注解的参数注入实例
doAutowired();
//5.初始化HandlerMapping(将url和method对应上),通过url找到对应的处理器
initHandlerMapping();
System.out.println(properties.getProperty("csdnUrl"));
}
private void doAutowired(){
if (ioc.isEmpty()) {
return;
}
//遍历所有被托管的对象
for (Map.Entry entry : ioc.entrySet()) {
//查找所有被Autowired注解的属性
// getFields()获得某个类的所有的公共(public)的字段,包括父类;
// getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
//没加autowired的不需要注值
if (!field.isAnnotationPresent(Autowired.class)) {
continue;
}
//默认bean名字为类名首字母小写
String beanName = toLowerFirstWord(field.getType().getSimpleName());
//获取Autowired注解的值
Autowired autowired = field.getAnnotation(Autowired.class);
//将私有化的属性设为true,不然访问不到,用于下方属性注入
field.setAccessible(true);
//获取beanName的实例化对象
Object beanClazz = ioc.get(beanName);
try {
if (autowired.required()) {
//当required为true时,bean必须存在,否则报错
if(null == beanClazz){
throw new NullPointerException("无法找到:"+beanName+"的实例");
}
field.set(entry.getValue(), ioc.get(beanName));
} else {
//当required为false时,bean如果不存在,则跳过
if(null != beanClazz){
field.set(entry.getValue(), ioc.get(beanName));
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
System.out.println("4:属性注入完成...");
}
private void initHandlerMapping() {
if(ioc.isEmpty()){
return;
}
try {
for (Entry entry: ioc.entrySet()) {
//获取ioc中注入的bean
Class extends Object> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(Controller.class)){
continue;
}
//拼url时,是controller头的url拼上方法上的url
String baseUrl ="";
if(clazz.isAnnotationPresent(RequestMapping.class)){
RequestMapping annotation = clazz.getAnnotation(RequestMapping.class);
baseUrl=annotation.value();
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if(!method.isAnnotationPresent(RequestMapping.class)){
continue;
}
RequestMapping annotation = method.getAnnotation(RequestMapping.class);
String url = annotation.value();
url =(baseUrl+"/"+url).replaceAll("/+", "/");
if(handlerMapping.containsKey(url)){
throw new InstantiationException("当前请求路径映射‘"+url+"'已存在!");
}
handlerMapping.put(url,method);
controllerMap.put(url,entry.getValue());
}
}
System.out.println("5: HandlerMapping初始化完成...");
} catch (Exception e) {
e.printStackTrace();
}
}
private void doInstance() {
if (classNames.isEmpty()) {
return;
}
for (String className : classNames) {
try {
//反射来实例化(加@Controller注解以及@Service需要实例化)
Class> clazz =Class.forName(className);
if(clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class)){
String clazzName = toLowerFirstWord(clazz.getSimpleName());
if(ioc.containsKey(clazzName)){
throw new InstantiationException("当前bean‘"+clazzName+"'已存在!");
}
ioc.put(clazzName,clazz.newInstance());
}else{
continue;
}
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
System.out.println("3:类实例化完成...");
}
/**
* 把字符串的首字母小写
* @param name
* @return
*/
private String toLowerFirstWord(String name){
char[] charArray = name.toCharArray();
charArray[0] += 32; //大小写正好间隔32的字节
return String.valueOf(charArray);
}
private void doScanner(String packageName) {
//把所有的.替换成/
URL url =this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
File dir = new File(url.getFile());
for (File file : dir.listFiles()) {
if(file.isDirectory()){
//递归读取包
doScanner(packageName+"."+file.getName());
}else{
String className =packageName +"." +file.getName().replace(".class", "");
classNames.add(className);
System.out.println("2:扫描到类"+className);
}
}
}
private void doLoadConfig(String contextConfigLocation) {
//把web.xml中的contextConfigLocation对应value值的文件加载到流里面
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
InputStreamReader inputStreamReader = null;
try {
//字符流读取配置文件中文数据,防止乱码
inputStreamReader = new InputStreamReader(resourceAsStream,"UTF-8");
//用Properties文件加载文件里的内容
properties.load(inputStreamReader);
System.out.println("1:配置文件加载完成...");
} catch (IOException e) {
e.printStackTrace();
}finally {
//关流
if(null!=resourceAsStream){
try {
resourceAsStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null!=inputStreamReader){
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void doDispatch(DispatcherServlet dispatcherServlet, HttpServletRequest req, HttpServletResponse resp) throws Exception{
if(dispatcherServlet.handlerMapping.isEmpty()){
return;
}
String url =req.getRequestURI(); //获取请求的uRL
String contextPath = req.getContextPath();
//去除URL中多余的"/"
url=url.replace(contextPath, "").replaceAll("/+", "/");
//如果访问的URL不存在,则直接返回404错误
if(!dispatcherServlet.handlerMapping.containsKey(url)){
resp.getWriter().write("404 NOT FOUND!");
return;
}
Method method = dispatcherServlet.handlerMapping.get(url);
//获取方法的参数列表
Parameter methodParameters[] = method.getParameters();
//获取请求的参数
Map parameterMap = req.getParameterMap();
//保存参数值
Object [] paramValues= new Object[methodParameters.length];
for (int i = 0; i < methodParameters.length; i++) {
if (ServletRequest.class.isAssignableFrom(methodParameters[i].getType())) { //绑定request
paramValues[i] = req;
} else if (ServletResponse.class.isAssignableFrom(methodParameters[i].getType())) {//绑定response
paramValues[i] = resp;
} else {// 请求参数,可类型自动转换,支持String,Integer,Float,Double
// 参数绑定的名称,默认为方法形参名
String bindingValue = methodParameters[i].getName();
if (methodParameters[i].isAnnotationPresent(RequestParam.class)) {
//获取RequestParam注解绑定的参数名
bindingValue = methodParameters[i].getAnnotation(RequestParam.class).value();
}
// 从请求中获取参数的值
String paramValue = req.getParameter(bindingValue);
paramValues[i] = paramValue;
//参数处理,根据参数类型自动转换
if (paramValue != null) {
if (Integer.class.isAssignableFrom(methodParameters[i].getType())) {
paramValues[i] = Integer.parseInt(paramValue);
} else if (Float.class.isAssignableFrom(methodParameters[i].getType())) {
paramValues[i] = Float.parseFloat(paramValue);
} else if (Double.class.isAssignableFrom(methodParameters[i].getType())) {
paramValues[i] = Double.parseDouble(paramValue);
}
}
}
}
//利用反射机制来调用
try {
method.invoke(dispatcherServlet.controllerMap.get(url), paramValues);//第一个参数是method所对应的实例,在controllerMap中;第二个参数为方法参数
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试controller以及service
package com.web.controller;
import com.annotation.Autowired;
import com.annotation.Controller;
import com.annotation.RequestMapping;
import com.annotation.RequestParam;
import com.web.service.TestService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
@RequestMapping("/Test")
public class TestController {
@Autowired
private TestService testService;
//处理bean名字相同问题
@RequestMapping("/Test")
public void test1(HttpServletRequest request, HttpServletResponse response, @RequestParam("name")String name){
try {
response.setContentType("text/html; charset=utf-8");
//response.setCharacterEncoding("UTF-8");
response.getWriter().write( "controller method success! param:"+name);
response.getWriter().write("
");
response.getWriter().write("serevice method success! param:"+this.testService.getName());
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.web.service;
import com.annotation.Service;
@Service
public class TestService {
public String getName(){
return "胡歌";
}
}
全部配置完成后启动项目。观察控制台:
请求/Test/Test?name=丁文浩,
得出结论,参数绑定正常,自动注入正常。、
代码链接:https://pan.baidu.com/s/1GsSa0k1kAccNJvyjJuAokA 提取码:1vae