听了tom老师的手写spring直播之后,今日用代码实现了手写spring框架,并没有实现aop功能
1)配置阶段
配置 web.xml:
MDispatchServlet
设定 init-param: contextConfigLocation = applicationContext.properties
设定 url-pattern: /*
配置 Annotation: @MController @MService @MAutowired @MRequestMapping
2)初始化阶段
IOC:
调用 init() 方法: 加载配置文件
IOC 容器初始化: Map<String, Object>
扫描相关的类: scan-package=“com.matao.spring”
创建实例化并保存到容器: 同过反射机制将类实例化放入 IOC 容器中
DI:
进行 DI 操作: 扫描 IOC 容器中的实例,给没有赋值的属性自动赋值
MVC:
初始化 HandlerMapping: 将一个 URL 和一个 Method 进行一对一的关联映射 Map<String, Method>
3)运行阶段
调用 doGet() / doPost() 方法: Web 容器调用 doGet() / doPost() 方法,获得 request/response 对象
匹配 HandleMapping: 从 request 对象中获得用户输入的 url,找到其对应的 Method
反射调用 method.invoker(): 利用反射调用方法并返回结果
response.getWrite().write(): 将返回结果输出到浏览器
项目:
pom文件:`
<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.bjsxt</groupId>
<artifactId>springmvc-ac</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<!--添加servlet依赖 -->
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<path>/</path>
<port>8080</port>
</configuration>
</plugin>
</plugins>
</build>
</project>
如果项目启动报ClassCastException异常,要加上 provided
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/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>mmvc</servlet-name>
<servlet-class>com.matao.spring.MDispatchServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--you can't use classpath*: -->
<param-value>application.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
application.properties代码
application.properties
注解: MAutowired.java代码
/**
*
*/
package com.matao.spring.annotion;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MAutowired {
String value() default "";
}
MController.java代码
/**
*
*/
package com.matao.spring.annotion;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author MT
*
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MController {
String value() default "";
}
MRequsetMapping.java代码
/**
*
*/
package com.matao.spring.annotion;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author MT
*
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MRequsetMapping {
String value() default "";
}
MService.java代码:
/**
*
*/
package com.matao.spring.annotion;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author MT
*
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MService {
String value() default "";
}
service接口代码:
/**
*
*/
package com.matao.spring.test.service;
/**
* @author MT
*
*/
public interface ITestService {
/**
* @return
*/
public String listClassName();
}
serviceImpl代码:
/**
*
*/
package com.matao.spring.test.service;
import com.matao.spring.annotion.MService;
/**
* @author MT
*
*/
@MService
public class TestServiceImpl implements ITestService{
/* (non-Javadoc)
* @see com.matao.spring.test.service.ITestService#listClassName()
*/
@Override
public String listClassName() {
// TODO Auto-generated method stub
return "classname is found";
}
}
Controller代码:
/**
*
*/
package com.matao.spring.test;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.matao.spring.annotion.MAutowired;
import com.matao.spring.annotion.MController;
import com.matao.spring.annotion.MRequsetMapping;
import com.matao.spring.test.service.ITestService;
/**
* @author MT
*
*/
@MController
@MRequsetMapping("/test")
public class MyController {
@MAutowired
ITestService testService;
/**
* 测试方法 /test/query
*
* @param req 请求体
* @param resp 响应体
*/
@MRequsetMapping("/query")
public void query(HttpServletRequest req, HttpServletResponse resp) {
if (req.getParameter("username") == null) {
try {
resp.getWriter().write("param username is null");
} catch (IOException e) {
e.printStackTrace();
}
} else {
String paramName = req.getParameter("username");
try {
resp.getWriter().write("param username is " + paramName);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("[INFO-req] New request param username-->" + paramName);
}
}
/**
* 测试方法 /test/listClassName
*
* @param req 请求体
* @param resp 响应体
*/
@MRequsetMapping("/listClassName")
public void listClassName(HttpServletRequest req, HttpServletResponse resp) {
String str = testService.listClassName();
System.out.println("testXService----------=-=-=>" + str);
try {
resp.getWriter().write(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
MDisptchServlet代码,主要代码
/**
*
*/
package com.matao.spring;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.matao.spring.annotion.MAutowired;
import com.matao.spring.annotion.MController;
import com.matao.spring.annotion.MRequsetMapping;
import com.matao.spring.annotion.MService;
/**
* @author MT
*
*/
public class MDispatchServlet extends HttpServlet{
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 属性配置文件
*/
private Properties contextConfig = new Properties();
private List<String> classNameList = new ArrayList<>();
/**
* IOC容器
*/
Map<String, Object> iocMap = new HashMap<>();
Map<String, Method> handlerMapping = new HashMap<>();
@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 {
doDispatch(req,resp);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
resp.getWriter().write("500 Exception Detail\n " + Arrays.toString(e.getStackTrace()));
}
}
/**
* @param req
* @param resp
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!this.handlerMapping.containsKey(url)) {
try {
resp.getWriter().write("404 NOT FOUND");
return;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Method method = this.handlerMapping.get(url);
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
method.invoke(iocMap.get(beanName), req,resp);
}
@Override
public void init(ServletConfig config) throws ServletException {
//加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//扫描相关类
doScanner(contextConfig.getProperty("scan-package"));
//初始化IOC容器,将所有的相关类实例化 并放入到IOC容器中
doInstance();
//依赖注入
doAutowired();
//初始化HandlerMapping,实现一对一
initHandlerMapping();
System.out.println("Spring IOC is start");
//打印数据
doTestPrintData();
}
/**
* 实现初始化HandlerMapping一对一映射功能
*/
private void initHandlerMapping() {
System.out.println("initHandlerMapping");
if (iocMap.isEmpty()) {
System.out.println("ioc is empty");
return;
}
for(Entry<String, Object> entry : iocMap.entrySet()){
Class<?> clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(MController.class)) {
continue;
}
String baseUrl = "";
if (clazz.isAnnotationPresent(MRequsetMapping.class)) {
MRequsetMapping mRequestMapping = clazz.getAnnotation(MRequsetMapping.class);
baseUrl = mRequestMapping.value();
}
for(Method method : clazz.getMethods()){
if (!method.isAnnotationPresent(MRequsetMapping.class)) {
continue;
}
MRequsetMapping mRequestMapping = method.getAnnotation(MRequsetMapping.class);
String url = (baseUrl + mRequestMapping.value().replaceAll("/+", "/"));
handlerMapping.put(url, method);
}
}
}
/**
* 实现依赖注入功能
*/
private void doAutowired() {
if (iocMap.isEmpty()) {
return;
}
for(Entry<String,Object> entry : iocMap.entrySet()){
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for(Field field : fields){
if (!field.isAnnotationPresent(MAutowired.class)) {
continue;
}
MAutowired mAutowired = field.getAnnotation(MAutowired.class);
String beanName = mAutowired.value().trim();
//获取XAutowired注解的值
if ("".equals(beanName)) {
beanName = field.getType().getName();
}
//只要加了注解 都要加载 不管是private 还是protect
field.setAccessible(true);
try {
field.set(entry.getValue(), iocMap.get(beanName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 实现初始化容器功能
*/
private void doInstance() {
System.out.println("doinstance");
if (classNameList.isEmpty()) {
System.out.println("classNamelist is empty");
return;
}
try {
for(String className : classNameList){
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(MController.class)) {
//将类名首字母小写,默认bean是将首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
System.out.println("beanname is " + beanName );
Object instance = clazz.newInstance();
//添加到IOC容器中
iocMap.put(beanName, instance);
}else if (clazz.isAnnotationPresent(MService.class)) {
String beanName = toLowerFirstCase(clazz.getSimpleName());
//如果注解中含有自定义名称
MService mService = clazz.getAnnotation(MService.class);
if (!"".equals(mService.value())) {
beanName = mService.value();
}
Object instance = clazz.newInstance();
iocMap.put(beanName, instance);
//寻找类的接口
for(Class<?> i : clazz.getInterfaces()){
if (iocMap.containsKey(i.getName())) {
throw new Exception("The Bean Name Is Exist.");
}
iocMap.put(i.getName(), instance);
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 实现扫描相关类功能
* @param property
*/
private void doScanner(String scanPackage) {
//com.matao.spring... ==> /com/matao/spring/...
URL url = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.", "/"));
System.out.println("page is " + url);
//判断url
if (url == null) {
return;
}
//获取路径下的文件
File classPath = new File(url.getFile());
for (File file : classPath.listFiles()) {
if (file.isDirectory()) {
//递归遍历子目录
System.out.println("is directory");
doScanner(scanPackage + "." + file.getName());
}else {
if (!file.getName().endsWith(".class")) {
continue;
}
String className = (scanPackage + "." + file.getName().replace(".class", ""));
//保存
classNameList.add(className);
}
}
}
/**
* 实现加载配置文件功能
* @param initParameter
*/
private void doLoadConfig(String contextConfigLocation) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(is);
System.out.println("property file has been loaded in properties");
} catch (IOException e) {
e.printStackTrace();
}finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 实现将首字母小写方法
* @param className
* @return
*/
private String toLowerFirstCase(String className){
char[] chars = className.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
/**
* 、打印数据
*/
private void doTestPrintData() {
System.out.println("[classNameList]-->");
for (String str : classNameList) {
System.out.println(str);
}
System.out.println("[iocMap]-->");
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
System.out.println(entry);
}
System.out.println("[handlerMapping]-->");
for (Map.Entry<String, Method> entry : handlerMapping.entrySet()) {
System.out.println(entry);
}
}
}
测试结果: