Spring以及各模块的讲解

Spring之核心模块

模块名称 主要功能
spring-core 依赖注入IOC和DI的基本实现
spring-beans Bean工厂与Bean的装配
spring-context 定义基础的Spring的Context上下文即IOC容器
spring-context-support 对Spring IOC容器的扩展支持,以及IOC子容器
spring-context-indexer Spring的类管理组件和ClassPath扫描
spring-expression Spring表达式语言
Spring之切面编程

模块名称 主要功能
spring-aop 面向切面编程应用模块,整合Asm,CGLIb,JDKProxy
spring-aspect 集成Aspect,Aop应用框架
spring-instrument 动态Class Loading模块
Spring之数据访问与集成

模块名称 主要功能
spring-jdbc Spring提供JDBC抽象框架的主要实现模块,用于简化Spring JDBC操作
spring-tx Spring JDBC事物控制实现模块
spring-orm 主要集成Hibernate,Java Persistence API(JPA)和Java Data Object(JDO)
spring-oxm 将java对象映射成XML数据,或者将XML数据映射成java对象
spring-jms Java Messaging Service 能够发送和接受信息
Spring之Web组件

模块名称 主要功能
spring-web 提供了最基础Web支持,主要建立于核心容器之上,通过Servlet或者Listener来·初始化IOC容器
spring-webmvc 实现了Spring Mvc(model-view-Controller)的Web应用
spring-websocket 主要是与Web前端的全双工通讯的协议
spring-webflux 一个新的非阻塞函数是Reactive Web 框架,可以用来建立异步的,非阻塞,事件驱动的服务
Spring之通信报文

模块名称 主要功能
spring-messaging 从Spring4开始新加入的一个模块,主要职责是为Spring框架集成一些基础的报文传送应用
Spring之集成测试

模块名称 主要功能
spring-test 主要为测试提供支持的
Spring之集成兼容

模块名称 主要功能
spring-framework Bill of Materials解决Spring的不同模块依赖版本不同问题
oop(面向对象) ——>bop (面向bean)
面向对象 面向bean
人工干预太重 把对Bean的干预由手动挡改成自动挡

  1. 先找到web.xml的DispatchServlet

DispatchServlet extends HttpServlet 生命周期

init() —— >入口,初始化方法
service() ——>浏览器输入url的时候回调用,会有web容器调用 doGet()/doPost()执行逻辑
destory() ——>生命结束
读取web.xml中contextConfigLocation对应的application.xml文件

gpmvc
com.gupaoedu.mvcframework.v1.servlet.DispatcherServlet

contextConfigLocation
application.xml

1

