Spring boot进行服务开发

作者: hutou | 来源:发表于2017-07-02 19:08 被阅读166次

    第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生产级特性)

    相关文章

      网友评论

        本文标题:Spring boot进行服务开发

        本文链接:https://www.haomeiwen.com/subject/izpwcxtx.html