1 eclipe安装sts插件
(当前使用的eclipse版本 version=4.7.3
)
help -> Eclipse Marketplace
等待插件安装完成,
2 新建项目
next之后,填写项目名称,如果是第一次构建项目,可以按照默认的来,然后看下构建出来的项目结构
然后next ,选择所需的支持,这里只选择web, 由于是通过maven下载的,选得越多,下载时间越长,可以抽个空闲时间,下载
然后选择finish即可 ,等待相关jar包的下载
构建完成的项目结构如下
构建出来的项目有3个文件时比较重要的, 对应上图的箭头指示部分
其中 pom.xml是maven项目做版本管理的,需要什么jar包,直接在里面添加
按照上面步骤构建的项目,内容如下:
<?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.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties 是springboot 默认的配置文件地址,可以将配置文件写到里面去 ,默认为空
DemoApplication.java 是程序的入口,名称生成的策略就是 项目名+Application ,项目名就是你在构建项目时填写的;这个类是最重要的,
默认内容如下
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3 启动解析
3.1注解
首先来看下这个启动类,只有一个注解,一个main方法,
首先里看这个注解 @SpringBootApplication, 直接查看其源码, 由于是maven项目,会自动下载源码,这个很方便
@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 {
...
}
可以看到这个注解被其它几个注解所修饰,相当于是 springboot使用这个注解,达到同时使用其它几个注解
其中,上面4个注解,是java.lang.annotation 包下的,是所有注解都要有的
详细一点的说明(均来自java API)
public @interface Documented
指示某一类型的注释将通过 javadoc 和类似的默认工具进行文档化。应使用此类型来注释这些类型的声明:其注释会影响由其客户端注释的元素的使用。如果类型声明是用 Documented 来注释的,则其注释将成为注释元素的公共 API 的一部分。
public @interface Inherited
指示注释类型被自动继承。如果在注释类型声明中存在 Inherited 元注释,并且用户在某一类声明中查询该注释类型,同时该类声明中没有此类型的注释,则将在该类的超类中自动查询该注释类型。此过程会重复进行,直到找到此类型的注释或到达了该类层次结构的顶层 (Object) 为止。如果没有超类具有该类型的注释,则查询将指示当前类没有这样的注释。
注意,如果使用注释类型注释类以外的任何事物,此元注释类型都是无效的。还要注意,此元注释仅促成从超类继承注释;对已实现接口的注释无效。
public @interface Retention
指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS。
只有元注释类型直接用于注释时,Target 元注释才有效。如果元注释类型用作另一种注释类型的成员,则无效。
public @interface Target
指示注释类型所适用的程序元素的种类。如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。如果存在这样的元注释,则编译器强制实施指定的使用限制。 例如,此元注释指示该声明类型是其自身,即元注释类型。它只能用在注释类型声明上:
@Target(ElementType.ANNOTATION_TYPE)
public @interface MetaAnnotationType {
...
}
下面的3个注释
@SpringBootConfiguration 源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
其实就相当于和 @configuratiion的功能一样,使用该注解之后,可以将这个类看做是 一个配置文件
例如
@Configuration
public class MyConfig {
@Bean
public MyService getUserService(){
UserService userService = new UserService();
userService.setUserDAO(null);
return userService;
}
@Bean
public MyDAO getUserDAO(){
return new UserDAO();
}
}
等价于xml的配置 ,
<beans>
<bean id = "MyService" class="com.shj.MyService">
<property name="userDAO" ref = "userDAO"></property>
</bean>
<bean id = "MyDAO" class="com.shj.MyDAO"></bean>
</beans>
@ComponentScan
相当于配置自动扫描
等价于spring的配置文件中的
<context:component-scan>
@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* 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 {};
}
查看文档中的文档说明,可以发现 这个注解的作用是 允许spring程序上下文的自动配置
可以帮助SpringBoot应用将所有符合条件的@Configuration
配置都加载到当前SpringBoot创建并使用的IoC容器。
3.2 类解析
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
查看run()
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
再查看返回时调用的run()
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
可以看到, 首先实例化了一个SpringApplication ,然后运行run()
首先来查看实例化的过程
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
真正调用的是这个
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
一共进行的操作有 :
判断项目类型
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
其中, 常量的定义
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org."
+ "springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
可能会出现三种结果:
1 WebApplicationType.REACTIVE - 当类路径中存在REACTIVE_WEB_ENVIRONMENT_CLASS并且不存在MVC_WEB_ENVIRONMENT_CLASS时
2 WebApplicationType.NONE - 也就是非Web型应用(Standard型),此时类路径中不包含WEB_ENVIRONMENT_CLASSES中定义的任何一个类时
3 WebApplicationType.SERVLET - 类路径中包含了WEB_ENVIRONMENT_CLASSES中定义的所有类型时
设置初始化器(Initializer)
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
查看代码
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
//避免重复的元素
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根据names进行实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//排序实例化后的对象
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
//根据完全限定名加载
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
项目启动很快,然后会发现没什么要显示的,可以做个简单的网页 输出点内容
package com.example.demo.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String index() {
return "hello boot";
}
}
重启项目,然后访问: http://127.0.0.1:8080/hello
有的小伙伴代码写得一样, 会访问不到,
这个的原因就是: 目录结构不对
这个很重要 ,可以参考官方推荐的项目结构: