实习修炼之第五天

SpringBoot的学习

SpringBoot启动原理

官方文档 https://www.breakyizhan.com/springboot/3032.html
轻量级的Spring经历了从xml配置到java注解配置再到现在的spring3.x的java配置(基于 Java 的配置能力),所有这些配置都代表了开发时的损耗。 因为在思考 Spring 特性配置和解决业务问题之间需要进行思维切换,所以写配置挤占了写应用程序逻辑的时间。SpringBoot的出现大大改善了这一缺点,SpringBoot简化了基于Spring的应用开发,只需要“run”就能创建一个独立的、生产级别的Spring应用。SpringBoot为Spring平台及第三方库提供开箱即用的设置(提供默认设置),这样我们就可以简单的开始。多数SpringBoot应用只需要很少的Spring配置。
下面是一个SpringBoot入口程序源代码


@Configuration
@SpringBootApplication
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
public class BootStrap extends WebMvcConfigurationSupport {

    public static void main(String[] args) {
        try {
            long start = System.currentTimeMillis();
            SpringApplication app = new SpringApplication(BootStrap.class, "classpath*:/spring/*.xml");
            ConfigurableApplicationContext appContext = app.run(args);
            BootStrap boot = appContext.getBean(BootStrap.class);
            List<HttpMessageConverter<?>> converts = boot.getMessageConverters();
            HeaderHttpMessageConverter convert = appContext.getBean(HeaderHttpMessageConverter.class);
            // 清除无效convert(非必须)
            converts.clear();
            // 添加唯一的convert , 避免多余的检测
            converts.add(convert);
            long end = System.currentTimeMillis();

            System.out.println("admin-web started in " + (end - start) + "ms .");

        } catch (Exception e) {
            e.printStackTrace();
            System.exit(0);
        }
    }
}

SpringBootApplication
不难注意到上述代码部分有三个注解;
其中@Configuration从Spring3.0用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
然而在这个启动类中最为耀眼和重要的还是@SpringBootApplication以及run方法。
从idea中查看SpringBootApplication注释源代码,如下所示,可以看到这个注解被 @SpringBootConfiguration、@EnableAutoConfiguration以及@ComponentScan 注解所修饰(虽然源代码中有很多注解,但实际上还是这三个最重要)。我在下面源代码部分加了相应用途注释。其实也就是说可以选择不用SpringBootApplication注解,而是用下面的注解,整个SpringBoot应用依然可以与之前的启动类功能对等。

@Configuration
@EnableAutoConfiguration
@ComponentScan

/**
 * Indicates a {@link Configuration configuration} class that declares one or more
 * {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
 * auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
 * annotation that is equivalent to declaring {@code @Configuration},
 * {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
 *
 * @author Phillip Webb
 * @author Stephane Nicoll
 * @since 1.2.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 继承了Configuration,表示当前是注解类
@SpringBootConfiguration         		
// 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@EnableAutoConfiguration	
// 扫描路径设置(具体使用待确认)
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
public @interface SpringBootApplication {

   /**
    * Exclude specific auto-configuration classes such that they will never be applied.
    * @return the classes to exclude
    */
   Class<?>[] exclude() default {};

   /**
    * Exclude specific auto-configuration class names such that they will never be
    * applied.
    * @return the class names to exclude
    * @since 1.3.0
    */
   String[] excludeName() default {};

   /**
    * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
    * for a type-safe alternative to String-based package names.
    * @return base packages to scan
    * @since 1.3.0
    */
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
   String[] scanBasePackages() default {};

   /**
    * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
    * scan for annotated components. The package of each class specified will be scanned.
    * <p>
    * Consider creating a special no-op marker class or interface in each package that
    * serves no purpose other than being referenced by this attribute.
    * @return base packages to scan
    * @since 1.3.0
    */
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
   Class<?>[] scanBasePackageClasses() default {};

}

run方法
上述介绍的SpringBootApplication注解是对Spring的启动原理的一个简单入门介绍,对于一个SpringBoot应用,主要的实现还是依靠run方法,阅览run方法源代码如下所示,并且相应语句实现的功能加了对应注释,

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.started();
		try {
		     //封装命令行参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			//准备环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
					//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
			Banner printedBanner = printBanner(environment);
			 //创建ApplicationContext;决定创建web的ioc还是普通的ioc
			context = createApplicationContext();
			 //异常报告
			analyzers = new FailureAnalyzers(context);
			//准备上下文环境;将environment保存到ioc中;而且applyInitializers();
      		 //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
     		  //回调所有的SpringApplicationRunListener的contextPrepared();
      		 //prepareContext运行完成以后回调所有的 SpringApplicationRunListener的contextLoaded();
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			//s刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版;扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
			refreshContext(context);
			//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
       		//ApplicationRunner先回调,CommandLineRunner再回调
			afterRefresh(context, applicationArguments);
			//所有的SpringApplicationRunListener回调finished方法
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			//整个SpringBoot应用启动完成以后返回启动的ioc容器;
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		}
	}

