博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring 必知
阅读量:5049 次
发布时间:2019-06-12

本文共 12402 字,大约阅读时间需要 41 分钟。

spring 为什么可以一统江湖

答案很简单,所有的对象都要在容器里存活。可见spring的重要程度,并不亚于jvm。懂了spring的原理后!我们可以很优雅地去集成我们的代码。

Inversion of Contro 简称IOC 是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。也就是面向接口编程的思想。
简单的说就是使用配置的方式,修改程序代码的实现。能灵活的切换代码,不用修改逻辑代码。其实就是解决硬编码创建对象的问题。

学习的路线设定

  • 了解有多少种方式添加对象到容器中

    这个很重要,在工作中经常出现类找不到的异常,熟悉这个的话,问题很容易找到。

  • 再从spring对一个类的处理的源码开始解析spring的原理

Spring配置有两种,详细法就可以参考一下Spring的使用教程。

xml配置与注解方式配置。其他本质是一样的。

  • xml
  • 配置类上加Configuration

扫描加载bean的规则有,这些不是很重要.

  • @bean
或在Configuration类中使用,现在的spring boot都是用@Configuration方式的 @Configuration@Import(value = { Dog.class })public class Cap6MainConfig {}
  • @@Component与@ComponentScan(value = "bgy.bean")

    一般用这种,在类上加@Component就可以ComponentScan加载到容器中

@Component     public class Dog{}
  • @@Import(bgy.beanout.Fly.class) 单独加入
@Configuration@Import(value = { Dog.class })public class Cap6MainConfig {}
  • 实现ImportSelector
public class MyImportSelector implements ImportSelector{    @Override    public String[] selectImports(AnnotationMetadata importingClassMetadata){        //返回全类名的bean        return new String[]{"com.enjoy.cap6.bean.Fish","com.enjoy.cap6.bean.Tiger"};    }}//把MyImportSelector当成bean导入@Import(value = {ImportSelector.class})
  • 使用FactoryBean
public class JamesFactoryBean implements FactoryBean
{ @Override public Monkey getObject() throws Exception { // TODO Auto-generated method stub return new Monkey(); } @Override public Class
getObjectType() { // TODO Auto-generated method stub return Monkey.class; } @Override public boolean isSingleton() { return true; }}
  • 使用JamesImportBeanDefinitionRegistrar
public class JamesImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {    /*    *AnnotationMetadata:当前类的注解信息    *BeanDefinitionRegistry:BeanDefinition注册类    *    把所有需要添加到容器中的bean加入;    *    @Scope    */    @Override    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {        boolean bean1 = registry.containsBeanDefinition("com.enjoy.cap6.bean.Dog");        boolean bean2 = registry.containsBeanDefinition("com.enjoy.cap6.bean.Cat");        //如果Dog和Cat同时存在于我们IOC容器中,那么创建Pig类, 加入到容器        //对于我们要注册的bean, 给bean进行封装,        if(bean1 && bean2){            RootBeanDefinition beanDefinition = new RootBeanDefinition(Pig.class);            registry.registerBeanDefinition("pig", beanDefinition);        }    }}

小结:

实现的spring包为:spring-context。上下文容器类都是继承了AbstractApplicationContext 类的。重点:开发时可以按需要自定义上下文。

277970-20190422170533268-1451223559.png

spring 源码解析

从加载容器开始

ApplicationContext applicationContext=new AnnotationConfigApplicationContext(myconfig.class);       // ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans.xml");

上下文抽象类,此设计用的是模板方法设计模式, 在refresh里 固定好加载时的方法调用顺序。

public void refresh() throws BeansException, IllegalStateException {        Object var1 = this.startupShutdownMonitor;        synchronized(this.startupShutdownMonitor) {            this.prepareRefresh();            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//加载 bean的配置定义            this.prepareBeanFactory(beanFactory);            try {                this.postProcessBeanFactory(beanFactory);                this.invokeBeanFactoryPostProcessors(beanFactory);//如果有配置类则添加配置类处理器,并将优先级排为最高                this.registerBeanPostProcessors(beanFactory);//注册其他的处理器,并设置好处理顺序。                this.initMessageSource();                this.initApplicationEventMulticaster();                this.onRefresh();                this.registerListeners();                this.finishBeanFactoryInitialization(beanFactory);//注册所有注入进来的bean实例到容器中                this.finishRefresh();            } catch (BeansException var9) {                if(this.logger.isWarnEnabled()) {                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);                }                this.destroyBeans();                this.cancelRefresh(var9);                throw var9;            } finally {                this.resetCommonCaches();            }        }    }

BeanPostProcessors

后置处理器接口,实现了这个接口类可以对所有的类初始化方法进行拦截处理,分别为:postProcessBeforeInitialization,postProcessAfterInitialization

@Componentpublic class MyBeanPostProcessor implements BeanPostProcessor{    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        //返回一个的对象(传过来的对象)        //在初始化方法调用之前进行后置处理工作,        //什么时候调用它: init-method=init之前调用        System.out.println("postProcessBeforeInitialization...."+beanName+"..."+bean);        return bean;    }    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        System.out.println("postProcessAfterInitialization...."+beanName+"..."+bean);        return bean;    }}

ApplicationContextAware

使用实现了这个接口的bean都可以获取到容器原上下文。下面讲到的spring mvc中就用到这个方法。

public interface ApplicationContextAware extends Aware {    void setApplicationContext(ApplicationContext var1) throws BeansException;}

bean中

public void setApplicationContext(ApplicationContext applicationContext) {        if(this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {            this.webApplicationContext = (WebApplicationContext)applicationContext;            this.webApplicationContextInjected = true;        }    }

spring 应用组件扩展

spring mvc 原理

先用一句话总结:通过url请求路径,反射调用容器中的bean方法。

首先从问题开始

  • 请求路径是如何定位到一个servlet的?

    这个问题开始学servlet时就会的。在web.xml配置一下就行了!其实就是一个map,key=请求路径  value=servlet。

    就可以快速找到浏览器上传过来的请求路径。这里重要不是讲servlet,感兴趣的可以看看tomcat的源码。http如果到servlet的。

  • 请求如何找到MVC中的@RequestMapping("view”)方法呢?

  • 在mvc的web.xml找到servlet的配置。

    277970-20190505160922503-86064480.png

  • 再看看DispatcherServlet是个什么玩意

    277970-20190505161431883-27296348.png

tomcat的所有servlet请求都会经过HttpServlet.service,MVC中的DispatcherServlet类就是一个实现了HttpServlet接口中的一个类型。DispatcherServlet.service中的方法的触> 发将从tomcat中发起。DispatcherServlet中将会获取请求的上下文。

容器类将加载@Controller的类型的实例加载到容器中。@Controller其实就是一个@Component

277970-20190505161938867-56042575.png

dispatcherServlet将@Controller的bean与RequestMapping的value做映射HandlerMappings

  • initStrategies 初始化相关信息
    277970-20190505165024696-1254280862.png

initHandlerMappings

将所有的RequestMapping的value与controller加到handlerMappings中。后面通过放射调用标注了RequestMapping的方法。

private List handlerMappings;

277970-20190505184911420-1750259645.png

doService()

  • 看看调用堆
    277970-20190505185708909-1117177967.png

277970-20190505190324153-1375665988.png

277970-20190505190448375-1529247957.png

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        AnnotationMethodHandlerAdapter.ServletHandlerMethodResolver methodResolver = this.getMethodResolver(handler);        Method handlerMethod = methodResolver.resolveHandlerMethod(request);//获取到controller中的方法        AnnotationMethodHandlerAdapter.ServletHandlerMethodInvoker methodInvoker = new AnnotationMethodHandlerAdapter.ServletHandlerMethodInvoker(methodResolver);        ServletWebRequest webRequest = new ServletWebRequest(request, response);        ExtendedModelMap implicitModel = new BindingAwareModelMap();        Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);        ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);        methodInvoker.updateModelAttributes(handler, mav != null?mav.getModel():null, implicitModel, webRequest);        return mav;    }

1 找到处理类controller

277970-20190505192439431-563761265.png

2 找到处理方法进行反射

277970-20190505193109814-272218305.png

3 调用到requestmapping的方法

277970-20190505193209615-2019906922.png

设计模式发现

277970-20190506005709987-148228515.png

可适配类型

277970-20190506005835008-1045149887.png

下面用实现controller接口的方式实现请求处理类。这种方式 使用的是SimpleControllerHandlerAdapter这种适配器,servlet的话就会使用SimpleServletHandlerAdapter

277970-20190506010023272-877306189.png

@Component@RequestMapping("index")public class TestController implements org.springframework.web.servlet.mvc.Controller {        @Override    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {        return new ModelAndView("index.jsp");    }}

总结

1加载:将requestmapping与controller类初始化到一个handlerMappings

2处理请求:通过request在handlerMappings中找到相应的controller与requestmapping的方法,反射调用方法。

spring aop

在Spring中,AOP是一个比较重要的组件,主要用于日志处理,如:记录操作日志,记录异常日志等。

原理也很简单,主要关注所有方法的调用前,中,后,还有异常。很明显适用代理模式。项目中的bean这么多,当然不能为每个bean写个代理实现类,静态代理肯定不行。所以用jdk的动态代理或CGLIB的动态代理来创建这些代理对象是唯一的选择。在spring中使用后置处理器,为每个bean创建代理对象,所以当程序调用bean时已经不是以前那个bean了!已经是带有增强的代理实例来的了。

通知方法

  • 前置通知: logStart(),在目标方法(div)运行之前运行 (@Before)
  • 后置通知:logEnd(), 在目标方法(div)运行结束之后运行,无论正常或异常结束 (@After)
  • 返回通知:logReturn, 在目标方法(div)正常返回之后运行 (@AfterReturning)
  • 异常通知:logException, 在目标方法(div)出现异常后运行(@AfterThrowing)
  • 环绕通知:@Around以上没写,动态代理, 手动执行目标方法运行joinPoint.procced(),最底层通知,

原理

  • 通过@Pointcut("execution(public int com.enjoy.cap10.aop.Calculator.*(..))") 配置找到要代理拦截的方法。

  • DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice 生成拦截链加到list遍历调用。
public List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class
targetClass) { List interceptorList = new ArrayList(config.getAdvisors().length); Class
actualClass = targetClass != null?targetClass:method.getDeclaringClass(); boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); Advisor[] var8 = config.getAdvisors(); int var9 = var8.length; for(int var10 = 0; var10 < var9; ++var10) { Advisor advisor = var8[var10]; MethodInterceptor[] interceptors; if(advisor instanceof PointcutAdvisor) { PointcutAdvisor pointcutAdvisor = (PointcutAdvisor)advisor; if(config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { interceptors = registry.getInterceptors(advisor); MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); if(MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { if(mm.isRuntime()) { MethodInterceptor[] var15 = interceptors; int var16 = interceptors.length; for(int var17 = 0; var17 < var16; ++var17) { MethodInterceptor interceptor = var15[var17]; interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); } } else { interceptorList.addAll(Arrays.asList(interceptors)); } } } } else if(advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor)advisor; if(config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } else { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } return interceptorList; }
  • 虽然是顺序结构,但是调用执行顺序却是从下面开始的,类似递归算法那样。下往上执行。

    277970-20190514175317781-1012701860.png

  • 执行拦截器链ReflectiveMethodInvocation.proceed()
public Object proceed() throws Throwable {        if(this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {            return this.invokeJoinpoint();        } else {            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);            if(interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {                InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;                return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)?dm.interceptor.invoke(this):this.proceed();            } else {                return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);            }        }    }

总结

spring 是管理bean的一个框架,在面向对象编程中,一个程序是由一个或多个对象组成的,spring就是用来管理这些对象的

,spring把这些对象抽象成bean,bean的生命周期分三个阶段:创建,初始化,销毁.
创建:spring把bean的定义描述从xml或class读出来,创建好放到容器中。
spring为bean抽象了两个方法:初始化,销毁。
初始化:可以按一定的逻辑去动态设置bean的初始化属性状态,或者执行其他的方法。
销毁:在销毁前执行其他的方法。
在开发过程中如果需要按一定的逻辑去处理bean的话,使用配置的方法比较麻烦,每个bean都是实现初始化方法。
spring 提供了一种后置处理器接口BeanPostProcessor,实现了这个接口就可以拦截初始化方法来统一处理。
如:BeanValidationPostProcessor验证bean,或ServletContextAwareProcessor servlet中的上下文档context的初始化。

转载于:https://www.cnblogs.com/wolf12/p/10751232.html

你可能感兴趣的文章
查找 EXC_BAD_ACCESS 问题根源的方法
查看>>
国外媒体推荐的5款当地Passbook通行证制作工具
查看>>
日常报错
查看>>
list-style-type -- 定义列表样式
查看>>
hibernate生成表时,有的表可以生成,有的却不可以 2014-03-21 21:28 244人阅读 ...
查看>>
mysql-1045(28000)错误
查看>>
Ubuntu 编译出现 ISO C++ 2011 不支持的解决办法
查看>>
1.jstl c 标签实现判断功能
查看>>
Linux 常用命令——cat, tac, nl, more, less, head, tail, od
查看>>
超详细的Guava RateLimiter限流原理解析
查看>>
VueJS ElementUI el-table 的 formatter 和 scope template 不能同时存在
查看>>
Halcon一日一练:图像拼接技术
查看>>
Swift - RotateView
查看>>
iOS设计模式 - 中介者
查看>>
centos jdk 下载
查看>>
HDU 1028 Ignatius and the Princess III(母函数)
查看>>
(转)面向对象最核心的机制——动态绑定(多态)
查看>>
token简单的使用流程。
查看>>
django创建项目流程
查看>>
UIActionSheet 修改字体颜色
查看>>