Spring boot进行服务开发

第0章:说明

为了进行服务的开发,简单介绍一下Spring Boot的使用
源码所在位置

第1章:起步

这里会介绍一个最基本的服务,体验一下Spring Boot进行开发的便捷性,学习一下如何指定web端口

具体代码:springbootDemo1

第2章:Spring之Java Config

这一节我们将学习一下Spring的工作机制,便于未来我们更好的进行服务开发。源码所在位置

  1. spring容器的简单讲解(为JavaConfig进行铺垫)
  1. 如何使用Java Config

Spring框架的本质

我们都知道Spring主要包括IoC和AOP,对Java应用技术进行了合理的设计和封装。这里主要讲解一下IoC的内容。
Spring IoC大体有两个步骤组成:

  1. 步骤一:收集和注册
    下面是传统的做法
<!-- 注册一个bean的配置文件 -->
<bean id="myTestService" class="..myTestServiceImpl">
</bean>
<!-- <!-- 注册一个bean的配置文件 --> -->
<context:component-scan base-package="com.test"/>
  1. 步骤二:分析和组装
    在完成第一阶段任务之后,IoC容器中充斥着一个个独立的Bean,他们之间没有任何关系。事实上这是不可能的,他们应该有依赖关系的,所有第二阶段就是根据依赖关系相互组装起来。
    下面是通过配置的做法
<bean id="myDependencyTest" class="..ServiceImpl">
  <property name="dependencyService" ref="myTestService" />
</bean>

总结一下:使用配置文件的方法

  1. 书写一个配置文件
  1. 注册Bean
  2. 定义依赖关系

相比较繁琐的配置文件,使用JavaConfig来实现上面的功能更加清爽。这也是Spring boot的核心逻辑之一
下面让我们看看JavaConfig如何进行对象的注入

  1. 书写一个配置文件等价于
@Configuration
public class TestConfiuration {
}
  1. 注册Bean等价于
@Configuration
public class TestConfiuration {
    @Bean
    public TestService myService(){
        return new TestService();
    }
}
  1. 定义依赖关系
@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());
    }

常用的注解

  1. @ComponentScan
    对应XML配置文件中的<context:component-scan>的元素
@ComponentScan(basePackages = { "me.helllp.demo.springbootDemo2, me.helllp.demo.test" })

默认情况下搜索的包是应用所在的包,如果我们想指定其他的包就需要自己配置一下

  1. @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;
    }
}
  1. @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只需要做两件事:

  1. 一个@SpringBootApplication注解
  2. 一个应用启动: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系列函数就可以知道有哪些扩展点。

可扩展点

启动流程大体分为如下几步:

  1. 构造SpringApplication 实例对象
    判断是否是web环境
    使用SpringFactoriesLoader查找并加载可用的ApplicationContextInitializer
    使用SpringFactoriesLoader查找并加载可用的ApplicationListener
    推断并设置main方法的定义类
  2. 执行run方法
    使用SpringFactoriesLoader查找并加载可用的SpringApplicationRunListener,调用他们的starting()方法
  3. 创建Environment
  4. 遍历所有的SpringApplicationRunListener,调用他们的environmentPrepared(environment)方法
  5. 处理bunner(这个就是好玩一点,没什么实际作用)
  6. 根据初始化的判断,是否为web应用决定创建什么类型的ApplicationContext,并创建完成。根据条件决定是否添加ShutdownHook,自定义BeanNameGenerator,自动以ResourceLoader;将Environment配置给ApplicationContext
  7. 使用SpringFactoriesLoader查找并加载可用的ApplicationContextInitializer,遍历执行这些对象的initialize(applicationContext),第6步创建的Context作为参数
  8. 遍历所有的SpringApplicationRunListener,调用他们的contextPrepared(context)方法
  9. 核心步骤:将@EnableAutoConfiguration 获得的配置加载到容器中
  10. 遍历所有的SpringApplicationRunListener,调用他们的contextLoaded(context)方法
  11. ApplicationContext.refesh()完成容器的最后一步
  12. 查看容器中是否注册了CommandLineRunner,如果有就遍历执行他们
  13. 遍历所有的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进行扩展

  1. 自定义事件
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);
    }
}
  1. 自定义监听器
public class MyStartListner implements ApplicationListener<MyTellEvent> {

    @Override
    public void onApplicationEvent(MyTellEvent event) {
        event.pring();
    }
}
  1. 触发事件
        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
  1. 自定义扩展
public class MyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>{

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("在这里对Context进行最后的设置工作!");
    }
}
  1. 配置
        //  对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都有自己约定的配置,这就是所谓“约定优先于配置”。我们也可以对配置进行干预,干预的方法有多种,并且具有优先级,下面的列表中是优先级从高到低排列

  1. 命令行参数
  2. 系统环境变量
  3. 位于文件系统中的配置文件
  4. 位于classpath中的配置文件
  5. 固话在代码中的配置项

日志: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

默认的静态文件和模板文件的放置位置:

  1. src/main/resources/static : 放置静态资源文件
  2. 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自动配置行为由两个部分组成

  1. 位于spring-boot-autoconfigurate的org.springframework.boot.autoconfigure.aop.AopAutoConfiguration提供的@Configuration配置类和相关配置项
  2. 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生产级特性)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容