0%

手写Spring框架


本文是对spring常见的如ioc等功能的手写实现

本文的代码的链接在下面
项目链接:https://github.com/ZhangJia97/Spring-demo

下面这个项目链接是对上面这个手写的spring框架的部分优化
项目链接:https://github.com/ZhangJia97/SpringMVC-demo


项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
├── pom.xml
├── spring-demo.iml
├── src
   └── main
   ├── java
   │   └── xyz
   │   └── suiwo
   │   ├── action
   │   │   ├── controller
   │   │   │   └── DemoController.java
   │   │   └── service
   │   │   ├── DemoService.java
   │   │   └── impl
   │   │   └── DemoServiceImpl.java
   │   └── framework
   │   ├── annotation
   │   │   ├── SWAutowried.java
   │   │   ├── SWController.java
   │   │   ├── SWRequestMapping.java
   │   │   ├── SWRequestParam.java
   │   │   └── SWService.java
   │   └── servlet
   │   └── SWDispatcherServlet.java
   ├── resources
   │   └── application.properties
   └── web
   ├── WEB-INF
   │   └── web.xml
   └── index.jsp

所用依赖

1
2
3
4
5
6
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet.api.version}</version>
<scope>provided</scope>
</dependency>

web.xml配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>Suiwo Web Application</display-name>
<servlet>
<servlet-name>swmvc</servlet-name>
<servlet-class>xyz.suiwo.framework.servlet.SWDispatcherServlet</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>swmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

application.properties配置如下

1
scanPackage=xyz.suiwo.action

下面是几个我们自定义的注解类

1
2
3
4
5
6
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWAutowried {
String value() default "";
}
1
2
3
4
5
6
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWController {
String value() default "";
}
1
2
3
4
5
6
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWRequestMapping {
String value() default "";
}
1
2
3
4
5
6
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWRequestParam {
String value() default "";
}
1
2
3
4
5
6
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWService {
String value() default "";
}

下面我们新建几个controller类和service类并使用我们自定义的注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@SWController
@SWRequestMapping("/get")
public class DemoController {

@SWAutowried
DemoService demoService;

@SWRequestMapping("/method")
public void get(HttpServletRequest request, HttpServletResponse response,
@SWRequestParam("name") String name){
demoService.get();
try {
response.getWriter().write("my name is " + name);
} catch (IOException e) {
e.printStackTrace();
}
}
}
1
2
3
public interface DemoService {
void get();
}
1
2
3
4
5
6
@SWService
public class DemoServiceImpl implements DemoService {
public void get() {
System.out.println("This is method get");
}
}

最后让我们看一下我们的Dispatcher类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
public class SWDispatcherServlet extends HttpServlet {

private Properties properties = new Properties();

private List<String> classNames = new ArrayList<>();

private Map<String, Object> ioc = new HashMap<>();

private Map<String, Method> handlerMapping = new HashMap<>();


@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatch(req,resp);

//注:当使用父类的doGet以及doPost可能会导致405错误
// super.doGet(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
doDispatch(req,resp);
// doDispatch(req, resp);
}

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

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

//3.初始化所有相关Class的实例,并且将其保存到IOC容器中
doInstance();

//4.自动化的依赖注入
doAutowired();

//5.初始化handlerMapping
initHandlerMapping();

System.out.println("初始化成功");
}

