springMVC学习笔记(三)手写springmvc
手写springmvc
1、tomcat加载web.xml,配置了dispatchServlet,会加载指定的配置文件
2、包扫描,扫描注解,@Controller、@Service、@RequestMapping、@AutoWired
3、IOC容器就要进行相应的Bean初始化以及依赖注入,维护
4、springmvc相关组件的初始化,需要简历url和method之间的映射关系,handlermapping(处理器映射器)
5、等待请求进来,处理请求
手写LgDispatcherServlet
public class LgDispatcherServlet extends HttpServlet {
private Properties properties=new Properties();
private List<String> classNames=new ArrayList<>();//缓存扫描到的全限定类名
//IOC容器
private Map<String,Object> map=new HashMap<>();
//handlerMapping 存储url和method之间的映射关系
// private Map<String,Method> handlerMapping=new HashMap<>();
private List<Handler> handlerMapping=new ArrayList<>();
@Override
public void init(ServletConfig config) throws ServletException {
String contextConfigLocation = config.getInitParameter("contextConfigLocation");
//1、加载配置文件 springmvc.properties
doLoadConfig(contextConfigLocation);
//2、扫描相关类,扫描注解
doScan(properties.getProperty("scanPackage"));
//3、初始化bean对象(实现IOC容器,基于注解)
doInstance();
//4、实现依赖注入
doAutoWired();
//5、构造一个handlerMapping(处理器映射器),将配置好的url和method建立映射关系
initHandlerMapping();
System.out.println("mvc初始化完成");
//6、等待请求进入,处理请求
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//进行请求
//根据url找到对应的method,进行调用处理
// String requestURI = req.getRequestURI();
//获取到一个反射方法
// Method method = handlerMapping.get(requestURI);
//反射调用,需要传入对象和参数,此处无法完成调用,没有把对象存储起来,也没有参数,改造initHandlerMapping方法
// method.invoke()
//根据url获取到能够处理当前请求的handler,
Handler handler=getHandler(req);
if (handler==null){
resp.getWriter().write("404 not found");
return;
}
//参数绑定
//获取所有参数类型数组,数组的长度就是最后我们要传入args数组的长度
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
//根据上述数组长度,创建一个新数组(参数数组,args,传入反射调用)
Object[] paraValues=new Object[parameterTypes.length];
//以下就是为了向参数数组中添加值,而且还要保证参数的顺序和方法中形参顺序一致
Map<String, String[]> parameterMap = req.getParameterMap();
//遍历req中所有参数(填充出了request、response之外的参数)
for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
//name=1&name=2 name=[1,2]
String value = StringUtils.join(param.getValue(), ",");//如同 1,2
//填充数据,如果参数和方法上的参数匹配上了
if(!handler.getParamIndexMapping().containsKey(param.getKey())){
continue;
}
//证明确实有该参数,找到他的索引位置,paraValues
Integer index = handler.getParamIndexMapping().get(param.getKey());
//把前段传递过来的值放到对应的形参中
paraValues[index]=value;
}
int requestIndex=handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
paraValues[requestIndex]=req;
int responseIndex=handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
paraValues[responseIndex]=resp;
//最终调用handler中的mehtod对象
try {
handler.getMethod().invoke(handler.getController(),paraValues);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
加载配置文件
//加载配置文件
private void doLoadConfig(String contextConfigLocation) {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
properties.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
扫描注解
private void doScan(String scanPackage) {
String scanPackagePath = Thread.currentThread().
getContextClassLoader().
getResource("").
getPath() + scanPackage.
replaceAll("\\.", "/");
File pack=new File(scanPackagePath);
File[] files = pack.listFiles();
for (File file : files) {
if(file.isDirectory()){//子package
doScan(scanPackage+"."+file.getName());
}else if (file.getName().endsWith(".class")){
String className = scanPackage + "." + file.getName().replaceAll(".class","");
classNames.add(className);
}
}
}
初始化bean对象
private void doInstance() {
if(classNames.size()==0) return;
try {
for (int i=0;i<classNames.size();i++){
String className = classNames.get(i); //全限定类名
Class<?> aClass = Class.forName(className);
if(aClass.isAnnotationPresent(LagouController.class)){
//controller的id不做过多处理,就拿类的首字母小写,作为id,保存到容器中
String simpleName = aClass.getSimpleName();
String lowerFirstSimpleName = lowerFirst(simpleName);
Object o = aClass.newInstance();
map.put(lowerFirstSimpleName,o);
}else if(aClass.isAnnotationPresent(LagouService.class)){
LagouService annotation = aClass.getAnnotation(LagouService.class);
String beanName = annotation.value();
//如果指定id就以指定id为准
if ("".equals(beanName.trim())) {
beanName = lowerFirst(aClass.getSimpleName());
}
map.put(beanName,aClass.newInstance());
//service经常是面向接口开发的,此时,再以接口名为ID,放入一份对象到IOC容器中,便于后期根据接口注入
Class<?>[] interfaces = aClass.getInterfaces();
for (int j = 0; j < interfaces.length; j++) {
Class<?> anInterface = interfaces[j];
map.put(anInterface.getName(),aClass.newInstance());
}
}else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
//首字母小写
public String lowerFirst(String string){
char[] chars = string.toCharArray();
if('A'<= chars[0] && chars[0] <='Z'){
chars[0]+=32;
}
return String.valueOf(chars);
}
实现依赖注入
private void doAutoWired() {
if (map.isEmpty()) return;
//如果有对象,再进行依赖注入
//遍历ioc中所有对象,查看对象字段是否有@LagouAutoWried这个注解,如果有,需要维护依赖注入关系
for (Map.Entry<String, Object> entry : map.entrySet()) {
//获取bean对象中的字段
Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
//遍历字段数组
for (Field declaredField : declaredFields) {
boolean annotationPresent = declaredField.isAnnotationPresent(LagouAutoWried.class);
if(!annotationPresent){
continue;
}
LagouAutoWried annotation = declaredField.getAnnotation(LagouAutoWried.class);
//需要注入的bean的id
String beanName = annotation.value();
if("".equals(beanName.trim())){
//没有配置配置具体的id,那就需要根据当前字段类型注入(接口注入)
beanName = declaredField.getType().getName();
}
//开启赋值
declaredField.setAccessible(true);
try {
declaredField.set(entry.getValue(),map.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
构造一个handlerMapping
手写mvc框架关键环节,将url和method进行关联
private void initHandlerMapping() {
if (map.isEmpty()) return;
for (Map.Entry<String, Object> entry : map.entrySet()) {
//获取ioc容器中当前遍历的class类型
Class<?> aClass = entry.getValue().getClass();
if(!aClass.isAnnotationPresent(LagouController.class)){
continue;
}
String baseUrl="";
if (aClass.isAnnotationPresent(LagouRequestMapping.class)){
baseUrl=aClass.getAnnotation(LagouRequestMapping.class).value();
}
//获取方法
Method[] methods = aClass.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if(!method.isAnnotationPresent(LagouRequestMapping.class)){
continue;
}
String methodUrl=method.getAnnotation(LagouRequestMapping.class).value();
String url=baseUrl+methodUrl;
//把method所有信息封装成一个handler
Object value = entry.getValue();
Handler handler=new Handler(value,method, Pattern.compile(url));
//处理方法的计算参数位置信息 //query(HttpServletRequest request, HttpServletResponse response,String name)
TypeVariable<Method>[] typeParameters = method.getTypeParameters();
Parameter[] parameters = method.getParameters();
for (int j = 0; j < parameters.length; j++) {
Parameter parameter = parameters[j];
if(parameter.getType()==HttpServletRequest.class||parameter.getType()==HttpServletResponse.class){
//如果是HttpServletRequest和HttpServletResponse对象,那么参数名称为HttpServletRequest,HttpServletResponse
handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j);
}else {
handler.getParamIndexMapping().put(parameter.getName(),j);
}
}
//建立url和method之间的映射(使用map缓存起来)
handlerMapping.add(handler);
}
}
}
Handler类
public class Handler {
private Object controller;//method.invoke中的obj
private Method method;//需要调用的方法
private Pattern pattern;//spring中url是支持正则的
private Map<String,Integer> paramIndexMapping;//参数顺序,是为了进行参数绑定,key是参数名,Integer是代表第几个参数,例如<name,2>
public Handler(Object controller, Method method, Pattern pattern) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
this.paramIndexMapping = new HashMap<>();
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Pattern getPattern() {
return pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public Map<String, Integer> getParamIndexMapping() {
return paramIndexMapping;
}
public void setParamIndexMapping(Map<String, Integer> paramIndexMapping) {
this.paramIndexMapping = paramIndexMapping;
}
}