純手寫SpringBoot+Spring MVC (第一階段)

純手寫SpringBoot+Spring MVC (第一階段)

我們知道springBoot相比於Spring的話省略了很多配置,而且可以通過main方法的形式啓動spring boot web項目。 (源碼地址:https://github.com/q920447939/java-study/tree/master/spring-boot/spring-boot-custom)

那麼我們今天也來實現一個自定義的SpringBoot +Spring MVC

當然,瞭解過Spring的童靴肯定知道Spring其實是依賴servlet做了一層處理而已。所以我們在pom.xml必須要依賴

 <dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>servlet-api</artifactId>
</dependency>

先複習一下spring 創建Bean的大致流程

  1. 定位
  2. 加載
  3. 註冊

那麼現在的開始

  1. 先創建一個SpringBoot項目(看看SpirngBoot和Spring的不同)
package cn.withmes.spring.boot.custom;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootCustomApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootCustomApplication.class, args);
    }

}

在主方法上面多了一個SpringBoot的註解,那麼我們也照護畫瓢弄一個自己的啓動註解

package cn.withmes.spring.boot.custom.annotation;


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;

/**
 * @Description:
 * @param: 
 * @return: 
 * @auther: liming
 * @date: 12/15/2019 3:18 PM
 */
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StartApplication {

    String scanPackage() default "";
}

/**
 * @Project:
 * @Author: leegoo
 * @Date: 2019年12月15日
 */
package cn.withmes.spring.boot.custom;

/**
 * ClassName: MyApplication
 * @Description:
 * @author leegoo
 * @date 2019年12月15日
 */
public class MyApplication {

    public static void run  () {

    }
}


package cn.withmes.spring.boot.custom;

import cn.withmes.spring.boot.custom.annotation.StartApplication;

@StartApplication()
public class SpringBootCustomApplication {

    public static void main(String[] args) {
        MyApplication.run();
    }
}

當前的目錄結構爲:

D:.
│  pom.xml
│  tree.txt
│  
└─src
    └─main
        ├─java
        │  └─cn
        │      └─withmes
        │          └─spring
        │              └─boot
        │                  └─custom
        │                      │  MyApplication.java
        │                      │  SpringBootCustomApplication.java
        │                      │  
        │                      └─annotation
        │                              StartApplication.java
        │                              
        └─resources
                application.properties
                

做了一個簡單的bean工廠,以及自動注入

/**
 * @Project:
 * @Author: leegoo
 * @Date: 2019年12月15日
 */
package cn.withmes.spring.boot.custom;

import cn.withmes.spring.boot.custom.annotation.MyController;
import cn.withmes.spring.boot.custom.annotation.MyResource;
import cn.withmes.spring.boot.custom.annotation.MyService;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ClassName: MyApplication
 *
 * @author leegoo
 * @Description:
 * @date 2019年12月15日
 */
public class MyApplication {

    //存儲bean,
    private static Map<String, Object> ioc = new ConcurrentHashMap<>();

    // 將所有的類全部加載進來
    private static List<String> cacheList = new ArrayList<>();

    public    <T> T  getBean (String beanName,Class<T> clz){
        return (T) ioc.get(beanName);
    }

    public static void  run() {
        //定位
        doLocation("");
        //加載
        doLoad();
        // 註冊
        doAutoInject();
    }



    private static void doLocation(String pt) {
        String path = StringUtils.isBlank(pt) ? MyApplication.class.getResource(pt).getPath() : pt;
        File files = new File(path);
        File[] listFiles = files.listFiles();

        for (File file : listFiles) {
            if (file.isDirectory()) {
                doLocation(file.getPath());
            } else {
                if (file.getPath().contains("annotation")) continue;

                String[] targets = file.getPath().split("\\\\target\\\\classes\\\\");
                String className = targets[1].replaceAll("\\\\", ".").replace(".class", "");
                //將包路徑下面的.java文件全部加載到cacheList中.
                cacheList.add(className);
            }
        }

    }


