spring 为什么可以一统江湖
spring 为什么可以一统江湖
答案很简单,所有的对象都要在容器里存活。可见spring的重要程度,并不亚于jvm。懂了spring的原理后!我们可以很优雅地去集成我们的代码。
Inversion of Contro 简称IOC 是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。也就是面向接口编程的思想。
简单的说就是使用配置的方式,修改程序代码的实现。能灵活的切换代码,不用修改逻辑代码。其实就是解决硬编码创建对象的问题。
学习的路线设定
- 了解有多少种方式添加对象到容器中
 
这个很重要,在工作中经常出现类找不到的异常,熟悉这个的话,问题很容易找到。
- 再从spring对一个类的处理的源码开始解析spring的原理
 
Spring配置有两种,详细法就可以参考一下Spring的使用教程。
xml配置与注解方式配置。其他本质是一样的。
- xml
 - 配置类上加Configuration
 
扫描加载bean的规则有,这些不是很重要.
- @bean
 
	<bean id="person" class="com.enjoy.cap1.Person">
		<property name="name" value="wolf"></property>
		<property name="age" value="19"></property>
	</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<Monkey>{
	@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;
	}
}
- 使用HdkImportBeanDefinitionRegistrar
 
public class HdkImportBeanDefinitionRegistrar 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);
		}
	}
}
- BeanDefinitionRegistryPostProcessor
 
