发布时间:2023-04-06 18:30
1,了解看源代码最有效的方式,先猜测后验证,不要一开始就去调试代码
2,用300行最简洁的代码提炼Spring的基础设计思想
3,结合设计模式,掌握Spring框架的基本脉络
Spring的如何开始的,我们先从原理来了解一下。
Spring主要有3个阶段:
在web.xml中设定DispatchServle,设置Spring-*.xml相关的配置信息或者直接设置*.properties文件,包含@Controller @Service @Autowrited @RequestMapping 等等
1,调用init()方法,加载配置文件
2,IOC容器初始化,根据spring-*.xml或者*.properties中的配置扫描相关的类。
3,根据扫描的类,创建实例化类并保存到IOC容器中,主要是通过反射机制将类实例化放入IOC容器中
4,进行DI操作,即依赖注入,通过扫描IOC容器中的实例,给没有赋值的属性自动赋值
5,初始化HandelerMappping,就是将URL和Method进行一对一的关联映射到Map
1,掉用doPost()/doGet()请求,获取request/response。
2,匹配handlerMapping,从request对象中获得用户输入的url,并从hangdlerMapping中找到对应的Method方法。
3,反射调用method.invoker(),返回结果
4,response.getWrite().wirte(),将结果返回给浏览器
这个不会建的请移步度娘,自己去百度。
我的项目结构如下图:
ChService注解代码如下:
package com.ch.framework.annotation;
import java.lang.annotation.*;
/**
* Created by lijianfang on 2021/10/25.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChService {
String value() default \"\";
}
ChRequestMapping注解代码如下:
package com.ch.framework.annotation;
import java.lang.annotation.*;
/**
* Created by lijianfang on 2021/10/25.
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChRequestMapping {
String name() default \"\";
@ChAliasFor(\"path\")
String[] value() default {};
@ChAliasFor(\"value\")
String[] path() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
ChController注解代码如下:
package com.ch.framework.annotation;
import java.lang.annotation.*;
/**
* Created by lijianfang on 2021/10/25.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChController {
String value() default \"\";
}
ChAutowired注解代码如下:
package com.ch.framework.annotation;
import java.lang.annotation.*;
/**
* Created by lijianfang on 2021/10/25.
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChAutowired {
boolean required() default true;
String value() default \"\";
}
ChAliasFor注解代码如下:
package com.ch.framework.annotation;
import java.lang.annotation.*;
/**
* Created by lijianfang on 2021/10/25.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface ChAliasFor {
// @ChAliasFor(\"attribute\");
String value() default \"\";
// @ChAutowired(\"value\");
String attribute() default \"\";
Class extends Annotation> annotation() default Annotation.class;
}
pom.xml文件中需要配置编译相关的组件,pom.xml具体如下:
4.0.0
com.ch.framework
springTest
1.0-SNAPSHOT
war
springTest Maven Webapp
http://www.example.com
UTF-8
1.7
1.7
junit
junit
4.11
test
javax.servlet
javax.servlet-api
3.0.1
org.glassfish
bean-validator
3.0-JBoss-4.0.2
springTest
maven-clean-plugin
3.1.0
maven-resources-plugin
3.0.2
maven-compiler-plugin
3.8.0
maven-surefire-plugin
2.22.1
maven-war-plugin
3.2.2
maven-install-plugin
2.5.2
maven-deploy-plugin
2.8.2
src/main/java
**/*.xml
resources
**/*.yml
**/*.properties
**/*.xml
**/*.jpg
**/*.ttf
**/*.xml
**/*.yml
web.xml中设置我们自己定义的dispatchServlet类,详情如下:
springTest
znnmvc
com.ch.framework.dispatcher.servlet.ChDispatchServlet
contextConfigLocation
application.properties
1
znnmvc
/*
index.jsp
application.properties文件中定义需要扫描的类包名。
scanPackage=com.ch.framework.demo
具体代码如下:
package com.ch.framework.dispatcher.servlet;
import com.ch.framework.annotation.ChAutowired;
import com.ch.framework.annotation.ChController;
import com.ch.framework.annotation.ChRequestMapping;
import com.ch.framework.annotation.ChService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
/**
* 手写自己的servlet
* Created by lijianfang on 2021/10/25.
*/
public class ChDispatchServlet extends HttpServlet{
private static final Logger logger = LoggerFactory.getLogger(ChDispatchServlet.class);
private Properties contextConfig = new Properties();
private List classNames = new ArrayList();
private Map ioc = new HashMap();
private Map handlerMapping = new HashMap();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try{
//6,调用
doDispatch(req,resp);
}catch (Exception e){
logger.error(\"调用报错,错误信息\",e);
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{
if(this.handlerMapping.isEmpty()){
return;
}
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, \"\").replaceAll(\"/\", \"/\");
//用户访问的路径是不是在hangdlerMapping中
if(!this.handlerMapping.containsKey(url)){
resp.getWriter().write(\"没有找到相关的路径,请确认路径是否正确!\");
return;
}
Method method = (Method) this.handlerMapping.get(url);
//获取方法的参数列表
Class>[] paramterTypes = method.getParameterTypes();
//获取请求的参数
Map parameterMap = req.getParameterMap();
Object [] parameValues = new Object[paramterTypes.length];
//方法的参数列表
for(int i = 0;i param : parameterMap.entrySet()) {
System.out.println(\"param.getValue()===\"+param.getValue()[0]);
String value = Arrays.toString(param.getValue()).replaceAll(\"\\\\[|\\\\]\",\"\").
replaceAll(\",\\\\s\",\",\");
parameValues[i] = value;
i++;
}
}
}
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
//反射调用方法
method.invoke(this.ioc.get(beanName), parameValues);
}
/**
* 初始化相关参数
* @param config
* @throws ServletException
*/
@Override
public void init(ServletConfig config) throws ServletException {
//1、加载配置文件
doLoadConfig(config.getInitParameter(\"contextConfigLocation\"));
//2、扫描相关的类
doScanner(contextConfig.getProperty(\"scanPackage\"));
//3、初始化扫描的类,并将他们放到IOC容器中
doInstance();
//4、完成依赖注入
doAutowired();
//5、初始化handerMapping
initHandlerMapping();
//6、调用
}
/**
* 初始化url跟method的一一对应关系
*/
private void initHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry entry : ioc.entrySet()) {
Class> clazz = entry.getValue().getClass();
//当前类的注解是不是controller,不是直接跳过
if (!clazz.isAnnotationPresent(ChController.class)) {
continue;
}
String baseUrl = \"\";
//是controller注解,获取注解上的路径名称
if (clazz.isAnnotationPresent(ChRequestMapping.class)) {
ChRequestMapping requestMapping = clazz.getAnnotation(ChRequestMapping.class);
baseUrl = requestMapping.value()[0];
}
// 默认获取所有的public,获取class类的方法(公共方法)
for (Method method : clazz.getMethods()) {
//判断方法上的注解是不是requestMap注解,不是就直接跳过
if (!method.isAnnotationPresent(ChRequestMapping.class)) {
continue;
}
//获取注解上的路径
ChRequestMapping requestMapping = method.getAnnotation(ChRequestMapping.class);
//合成最终访问的路径
String url = (\"/\"+baseUrl + \"/\" + requestMapping.value()[0]).replaceAll(\"/\", \"/\");
// 将访问的路径放到handlerMapping中
handlerMapping.put(url, method);
logger.info(\"Mapped\" + url + method);
}
}
}
/**
* 完成依赖注入
*/
private void doAutowired() {
if(ioc.isEmpty()){
return;
}
//循环ioc中的类,给对应的类中的依赖的属性注入相应的bean
for(Map.Entry entry:ioc.entrySet()){
//获取对应类的私有属性
Field[] fields = entry.getValue().getClass().getDeclaredFields();
//循环私有属性,看是否是autowired注解
for(Field field:fields){
//看是不是service注解
if(field.isAnnotationPresent(ChService.class)){
continue;
}
//查看是不是依赖注入的注解
ChAutowired autowired = field.getAnnotation(ChAutowired.class);
//获取对应注解的名字,先查看是否注解上是不是有写了默认的值,没有值时,使用类型注入
String beanName = autowired.value().trim();
if(\"\".equals(beanName)){
beanName=field.getType().getName();
}
//设置注解类的可用属性
field.setAccessible(true);
try {
field.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/**
* 初始化扫描的类
*/
private void doInstance(){
if(classNames.isEmpty()){
return;
}
try {
for(String className:classNames){
Class> clazz = Class.forName(className);
// 什么样的类才需要初始化,添加了注解的类才初始化
//controller,service
if (clazz.isAnnotationPresent(ChController.class)){
Object instance = clazz.newInstance();
String beanName = toLowerFirstCase(clazz.getSimpleName());
ioc.put(beanName, instance);
}else if(clazz.isAnnotationPresent(ChService.class)){
ChService service = clazz.getAnnotation(ChService.class);
// 1、默认首字母小写
String beanName = service.value();
// 2、自定义bean名字
if (\"\".equals(beanName.trim())) {
beanName = toLowerFirstCase(clazz.getSimpleName());
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
// 3、根据类自动赋值
for (Class> i : clazz.getInterfaces()) {
if (ioc.containsKey(i.getName())) {
try {
throw new Exception(\"这个\" + i.getName() + \" 已经存在!\");
} catch (Exception e) {
logger.error(\"这个\" + i.getName() + \" 已经存在!\");
}
}
ioc.put(i.getName(), instance);
}
}else {
continue;
}
}
} catch (ClassNotFoundException e) {
logger.error(\"报错信息:\",e);
} catch (InstantiationException e) {
logger.error(\"报错信息:\",e);
} catch (IllegalAccessException e) {
logger.error(\"报错信息:\",e);
}
}
/**
*
* 首字母小写
*
*
* @param simpleName
* @return
*/
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
/**
*
* 扫描配置文件配置的类路径
*
*
* @param scanPackage
*/
private void doScanner(String scanPackage) {
URL url = this.getClass().getClassLoader().getResource(\"\"+scanPackage.replaceAll(\"\\\\.\",\"/\"));
File classPath = new File(url.getFile());
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\", \"\"));
classNames.add(className);
}
}
}
/**
*
* 加载配置文件,直接从类路径下找到spring主配置文件夹所在的路径并且将其读取出来放到Properties对象中
* 将application.properties文件中的scanPackage=com.ch.framework.demo 放到properties对象中
*
* @param contextConfigLocation
*/
private void doLoadConfig(String contextConfigLocation){
InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try{
contextConfig.load(fis);
}catch (Exception e){
logger.error(\"加载配置失败,错误原因:\", e);
}finally{
if(null!=fis){
try {
fis.close();
} catch (IOException e) {
logger.error(\"关闭流失败,错误原因:\", e);
}
}
}
}
}
我新建了一个demo包,
TestController.java文件代码如下:
package com.ch.framework.demo.controller;
import com.ch.framework.annotation.ChAutowired;
import com.ch.framework.annotation.ChController;
import com.ch.framework.annotation.ChRequestMapping;
import com.ch.framework.demo.service.DomeService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by lijianfang on 2021/11/15.
*/
@ChController
@ChRequestMapping(\"test\")
public class TestController {
@ChAutowired
private DomeService domeService;
@ChRequestMapping(\"getData\")
public void getData(HttpServletRequest request, HttpServletResponse response){
String name = request.getParameter(\"name\");
System.out.println(\"name==\"+name);
try {
String result = domeService.getData(name);
response.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
@ChRequestMapping(\"addData\")
public void addData(HttpServletRequest request, HttpServletResponse response){
int a = Integer.parseInt(request.getParameter(\"a\"));
int b = Integer.parseInt(request.getParameter(\"b\"));
System.out.println(\"name==\"+a);
try {
int result = domeService.addData(a,b);
response.getWriter().write(\"result:a+b=\"+result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
DomeService.java文件代码如下:
package com.ch.framework.demo.service;
/**
* Created by lijianfang on 2021/11/15.
*/
public interface DomeService {
public String getData(String name);
public int addData(int a,int b);
}
DomeServiceImpl.java文件代码如下:
package com.ch.framework.demo.service.impl;
import com.ch.framework.annotation.ChService;
import com.ch.framework.demo.service.DomeService;
/**
* Created by lijianfang on 2021/11/15.
*/
@ChService
public class DomeServiceImpl implements DomeService {
@Override
public String getData(String name) {
return \"My name is \"+name;
}
@Override
public int addData(int a, int b) {
return a+b;
}
}
第五步,测试结果