    private static void doLoad() {
        if (cacheList.size() <= 0) return;

        cacheList.forEach(k->{
            try {
                Class<?> cls = Class.forName(k);
                MyController controller = cls.getAnnotation(MyController.class);
                if (null != controller) {
                    ioc.put(lowerFirstChar(cls.getSimpleName()),cls.newInstance());
                    return;
                }
                MyService service = cls.getAnnotation(MyService.class);
                if (null != service){
                    Class<?>[] interfaces = cls.getInterfaces();
                    if (null == interfaces || interfaces.length  <= 0) return;
                    //此處只取第一個接口
                    Class<?> firstInterface = interfaces[0];
                    ioc.put(lowerFirstChar(firstInterface.getSimpleName()),cls.newInstance());
                }
            } catch (ClassNotFoundException |IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        });
    }


    private static void doAutoInject() {
        if (ioc.size() <= 0) return;

        //循環cacheMap ,如果類裏面的屬性包含 @MyResource ,那麼自動注入對象
        ioc.forEach((k, v) -> {
            Field[] fields = v.getClass().getDeclaredFields();
            if (fields.length <= 0 ) return;

            for (Field fd : fields) {
                MyResource myResource = fd.getAnnotation(MyResource.class);
                if (null == myResource) continue;

                //設置訪問權限
                fd.setAccessible(true);
                try {
                    //給帶有@MyResource,set具體的值
                    fd.set(v, ioc.get(fd.getName()));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 將首字母小寫
     * @param str
     * @return
     */
    private static String lowerFirstChar(String str){
        char [] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
}

controller

/**
 * @Project:
 * @Author: leegoo
 * @Date: 2019年12月15日
 */
package cn.withmes.spring.boot.custom;

import cn.withmes.spring.boot.custom.annotation.MyController;
import cn.withmes.spring.boot.custom.annotation.MyResource;

/**
 * ClassName: UserController
 * @Description:
 * @author leegoo
 * @date 2019年12月15日
 */
@MyController
public class UserController {

    @MyResource
    private UserService userService;

    public void getId (String id ) {
        String name = userService.getName(id);
        System.out.println("name:"+name);
    }
}

serivce

/**
 * @Project:
 * @Author: leegoo
 * @Date: 2019年12月15日
 */
package cn.withmes.spring.boot.custom;

/**
 * ClassName: UserService
 * @Description:
 * @author leegoo
 * @date 2019年12月15日
 */
public interface UserService {

    String getName (String id );
}

serviceimpl


/**
 * @Project:
 * @Author: leegoo
 * @Date: 2019年12月15日
 */
package cn.withmes.spring.boot.custom;

import cn.withmes.spring.boot.custom.annotation.MyService;

import java.util.HashMap;
import java.util.Map;

/**
 * ClassName: UserServiceImpl
 * @Description:
 * @author leegoo
 * @date 2019年12月15日
 */
@MyService
public class UserServiceImpl implements  UserService {

    private static Map<String,String> userMap = new HashMap<>();

    static {
        userMap.put("1", "張三");
        userMap.put("2", "李四");
        userMap.put("3", "王五");
    }

    @Override
    public String getName(String id) {
        return null == id  || !userMap.containsKey(id) ? "沒有此用戶":
                userMap.get(id) ;
    }
}

註解類省略

開始測試

package cn.withmes.spring.boot.custom;

import cn.withmes.spring.boot.custom.annotation.StartApplication;

@StartApplication() //啓動註解 ,此處只是加一個註解.沒有做掃描包的處理
public class SpringBootCustomApplication {

    public static void main(String[] args) {
        //spring boot 啓動方式
        MyApplication.run();

        //測試是否注入
        MyApplication myApplication = new MyApplication();
        UserController controller = myApplication.getBean("userController", UserController.class);
        controller.getId("1"); //  name:張三   自動注入成功
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章