SpringIOC简单模拟,菜鸟篇

IOC是Spring两大特征之一,今天我们就来用最最最土的方式模拟下它。本文全是基础,不涉及设计模式,适合初级程序员阅读。

到底什么是IOC、DI

IOC(控制反转),不是什么技术,而是一种设计思想。Ioc意味着,将你设计好的对象交给容器控制,而不是传统的,在你的对象内部直接控制。

所以控制反转就是说把创建对象的控制权进行转移,以前创建对象的主动权和创建时机都是有自己控制的,而现在把这种权利交给第三方,比如IOC容器,它是一个专门用来创建对象的工厂,有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合。

DI(依赖注入)

一种IoC的实现方式。即将依赖对象的创建和绑定工作转移到第三者(调用方)

想象下Spring加载过程

假设有一个URL请求,http://demo/get/uid/1

当这个请求到达服务器之后,首先获取url,接着找到url所对应的处理方法,最后把url参数传给这个方法,然后在调用下这个方法。

问题是:如何通过通过url找到这个方法的映射,拢共分几步?

分六个步骤

第一步:自定义几个注解

我们需要自定义几个注解来标识我们bean对象:

1. @Controller 用来标识controller层。

2. @Service用来标识Sevice层。

3. @Qualifier 用来标识类中属性。

4. @RequestMapping 用来标识方法。

这些注解中都有一个默认的value属性。

第二步:收集bean

把整个工程文件遍历一遍,通过IO读取所有java文件,java的name放在一个list容器里,就称他为beanName集合吧。

第三步:注册bean

遍历beanName集合,获取java文件的类名,用反射的方法通过名字,获取该类的Class,判断Class是否包含Controller或Service注解,若包含,则通过反射的方式把当前Class生成bean对象,用Controller或Service注解中的value值做key来标识。放到一个map容器里,就叫他instanceMap容器吧。

第四步:注入bean

遍历instanceMap容器,instanceMap存的都是实例对象,我们遍历每个对象,获取当前对象内的所有变量fields,然后在遍历这个fields,看当前field是否包含Qualifier注解,包含的话,就用Qualifier注解设置的value当做key去instanceMap容器里找到bean实例对象,然后把这个实例对象设置到当前的field中。

第五步:收集RequestMapping方法

遍历instanceMap容器,找到带有Controller注解的类,获取该类的所有方法methods,再遍methods方法,找到带有RequestMapping注解的方法对象,用RequestMapping中的value加上Controller注解中的value拼接成url,当做key,把当前方法对象放到一个map容器,就把这个容器叫做methodMap容器吧。

第六步:实现url映射

当一个请求到达Servlet时,获取这个请求的url,通过字符串分割获取url中的参数,把参数设置到request.Attribute中,删除url中的参数,做key去methodMap获取方法对象,再用反射的方法,将request,response做参数,调用这个方法,获取返回值,这个返回值就是我们要返回给客户端的页面,在通过request转发的方式,请页面返回。

记住这六步,下面看代码。

Coding1:自定义几个注解


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
    String value() ;
}

 

 

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Service {
    String value();
}

 

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Qualifier {
    String value();
}

 

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestMapping {
    String value();
}

Coding2:收集bean

basePackages为配置扫描的包。

就像spring配置文件中的<context:component-scan base-package="" />

public void scanClassName(String basePackages){

//获取扫描注解所在的路径
    URL url =this.getClass().getResource("/"+replacePath(basePackages));
    File files = new File(url.getFile());
    String[] fileList = files.list();
    for(String file: fileList){
        File eachFile = new File(url.getFile()+file);
        if(eachFile.isDirectory()){
            scanClassName(basePackages+"."+file);
        }else{

//将java类的名称放在classList中
            classlist.add(basePackages+"."+eachFile.getName());
        }
    }

}

Coding3:注册bean

public void filterAndInstance() throws Exception {
    for(String current:classlist){
        //将.class后缀去掉,获取文件名,用反射获取class
        Class clazz = Class.forName(current.replace(".class",""));
        //判断是否包含Controller注解
        if(clazz.isAnnotationPresent(Controller.class)){
            //创建对象
            Object object = clazz.newInstance();
            String key = ((Controller)clazz.getAnnotation(Controller.class)).value();
            instanceMap.put(key,object);
        }else if(clazz.isAnnotationPresent(Service.class)){
            Object object = clazz.newInstance();
            String key = ((Service)clazz.getAnnotation(Service.class)).value();
            instanceMap.put(key,object);
        }else{
            continue;
        }
    }
}

Coding4:注入bean

public void springDi() throws IllegalAccessException {
    for(Map.Entry entry: instanceMap.entrySet()){
        Field[] fields = entry.getValue().getClass().getDeclaredFields();
        for(Field field :fields){
            if(field.isAnnotationPresent(Qualifier.class)){
                field.setAccessible(true);
                String key = field.getAnnotation(Qualifier.class).value();
                field.set(entry.getValue(),instanceMap.get(key));
            }
        }
    }
}

Coding5:收集RequestMapping方法

private void mvc() throws ServletException {
    if(instanceMap == null){
        throw new ServletException("instanceMap is null");
    }
    for(Map.Entry entry : instanceMap.entrySet()){
        if(entry.getValue().getClass().isAnnotationPresent(Controller.class)){
            String ctlUrl = entry.getValue().getClass().getAnnotation(Controller.class).value();
            Method[] methods = entry.getValue().getClass().getMethods();
            for(Method current: methods){
                if (current.isAnnotationPresent(RequestMapping.class)){
                    String methodUrl = current.getAnnotation(RequestMapping.class).value();
                    methodMap.put("/"+ctlUrl+"/"+methodUrl,current);
                }
            }
        }
    }
}

Coding6:实现url映射

private void methodInvoke(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String url = req.getServletPath();
    url = url.replace(".html","");
    String className = url;
    Method method = methodMap.get(className);
    if(method==null){
        req.getRequestDispatcher("/404.jsp").forward(req, resp);
    }else {
        Object o = instanceMap.get(className.split("/")[1]);
        String forwod = null;

        try {
            forwod = (String) method.invoke(o, new Object[]{req, resp});
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        req.getRequestDispatcher("/" + forwod + ".jsp").forward(req, resp);
    }
}

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值