likes
comments
collection
share

这五种方式拓展Bean的生命周期,你必须记住

作者站长头像
站长
· 阅读数 19

引言

大家好,我是有清。

又快到了吃西瓜的季节,不知道大家有没有吃过无籽西瓜,西瓜从它的幼苗到结果需要经过很漫长的一段过程,在其幼苗的时候,我们利用秋水仙素对其进行处理,然后再进行杂交,就可以得到无籽西瓜

那么,如果我想打破 Bean 传统的生命周期,在其创建到销毁的过程中,去指定一些操作,有没有什么开箱即用的手段呢?学会这些对我们的日常开发又有什么帮助呢?接下来,让我们一起学习一下

这五种方式拓展Bean的生命周期,你必须记住

拓展方式

使用 Spring 的接口

我们可以实现 InitializingBean 接口,重写 afterPropertiesSet 方法,在这里你就可以在 Bean 实例化之后自定义一些操作

代码示例

@Component  
public class ImproveBeanTest1 implements InitializingBean {  
  
    @Override  
    public void afterPropertiesSet() throws Exception {  
        System.out.println("我进来了。。。");  
    }  
}

这五种方式拓展Bean的生命周期,你必须记住

既然在实例化之后有这样的接口,那么有没有类似的接口可以自定义我们的销毁 Bean 的时候做点拓展?那当然有,就是 DisposableBean 接口

代码示例

@Component  
public class ImproveBeanTest2 implements DisposableBean {  
  
    @Override  
    public void destroy() throws Exception {  
        System.out.println("糟糕。我被销毁了");  
    }  
}

这五种方式拓展Bean的生命周期,你必须记住

可能一下子你看了这两个示例,你会觉得这有个球用,但是格局打开,一旦我们在框架级别上去思考这个问题,我们是不是可以借助这个特性,进行一些预热操作、以及下线的时候进行优雅停机呢?

这五种方式拓展Bean的生命周期,你必须记住

使用JSR-250注解

其实 JSR-250 的注解和上面提供的 Spring 的接口的功能很类似,别因为人家是 250 就瞧不起人家,它的作用一般就是使我们的框架解耦与 Spring 框架,尽管我们目前国内大部分用的都是 Spring 框架,不过,技多不压身,我们来看一下

这五种方式拓展Bean的生命周期,你必须记住

250 给我们提供了了两个注解,分别是 @PostConstruct、@PreDestroy

有清老师小课堂上线,Post 表示后置、Construct 表示构造,连起来就是后置构造的时候搞事情,就是在 Bean 初始化完成后对 Bean 进行处理

Pre 表示提前,Destroy 表示消毁,连起来就是前置销毁的时候搞事情,就是在 Bean 销毁阶段前做一些处理 代码示例

@Component  
public class ImproveBeanTest3{  
  
    @PostConstruct  
    public void postConstruct() {  
        System.out.println("我是构造后置");  
    }  
  
    @PreDestroy  
    public void preDestroy() {  
        System.out.println("我是销毁前置");  
    }  
}

这五种方式拓展Bean的生命周期,你必须记住

使用 @Bean 的属性

临时抽查:如何使一个类交由 Spring 进行 Bean 管理,除了 @Component、@Controller、@Service、xml 配置,还有什么方法? 举手回答🙋:“还可以使用 @Configuration 搭配 @Bean 注解”

对,此种方式也可以生成我们的 Bean,当然也提供了“前置、后置”方法,show me code

@Configuration  
public class ImproveBeanTest4 {  
  
    @Bean(initMethod = "onInitialize", destroyMethod = "onDestroy")  
    public ImproveBeanTest4 mySpringBean() {  
        return new ImproveBeanTest4();  
    }  
  
    public void onInitialize() {  
        System.out.println("我我我我进来");  
    }  
   
    public void onDestroy() {  
        System.out.println("我我我我出去了");  
    }  
}

这五种方式拓展Bean的生命周期,你必须记住

这边还有一点需要注意的是,如果我们的 Bean 中含有 public 修饰的 close() 或者 shutdown() 方法,那么默认情况是会自动触发回调的,如果你不希望多此一举,你可以使用 destroyMethod="" 去禁止这个行为

@Bean(destroyMethod = "")  
public ImproveBeanTest2 mySpringBean2() {  
    return new ImproveBeanTest2();  
}  
  
public void close() {  
    System.out.println("我不能被执行了");  
}

xml 配置文件中同样可以支持这些骚操作,但是基于 xml 配置文件用的比较少,我就不展开说明了,感兴趣的小伙伴可以 官网走起:docs.spring.io/spring-fram…

使用 BeanPostProcessor

BeanPostProcessor 在整个 Bean 的周期应该是比较男一号的角色了,被用到的地方也很多,

在 Bean 初始化之前或之后,去自定义操作,甚至去狸猫换太子,怎么理解狸猫换太子,本来我准备生成 ABean,然后直接给他替换成 BBean,你或许会有疑惑,这是什么骚操作,一会我们的最佳实践会带你解开疑惑,暂且先插个眼在这里

这五种方式拓展Bean的生命周期,你必须记住

我们需要实现 BeanPostProcessor 接口,并且需要主动重写方法,上个代码,look 一下

@Configuration  
public class ImproveBeanTest5 implements BeanPostProcessor {  
  
    @Override  
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
        return bean;  
    }  
  
    @Override  
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
        return bean;  
    }  
}

这边我们需要注意的是 BeanPostProcessor 的杀伤力非常广,对所有的 Bean 都会进行这个 processor 处理,所以一般我们需要对 Bean 进行判断处理一下,不然我就想换一个太子,结果整个皇氏家族都被你换了

使用 Aware 接口

aware 接口,也可以拓展 Bean 的生命周期,但是我这边认为一般我们去实现 Aware 相关接口,最好去取东西而不是塞东西,为什么这么说?

我们来翻译一下 aware 的意思,这个英文的意思是可感知、察觉的,如果 bean 实现了一些 aware 方法,可以说是上级方法调用到我们这里来,把变量传递给了我们,这个时候我们尽可能去取东西,而不是写,想写操作或者更改属性,建议使用 BeanPostProcessors

就好比我们业务开发的时候,别人调用我们 的方法,给了我们一个 model 对象,我们去取其中一两个属性,完成我们的业务逻辑,有必要的话,再分装一个新的 model 回去,如果我们把这个 model 对象改了,一旦出问题了,就很有可能被 diss 说,你为啥改了我给你的对象,日志打的都不对了, 这问题都没法排查了

这五种方式拓展Bean的生命周期,你必须记住

当然使用 aware 去写当然没问题哈,只是一个编程习惯的问题,来上代码

@Component  
public class ImproveBeanTest6 implements BeanNameAware {  
  
    @Override  
    public void setBeanName(String name) {  
        System.out.println("-------" + name);  
    }  
}

这五种方式拓展Bean的生命周期,你必须记住

Spring 给我们提供了非常多的 Aware接口,可以按需使用:docs.spring.io/spring-fram…

最佳实践

借助 InitializingBean 实现工厂模式

我们知道工厂模式,其实在我们日常的开发过程中出镜率还是比较高的,那么我们如何使用 InitializingBean 这个接口,去优雅实现我们的工厂模式呢

我们这边举一个🌰,我们知道我们房贷可以正常还款,和排个半年的队提前还款,或者刺激的逾期不还,我们就借助这样的一个小 case,去展开一下我们的代码

闲话少说,直接看代码

@Component  
public class CommonRepayFactory {  
  
    private static final Map<String, IRepayFactory> REPAY_FACTORY_MAP = new ConcurrentHashMap<>();  
  
    public static IRepayFactory getRepayFactory(String type) throws Exception {  
        IRepayFactory repayFactory = REPAY_FACTORY_MAP.get(type);  
        if (Objects.isNull(repayFactory)) {  
            throw new Exception("搞的啥,没有这个工厂");  
        }  
        return repayFactory;  
    }  
  
    public static void register(String factoryType, IRepayFactory repayFactory) {  
        REPAY_FACTORY_MAP.put(factoryType, repayFactory);  
    }  
}

// 提前还款工厂
@Component  
@Slf4j  
public class AdvanceRepayFactory implements IRepayFactory, InitializingBean {  
  
