目录的大致情况:所有的类都会加进来.
controller service serviceimpl是用来验证下面写的框架信息是否正常运行
package demo.controller;
import demo.service.helloservice;
import framework.annotation.fautowried;
import framework.annotation.fcontroller;
import framework.annotation.frequestmapping;
import framework.annotation.frequestparam;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
import java.io.printwriter;
/**
* democontroller.
*
* @author feng yongkang, 2019/9/9
* @version v1.0
*/
@fcontroller
@frequestmapping("/demo")
public class hellocontroller {
@fautowried
private helloservice service;
@frequestmapping("/getname")
public void getname(httpservletrequest req, httpservletresponse resp, @frequestparam("name") string name) {
service.firstservlet(name);
try {
system.out.println(name);
printwriter writer = resp.getwriter();
writer.write(name);
writer.flush();
writer.close();
} catch (ioexception e) {
e.printstacktrace();
} finally {
}
}
}
package demo.service;
/**
* helloservice.
*
* @author feng yongkang, 2019/9/10
* @version v1.0
*/
public interface helloservice {
void firstservlet(string str);
}
package demo.service.impl;
import demo.service.helloservice;
import framework.annotation.fservice;
/**
* helloservice.
*
* @author feng yongkang, 2019/9/10
* @version v1.0
*/
@fservice
public class helloserviceimpl implements helloservice {
public void firstservlet(string str) {
system.out.println("你好,我接收到一个参数:" + str);
}
}
这里是考虑最简单常用的几个注解.可以支持简单的请求处理
package framework.annotation;
import java.lang.annotation.*;
@target(elementtype.field)//这个注解的使用范围
@retention(retentionpolicy.runtime)//生命周期
@documented
public @interface
fautowried {
string value() default "";
}
package framework.annotation;
import java.lang.annotation.*;
@target(elementtype.type)//这个注解的使用范围
@retention(retentionpolicy.runtime)//生命周期
@documented
public @interface fcontroller {
string value() default "";
}
package framework.annotation;
import java.lang.annotation.*;
@target({elementtype.method, elementtype.type})//这个注解的使用范围
@retention(retentionpolicy.runtime)//生命周期
@documented
public @interface frequestmapping {
string value() default "";
}
package framework.annotation;
import java.lang.annotation.*;
@target(elementtype.parameter)//这个注解的使用范围
@retention(retentionpolicy.runtime)//生命周期
@documented
public @interface frequestparam {
string value() default "";
}
package framework.annotation;
import java.lang.annotation.*;
@target(elementtype.type)//这个注解的使用范围
@retention(retentionpolicy.runtime)//生命周期
@documented
public @interface fservice {
string value() default "";
}
这个是关键.所有的功能全包含在这里.
dispatcherservlet查看顺序,先看init方法(里面方法步骤一个一个看).init方法执行完毕,则spring容器也就启动完毕.
再看service方法,如何处理请求.一个请求到达的时候,根据uri去handlermapping用适合的方法处理请求.
package framework;
import framework.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 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;
/**
* fdispatcherservlet.
*
* @author feng yongkang, 2019/9/9
* @version v1.0
*/
public class fdispatcherservlet1 extends httpservlet {
//resources下的配置文件中的内容
private properties contextconfig = new properties();
//指定要扫描路径的下的类名的集合
private list<string> classnames = new arraylist<string>();
//classnames中被注解标识为bean的类实例
private map<string, object> ioc = new hashmap<string, object>();
//保存所有url与映射的关系
private list<handler> handlermappping = new arraylist<handler>();
@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 {
try {
dodispatch(req, resp);
} catch (exception e) {
e.printstacktrace();
resp.getwriter().write("500 exception");
}
}
/**
* 根据uri得到匹配的handler.根据handler中的paramindexmapping填充方法中的参数.
* 参数是实参,根据请求中的数据获取的.
* 通过代理模式,执行ioc容器下保存的controller的bean对应的uri的方法.完成请求处理.
*
* @param req
* @param resp
* @throws ioexception
*/
private void dodispatch(httpservletrequest req, httpservletresponse resp) throws exception {
try {
//查找与请求匹配的handler
handler handler = gethandler(req);
if (handler == null) {
resp.getwriter().write("404 not found");
return;
}
//获得方法的形参类型
class<?>[] parametertypes = handler.method.getparametertypes();
//在调用invoke代理时,根据参数的位置,传递参数用.
object[] paramvalues = new object[parametertypes.length];
//获得请求中的所有参数,并遍历,看请求中参数的key是否出现
map<string, string[]> params = req.getparametermap();
for (map.entry<string, string[]> param : params.entryset()) {
string value = arrays.tostring(param.getvalue()).replaceall("\\[|\\]", "").replaceall("/+", "/");
//如果找到对象开始填充参数值
if (!handler.paramindexmapping.containskey(param.getkey())) {
continue;
}
integer index = handler.paramindexmapping.get(param.getkey());
if (index != null) {
paramvalues[index] = convert(parametertypes[index], value);
}
}
//设置方法中的request与response对象,如果方法中有这个key就返回对应的value如果没有就返回null
integer reqindex = handler.paramindexmapping.get(httpservletrequest.class.getname());
if (reqindex != null) {
paramvalues[reqindex] = req;
}
integer respindex = handler.paramindexmapping.get(httpservletresponse.class.getname());
if (respindex != null) {
paramvalues[respindex] = resp;
}
handler.method.invoke(handler.controller, paramvalues);
} catch (illegalaccessexception e) {
e.printstacktrace();
} catch (illegalargumentexception e) {
e.printstacktrace();
} catch (invocationtargetexception e) {
e.printstacktrace();
}
}
@override
public void init(servletconfig config) throws servletexception {
//1 加载配置文件
doloadconfig(config.getinitparameter("contextconfiglocation"));
//2 扫描所有相关的类
doscanner(contextconfig.getproperty("scanpackage"));
//3 初始化所有相关的类
doinstance();
//4 自动注入.到这里spring的功能已经完成
doautowired();
//5 初始handlermapping,这里开始就是springmvc的功能
dohandlermapping();
system.out.println("spring初始化完成");
}
/**
* 遍历ioc容器的中bean的方法.private的方法一般不作为请求.
* public类型的被fautowried修饰.根据类上面的注解和方法上面的注解,生成uri转为正则方便后续的匹配.
* uri的正则,controller,还有对应的方法组成一个handler.放入list集合中.方便请求时生成代理,处理请求.
*/
private void dohandlermapping() {
if (ioc.isempty()) {
return;
}
for (map.entry<string, object> entry : ioc.entryset()) {
class<?> claszz = entry.getvalue().getclass();
if (!claszz.isannotationpresent(fcontroller.class)) {
continue;
}
string baseurl = "";
//获取公用uri
if (claszz.isannotationpresent(frequestmapping.class)) {
frequestmapping annotation = claszz.getannotation(frequestmapping.class);
baseurl = annotation.value();
}
//扫描所有公共方法
for (method method : claszz.getmethods()) {
if (!method.isannotationpresent(frequestmapping.class)) {
continue;
}
frequestmapping annotation = method.getannotation(frequestmapping.class);
//如果用户的frequestmapping注解中没有/,在类注解和方法注解中,拼接的时候给他加上,防止用户加了,在拼接后的uri中,把多于一个/换成一个/
string methodurl = ("/" + baseurl + "/" + annotation.value()).replaceall("/+", "/");
//handlermapping.put(methodurl, method);
pattern pattern = pattern.compile(methodurl);
//pattern:uri的正则匹配格式,method:处理对应请求的方法,entry.getvalue():方法所在的实例化后放在ioc中的bean(controller实例)
handlermappping.add(new handler(entry.getvalue(), method, pattern));
}
}
}
/**
* 迭代ioc容器中所有bean的字段.如果这个字段有fautowried修饰.
* 1.优先根据注解的别名去ioc容器中查找对应的实例进行注入.
* 2.字段所属类型的类型全名(一般写的都是接口类型)
* 3.没有考虑controller通过接口的实现进行声明而不是接口类型进行声明.
*/
private void doautowired() {
if (ioc.isempty()) {
return;
}
//循环ioc中的所有类,然后对需要赋值的属性进行赋值
for (map.entry<string, object> entry : ioc.entryset()) {
//获得bean实例中所有字段,判断字段是否有@autowried,
// 如果有,优先注解自定义的属性名,默认字段对应的类型名(在放入ioc时,对于接口类型,直接接口的全名作为key,对于接口实现类,类名首字母小写作为类型名)
field[] declaredfields = entry.getvalue().getclass().getdeclaredfields();
for (field declaredfield : declaredfields) {
if (!declaredfield.isannotationpresent(fautowried.class)) {
continue;
}
fautowried annotation = declaredfield.getannotation(fautowried.class);
string beanname = annotation.value().trim();
if ("".equals(beanname)) {
beanname = declaredfield.gettype().getname();
}
//根据key去容器中查找,注入到字段中.
boolean accessible = declaredfield.isaccessible();
declaredfield.setaccessible(true);
try {
declaredfield.set(entry.getvalue(), ioc.get(beanname));//注入
} catch (illegalaccessexception e) {
e.printstacktrace();
}
declaredfield.setaccessible(accessible);
}
}
}
/**
* 把classnames中保存的符合的作为bean的类进行实例化并放入到容器中
* 生成的bean的命名规则1.默认类名首字母小写 2.优先使用自定义名字 3.如果是一个接口,接口全名作为key,
* 如果一个接口有多个实例就会报错,体现在.查看这个实例的接口名是否已经存在,已经存在则说明,这个接口有别的实现类.
*/
private void doinstance() {
if (classnames.isempty()) {
return;
}
try {
for (string classname : classnames) {
class<?> clazz = class.forname(classname);
//实例化加了注解的类
//controller注解的类实例化
if (clazz.isannotationpresent(fcontroller.class)) {
string beanname = lowerfirstcase(clazz.getsimplename());
ioc.put(beanname, clazz.newinstance());
//service注解的类的实例化
} else if (clazz.isannotationpresent(fservice.class)) {
//获得这个注解,看注解中是否有自定义的注解名,自定义的优先,然后是默认的类名首字符小写
//1.自定义的beanname
fservice annotation = clazz.getannotation(fservice.class);
string beanname = annotation.value();
//2.默认首字母小写的beanname
if ("".equals(beanname)) {
beanname = lowerfirstcase(clazz.getsimplename());
}
object instance = clazz.newinstance();
ioc.put(beanname, instance);
//3.这个类如果有接口.
//getinterfaces获得这个类所实现的所有接口,如果这个类对应的接口的getname已经存在,证明,这个接口不止自己一个实现类.
//一个接口如果有多个实现类,在注入的时候不知道注入哪一个,如果存在这种情况抛出异常.不存在就以接口名为key,存入实现类的实例.
for (class<?> aninterface : clazz.getinterfaces()) {
if (ioc.containskey(aninterface.getname())) {
throw new exception("the beanname is exists");
}
ioc.put(aninterface.getname(), instance);
}
} else {
continue;
}
}
} catch (classnotfoundexception e) {
e.printstacktrace();
} catch (illegalaccessexception e) {
e.printstacktrace();
} catch (instantiationexception e) {
e.printstacktrace();
} catch (exception e) {
e.printstacktrace();
}
}
/**
* 把指定的路径下所有的class类的类名保存到classnames集合中.
*
* @param scanpackage 要扫描的包路径
*/
//得到所有的class.
private void doscanner(string scanpackage) {
//要把所有的文件路径处理成包路径
url url = this.getclass().getclassloader().getresource("/" + scanpackage.replaceall("\\.", "/"));
file classdir = new file(url.getfile());
for (file file : classdir.listfiles()) {
if (file.isdirectory()) {
doscanner(scanpackage + "." + file.getname());
} else {
if (!file.getname().endswith(".class")) {
//如果不是class文件进行下一次循环.
continue;
}
string classname = scanpackage + "." + file.getname().replace(".class", "");
classnames.add(classname);
}
}
}
/**
* 加载外部的配置文件.这里用比较简单的properties;key-value类型的对象.
*
* @param contextconfiglocation 对应web.xml中的配置的资源文件的路径
*/
private void doloadconfig(string contextconfiglocation) {
inputstream is = this.getclass().getclassloader().getresourceasstream(contextconfiglocation);
try {
contextconfig.load(is);
} catch (ioexception e) {
e.printstacktrace();
} finally {
if (null != is) {
try {
is.close();
} catch (ioexception e) {
e.printstacktrace();
}
}
}
}
/**
* 把一个首字符大写的字符串转为首字母小写的字符串.
*
* @param str 首字母大写的字符串
* @return 首字母小写的字符串
*/
private string lowerfirstcase(string str) {
char[] chars = str.tochararray();
chars[0] += 32;
return string.valueof(chars);
}
/**
* 根据请求路径获得与路径匹配的handler
*
* @param req
* @return
* @throws exception
*/
private handler gethandler(httpservletrequest req) throws exception {
if (handlermappping.isempty()) {
return null;
}
//绝对路径
string requesturi = req.getrequesturi();
//绝对路径中uri之外的路径
string contextpath = req.getcontextpath();
requesturi.replace(contextpath, "").replaceall("/+", "/");
for (handler handler : handlermappping) {
try {
//比较请求中的url与存在handler中的是否匹配
matcher matcher = handler.pattern.matcher(requesturi);
//如果没有继续下一个匹配
if (!matcher.matches()) {
continue;
}
return handler;
} catch (exception e) {
throw e;
}
}
return null;
}
private object convert(class<?> type, string value) {
if (integer.class == type) {
return integer.valueof(value);
}
return value;
}
/**
* handler 记录conroller中的requestmapping和method的对应关系
*/
private class handler {
protected object controller;
protected method method;
protected pattern pattern;
protected map<string, integer> paramindexmapping;//参数的别名,或者类型名作为key,位置作为value.
public handler(object controller, method method, pattern pattern) {
this.controller = controller;//保存方法对应的实例
this.method = method;//保存映射的方法
this.pattern = pattern;
paramindexmapping = new hashmap<string, integer>();//参数顺序
putparamindexmapping(method);
}
/**
* 把controller中方法中的参数有注解的根据注解名,或者request response类型的参数放入到map中
* 只考虑有frequestparam注解的别名与request,response的类型名.作为key,在参数中的位置作为value.
* 在调用invoke方法的时候,可以按照参数顺序进行传参.
*
* @param method
*/
private void putparamindexmapping(method method) {
//1.一个参数可能有很多注解,一个方法有可能有很多参数,所以返回一个注解的二维数组.如果注解里面指定了参数的参数的别名,就把参数别名作为key,参数位置作为value放入map.
//这里仅仅考虑frequestparam注解,像验证,requestbody,没有注解没有考虑.
annotation[][] pa = method.getparameterannotations();
for (int i = 0; i < pa.length; i++) {
for (annotation a : pa[i]) {
if (a instanceof frequestparam) {
string paranname = ((frequestparam) a).value();
if (!"".equals(paranname.trim())) {
paramindexmapping.put(paranname, i);
}
}
}
}
//2.对于 request与response,默认放入根据类型名与参数位置放入map
class<?>[] parametertypes = method.getparametertypes();
for (int i = 0; i < parametertypes.length; i++) {
class<?> type = parametertypes[i];
if (type == httpservletrequest.class || type == httpservletresponse.class) {
paramindexmapping.put(type.getname(), i);
}
}
}
}
}
如对本文有疑问, 点击进行留言回复!!
Postgresql结合postgis使用java的JDBC连接
网友评论