Spring Boot入门教程(二)

@SpringBootApplication项目启动入口

1.先看注解@SpringBootApplication的用处,@SpringBootApplication作为整个项目的启动入口

package com.loafer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class StartApplication {
    public static void main(String[] args){
        SpringApplication.run(StartApplication.class,args);
    }
}

2.@SpringBootApplication注解的组成

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

由上可以看出@SpringBootApplication 中最重要的注解有三个:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan,其中@SpringBootConfiguration实际上是@Configuration。即@SpringBootApplication是一个复合注解,包括三个:@Configuration
@ComponentScan
@EnableAutoConfiguration

@Documented注解: Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。

@Inherited 注解:Inherited作用是,使用此注解声明出来的自定义注解,在使用此自定义注解时,如果注解在类上面时,子类会自动继承此注解,否则的话,子类不会继承此注解。这里一定要记住,使用Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效

@SpringBootConfiguration注解:

  1. @SpringBootConfiguration继承自@Configuration,二者功能也一致,标注当前类是配置类,
  2. 并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名

@EnableAutoConfiguration注解:借助@Import的支持,收集和注册特定场景相关的bean定义

  • @EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到IoC容器。
  • @EnableMBeanExport是通过@Import将JMX相关的bean定义加载到IoC容器。

而@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,仅此而已!

@EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

其中最关键的要属@Import(AutoConfigurationImportSelector.class),借助AutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。

借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

把 spring-boot-autoconfigure.jar/META-INF/spring.factories中每一个xxxAutoConfiguration文件都加载到容器中,spring.factories文件里每一个xxxAutoConfiguration文件一般都会有下面的条件注解:

@ConditionalOnClass : classpath中存在该类时起效
@ConditionalOnMissingClass : classpath中不存在该类时起效
@ConditionalOnBean : DI容器中存在该类型Bean时起效
@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
@ConditionalOnExpression : SpEL表达式结果为true时
@ConditionalOnProperty : 参数设置或者值一致时起效
@ConditionalOnResource : 指定的文件存在时起效
@ConditionalOnJndi : 指定的JNDI存在时起效
@ConditionalOnJava : 指定的Java版本存在时起效
@ConditionalOnWebApplication : Web应用环境下起效
@ConditionalOnNotWebApplication : 非Web应用环境下起效

SpringFactoriesLoader
SpringFactoriesLoader属于Spring框架私有的一种扩展方案(类似于Java的SPI方案java.util.ServiceLoader),其主要功能就是从指定的配置文件META-INF/spring-factories加载配置,spring-factories是一个典型的java properties文件,只不过Key和Value都是Java类型的完整类名,比如:

example.MyService=example.MyServiceImpl1,example.MyServiceImpl2

对于@EnableAutoConfiguration来说,SpringFactoriesLoader的用途稍微不同一些,其本意是为了提供SPI扩展的场景,而在@EnableAutoConfiguration场景中,它更多提供了一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfig.EnableAutoConfiguration作为查找的Key,获得对应的一组@Configuration类。

SpringFactoriesLoader是一个抽象类,类中定义的静态属性定义了其加载资源的路径public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories",此外还有三个静态方法:

loadFactories:加载指定的factoryClass并进行实例化。
loadFactoryNames:加载指定的factoryClass的名称集合。
instantiateFactory:对指定的factoryClass进行实例化。

在loadFactories方法中调用了loadFactoryNames以及instantiateFactory方法。

  public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryClass, "'factoryClass' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
        }

        List<T> result = new ArrayList(factoryNames.size());
        Iterator var5 = factoryNames.iterator();

        while(var5.hasNext()) {
            String factoryName = (String)var5.next();
            result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
        }

        AnnotationAwareOrderComparator.sort(result);
        return result;
    }

loadFactories方法首先获取类加载器,然后调用loadFactoryNames方法获取所有的指定资源的名称集合、接着调用instantiateFactory方法实例化这些资源类并将其添加到result集合中。最后调用AnnotationAwareOrderComparator.sort方法进行集合的排序。