public class JamesBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor{
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("JamesBeanDefinitionProcessor..postProcessBeanFactory(),Bean的数量"+beanFactory.getBeanDefinitionCount());
	}
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		System.out.println("amesBeanDefinition.postProcessBeanDefinitionRegistry()...bean的数量"+registry.getBeanDefinitionCount());
		//RootBeanDefinition rbd = new RootBeanDefinition(Moon.class);
		AbstractBeanDefinition rbd = BeanDefinitionBuilder.rootBeanDefinition(Moon.class).getBeanDefinition();//注册一个beandefinition
		registry.registerBeanDefinition("hello", rbd);
	}
}
Condition 条件加载,在spring boot中使用比较广泛,用来过滤加载到IOC容器中的bean
public class WinCondition implements Condition{
	/*
	*ConditionContext: 判断条件可以使用的上下文(环境)
	*AnnotatedTypeMetadata: 注解的信息
	*
	*/
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		// TODO 是否为WINDOW系统
		//能获取到IOC容器正在使用的beanFactory
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		//获取当前环境变量(包括我们操作系统是WIN还是LINUX??)
		Environment environment = context.getEnvironment();
		String os_name = environment.getProperty("os.name");
		if(os_name.contains("Windows")){
			return true;
		}
		return false;
	}
}
小结:
实现的spring包为:spring-context。上下文容器类都是继承了AbstractApplicationContext 类的。重点:开发时可以按需要自定义上下文。
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
AutowiredAnnotationBeanPostProcessor @autowired就是使用这个处理器去处理的,很多Bean处理的阶段,都是使用BeanPostProcessors去处理的,可以在所有的bean创建后第一时间处理bean的一系列动作
@Component
////每个对象创建时都会执行
public 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 {
                //init-method=init之后调用
		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的配置。

再看看DispatcherServlet是个什么玩意

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

dispatcherServlet将@Controller的bean与RequestMapping的value做映射HandlerMappings
- initStrategies 初始化相关信息

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

doService()
- 看看调用堆

 


    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

2 找到处理方法进行反射

3 调用到requestmapping的方法

设计模式发现

可适配类型

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

@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<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {
        List<Object> 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;
    }
虽然是顺序结构,但是调用执行顺序却是从下面开始的,类似递归算法那样。下往上执行。

执行拦截器链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的初始化。
spring 为什么可以一统江湖的更多相关文章
- 13、Cocos2dx 3.0游戏开发找小三之3.0中的Director :郝萌主,一统江湖
		
重开发人员的劳动成果.转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27706967 游戏中的基本元素 在曾经文章中.我们具 ...
 - 13、Cocos2dx 3.0三,找一个小游戏开发3.0中间Director :郝梦主,一统江湖
		
重开发人员的劳动成果.转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27706967 游戏中的基本元素 在曾经文章中,我们具 ...
 - 一统江湖的大前端(1)——PPT制作库impress.js
		
<一统江湖的大前端>系列是自己的学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有点O ...
 - 一统江湖的大前端(2)—— Mock.js + Node.js 如何与后端潇洒分手
		
<一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...
 - 一统江湖的大前端(3) DOClever——你的postman有点low
		
<一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...
 - 一统江湖的大前端(4)shell.js——穿上马甲我照样认识你
		
<一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...
 - 一统江湖的大前端(5)editorconfig + eslint——你的代码里藏着你的优雅
		
<一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...
 - 一统江湖的大前端(6)commander.js + inquirer.js——懒,才是第一生产力
		
<一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...
 - 一统江湖的大前端(7)React.js-从开发者到工程师
		
目录 一. 前端打怪升级指南 1.1 我应该从哪个框架开始学? 1.2 一次转职 1.3 二次转职 1.4 转职-其他 二. 为什么你应该学习React 2.1 技术栈的延伸 2.2 组件化开发 2. ...
 
随机推荐
- ss查看状态
			
ps -ef | grep ss-server | grep -v ps | grep -v grep
 - 【ZOJ3329】One Person Game
			
题意 你有三枚色子,第i个色子有ki面,你有一个计数器. 1.开始的时候将计数器调至0 2.扔三个色子,如果色子1是a,色子2是b,色子3是c,则将计数器归零.否则计数器加上三个色子的和. 3.如果计 ...
 - Linux网络配置之虚拟网卡的配置(ubuntu 16.04)
			
关于图形界面的配置,我这里就不多介绍了,这个很简单.这里介绍的是如何通过修改配置文件来实现虚拟网卡. 首先介绍ubuntu(我这里使用的是ubuntu-16.04)下虚拟网卡的配置 1.先用ifcon ...
 - C++学习--入口函数
			
在学习第一个C++程序的时候发现控制台程序的入口函数是int _tmain而不是main,查了资料才发现_tmain()是为了支持unicode所使用的main一个别名,宏定义在<stdafx. ...
 - Exception (3) Java exception handling best practices
			
List Never swallow the exception in catch block Declare the specific checked exceptions that your me ...
 - 引用数据类型(类)和ArrayList
			
引用数据类型(类) 类的类型为两种: 第一种,Java为我们提供好的类,如Scanner类,Scanner类等,这些已存在的类中包含了很多的方法与属性,可供我们使用. 第二种,我们自己创建的类,按照类 ...
 - Alpha冲刺(七)
			
Information: 队名:彳艮彳亍团队组长博客:戳我进入作业博客:班级博客本次作业的链接 Details: 组员1(组长)柯奇豪 过去两天完成了哪些任务 改用更易用的springboot+myb ...
 - JPA和Hibernate的相关使用技巧
			
介绍 尽管有SQL标准,但每个关系数据库终将是唯一的,因此你需要调整数据访问层,以便充分利用在使用中的关系数据库. 在本文中,我们将介绍在使用带有JPA和Hibernate的MySQL时,为了提高性能 ...
 - 【小梅哥SOPC学习笔记】SOPC开发常见问题及解决办法集锦
			
SOPC开发常见问题及解决办法集锦 一.Symbol 'NULL' could not be resolved 近期在评估使用NIOS II处理器进行项目的开发,我使用的软件是Quartus II 1 ...
 - 关于SoftReference的使用
			
SoftReference一般可以用来创建缓存的,缓存我们经常使用,例如:我们在浏览器中浏览了一个网页后,点击跳转到新的网页,我们想回去看之前的网页,一般是点击回退按钮,那么这个时候之前的网页一般就是 ...
 
			
		