导言

我们好,我是有清。

又快到了吃西瓜的季节,不知道我们有没有吃过无籽西瓜,西瓜从它的麦苗到成果需求通过很漫长的一段进程,在其麦苗的时分,咱们运用秋水仙素对其进行处理,然后再进行杂交,就能够得到无籽西瓜

那么,假如我想打破 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…

闲言碎语

上周二,是情人节,我们都在讨论关于爱的出题,而我在公司苦苦加班考虑这行代码怎样写

后来下班了,路上人山人海,有出售爱情的商贩、有对浪漫过敏的城管、有相拥而笑的恋人、还有拉着行李箱仓促而过的他和她,好像我们都在为这个出题写上自己以为的答案

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

我问她:“你的花呢”?

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