从上面的源码可以总结出,run方法的主要执行流程是:

  1. 创建一个SpringApplication的实例并对其进行初始化,对应上述源代码的2~6行
  2. 对SpringApplication实例初始化完成且完成设置后,就开始执行run方法的逻辑了,最开始首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些RunListener本SpringBoot应用要开始执行了。对应源代码7、8行
  3. 然后就开始创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。对应上述源代码11~16
  4. 如果SpringApplication的showBanner属性被设置为true,则打印banner(默认的启动图案)。对应源代码17行
  5. 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成。18~19
  6. ApplicationContext创建好之后进行以下步骤,源代码21~26

将environment(之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置)保存到ioc中(这是最核心的一步骤),准备上下文环境;
applyInitializers():查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize方法对已经创建好的ApplicationContext进行进一步的处理。
回调所有的SpringApplicationRunListener的contextPrepared();主要代码是listener.contextLoaded(context);
prepareContext运行完成以后还会回调所有的 SpringApplicationRunListener的contextLoaded();

  1. 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。对应代码28、29
  2. 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。30~32
  3. 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)
    进一步拓展学习可参考 https://www.cnblogs.com/shamo89/p/8184960.html
    以及 https://www.cnblogs.com/MagicAsa/p/10745447.html

Spring常用的四大注解

1.@Componet
是所有受Spring 管理组件的通用形式,@Component注解可以放在类的头上,用于把普通pojo实例化到spring容器中,相当于配置文件中的

<bean id="" class=""/>

2.@Repository 持久层
@Repository对应数据访问层Bean;@Repository(value=“xxDao”)注解是告诉Spring,让Spring创建一个名字叫“xxDao”的XxDaoImpl实例。
当Service需要使用Spring创建的名字叫“xxDao”的XxDaoImpl实例时,就可以使用@Resource(name = “xxDao”)注解告诉Spring,Spring把创建好的xxrDao注入给Service即可。

3.@Service 业务层
@Service对应的是业务层Bean;如代码
@Service("xxService") public class XxServiceImpl implements XxService { ……… }
@Service(“xxService”)注解是告诉Spring,当Spring要创建XxServiceImpl的的实例时,bean的名字必须叫做"xxService",这样当Action需要使用XxServiceImpl的的实例时,就可以由Spring创建好的"xxService",然后注入给Action:在Action只需要声明一个名字叫“xxService”的变量来接收由Spring注入的"xxService"即可,具体代码如下:// 注入xxService @Resource(name = "xxService") private XxService x'xService;
需要注意的是

:在Action声明的“xxService”变量的类型必须是“XxServiceImpl”或者是其父类“XxService”,否则由于类型不一致而无法注入;
控制反转:Action由原来的自己创建XxServiceImpl实例后就可以马上使用,变成了被动等待Spring创建好XxServiceImpl实例之后再注入给Action再使用。这说明Action对“XxServiceImpl”类的“控制权”已经被“反转”了,原来主动权在自己手上,自己要使用“XxServiceImpl”类的实例,自己主动去new一个出来马上就可以使用了,但现在只有Spring才能够new“XxServiceImpl”类的实例,而Action只能等Spring创建好“XxServiceImpl”类的实例后,再“恳求”Spring把创建好的“XxServiceImpl”类的实例给他,这样他才能够使用“XxServiceImpl”,这就是Spring核心思想“控制反转”
依赖注入:Spring把Acion需要依赖的XxServiceImpl注入(也就是“给”)给Action,这就是所谓的“依赖注入”。

4.@Controller WEB层
@Controller对应表现层的Bean,也就是Action,在使用@Controller注解标识xxAction之后,就表示要把xxAction交给Spring容器管理,在Spring容器中会存在一个名字为"xxAction"的action,这个名字是根据xxAction类名来取的。

注意:如果@Controller不指定其value【@Controller】,则默认的bean名字为这个类的类名首字母小写,如果指定value【@Controller(value=“XxAction”)】或者【@Controller(“XxAction”)】,则使用value作为bean的名字。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值