private void doLoadConfig(String location) {

InputStream inputStream =null;
try {
inputStream = this.getClass().getClassLoader().getResourceAsStream(location);
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

//进行扫描
private void doScanner(String packageName) {
URL url = this.getClass().getClassLoader()
.getResource("/" + packageName.replaceAll("\\.", "/"));
File classDir = new File(Objects.requireNonNull(url).getFile());
for(File file : Objects.requireNonNull(classDir.listFiles())){

//递归获取scan下所有的类,并将类名添加至classNames中用于之后实例化
if(file.isDirectory()){
doScanner(packageName + "." + file.getName());
}else {
String className = packageName + "." + file.getName().replace(".class","").trim();
classNames.add(className);
}
}

}

//进行实例化
private void doInstance() {
if(classNames.isEmpty()){
return;
}
for(String className : classNames){
try {
Class<?> clazz = Class.forName(className);

//进行实例化
//判断不是所有的都需要实例化,只有添加了例如Controller或者Service的注解才初始化
if(clazz.isAnnotationPresent(SWController.class)){
//beanName beanId
//1.默认采用类名的首字母小写
//2.如果自定义了名字,默认使用自定义名字
//3.根据类型匹配,利用实现类的接口名作为Key
String beanName = toLowStr(clazz.getSimpleName());
ioc.put(beanName, clazz.newInstance());
}else if(clazz.isAnnotationPresent(SWService.class)){
SWService swService = clazz.getAnnotation(SWService.class);
String beanName = swService.value();
if(!"".equals(beanName.trim())){
ioc.put(beanName, clazz.newInstance());
continue;
}

//获取对象所实现的所有接口
Class<?>[] interfaces = clazz.getInterfaces();
for(Class<?> i : interfaces){
ioc.put(i.getName(), clazz.newInstance());
}
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}


}
}

private void doAutowired() {
if(ioc.isEmpty()){
return;
}

for (Map.Entry<String, Object> entry : ioc.entrySet()){
//在spring中没有隐私
//咱们只认 @Autowried,获取所有属性
Field[] fields = entry.getValue().getClass().getDeclaredFields();

for(Field field : fields){
if(!field.isAnnotationPresent(SWAutowried.class)){
continue;
}

SWAutowried swAutowried = field.getAnnotation(SWAutowried.class);

String beanName = swAutowried.value().trim();
//如果为空说明使用默认名字,所以使用getName获取默认名字
if("".equals(beanName)){
beanName = field.getType().getName();
}

field.setAccessible(true);

try {
//向属性set进已经实例化的对象
field.set(entry.getValue(), ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

}

private void initHandlerMapping() {
if(!ioc.isEmpty()){
for(Map.Entry<String, Object> entry : ioc.entrySet()){
Class<?> clazz = entry.getValue().getClass();
//HandlerMapping只认识SWController
if(!clazz.isAnnotationPresent(SWController.class)){
continue;
}
String url = "";
//获取类上的RequestMapping地址
if(clazz.isAnnotationPresent(SWRequestMapping.class)){
SWRequestMapping swRequestMapping = clazz.getAnnotation(SWRequestMapping.class);
url = swRequestMapping.value();
}
Method[] methods = clazz.getMethods();
for(Method method : methods){
if(!method.isAnnotationPresent(SWRequestMapping.class)){
continue;
}
//获取实际方法上的requestMapping
SWRequestMapping swRequestMapping = method.getAnnotation(SWRequestMapping.class);
String mUrl = url + swRequestMapping.value();
handlerMapping.put(mUrl, method);
System.out.println("Mapping : " + mUrl + " " + method);
}

}
}
}

private void doDispatch(HttpServletRequest request, HttpServletResponse response) {
String url = request.getRequestURI();
String contextPath = request.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
if(!handlerMapping.containsKey(url)){
try {
response.getWriter().write("404");
return;
} catch (IOException e) {
e.printStackTrace();
}
}
//获取当前路由对应的方法
Method method = handlerMapping.get(url);
System.out.println("获得对应的方法" + method);

//获取方法列表
Class<?>[] parameterTypes = method.getParameterTypes();
Map<String, String[]> parameterMap = request.getParameterMap();

Object[] paramValues = new Object[parameterTypes.length];
for(int i = 0; i < parameterTypes.length; i++){
Class parameterType = parameterTypes[i];
if(parameterType == HttpServletRequest.class){
paramValues[i] = request;
continue;
}else if(parameterType == HttpServletResponse.class){
paramValues[i] = response;
}else if(parameterType == String.class){
for(Map.Entry<String, String[]> entry : parameterMap.entrySet()){
String value = Arrays.toString(entry.getValue()).replaceAll("\\[|\\]","")
.replaceAll(",\\s", ",");
paramValues[i++] = value;
if(i == parameterTypes.length){
break;
}
}
}
}
try{
String beanName = toLowStr(method.getDeclaringClass().getSimpleName());
method.invoke(this.ioc.get(beanName), paramValues);
}catch (Exception e){
e.printStackTrace();
}
}

private String toLowStr(String str){
char[] ch = str.toCharArray();
ch[0] += 32;
return String.valueOf(ch);
}
}

此时我们就可以在浏览器中访问对应的网页了,至此一个简易的Spring框架就完成了