欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

徒手编写Spring的初始化之山寨版IOC容器

程序员文章站 2022-03-08 12:20:27
建一个简单的web工程。 工程目录: 配置application.properties scanPackage=com.gys.demo #扫描该包下的类 编写注解 package annotation; import java.lang.annotation.*; @Target({Element ......

建一个简单的web工程。

工程目录:

徒手编写Spring的初始化之山寨版IOC容器

  配置application.properties

scanpackage=com.gys.demo #扫描该包下的类

编写注解

package annotation;

import java.lang.annotation.*;

@target({elementtype.field})
@retention(retentionpolicy.runtime)
@documented
public @interface gysautowired {
    string value() default "";
}

 

package annotation;

import java.lang.annotation.*;

@target({elementtype.type})
@retention(retentionpolicy.runtime)
@documented
public @interface gyscontroller {
    string value() default "";
}

 

package annotation;

import java.lang.annotation.*;

@target({elementtype.type,elementtype.method})
@retention(retentionpolicy.runtime)
@documented
public @interface gysrequestmapping {
    string value() default "";
}
package annotation;

import java.lang.annotation.*;

@target({elementtype.parameter})
@retention(retentionpolicy.runtime)
@documented
public @interface gysrequestparam {
    string value() default "";
}

 

package annotation;

import java.lang.annotation.*;

@target({elementtype.type})
@retention(retentionpolicy.runtime)
@documented
public @interface gysservice {
    string value() default "";
}

service编写

package com.gys.demo.service;

public interface idemoservice {
    string get(string name);
}
package com.gys.demo.service.impl;


import annotation.gysservice;
import com.gys.demo.service.idemoservice;

@gysservice
public class demoservice implements idemoservice {

    @override
    public string get(string name) {
        return "<h1>hello,"+name+"</h1>";
    }
}

controller代码

package com.gys.demo.controller;

import annotation.gysautowired;
import annotation.gyscontroller;
import annotation.gysrequestmapping;
import annotation.gysrequestparam;
import com.gys.demo.service.idemoservice;

import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import javax.servlet.http.httpsession;
import java.io.ioexception;

@gyscontroller
@gysrequestmapping("/demo")
public class democontroller {

    @gysautowired
    private idemoservice idemoservice;

    @gysrequestmapping("/query")
    public void query(httpservletrequest request,httpsession session,httpservletresponse response, @gysrequestparam("name") string name) throws ioexception {
        system.out.println("query..............");
        string res=idemoservice.get(name);
        response.getwriter().write(res);

    }
}

新建servlet,配置servlet

<!doctype web-app public
 "-//sun microsystems, inc.//dtd web application 2.3//en"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>archetype created web application</display-name>
<servlet>
  <servlet-name>gysmvc</servlet-name>
  <servlet-class>servlet.gysdispatcherservlet</servlet-class>
  <init-param>
    <param-name>contextconfiglocation</param-name>
    <param-value>application.properties</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
  <servlet-mapping>
    <servlet-name>gysmvc</servlet-name>
    <url-pattern>/*</url-pattern>

  </servlet-mapping>
</web-app>

核心代码servlet,代码有点长,直接折叠了。

package servlet;

import annotation.*;

import javax.servlet.servletconfig;
import javax.servlet.servletexception;
import javax.servlet.http.httpservlet;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import javax.servlet.http.httpsession;
import java.io.file;
import java.io.ioexception;
import java.io.inputstream;
import java.lang.annotation.annotation;
import java.lang.reflect.field;
import java.lang.reflect.invocationtargetexception;
import java.lang.reflect.method;
import java.net.url;
import java.util.*;
import java.util.regex.matcher;
import java.util.regex.pattern;

public class gysdispatcherservlet extends httpservlet {

    private properties contextconfig = new properties();
    //包+类文件名(去除.class后缀)
    private list<string> classnames = new arraylist<>();
    //名称,对象
    private map<string, object> ioc = new hashmap<>();
    //url,方法
    private list<handler> handlermapping=new arraylist<>();


    @override
    public void init(servletconfig config) throws servletexception {

        //1.加载配置文件
        doloadconfig(config.getinitparameter("contextconfiglocation"));
        //2.扫描相关类
        doscanner(contextconfig.getproperty("scanpackage"));
        //3.初始化扫描的类,并放入ioc容器
        doinstance();
        //完成依赖注入
        doautowired();

        //url和method的一对一关系
        inithandlermapping();
        system.out.println("servlet init finsh==================");
    }

    @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 {
        try {
            req.setcharacterencoding("utf-8");
            resp.setcharacterencoding("utf-8");
            resp.setcontenttype("text/html;charset=utf-8");
            //具体处理逻辑
            dodispatch(req, resp);
        } catch (invocationtargetexception e) {
            e.printstacktrace();
        } catch (illegalaccessexception e) {
            resp.getwriter().write("500 exception" + arrays.tostring(e.getstacktrace()));
        }
    }

    private void dodispatch(httpservletrequest request, httpservletresponse response) throws ioexception, invocationtargetexception, illegalaccessexception {
        handler handler=gethandler(request);
        if(handler==null){
            response.getwriter().write("404 对不起没有您要的页面资源");
            return;
        }
        //参数类型数组
        class<?>[] parametertypes=handler.method.getparametertypes();
        //参数数组
        object[] paramvalues=new object[parametertypes.length];
        map<string, string[]> parametermap = request.getparametermap();
        for(map.entry<string,string[]> entry:parametermap.entryset()){
            //数组中的[[ 和 ]] 替换
            string value=arrays.tostring(entry.getvalue()).replaceall("\\[|\\]","");
            if(!handler.paramindexmapping.containskey(entry.getkey())){
                continue;
            }
            int index=handler.paramindexmapping.get(entry.getkey());
            value=new string(value.getbytes("iso-8859-1"), "utf-8");
            paramvalues[index]=convert(parametertypes[index],value);
        }
        //如果方法参数是request
        if(handler.paramindexmapping.containskey(httpservletrequest.class.getname())){
            int reqindex=handler.paramindexmapping.get(httpservletrequest.class.getname());
            paramvalues[reqindex]=request;
        }
        //如果方法参数是response
        if(handler.paramindexmapping.containskey(httpservletresponse.class.getname())){
            int reqindex=handler.paramindexmapping.get(httpservletresponse.class.getname());
            paramvalues[reqindex]=response;
        }
        //如果方法参数是session
        if(handler.paramindexmapping.containskey(httpsession.class.getname())){
            int reqindex=handler.paramindexmapping.get(httpsession.class.getname());
            paramvalues[reqindex]=request.getsession();
        }
        //利用反射调用mapping标识的方法
        object returnvalue=handler.method.invoke(handler.controller,paramvalues);
        if(returnvalue==null||returnvalue instanceof void){
            return;
        }
        //向浏览器输出类容
        response.getwriter().write(returnvalue.tostring());
    }

    private void doloadconfig(string contextconfiglocation) {
        inputstream is = this.getclass().getclassloader().getresourceasstream(contextconfiglocation);
        try {
            contextconfig.load(is);
        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }

    }

    private void doscanner(string scanpackage) {
        string xgscanpackage = scanpackage.replaceall("\\.", "/");
        url url = this.getclass().getclassloader().getresource("/" + xgscanpackage);
        string path = url.getfile();
        file classdir = new file(path);
        for (file file : classdir.listfiles()) {
            if (file.isdirectory()) {
                doscanner(scanpackage + "." + file.getname());
            } else {
                if (!file.getname().endswith(".class")) {
                    continue;
                }
                //所有类的文件路径+包名
                string clazzname = (scanpackage + "." + file.getname()).replace(".class", "");
                classnames.add(clazzname);
            }
        }
    }

    private void doinstance() {
        if (classnames.isempty()) {
            return;
        }
        try {
            for (string classname : classnames) {
                class<?> clazz = class.forname(classname);
                //controller注解
                if(clazz.isannotationpresent(gyscontroller.class)){
                    object instance=clazz.newinstance();
                    string beanname=tolowerfirstcase(clazz.getsimplename());
                    //类名,实例对象
                    ioc.put(beanname,instance);
                }else if(clazz.isannotationpresent(gysservice.class)){//service注解
                    object instance=clazz.newinstance();
                    gysservice gysservice=clazz.getannotation(gysservice.class);
                    string beanname=gysservice.value();
                    //没规定类名
                    if("".equals(beanname.trim())){
                        //类名首字母小写
                        beanname=tolowerfirstcase(clazz.getsimplename());
                    }
                    //类名,对象
                    ioc.put(beanname,instance);
                    //循环接口
                    for(class inter:clazz.getinterfaces()){
                        //接口长类名,子类实现对象
                        ioc.put(inter.getname(),instance);
                    }
                }else{
                    continue;
                }
            }
        } catch (classnotfoundexception e) {
            e.printstacktrace();
        } catch (illegalaccessexception e) {
            e.printstacktrace();
        } catch (instantiationexception e) {
            e.printstacktrace();
        }
    }


    private void  doautowired(){
        if(ioc.isempty()){
            return;
        }

        //循环ioc容器
        for (map.entry<string,object> entry:ioc.entryset()) {
            //获取所有的字段
            field[] fields = entry.getvalue().getclass().getdeclaredfields();
            for(field field:fields){
                field.setaccessible(true);
                //判断是否有有依赖注入
                if (!field.isannotationpresent(gysautowired.class)) {
                    continue;
                }
                gysautowired gysautowired = field.getannotation(gysautowired.class);
                //获取依赖名称
                string beanname=gysautowired.value().trim();
                if (beanname.isempty()) {//未定义依赖名称
                    //获取字段类型长路径名
                    class type= field.gettype();
                    if (type.isinterface()) {//接口用长路径名
                        beanname=type.getname();
                    }else{//实体类用类名
                        beanname=tolowerfirstcase(type.getsimplename());
                    }
                }
                try {
                    //设置字段值,实现依赖注入
                    field.set(entry.getvalue(),ioc.get(beanname));
                } catch (illegalaccessexception e) {
                    e.printstacktrace();
                }
            }
        }

    }

    private void inithandlermapping(){
        if(ioc.isempty()){
            return;
        }
        for(map.entry<string,object> entry:ioc.entryset()){
            class<?> clazz=entry.getvalue().getclass();
            if(!clazz.isannotationpresent(gyscontroller.class)){
                continue;
            }
            string url="";
            if(clazz.isannotationpresent(gysrequestmapping.class)){
                gysrequestmapping gysrequestmapping=clazz.getannotation(gysrequestmapping.class);
                url=gysrequestmapping.value();
            }
            for(method method:clazz.getmethods()){
                if(!method.isannotationpresent(gysrequestmapping.class)){
                    continue;
                }
                gysrequestmapping gysrequestmapping=method.getannotation(gysrequestmapping.class);
                string regex=url+gysrequestmapping.value();
                pattern pattern=pattern.compile(regex);
                handlermapping.add(new handler(pattern,entry.getvalue(),method));
                system.out.println("mapped:"+url+","+method);
            }
        }
    }

    //首字母小写
    private string tolowerfirstcase(string simplename){
        char[] chars=simplename.tochararray();
        chars[0]+=32;
        return string.valueof(chars);
    }

    private class handler{
        protected object controller;//保存方法对应的实例
        protected  method method;//保存映射方法
        protected pattern pattern;
        protected map<string,integer> paramindexmapping;//参数顺序

        public handler(pattern pattern,object controller, method method) {
            this.controller = controller;
            this.method = method;
            this.pattern = pattern;
            this.paramindexmapping = new hashmap<>();
            putparamindexmapping(this.method);

        }

        private void putparamindexmapping(method method){
            annotation[][] annotations=method.getparameterannotations();
            for(int i=0;i<annotations.length;i++){
                for(annotation annotation:annotations[i]){
                    if(annotation instanceof gysrequestparam){
                        string paranname=((gysrequestparam) annotation).value();
                        if(!paranname.trim().isempty()){
                            paramindexmapping.put(paranname,i);
                        }
                    }
                }
            }
            class<?>[] paramstypes=method.getparametertypes();
            for(int i=0;i<paramstypes.length;i++){
                class<?> type=paramstypes[i];
                if(type==httpservletrequest.class||type==httpservletresponse.class||type==httpsession.class){
                    paramindexmapping.put(type.getname(),i);
                }
            }

        }
    }

    private handler gethandler(httpservletrequest request){
        if(handlermapping.isempty()){
            return null;
        }
        string url=request.getrequesturi();
        string contextpath=request.getcontextpath();
        url=url.replace(contextpath,"");
        for(handler handler:handlermapping){
            matcher matcher=handler.pattern.matcher(url);
            if(!matcher.matches()){
                continue;
            }
            return handler;
        }
        return null;
    }

    //由于http基于字符串协议,url传过来的参数都是string类型的;
    private object convert(class<?> type,string value){
        if(integer.class==type){
            return  integer.valueof(value);
        }else if(double.class==type){
            return double.valueof(value);
        }
        //.......
        return value;
    }

}

 

运行项目

徒手编写Spring的初始化之山寨版IOC容器