框架和中间件(Spring BootSpringSpringMVC)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了框架和中间件(Spring BootSpringSpringMVC)相关的知识,希望对你有一定的参考价值。

框架和中间件

Spring Boot

AutoConfiguration 自动配置类,启动核心

导入的类是注解实现的关键

Spring

依赖对象的获得被反转了
例如,要获得一个Person对象,朴素的方法是取new 一个Person对象
而在控制反转,把创建对象的权利交给了别人,别人给你一个对象,直接拿来用就可以了(不是所有的对象都不用自己创建了,而是核心的,能够复用的对象由别人创建)
在编程中,有很多对象需要共用,之间有耦合、依赖关系,如果自己去维护,可能会出现问题,有可能业务发生变化的时候,会很麻烦;如果有了依赖注入,交给别人去管理,可以帮我们解决

框架的开发者,用BeanFcatory,本质上是工厂,用来生产bean的
框架的使用者,用ApplicationContext

装载bean,bean未必来源于磁盘上,jar包,也可能来源于内存,网络,数据库,动态代理创建bean
bean的获取需要有人来完成,resourceLoader加载resource,从哪里加载resource接口有不同的实现,resourceLoader从不同的渠道把所要的bean信息加载到内存中,然后把信息读到以后,封装到beanDefinition里,于是就得到了抽象的bean

可以清晰的看到这里接口里面的方法,是判断资源的类型,是对资源访问的定义,可见,它代表的是一类资源

ResourceLoader用来加载resource
用这两个接口就可以把资源加载到内存里

BeanDefinition是对所有bean的一个抽象

BeanDefinition抽象的实现AbstractBeanDefiniton

构造器,初始化bean的过程

初始化完以后,由BeanDefinitionRegistry注册到容器里,其实就是通过容器的方法,装到HashMap里
存放和删除的方法:

刚刚是bean初始化和放到容器里面
但是bean之间还有依赖关系,依赖注入是重中之重
从左到右调用

BeanFactory和FactoryBean有什么关系?在这里体现
缓存中没有,从当前工厂找,找不到,从父亲里找
mdb就是beanDefinition

FactoryBean不是简单的bean,它能修饰别的bean,能产生别的bean

例子:
首先一个普通的类AlphaObject,没有任何注解,启动的时候当然是不会放到spring容器里,

package com.nowcoder.seckill;

public class AlphaObject {

    private String info;

    public AlphaObject(String info) {
        this.info = info;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "AlphaObject{" +
                "info='" + info + '\\'' +
                '}';
    }

}

实现了FactoryBean接口,实际要产生的对象是AlphaObject这种类型的
现在有了这个注解,实现了这个接口,spring不会把AlphaObjectFactoryBean装到容器里,而是把接口所产生的对象装到容器里,装的是第一个方法返回的对象

所以FactoryBean的作用是装载一个特殊的对象,而不是这个对象本身

但是为什么不直接在那个类上加注解,放到容器里呢。这是因为有时候那个类可能不是自己写的,不能改,所以写这样一个类,由这个类来实现装到容器里;或者AOP底层是由动态代理实现的,如果动态代理现生成一个对象,可以用这种方式来处理
FactoryBean是为了装载比较复杂的,不好创建的目标bean

package com.nowcoder.seckill;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component("alphaObject")
public class AlphaObjectFactoryBean implements FactoryBean<AlphaObject> {

    @Override
    public AlphaObject getObject() throws Exception {
        System.out.println("初始化AlphaObject...");
        return new AlphaObject("alpha");
    }

    @Override
    public Class<?> getObjectType() {
        return AlphaObject.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public String toString() {
        return "AlphaObjectFactoryBean{}";
    }
}

测试类

package com.nowcoder.seckill;

import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

@SpringBootTest
public class AlphaObjectFactoryBeanTest implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Autowired
    private AlphaObject alphaObject;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Test
    public void test1() {
        // alphaObject由AlphaObjectFactoryBean产生
        System.out.println(alphaObject);
        // 通过name取到的是目标Bean
        System.out.println(applicationContext.getBean("alphaObject"));
        // 通过&name取到的是FactoryBean本身
        System.out.println(applicationContext.getBean("&alphaObject"));
    }

}

例如,要在很多方法开始的时候记录日志,我们可以把这个分散的关注点分离出来,封装一下,不在每一个地方分散的调用了
切面就是分散的关注点

被代理的对象:就是分散的对象,有共同需求的分散的对象
连接点:被拦截对象的方法
切面:解决问题的类,在统一的类中编程,这个位置就叫做切面

无接口的bean用CGLIB动态代理

例子:

package example;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyDemo {

    public static void main(String[] args) {
        Vehicle v = new Car();
        //创建一个代理对象,传入对象的类加载器和接口,还有这个对象
        Vehicle p = (Vehicle) Proxy.newProxyInstance(
                v.getClass().getClassLoader(), v.getClass().getInterfaces(), new VehicleProxy(v));
        p.run(100);
    }

}

interface Vehicle {

    void run(int speed);

}

class Car implements Vehicle {

    @Override
    public void run(int speed) {
        System.out.println("The car is running at " + speed + " km/h.");
    }

}

class VehicleProxy implements InvocationHandler {

    private Object target;

    public VehicleProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 代理对象
        System.out.println(proxy.getClass().getName());
        // 代理方法
        System.out.println(method.getName());
        // 方法参数
        System.out.println(args == null ? null : args[0]);

        System.out.println("before ... ");
        Object obj = method.invoke(target, args);
        System.out.println("after  ...");

        return obj;
    }

}

输出:

Spring MVC

Spring最终要执行的是Controller,执行的过程中会受到拦截器的拦截Interceptor
拦截器和Controller有什么关系,Controller由谁来调用,涉及到哪些组件呢

中间的DispathcherServlet是分发请求的,所有请求进来提交给DispathcherServlet,根据请求的路径url把请求分发给不同Controller的不同方法,是SpringMVC的核心

这个过程有4步:
第一步,遍历HandlerMapping,里面封装了URL和控制器的映射关系。SpringMVC也有自动配置类,自动配置类触发带有Controller注解的扫描,扫描到以后就会存到这里map里面。请求来了,就去找和当前路径匹配的HandlerMapping,里面不止有方法,还有拦截器
然后封装成一个chain,就知道先调谁后调谁

第二步:根据信息,实例化HandlerAdapter,也就是Controller,在控制器前后,可能有拦截器拦截,在这之前,调用拦截器的preHandler方法。调用完成以后,返回ModelAndView对象,里面封装了数据和模板路径

第三步:现在知道了视图在哪里,模板在哪里,数据是什么,然后调用ViewResolver,把信息给它,装到模板里去,然后调用view返回给客户端

第四步:在finally里面,调用afterComletion()方法

以上是关于框架和中间件(Spring BootSpringSpringMVC)的主要内容,如果未能解决你的问题,请参考以下文章

spring的SOA应用框架的搭建

Gin框架源码解读(一)

SpringCloud - Spring Cloud 之 Stream构建消息驱动微服务框架;Spring Cloud Alibaba集成RocketMQ(二十四)

SpringCloud - Spring Cloud 之 Stream构建消息驱动微服务框架(十九)

spring cloud bus原理总结

JavaWeb_(Spring框架)Spring中IoC与DI概念入门