对配置文件进行初始化
@Override public void init(ServletConfig config) throws ServletException {
//1、加载配置文件
doLoadConfig(config.getInitParameter(“contextConfigLocation”));
//2、扫描相关的类
doScanner(contextConfig.getProperty(“scanPackage”));
//3、初始化扫描到的类,并且将它们放入到 IOC 容器之中
doInstance();
//4、完成依赖注入
doAutowired();
//5、初始化
HandlerMapping initHandlerMapping();
}
实现 doLoadConfig()方法
//保存 application.xml 配置文件中的内容
private Properties contextConfig = new Properties();
//保存扫描的所有的类名
private List classNames = new ArrayList();
//IOC容器
private Map<String,Object> ioc = newConcurrentHashMap <String,Object>();
//保存 url 和 Method 的对应关系
private Map<String,Method> handlerMapping = new ConcurrentHashMap <String,Method>();
//加载配置文件
private void doLoadConfig(String contextConfigLocation) {
//直接从类路径下找到 Spring 主配置文件所在的路径
//并且将其读取出来放到 Properties 对象中
//相对于 scanPackage=com.gupaoedu.demo 从文件中保存到了内存中
InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
private Map<String,Object> ioc = new HashMap<String,Object>();
//保存 url 和 Method 的对应关系
private Map<String,Method> handlerMapping = new ConcurrentHashMap <String,Method>();
try {
contextConfig.load(fis);
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null != fis){
try { fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实现 doScanner()方法
//扫描出相关的类
private void doScanner(String scanPackage) {
//scanPackage = com.gupaoedu.demo ,存储的是包路径
//转换为文件路径,实际上就是把.替换为/就 OK 了
//classpath
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);
}
}
}
doInstance()方法,doInstance()方法就是工厂模式的具体实现
private void doInstance() {
//初始化,为 DI 做准备
if(classNames.isEmpty()){return;}
try {
for (String className : classNames) {
Class<?> clazz = Class.forName(className);
//什么样的类才需要初始化呢?
//加了注解的类,才初始化,怎么判断?
//为了简化代码逻辑,主要体会设计思想,只举例 @Controller 和@Service, // @Componment…就一一举例了
if(clazz.isAnnotationPresent(GPController.class)){
Object instance = clazz.newInstance();
//Spring 默认类名首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
ioc.put(beanName,instance);
}else if(clazz.isAnnotationPresent(GPService.class)){
//1、自定义的 beanName
GPService service = clazz.getAnnotation(GPService.class);
String beanName = service.value();
//2、默认类名首字母小写
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())){
throw new Exception(“The “” + i.getName() + “” is exists!!”);
}
//把接口的类型直接当成 key 了
ioc.put(i.getName(),instance);
}
}else { continue; }
}catch (Exception e){
e.printStackTrace();
}
}
private String toLowerFirstCase(String simpleName) {
char [] chars = simpleName.toCharArray();
//之所以加,是因为大小写字母的 ASCII 码相差 32,
// 而且大写字母的 ASCII 码要小于小写字母的 ASCII 码
//在 Java 中,对 char 做算学运算,实际上就是对 ASCII 码做算学运算
chars[0] += 32;
return String.valueOf(chars);
}
实现 doAutowired()方法:
//自动依赖注入
private void doAutowired() {
if(ioc.isEmpty()){return;}
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
//Declared 所有的,特定的 字段,包括 private/protected/default //正常来说,普通的 OOP 编程只能拿到 public 的属性
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if(!field.isAnnotationPresent(GPAutowired.class)){continue;}
GPAutowired autowired = field.getAnnotation(GPAutowired.class);
//如果用户没有自定义 beanName,默认就根据类型注入
String beanName = autowired.value().trim();
if("".equals(beanName)){
//获得接口的类型,作为 key 待会拿这个 key 到 ioc 容器中去取值
beanName = field.getType().getName();
//如果是 public 以外的修饰符,只要加了@Autowired 注解,都要强制赋值
//反射中叫做暴力访问
field.setAccessible(true);
try {
//用反射机制,动态给字段赋值
field.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
实现 initHandlerMapping()方法,handlerMapping就是策略模式的应用案例:
//初始化 url 和 Method 的一对一对应关系
private void initHandlerMapping() {
if(ioc.isEmpty()){ return; }
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(GPController.class)){continue;}
//保存写在类上面的@GPRequestMapping("/demo")
String baseUrl = “”;
if(clazz.isAnnotationPresent(GPRequestMapping.class)){
GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
baseUrl = requestMapping.value();
}
//默认获取所有的 public 方法
for (Method method : clazz.getMethods()) {
if(!method.isAnnotationPresent(GPRequestMapping.class)){continue;}
GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
//优化demo
//query
String url = ("/" + baseUrl + “/” + requestMapping.value()) .replaceAll("/+","/");
handlerMapping.put(url,method);
}
}
}

到这里位置初始化阶段就已经完成,接下实现运行阶段的逻辑,来看 doPost/doGet 的代码
@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 {
//6、调用,运行阶段
try {
doDispatch(req,resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write(“500 Exection,Detail : " + Arrays.toString(e.getStackTrace()));
}
doPost()方法中,用了委派模式,委派模式的具体逻辑在doDispatch()方法中
private void doDispatch(HttpServletRequest req, HttpServletResponse resp)throws Exception {
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath,”").replaceAll("/+","/");
if(!this.handlerMapping.containsKey(url)){
resp.getWriter().write(“404 Not Found!!”); return;
}
method = this.handlerMapping.get(url);
//第一个参数:方法所在的实例 //第二个参数:调用时所需要的实参
Map<String,String[]> params = req.getParameterMap();
//投机取巧的方式
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
method.invoke(ioc.get(beanName),new Object[]{
req,resp,params.get(“name”)[0]});
}
在以上代码中,doDispatch()虽然完成了动态委派并反射调用,但对url 参数处理还是静态代码。要实现url 参数的动

态获取,其实还稍微有些复杂。我们可以优化 doDispatch()方法的实现逻辑,代码如下:
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
//绝对路径
String url = req.getRequestURI();
//处理成相对路径
String contextPath = req.getContextPath(); url = url.replaceAll(contextPath,"").replaceAll("/+","/");
if(!this.handlerMapping.containsKey(url)){
resp.getWriter().write(“404 Not Found!!!”);
return;
}
Method method = this.handlerMapping.get(url);
//从 reqest 中拿到 url 传过来的参数
Map<String,String[]> params = req.getParameterMap();
//获取方法的形参列表
Class<?> [] parameterTypes = method.getParameterTypes();
Object [] paramValues = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i ++) {
Class parameterType = parameterTypes[i];
//不能用 instanceof,parameterType 它不是实参,而是形参
if(parameterType == HttpServletRequest.class){
paramValues[i] = req; continue;
}else if( parameterType == HttpServletResponse.class){
paramValues[i] = resp; continue;
}else if(parameterType == String.class){
GPRequestParam requestParam = (GPRequestParam)parameterType.getAnnotation(GPRequestParam.class);
if(params.containsKey(requestParam.value())) {
for (Map.Entry<String,String[]> param : params.entrySet()){
String value = Arrays.toString(param.getValue()) .replaceAll("\[|\]","") .replaceAll("\s",",");
paramValues[i] = value;
}
}
}
}
//投机取巧的方式
//通过反射拿到 method 所在 class,拿到 class 之后还是拿到 class 的名称
//再调用 toLowerFirstCase 获得 beanName
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
method.invoke(ioc.get(beanName),paramValues);
}
//url 传过来的参数都是 String 类型的,HTTP 是基于字符串协议
//只需要把 String 转换为任意类型就好
private Object convert(Class<?> type,String value){
//如果是 int
if(Integer.class == type){
return Integer.valueOf(value);
}
//如果还有 double 或者其他类型,继续加 if
return value;
}
zz_xpc

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值