致谢:首先感谢Tom老师,看了Tom老师的视频后,对Spring的实现思路有了更清晰的认识。
首先先总结一下实现Spring功能的一个整体思路。
实现Spring基本需要经过一下步骤:
配置阶段–>初始化阶段–>运行阶段
大致流程了解之后我们再细化每个阶段。
1、配置阶段:
我们需要在web.xml中配置Servlet,init-param,url-pattern,以及新建自定义注解(实现Spring过程中,不需要引入Spring依赖,注解也需要自己自定义)
2、初始化阶段:
配置阶段完成之后,我们需要在初始画阶段做以下处理:
3、运行阶段
用户请求后调用doGet/doPost方法,
拿到请求地址,取出requestMapping路径,
拿resquestMapping路径与HandlerMapping匹配,
反射调用method.invoke(),
返回前端
配置阶段:
1、配置application.properties
#要扫描的包#
scanPackage=com.nxw.demo
2、配置web.xml
MySpring
com.nxw.spring.webmvc.NDispatcherServlet
contextConfigLocation
classpath:application.properties
1
MySpring
/*
3、自定义注解
/**
* Cntroller注解
* @author Silence
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NController {
String value() default "";
}
/**
* 业务逻辑注入注解
* @author Silence
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NService {
String value() default "";
}
/**
* 请求url
* @author Silence
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NRequestMapping {
String value() default "";
}
/**
* 请求参数映射
* @author Silence
*
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NRequestParam {
String value() default "";
boolean required() default true;
}
/**
* 自动注入注解
* @author Silence
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NAutowired {
String value() default "";
}
初始化阶段:
1、读取配置文件
private void doLoadConfig(String contextConfigLocation) {
//classpath下找到application.properties配置文件,并且读取出来
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:",""));
try {
contextConfig.load(is);
} catch (IOException e) {
System.err.println("没有找到配置文件");
e.printStackTrace();
}finally {
if(null != is){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2、初始化IOC容器 就是创建一个map
private Map ioc = new HashMap();
3、扫描相关的类
private void doScanner(String scanPackage) {
URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll(("\\."),"/"));
File classpath = new File(url.getFile());
for (File file : classpath.listFiles()) {
//判断是不是文件夹,如果是递归
if(file.isDirectory()){
doScanner(scanPackage + "." + file.getName());
}else {
//判断是不是以.class结尾的文件
if(!file.getName().endsWith(".class")) {continue;}
//拼接全类名,可以通过Class.forName()
String className = (scanPackage + "." + file.getName().replace(".class", ""));
//存入list
classNames.add(className);
}
}
}
4、实例化相关的类,并且缓存到IOC容器中
private void doInstance() {
//扫描刚刚扫描的类
if(classNames.isEmpty()){return;}
for (String className : classNames) {
try {
Class clazz = Class.forName(className);
if(clazz.isAnnotationPresent(NController.class)) {
String beanName = toLowerFirstCase(clazz.getSimpleName());
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
}else if(clazz.isAnnotationPresent(NService.class)){
String beanName = toLowerFirstCase(clazz.getSimpleName());
//自定义beanName,不同包下相同类名
NService service = (NService) clazz.getAnnotation(NService.class);
if(!"".equals(service.value())){
beanName = service.value();
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
//如果是接口,用它的实现类赋值
for (Class i : clazz.getInterfaces()) {
if (ioc.containsKey(i.getName())){
throw new Exception("This beanName already exists!");
}
//匹配接口类型
ioc.put(i.getName(),instance);
}
}else{
continue;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5、完成依赖注入
private void doAutowrited() {
if(ioc.isEmpty()){return;}
for (Map.Entry entry : ioc.entrySet()) {
//把所有的private,public,default,prodected都拿到
Field fields [] = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if(!field.isAnnotationPresent(NAutowired.class)){
continue;
}
NAutowired autowired = field.getAnnotation(NAutowired.class);
String beanName = autowired.value().trim();
if("".equals(beanName.trim())){
beanName = field.getType().getName();
}
//暴力访问,可以通过反射拿到所有的private,public,default,prodected
field.setAccessible(true);
try {
field.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
6、初始化HandlerMapping
private void doHandlerMapper() {
if(ioc.isEmpty()){return;}
for (Map.Entry entry : ioc.entrySet()) {
Class> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(NController.class)){continue;}
String baseUrl = null;
if(clazz.isAnnotationPresent(NRequestMapping.class)){
NRequestMapping requestMapping = clazz.getAnnotation(NRequestMapping.class);
baseUrl = requestMapping.value();
}
for (Method method : clazz.getMethods()) {
if(!method.isAnnotationPresent(NRequestMapping.class)){continue;}
NRequestMapping requestMapping = method.getAnnotation(NRequestMapping.class);
String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
handleMapping.put(url,method);
System.out.println("Mapping : " + url + " , "+method);
}
}
}
运行阶段:
doPost()
//7、调用具体的方法
String url = req.getRequestURL().toString();
String contextPath = req.getContextPath();
String url1 = url.replaceAll("http://localhost:8080","");
System.out.println("URL : "+url);
if(!this.handleMapping.containsKey(url1)){
resp.getWriter().write("404 Not Found!");
return;
}
Map params = req.getParameterMap();
Method method = this.handleMapping.get(url1);
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
method.invoke(ioc.get(beanName),new Object[] {req,resp,params.get("name")[0],params.get("addr")[0]});
至此Spring基本功能已经实现。
源码请到 https://download.csdn.net/download/nxw_tsp/12646030 下载