项目地址:https://github.com/gongxianshengjiadexiaohuihui/noobspring
准备工作:
这个项目是一个maven项目,pom文件内容
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ggp.noobspring</groupId>
<artifactId>noobspring</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<servlet.api.version>2.4</servlet.api.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet.api.version}</version>
<scope>provided</scope>
</dependency>
<!--日志文件配置-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<!--Cglib动态代理-->
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.2</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.26</version>
<configuration>
<contextPath>noobspring</contextPath>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8090</port>
</connector>
</connectors>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
关于spring的包就一个,java.servlet的api
然后是日志文件的包,至于为什么这样写,可以参考我的一篇博客设计模式学习笔记---------------------------------------------门面模式(外观模式)和日志框架(sl4j、logback、log4j
后面是cglib代理需要的包在aop中会用到
然后是jetty的包,我们的web容器是jetty
接着是配置maven的编译插件,因为这个项目需要用到方法的形参,所以需要设置一个编译参数 -parameters 如果你不用maven编译的话,需要在IDE中设置这个编译参数IDEA中的位置
我们知道spring项目首先会加载web.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_4.xsd"
version="2.4">
<display-name>Noob Web Application</display-name>
<servlet>
<servlet-name>nbmvc</servlet-name>
<servlet-class>com.ggp.framework.servlet.NBDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>nbmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
实现spring的第一步是重写DispatcherServlet 继承HttpServlet,我们重写它的名字是NBDispatcherServlet,并且把它配置到了web.xml中去,并且配置了它的一个初始化参数contextConfigLocation参数,参数的值是我们配置文件所在的位置,我们配置文件的
scanPackage=com.ggp
我们看到下面还配置了一个servlet-mapping从字面的意思我们知道它是一个映射,而且通过servlet-name属性可以知道这个映射的属性指向nbmvc这个我们自己写的servlet,而url-pattern这个属性是一个正则表达式把符合表达式的请求路径分配到此servlet中去。
重写DispatcherServlet时,我们需要重写两个重要的方法
一个是init,这个方法会在DispatcherServlet初始化的时候首先加载
@Override
public void init(ServletConfig config) throws ServletException {
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>开始初始化noobspring");
try{
/**
* 加载配置文件 config来自父类
*/
doLoadConfig(config.getInitParameter(LOCATION));
/**
* 扫描相关的类 (从配置文件中读取扫描包的范围)把扫描到的类名保存到className中
*/
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>开始扫描类");
doScanner(p.getProperty("scanPackage"));
logger.debug("扫描到的类:{}",classNames.toString());
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>扫描成功");
/**
* 通过反射初始化className中的所有类,保存到ioc容器中(ioc容器的key默认是类名字母小写)
*/
doInstance();
/**
* 织入切点
*/
doAspect();
/**
* 依赖注入
*/
doAutowired();
/**
* 构造handlerMapping
*/
initHandlerMapping();
}catch (Exception e){
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>初始化noobspring失败");
e.printStackTrace();
throw e;
}
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>初始化noobspring完成");
}
我们看一下加载的流程
首先是加载配置文件doLoadConfig(),通过获取servlet的初始化参数contextConfigLocation,获取配置所在的路径,然后进行解析并保存
/**
* 保存所有配置信息
*/
private Properties p = new Properties();
接着是扫描相关的类
doScanner(),通过配置文件信息p获取扫描包的路径,然后对应路径下的所有类的名字保存起来,保存位置
private List<String> classNames = new ArrayList<String>();
接着就是spring的核心IOC容器
doInstance(),通过我们扫描到的类名,有选择的根据java的反射机制,生成类实例,并保存起来,保存位置
private Map<String,Object> ioc = new HashMap<String, Object>();
接着就是AOP织入切点,doAspect(),这里要提及的一点,包括前面的ioc很关键的一点就是通过注解来进行标记,标记哪些类需要生成实例,那些方法需要织入通知,以及那些字段需要进行依赖注入。
DI依赖注入,doAutowired(),也是通过反射的方法,进行注入。
接着就是进行initHandlerMapping方法了,将请求路径和处理方法映射关联在一起,保存在
private Map<String,Method> handlerMapping = new HashMap<String, Method>();