spring源码第一天,300行代码体验spring核心

一:实现思路

spring源码第一天,300行代码体验spring核心_第1张图片

spring源码第一天,300行代码体验spring核心_第2张图片

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

你可能感兴趣的:(spring)