一:实现思路
pom文件
4.0.0
com.gupaoedu.vip.gpmvc
gupaoedu-vip-spring
1.0
war
2.4
javax.servlet
servlet-api
${servlet.api.version}
provided
${artifactId}
${basedir}/src/main/resources
**/*
${basedir}/src/main/java
**/*.java
**/*.class
maven-compiler-plugin
2.3.2
1.6
1.6
UTF-8
${java.home}/lib/rt.jar
maven-resources-plugin
2.5
copy-resources
validate
copy-resources
UTF-8
${basedir}/target/classes
src/main/resources
**/*.*
true
org.mortbay.jetty
maven-jetty-plugin
6.1.26
src/main/resources/webdefault.xml
/
8080
0
src/main/webapp
**/*.xml
**/*.properties
javax.xml.parsers.DocumentBuilderFactory
com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
javax.xml.parsers.SAXParserFactory
com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
javax.xml.transform.TransformerFactory
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
org.eclipse.jetty.util.URI.charset
UTF-8
org.apache.maven.plugins
maven-war-plugin
2.2
false
src/main/resources/
WEB-INF/classes
**/*.*
true
src/main/resources
WEB-INF/classes
true
org.zeroturnaround
javarebel-maven-plugin
generate-rebel-xml
process-resources
generate
1.0.5
org.eclipse.m2e
lifecycle-mapping
1.0.0
org.zeroturnaround
javarebel-maven-plugin
[1.0.5,)
generate
二:配置阶段
1:配置web.xml,设定init-param,设定url-pattern
Gupao Web Application
gpmvc
com.gupaoedu.mvcframework.servlet.v2.GPDispatcherServlet
contextConfigLocation
application.properties
1
gpmvc
/*
2:配置Annotation
(1)自定义@GPAutowired
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPAutowired {
String value() default "";
}
(2)自定义@GPController
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPController {
String value() default "";
}
(3)自定义@GPRequestMapping
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestMapping {
String value() default "";
}
(4)自定义@GPRequestParam
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestParam {
String value() default "";
}
(5)自定义@GPService
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPService {
String value() default "";
}
二:初始化阶段
配置文件application.properties
scanPackage=com.gupaoedu.demo
1:创建GPDispatcherServlet并继承HttpServlet
2:重写doGet,doSet,init方法
3:在init方法中创建方法,理清流程
package com.gupaoedu.mvcframework.servlet.v2;
import com.gupaoedu.mvcframework.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.Method;
import java.net.URL;
import java.util.*;
/**
* Created by Tom.
*/
public class GPDispatcherServlet2 extends HttpServlet {
//保存application.properties配置文件中的内容
private Properties contextConfig = new Properties();
//保存扫描的所有的类名
private List classNames = new ArrayList();
//传说中的IOC容器,我们来揭开它的神秘面纱
//为了简化程序,暂时不考虑ConcurrentHashMap
// 主要还是关注设计思想和原理
private Map ioc = new HashMap();
//保存url和Method的对应关系
private Map handlerMapping = new HashMap();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
public void init() throws ServletException {
//工厂类 GPApplicationContext IOC、DI
//=========== IOC ===========
//1、加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2、扫描相关的类
doScanner(contextConfig.getProperty("scanPackage"));
//3、初始化扫描到的类,并且放入到IOC容器之中
doInstance();
//========= DI =========
//4、完成自动化的依赖注入
doAutowired();
//======= MVC =============
//5、初始化HandlerMapping
doInitHandlerMapping();
System.out.println("GP Spring framework is init.");
}
}
4:加载配置文件
//加载配置文件
private void doLoadConfig(String contextConfigLocation) {
//直接从类路径下找到Spring主配置文件所在的路径
//并且将其读取出来放到Properties对象中
//相对于scanPackage=com.gupaoedu.demo 从文件中保存到了内存中
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();
}
}
}
}
6:扫描相关的类
//扫描出相关的类
private void doScanner(String scanPackage) {
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.","/"));
//scanPackage = com.gupaoedu.demo ,存储的是包路径
//转换为文件路径,实际上就是把.替换为/就OK了
//classpath下不仅有.class文件, .xml文件 .properties文件
File classPath = new File(url.getFile());
for (File file : classPath.listFiles()) {
if(file.isDirectory()){
doScanner(scanPackage + "." + file.getName());
}else {
//变成包名.类名
//Class.forname()
if (!file.getName().endsWith(".class")) { continue; }
classNames.add(scanPackage + "." + file.getName().replace(".class", ""));
}
}
}
7:初始化扫描到的类,并且放入到IOC容器之中
private void doInstance() {
if(classNames.isEmpty()){return;}
try {
for (String className : classNames) {
Class> clazz = Class.forName(className);
//什么样的类才需要初始化呢?
//加了注解的类,才初始化,怎么判断?
//为了简化代码逻辑,主要体会设计思想,只举例 @Controller和@Service,
// @Componment...就一一举例了
if(clazz.isAnnotationPresent(GPController.class)){
Object instance = clazz.newInstance();
String beanName = toLowerFirstCase(clazz.getSimpleName());
//key-value
//class类名的首字母小写
ioc.put(beanName,instance);
}else if(clazz.isAnnotationPresent(GPService.class)) {
//1、默认就根据beanName类名首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
//2、使用自定义的beanName
GPService service = clazz.getAnnotation(GPService.class);
if(!"".equals(service.value())){
beanName = service.value();
}
Object instance = clazz.newInstance();
ioc.put(beanName,instance);
//3、根据包名.类名作为beanName
for (Class> i : clazz.getInterfaces()) {
if(ioc.containsKey(i.getName())){
throw new Exception("The beanName is exists!!");
}
//把接口的类型直接当成key了
ioc.put(i.getName(),instance);
}
}else {
continue;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
//如果类名本身是小写字母,确实会出问题
//但是我要说明的是:这个方法是我自己用,private的
//传值也是自己传,类也都遵循了驼峰命名法
//默认传入的值,存在首字母小写的情况,也不可能出现非字母的情况
//为了简化程序逻辑,就不做其他判断了,大家了解就OK
//其实用写注释的时间都能够把逻辑写完了
private String toLowerFirstCase(String simpleName) {
char [] chars = simpleName.toCharArray();
//之所以加,是因为大小写字母的ASCII码相差32,
// 而且大写字母的ASCII码要小于小写字母的ASCII码
//在Java中,对char做算学运算,实际上就是对ASCII码做算学运算
chars[0] += 32;
return String.valueOf(chars);
}
8:完成自动化依赖注入
private void doAutowired() {
if(ioc.isEmpty()){return;}
for (Map.Entry entry : ioc.entrySet()) {
//拿到实例的所有的字段
//Declared 所有的,特定的 字段,包括private/protected/default
//正常来说,普通的OOP编程只能拿到public的属性
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if(!field.isAnnotationPresent(GPAutowired.class)){
continue;
}
GPAutowired autowired = field.getAnnotation(GPAutowired.class);
//如果用户没有自定义beanName,默认就根据类型注入
//这个地方省去了对类名首字母小写的情况的判断,这个作为课后作业
//小伙伴们自己去完善
String beanName = autowired.value().trim();
if("".equals(beanName)){
//获得接口的类型,作为key待会拿这个key到ioc容器中去取值
beanName = field.getType().getName();
}
//如果是public以外的修饰符,只要加了@Autowired注解,都要强制赋值
//反射中叫做暴力访问,添加访问权限
field.setAccessible(true);
//反射调用的方式
//给entry.getValue()这个对象的field字段,赋ioc.get(beanName)这个值
try {
field.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
continue;
}
}
}
}
9:初始化HandlerMapping
//初始化url和Method的一对一对应关系
private void doInitHandlerMapping() {
if(ioc.isEmpty()){return;}
for (Map.Entry entry : ioc.entrySet()) {
Class> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(GPController.class)){ continue; }
//保存写在类上面的@GPRequestMapping("/demo")
String baseUrl = "";
if(clazz.isAnnotationPresent(GPRequestMapping.class)){
GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
baseUrl = requestMapping.value();
}
//默认获取所有的public方法
for (Method method : clazz.getMethods()) {
if(!method.isAnnotationPresent(GPRequestMapping.class)){continue;}
GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
//demoquery
// //demo//query
String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
handlerMapping.put(url,method);
System.out.println("Mapped " + url + "," + method);
}
}
}
10:编写DemoAction,进行启动测试
package com.gupaoedu.demo.mvc.action;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gupaoedu.demo.service.IDemoService;
import com.gupaoedu.mvcframework.annotation.GPAutowired;
import com.gupaoedu.mvcframework.annotation.GPController;
import com.gupaoedu.mvcframework.annotation.GPRequestMapping;
import com.gupaoedu.mvcframework.annotation.GPRequestParam;
@GPController
@GPRequestMapping("/demo")
public class DemoAction {
@GPAutowired private IDemoService demoService;
private IDemoService demoService2;
@GPRequestMapping("/query")
public void query(HttpServletRequest req, HttpServletResponse resp,
@GPRequestParam("name") String name,@GPRequestParam("id") String id){
// String result = demoService.get(name);
String result = "My name is " + name + ",id = " + id;
try {
resp.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
@GPRequestMapping("/add")
public void add(HttpServletRequest req, HttpServletResponse resp,
@GPRequestParam("a") Integer a, @GPRequestParam("b") Integer b){
try {
resp.getWriter().write(a + "+" + b + "=" + (a + b));
} catch (IOException e) {
e.printStackTrace();
}
}
@GPRequestMapping("/remove")
public void remove(HttpServletRequest req,HttpServletResponse resp,
@GPRequestParam("id") Integer id){
}
}
源码及视频
链接:https://pan.baidu.com/s/1vBnEZAoI2AEx-0jze4sl9Q
提取码:uwet