徒手编写Spring的初始化之山寨版IOC容器
程序员文章站
2022-07-01 15:49:56
建一个简单的web工程。 工程目录: 配置application.properties scanPackage=com.gys.demo #扫描该包下的类 编写注解 package annotation; import java.lang.annotation.*; @Target({Element ......
建一个简单的web工程。
工程目录:
配置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; } }
运行项目