純手寫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的大致流程
- 定位
- 加載
- 註冊
那麼現在的開始
- 先創建一個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:張三 自動注入成功
}
}