手写Spring之spring初体验V1版本

手写Spring之spring初体验V1版本

1.目标及说明

本文讲述spring框架的基础部分,如何用自己的方式手动去实现GPDispatcherServlet,其中配置文件只有一个application.properties。Controller、service、dao部分也不进行赘述。

2.基本思路

手写Spring之spring初体验V1版本

3.源码实现

3.1.自定义配置 application.properties 文件

为了解析方便,我们用application.properties来代替application.xml文 件 ,具体配置内容如下:

scanPackage=com.gupaoedu.demo

3.2.配置web.xml文件

大家都知道,所有依赖于web容器的项目,都是从读取web.xml文件开始的。我们先配置好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"
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">
	<display-name>Gupao Web Application</display-name>
	<servlet>
		<servlet-name>gpmvc</servlet-name>
		<servlet-class>com.gupaoedu.mvcframework.v2.servlet.GPDispatchServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>application.properties</param-value>
		</init-param>

		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>gpmvc</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

其中GPDispatcherServlet是有自己模拟Spring实现的核心功能类。

3.3自定义 Annotation

@GPService 注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPService {
    String value() default "";
}

@GPAutowired 注 解 :

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPAutowired {
    String value() default "";
}

@GPController 注 解 :

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPController {
    String value() default "";
}

@GPRequestMapping 注 解 :

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestMapping {
    String value() default "";
}

@GPRequestParam 注 解 :

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestParam {
    String value() default "";
}

3.4 实现GPDispatcherServlet.java

public class GPDispatcherServlet extends HttpServlet {
    private Properties contextConfig = new Properties();

    //享元模式,缓存
    private List<String> classNames = new ArrayList<String>();

    //ioc容器:key:类名首字母小写,value:对应的实例对象
    private Map<String,Object> ioc = new HashMap<String, Object>();

    private Map<String,Method> handlerMapping = new HashMap<String, Method>();
    @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 {
        //6.委派,根据URL去找到一个对应的Method并通过response返回
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception,Detail :" + Arrays.toString(e.getStackTrace()));
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
        if(!this.handlerMapping.containsKey(url)){
            resp.getWriter().write("404 Not Found!!!");
            return;
        }
        Map<String,String[]> params = req.getParameterMap();
        Method method = this.handlerMapping.get(url);
        Class<?> [] parameterTypes = method.getParameterTypes();
        Object [] paramValues = new Object[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            Class paramterType = parameterTypes[i];
            if(paramterType == HttpServletRequest.class){
                paramValues[i] = req;
            }else if(paramterType == HttpServletResponse.class){
                paramValues[i] = resp;
            }else if(paramterType == String.class){
                //通过运行时的状态拿到注解的值
                Annotation[] [] pa = method.getParameterAnnotations();
                for (int j = 0; j < pa.length ; j ++) {
                    for(Annotation a : pa[j]){
                        if(a instanceof GPRequestParam){
                            String paramName = ((GPRequestParam) a).value();
                            if(!"".equals(paramName.trim())){
                                String value = Arrays.toString(params.get(paramName)).replaceAll("\\[|\\]","")
                                        .replaceAll("\\s,",",");
                                paramValues[i] = value;
                            }
                        }
                    }
                }

            }
        }

        //暂时硬编码
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(beanName),new Object[]{req,resp,params.get("name")});
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //1.加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

        //2.扫描相关的类
        doScanner(contextConfig.getProperty("scanPackage"));

        //============IOC=============

        //3.初始化Ioc容器,将扫描到的相关的类实例化,保存到Ioc容器中
        doInstrance();

        //在DI之前加入AOP。
        //AOP:新生成的代理对象

        //============DI=============

        //4.完成依赖注入
        doAutowired();

        //============MVC=============

        //5.初始化HandlerMapping
        doInitHandlerMapping();

        System.out.println("GP Spring framework init Finished");
    }

    private void doInitHandlerMapping() {
        if(ioc.isEmpty()){
            return;
        }
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if(!clazz.isAnnotationPresent(GPController.class)){
                continue;
            }
            //提取Controller类上的url
            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;
                }
                //提取每个方法上的url
                GPRequestMapping gpRequestMapping = method.getAnnotation(GPRequestMapping.class);
                //使用正则/+,处理多/问题
                String url = "/" + baseUrl + "/" + gpRequestMapping.value().replaceAll("/+","/");
                handlerMapping.put(url,method);
                System.out.println("Mapped : " + url + "," + method);
            }
        }
    }

    private void doAutowired() {
        if(ioc.isEmpty()){
            return;
        }
        for (Map.Entry<String,Object> entry:ioc.entrySet()) {
            //把所有private/protected/default/public修饰的变量都获取出来
            for (Field field:entry.getValue().getClass().getDeclaredFields()) {
                if(!field.isAnnotationPresent(GPAutowired.class)){
                    continue;
                }
                GPAutowired autowired = field.getAnnotation(GPAutowired.class);
                String beanName = autowired.value().trim();
                //如果用户没有自定义的beanName,就默认根据类型注入
                if("".equals(beanName)){
                    //field.getType().getName() 获取字段的类型
                    beanName = field.getType().getName();
                }
                //暴力访问
                field.setAccessible(true);
                try {
                    //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doInstrance() {
        if(classNames.isEmpty()){
            return;
        }
        try {
        for (String className : classNames) {
                Class<?> clazz = Class.forName(className);
                //包含GPController注解或GPService注解
                if(clazz.isAnnotationPresent(GPController.class )){
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    Object instance = clazz.newInstance();
                    //赋值到ioc容器
                    ioc.put(beanName,instance);
                }else if(clazz.isAnnotationPresent(GPService.class)){

                    //2.在多个包下出现相同类名,只能规定自己起一个全局唯一名字
                    //如使用注解GPService("aService")自定义命名
                    String beanName = clazz.getAnnotation(GPService.class).value();
                    if("".equals(beanName.trim())){
                        beanName = toLowerFirstCase(clazz.getSimpleName());
                    }

                    //1.默认的类名首字母小写
                    Object instance = clazz.newInstance();
                    ioc.put(beanName,instance);

                    //3.如果是接口
                    //判断有多少个实现类,如果只有一个,默认就选择这个实现类
                    //如果有多个,只能抛异常
                    for (Class<?> i :clazz.getInterfaces()) {
                        if(ioc.containsKey(i.getName())){
                            throw new Exception("The" + i.getName() + "is exists!!");
                        }
                        ioc.put(i.getName(),instance);
                    }

                }else{
                    continue;
                }
            }

        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String toLowerFirstCase(String simpleName) {
        char [] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    private void doScanner(String scanPackage) {
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.","/"));
        File classPath = new File(url.getFile());
        // 当成是一个classPath文件夹
        for (File file : classPath.listFiles()) {
            if(file.isDirectory()){
                doScanner(scanPackage + "." + file.getName());
            }else{
                if(!file.getName().endsWith(".class")){
                    continue;
                }
                String className = (scanPackage + "." + file.getName().replace(".class",""));
                //Class.forName(className);
                classNames.add(className);
            }

        }

    }

    private void doLoadConfig(String contextConfigLoation) {
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLoation);
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null != is){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

上一篇:sql中的内连接和外连接


下一篇:java 面向对象(四十一):反射(五)反射应用二:获取运行时类的完整结构