第0章:说明
为了进行服务的开发,简单介绍一下Spring Boot的使用
源码所在位置
第1章:起步
这里会介绍一个最基本的服务,体验一下Spring Boot进行开发的便捷性,学习一下如何指定web端口
具体代码:springbootDemo1
第2章:Spring之Java Config
这一节我们将学习一下Spring的工作机制,便于未来我们更好的进行服务开发。源码所在位置
- spring容器的简单讲解(为JavaConfig进行铺垫)
- 如何使用Java Config
Spring框架的本质
我们都知道Spring主要包括IoC和AOP,对Java应用技术进行了合理的设计和封装。这里主要讲解一下IoC的内容。
Spring IoC大体有两个步骤组成:
- 步骤一:收集和注册
下面是传统的做法
<!-- 注册一个bean的配置文件 -->
<bean id="myTestService" class="..myTestServiceImpl">
</bean>
<!-- <!-- 注册一个bean的配置文件 --> -->
<context:component-scan base-package="com.test"/>
- 步骤二:分析和组装
在完成第一阶段任务之后,IoC容器中充斥着一个个独立的Bean,他们之间没有任何关系。事实上这是不可能的,他们应该有依赖关系的,所有第二阶段就是根据依赖关系相互组装起来。
下面是通过配置的做法
<bean id="myDependencyTest" class="..ServiceImpl">
<property name="dependencyService" ref="myTestService" />
</bean>
总结一下:使用配置文件的方法
- 书写一个配置文件
- 注册Bean
- 定义依赖关系
相比较繁琐的配置文件,使用JavaConfig来实现上面的功能更加清爽。这也是Spring boot的核心逻辑之一
下面让我们看看JavaConfig如何进行对象的注入
- 书写一个配置文件等价于
@Configuration
public class TestConfiuration {
}
- 注册Bean等价于
@Configuration
public class TestConfiuration {
@Bean
public TestService myService(){
return new TestService();
}
}
- 定义依赖关系
@Configuration
public class TestConfiuration {
@Bean
public TestLogic myLogik(){
return new TestLogic(myService());
}
@Bean
public TestService myService(){
return new TestService();
}
}
在容器中bean的名字是方法的名字,可以通过下面的代码验证一下!
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringBootRunner.class, args);
Object o = context.getBean("myService");
System.out.println(o.getClass().getName());
o = context.getBean("myLogik");
System.out.println(o.getClass().getName());
}
常用的注解
- @ComponentScan
对应XML配置文件中的<context:component-scan>的元素
@ComponentScan(basePackages = { "me.helllp.demo.springbootDemo2, me.helllp.demo.test" })
默认情况下搜索的包是应用所在的包,如果我们想指定其他的包就需要自己配置一下
- @PropertySource,@PropertySources
指定从某些地方加载*.properties文件的内容
@Configuration
@PropertySource("classpath:me/helllp/demo/test/test.properties")
public class SomeConfig {
@Value("${test.name}")
private String user;
@Value("${test.pass}")
private String pass;
@Bean
public String getDemoBean(){
return user + " " + pass;
}
}
- @Import, @ImportResource
类比配置文件中的<import soruce="abc.xml"/>,可以将多个分开的容器配置和到一个配置中
基于条件装配
我们介绍了自动配置,还可以基于条件进行配置,org.springframework.boot.autoconfigure.condition包下条件Annotation就可以完成条件装配
@ConditionalOnClass:当类路径下有指定的类的条件下。
@ConditionalOnExpression:基于SpEL 表达式作为判断条件。
@ConditionalOnJava:基于JVM 版本作为判断条件。
@ConditionalOnJndi:在JNDI 存在的条件下查找指定的位置。
@ConditionalOnMissingBean:当容器里没有指定Bean 的情况下。
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下。
@ConditionalOnNotWebApplication:当前项目不是Web 项目的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值。
@ConditionalOnResource:类路径是否有指定的值。
@ConditionalOnSingleCandidate:当指定Bean 在容器中只有一个,或者虽然有多个但是指定首选的Bean。
@ConditionalOnWebApplication:当前项目是Web 项目的条件下。
...
调整配置顺序
org.springframework.boot.autoconfigure.AutoConfigureAfter
org.springframework.boot.autoconfigure.AutoConfigureBefore
org.springframework.boot.autoconfigure.AutoConfigureOrder
第3章:Spring Boot工作原理
源码所在位置
启动Spring boot只需要做两件事:
- 一个@SpringBootApplication注解
- 一个应用启动:SpringApplication.run
下面我们分别来说明一下:
@SpringBootApplication
这个注解是由三个主要注解组成的
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
事实上:@SpringBootConfiguration 就是我们熟悉的 @Configuration;@ComponentScan我们上面已经说过了,我们把重点放到@EnableAutoConfiguration上,它告诉我们要自动进行配置
它也是一个复合注解(下文只显示最重要的内容),通过@Import将收集和注册特定的Bean
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
借助EnableAutoConfigurationImportSelector,将帮助Spring boot应用将所有符合条件的@Configuration配置都加载到当前。需要借助一个Spring框架的工具类:SpringFactoriesLoader的支持。
执行的结果:查找依赖包中的META-INF/spring.factories配置文件,将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
SpringApplication 程序启动
SpringApplication将程序启动流程“模板化”,同时提供了不同类型的各种扩展点可供开发者设置。我们查看一下setter系列函数就可以知道有哪些扩展点。
启动流程大体分为如下几步:
- 构造SpringApplication 实例对象
判断是否是web环境
使用SpringFactoriesLoader查找并加载可用的ApplicationContextInitializer
使用SpringFactoriesLoader查找并加载可用的ApplicationListener
推断并设置main方法的定义类 - 执行run方法
使用SpringFactoriesLoader查找并加载可用的SpringApplicationRunListener,调用他们的starting()方法 - 创建Environment
- 遍历所有的SpringApplicationRunListener,调用他们的environmentPrepared(environment)方法
- 处理bunner(这个就是好玩一点,没什么实际作用)
- 根据初始化的判断,是否为web应用决定创建什么类型的ApplicationContext,并创建完成。根据条件决定是否添加ShutdownHook,自定义BeanNameGenerator,自动以ResourceLoader;将Environment配置给ApplicationContext
- 使用SpringFactoriesLoader查找并加载可用的ApplicationContextInitializer,遍历执行这些对象的initialize(applicationContext),第6步创建的Context作为参数
- 遍历所有的SpringApplicationRunListener,调用他们的contextPrepared(context)方法
- 核心步骤:将@EnableAutoConfiguration 获得的配置加载到容器中
- 遍历所有的SpringApplicationRunListener,调用他们的contextLoaded(context)方法
- ApplicationContext.refesh()完成容器的最后一步
- 查看容器中是否注册了CommandLineRunner,如果有就遍历执行他们
- 遍历所有的SpringApplicationRunListener,调用他们的finished(context, exception)方法
扩展点:SpringApplicationRunListener
SpringApplication类中使用它们来间接调用ApplicationListener。
SpringApplicationRunListener规定了Spring Boot的生命周期,包含多个SpringApplicationRunListener。
public interface SpringApplicationRunListener {
// 刚执行run方法时,对应启动流程的第2步
void starting();
// 环境建立好时候,对应启动流程的第4步
void environmentPrepared(ConfigurableEnvironment environment);
// 上下文建立好的时候,对应启动流程的第8步
void contextPrepared(ConfigurableApplicationContext context);
// 上下文载入配置时候,对应启动流程的第10步
void contextLoaded(ConfigurableApplicationContext context);
// 上下文刷新完成后,run方法执行完之前,对应启动流程的第13步
void finished(ConfigurableApplicationContext context, Throwable exception);
}
这个接口在Spring boot中有一个默认的实现:org.springframework.boot.context.event.EventPublishingRunListener
这个实现类使用了spring的广播器:org.springframework.context.event.SimpleApplicationEventMulticaster
不推荐对SpringApplicationRunListener进行扩展,默认也没有提供扩展接口,但是依然可以通过修改spring.factory来完成扩展
public class MyRunListener implements SpringApplicationRunListener{
public MyRunListener(SpringApplication application, String[] args) {
}
@Override
public void starting() {
System.out.println("============MyRunListener starting==========");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("============MyRunListener environmentPrepared==========");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("============MyRunListener contextPrepared==========");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("============MyRunListener contextLoaded==========");
}
@Override
public void finished(ConfigurableApplicationContext context,
Throwable exception) {
System.out.println("============MyRunListener finished==========");
}
}
META-INF\spring.factories文件中添加下面的内容
org.springframework.boot.SpringApplicationRunListener=me.helllp.demo.springbootDemo3.MyRunListener
扩展点:SpringApplicationListener
是Spring框架对Java中实现的监听者模式的一种框架实现。可以通过SpringApplication.addListeners进行扩展
- 自定义事件
public class MyTellEvent extends ApplicationEvent{
private String message;
public MyTellEvent(Object source) {
super(source);
}
public MyTellEvent(Object source,String message){
super(source);
this.message = message;
}
public void pring(){
System.out.println("事件的消息:" + message);
}
}
- 自定义监听器
public class MyStartListner implements ApplicationListener<MyTellEvent> {
@Override
public void onApplicationEvent(MyTellEvent event) {
event.pring();
}
}
- 触发事件
MyTellEvent event = new MyTellEvent(context, "测试的事件信息");
context.publishEvent(event);
扩展点:ApplicationContextInitializer
目的是在ApplicationContext在做refresh之前,可以对实例进行进一步的设置(对Context进行配置)。可以通过addInitializers方法进行扩展
boot默认的主要实现类:
org.springframework.boot.context.ContextIdApplicationContextInitializer类:设置 上下文 的id
org.springframework.boot.context.config.DelegatingApplicationContextInitializer类:从环境中取出所有的 ApplicationContextInitializer 并执行
org.springframework.context.ConfigurableApplicationContext类:设置上下文的 servletContext
- 自定义扩展
public class MyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>{
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("在这里对Context进行最后的设置工作!");
}
}
- 配置
// 对Context进行最后的设置工作
bootstrap.addInitializers(new MyContextInitializer());
扩展点:CommandLineRunner
运行的时机是ApplicationContext初始化完成之后;通过自动加载扫描进入IoC容器即可;可以通过Order调整运行的顺序
@Component
@Order(value=1)
public class MyCommandRunner1 implements CommandLineRunner{
@Override
public void run(String... args) throws Exception {
System.out.println("===========执行命令1的逻辑!=================");
}
}
@Component
@Order(value=2)
public class MyCommandRunner2 implements CommandLineRunner, ApplicationContextAware{
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context = applicationContext;
}
@Override
public void run(String... args) throws Exception {
System.out.println("=============命令2,可以使用ApplicationContext====" + context);
}
}
第4章:Spring Boot Starter
具体代码:springbootStarter
Spring boot提供了“开箱即用”的依赖模块都约定以spring-boot=starter-作为命名的前缀。每个starter都有自己约定的配置,这就是所谓“约定优先于配置”。我们也可以对配置进行干预,干预的方法有多种,并且具有优先级,下面的列表中是优先级从高到低排列
- 命令行参数
- 系统环境变量
- 位于文件系统中的配置文件
- 位于classpath中的配置文件
- 固话在代码中的配置项
日志:spring-boot-starter-logging
配置文件
<!-- 日志处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
spring boot将自动使用logback作为日志框架,启动时由org.springframework.boot.logging.LoggingApplicationListener根据情况进行初始化。
日志的配置文件:classspath:logback.xml;或者可以在配置中增加下面的配置项,指定日志配置文件的位置
logging.config=c:/config.xml
如果想使用log4j
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
如果想使用log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
web开发:spring-boot-starter-web
默认的静态文件和模板文件的放置位置:
- src/main/resources/static : 放置静态资源文件
- src/main/resources/template:放置模板文件
数据库访问:spring-boot-starter-web
pom.xml文件
<!-- 数据库操作,使用JPA方式 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 内存数据库H2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
修改application.properties文件
spring.jpa.generat-dll=false
spring.jpa.show-sql=true
spring.jpa.hibernate.dll-auto=none
spring.datasource.platfor=h2
spring.datasource.schema=classpath:schema.sql
spring.datasource.data=classpath:data.sql
spring.h2.console.settings.web-allow-others=true
spring.h2.console.path=/h2-console
spring.h2.console.enabled=true
schema.sql文件中可以书写创建表的脚本
data.sql文件中可以书写创建数据的脚本
数据库访问:spring-boot-starter-jdbc
POM.xml文件修改
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
通过修改application.properties文件进行数据库连接的设置
spring.datasource.url=jdbc连接串
spring.datasource.username=user
spring.datasource.password=pass
代码中进行注入操作
@Autowired
JdbcTemplate jdbcTemplate;
AOP的使用:spring-boot-starter-aop
POM.xml文件修改
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
spring-boot-starter-aop自动配置行为由两个部分组成
- 位于spring-boot-autoconfigurate的org.springframework.boot.autoconfigure.aop.AopAutoConfiguration提供的@Configuration配置类和相关配置项
- spring-boot-starter-aop模块本身提供的针对spring-aop,apsectjrt和apsectjweaver的依赖
具体使用方法
@Component
@Aspect
public class Advices {
private static final Logger logger = LoggerFactory.getLogger(Advices.class);
@Pointcut("execution(* me.helllp.demo.springbootStarter.SpringBootRunner.*(..))")
public void controllerMethodPointcut(){}
@Around("controllerMethodPointcut()")
public Object before(ProceedingJoinPoint pjp) {
long beginTime = System.currentTimeMillis();
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod(); //获取被拦截的方法
String methodName = method.getName(); //获取被拦截的方法名
logger.info("请求开始,方法:{}", methodName);
Object result = null;
Object[] args = pjp.getArgs();
for(Object arg : args){
logger.info(arg.toString());
}
try {
result = pjp.proceed();
} catch (Throwable e) {
logger.info("exception: ", e);
}
long costMs = System.currentTimeMillis() - beginTime;
logger.info("{}请求结束,耗时:{}ms", methodName, costMs);
return result;
}
}
Actuator的使用:spring-boot-starter-actuator
这部分内容准备单独开一章进行讲解(Spring boot生产级特性)