    @Override  
    public void repay(RepayRequest request) { 
	    // 还款操作  
    }  
  
    @Override  
    public void afterPropertiesSet() {  
        CommonRepayFactory.register("提前还款", this);  
    }  
}

// 调用处直接 CommonRepayFactory.getRepayFactory(request.getRepayBizType()).repay(request)

这一个工厂模式你改吧改吧,就能直接搬到你的项目中使用了,不用谢,我叫雷锋,具体代码地址见文章尾部

借助 ApplicationContextAware 获取所需的 Bean

我们在实际开发的过程中可能会碰到这样的问题,我在某个工具类的静态方法中想注入了某个 Bean,但是不能直接 @Autworied 这个 Bean,这个时候我们有取巧注入的方法,但是当你这个 Bean 是 jar 包引入的并且是多态的你可以直接 @Autworied 么?所以我们通常需要一个工具类去获取 Bean

@Component  
public class ApplicationContextUtils implements ApplicationContextAware {  
  
    private static ApplicationContext applicationContext;  
  
    private ApplicationContextUtils() { }  
  
    @Override  
    public void setApplicationContext(ApplicationContext context) throws BeansException {  
        applicationContext = context;  
    }  
  
    
    public static <T> T getBeanByType(Class<T> clazz) throws Exception {  
        if (applicationContext == null) {  
            throw new Exception("搞什么,瞎拿");  
        }  
        return applicationContext.getBean(clazz);  
    }  
      
    public static <T> T getBeanByName(String beanName) throws Exception {  
        if (applicationContext == null) {  
            throw new Exception("搞什么,瞎拿");  
        }  
        return (T) applicationContext.getBean(beanName);  
    }  
}

借助 BeanPostProcessor 实现狸猫换太子

先看个代码示例,应该是比较简单易懂的

@Component  
public class ImproveBeanTest7 implements ApplicationContextAware, BeanPostProcessor {  
      
    private ApplicationContext applicationContext;  
    
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
        this.applicationContext = applicationContext;  
    }  
    
    @Override  
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
        if (beanName.equals("defaultConfig")) {  
            // 如果遇到需要替换的Bean,我们直接换成自己实现的Bean即可(这里可以把就得removeBeanDefinition,然后注册新的registerBeanDefinition)  
            // 这里的myConfig要继承自defaultConfig,否则引用的地方会报错            return applicationContext.getBean("myConfig");  
        }  
        return bean;  
    }  
}

这时候可能会有同学有疑问了,如果我有多个实现了 BeanPostProcessor 的接口这么办,其实这个问题,代码里给了我们答案,我们会进行循环处理,一旦 postProcessBeforeInitialization 返回值不为空,我们就直接取当前的返回值了,不再进行循环处理


@Override  
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)  
      throws BeansException {  
  
   Object result = existingBean;  
   for (BeanPostProcessor processor : getBeanPostProcessors()) {  
      Object current = processor.postProcessBeforeInitialization(result, beanName);  
      if (current == null) {  
         return result;  
      }  
      result = current;  
   }  
   return result;  
}

总结

通过上面的讲解,其实我们知道了如何对 Bean 进行拓展,以及了解了部分的实现场景,可能还收获了两个小轮子,但是可能现在你暂时还用不上,你只需收藏,其余的交给收藏夹处理

当然如果这篇文章真的对你有所帮助,希望你点赞分享,你的支持是我写作最大的动力,有什么问题都欢迎私信进行沟通交流

文章中的代码地址:github.com/isysc1/Issu…

闲言碎语

上周二,是情人节,大家都在讨论关于爱的命题,而我在公司苦苦加班思考这行代码怎么写

后来下班了,路上熙熙攘攘,有出售爱情的商贩、有对浪漫过敏的城管、有相拥而笑的恋人、还有拉着行李箱匆匆而过的他和她,似乎大家都在为这个命题写上自己认为的答案

而这时,突然间一个小女孩跑过来对我说:“哥哥,你要买花吗?”

我问她:“你的花呢”?

小女孩说:“人要先感到幸福,才能看到玫瑰。”