将@SpringBootApplication注解替换成其等价的三个注解是完全的相等的,可以启动并正常访问。

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class StartApplication {
    public static void main(String[] args){
        SpringApplication.run(StartApplication.class,args);
    }
}

@Configuration
@Configuration 就是指的Java Config(Java 配置),是一个Ioc容器类,相当于传统项目中见到的一个spring 的xml配置文件。

一个空Java Config类 相当于一个空的xml配置文件,都是用来作为配置bean的容器

SpringMVC的XML配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
       default-lazy-init="true">
    <!--bean定义-->
</beans>

SpringBoot的配置

@Configuration
public class XxxConfiguration{
    //bean定义
}

在xml中配置一个bean,就等价于在Java Config中定义一个方法,使用@Bean注解就相当于xml中的bean标签,bean标签中的id就是方法的名称, bean标签中的class就是Java Config中的返回值,Java Config中的方法的内部就是创建一个实例

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
       default-lazy-init="true">
    <!--bean定义-->
    <bean id="xxxService" class="com.xxx.yyy.impl.XxxServiceImpl">
        ...
    </bean>
</beans>
@Configuration
public class XxxConfiguration{
    @Bean
    public XxxService xxxService(){
        return new XxxServiceImpl();
    }
}

xml中一个bean的属性需要引用另一个bean时,在Java Config中需要先定义出需要的依赖类,然后通过Setter方法或者构造方法给依赖的属性赋值,从而创建出一个完整的实例。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
       default-lazy-init="true">
    <!--bean定义-->
    <bean id="xxxService" class="com.xxx.yyy.impl.XxxServiceImpl">
        <propery name ="yyyService" ref="yyyService" />
    </bean>

    <bean id="yyyService" class="com.xxx.yyy.impl.YyyServiceImpl"/>
</beans>
@Configuration
public class XxxConfiguration{
    @Bean
    public XxxService xxxService(){
        // XxxService xxxService = new XxxServiceImpl();
        // xxxService.setYyyService(yyyService());

        return new XxxServiceImpl(yyyService());
    }

    @Bean
    public YyyService yyyService(){
        return new YyyServiceImpl();
    }
}

一个Java Config就相当于一个Spring配置文件,Java配置和spring配置文件两者都是一种Ioc容器,都是为了让Spring去加载定义好的类,放入到IoC容器中。

小案例

目录结构:

pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.loafer</groupId>
  <artifactId>springboot01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>springboot01</name>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

</project>

JAVA代码

项目启动类

@SpringBootApplication
public class StartApplication {
    public static void main(String[] args){
        SpringApplication.run(StartApplication.class,args);
    }
}
#配置类(将对象初始化)
@Configuration
public class BootConfiguration {

    @Bean
    public UserService userService(){
        return new UserServiceImpl(roleService());
    }

    @Bean
    public RoleService roleService(){
        return new RoleServiceImpl();
    }

}
#业务接口层
public interface RoleService {
    public void saveRole();
}

public interface UserService {
    public void save();
}

 

#业务接口实现层
public class RoleServiceImpl implements RoleService {
    @Override
    public void saveRole() {
        System.out.println("保存角色...");
    }
}


public class UserServiceImpl implements UserService {

    private RoleService roleService;

    public UserServiceImpl(RoleService roleService){
        this.roleService = roleService;
    }

    @Override
    public void save() {
        System.out.println("this is UserServiceImpl save mthod ...");
        roleService.saveRole();
    }
}

控制层

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/save")
    public void save(){
        System.out.println("开始UserController方法...");
        userService.save();
        System.out.println("结束UserController方法...");
    }
}

启动项目,再浏览器访问:http://localhost:8080/user/save

控制台会输出

源码下载地址:https://github.com/loafer7423/springboot/tree/master/springboot01

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值