联系方式
Java服务器开发群:66728073
游戏开发者高级群:398808948
Unity3d游戏开发:286114103

Spring重点记录(一)

 二维码 22
发表时间:2019-12-26 21:35

在当下的Java服务开发中,Spring框架占有着主要的地位。比如Web服务开发,游戏服务开发。使用Spring可以更好的管理代码,减少代码之间的耦合,利用Spring现成的功能,减少某些功能的开发量,使框架设计更加优雅。所以了解和学习Spring框架是非常有必要的。

1. BeanDefinition

在Spring 中,一个Bean的信息被定义在 BeanDefinition。

2. Spring 中自动生成的Bean的名字规则,

一般来说,都是使用类的名字,并且把首字母变小写,比如UserService的Bean名字是userService,但是如果类的名字前两个都是大写字母,那么它的bean名字和类的名字一样,比如URL,它的bean名字也是URL。Bean名字的生成在java.beans.Introspector.decapitalize的方法中,如下所示:

/**

     * Utility method to take a string and convert it to normal Java variable

     * name capitalization.   This normally means converting the first

     * character from upper case to lower case, but in the (unusual) special

     * case when there is more than one character and both the first and

     * second characters are upper case, we leave it alone.

     * <p>

     * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays

     * as "URL".

     *

     * @param   name The string to be decapitalized.

     * @return   The decapitalized version of the string.

     */

    public static String decapitalize(String name) {

        if (name == null || name.length() == 0) {

            return name;

        }

        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&

                        Character.isUpperCase(name.charAt(0))){

            return name;

        }

        char chars[] = name.toCharArray();

        chars[0] = Character.toLowerCase(chars[0]);

        return new String(chars);

    }



3. Spring 循环依赖

如果两个类都使用构造方法注入,并且它们之间相互依赖,就会形成循环依赖,比如类 A和B,在A的构造方法中需要B,在B的构造方法中需要A,它们之间就会因为循环依赖,导致注入失败,如果Spring 发现了循环依赖,会抛出异常:BeanCurrentlyInCreationException。解决循环依赖的方法是不使用构造方法注入,应该使用Setter方式注入。

4. BeanPostProcessor

在一个Bean被创建成功之后,在调用初化方法之前,会调用此类中的postProcessBeforeInitialization方法,在调用初始化方法之后,会调用postProcessAfterInitialization方法。所谓的初始化方法,即Bean中被@PostConstruct注解标记的方法,或在xml配置中指定的init-method,它属于bean生命期的一部分。注意,这些方法对所有的bean都生效,即每一个bean被创建之后,都会调用此接口的方法。

5. 服务启动与停止:SmartLifecycle

有时候,在服务启动之后或者停止的时候,会执行一些额外的操作。比如服务启动成功之后,启动消息队列的监听接口或发布一下启动成功的消息,在停止服务的时候,需要刷新缓存数据到数据库,清空等待执行的任务等。这个时间,实现一个SmartLifecycle接口即可。如下面代码所示:

@Service

public class SmartLifeCyleImpl implements SmartLifecycle{

    private boolean running =false;

    @Override

    public void start() {

        System.out.println("===服务启动成功===");

        running = true;

    }

    @Override

    public void stop() {

        System.out.println("===服务停止成功===");

    }

    @Override

    public boolean isRunning() {

        return running;

    }

    @Override

    public int getPhase() {

    //如果有多个类实现了此接口,使用此方法返回的值可以指定执行的顺序。默认值是Integer.MAX_VALUE

    //在执行start方法时,值越小,越优化执行。执行stop方法时,值越大越先执行

        return SmartLifecycle.super.getPhase();

    }

}


  • 注意,想要stop生效,不能使用kill -9 命令杀死进程,可以使用kill -15 命令。

  • 在stop中异常执行某些任务
    如果想要在stop中异步执行某些操作,需要重写stop(Runnable callback)方法,在执行完异步任务之后,调用 callback.run()方法。比如游戏开发中,异步保存数据或异步发送消息队列等。

  • 修改stop方法等待的超时时间
    其实stop方法类似jvm的回调钩子,让服务在停止的时候执行一些收尾操作,比如释放某些资源,保存数据等。但是它不能无限等待stop执行,必须有一个超时时间。因为stop方法是在DefaultLifecycleProcessor类中调用的,所以这个超时间可以在这个类中配置timeoutPerShutdownPhase,默认是30秒。

6. CommandLineRunner

在Spring Boot中,可以实现这个接口,在服务启动之后,会调用这个接口的run方法。

7. 单个Bean的初始化与销毁

在单个Bean中,可以使用@PostConstruct注解在Bean创建完成之后完成一些初始化的工作,比如从数据库加载一些数据,缓存在内存中等。在Bean销毁时,可以使用@PreDestroy注解,在Bean销毁之前做一些收尾工作,比如把缓存中的数据刷新到数据库等。

8. Bean的初始化方法调用顺序

  1. 调用被@PostConstruct标记的方法,它会在此Bean所有的依赖都注入成功之后调用。

  2. 调用InitializingBean接口实现的afterPropertiesSet方法,它会在所有的Bean都完成初始化之后调用。

  3. 调用自定义的init()方法。

9. Bean的销毁方法调用顺序

  1. 调用被@PreDestroy注解标记的方法。如果使用kill -15 pid的方法关闭服务,被此注解标记的方法就会被调用。

  2. 调用DisposableBean接口实现的destroy方法。

  3. 调用自定义的destroy方法

9. 如果是非web服务,需要注册jvm关闭钩子

在web服务中,web服务实现的ApplicatonContext接口中已经实现了自动注册jvm关闭钩子。如果是非web服务,需要开发者手动注册关闭钩子,如下面代码所示:

     

public final class Boot {

    public static void main(final String[] args) throws Exception {

        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // add a shutdown hook for the above context...

        ctx.registerShutdownHook();

        // app runs here...

        // main method exits, hook is called prior to the app shutting down...

    }

}