beanfactory篇-(六)BeanPostProcessor接口的使用-九零后大叔的技术博客

沙海
沙海
沙海
994
文章
2
评论
2021年4月24日09:12:21
评论
4 4329字阅读14分25秒
摘要

回顾:通过前面的章节的了解,我们又了解了InitializingBean接口的使用,当spring创建一个业务bean之后,只要该业务bean实现了InitializingBean接口,那么spring框架会自动的调用该业务bean的afterPropertiesSet方法。本章节开始,我们将开始了解一些重要的spring接口的使用,弄懂这些接口的基本使用后,我们在去阅读spring源码的时候,就会更加得心应手。本章节要讲述的接口是BeanPostProcessor接口。

回顾:通过前面的章节的了解,我们又了解了InitializingBean接口的使用,当spring创建一个业务bean之后,只要该业务bean实现了InitializingBean接口,那么spring框架会自动的调用该业务bean的afterPropertiesSet方法。本章节开始,我们将开始了解一些重要的spring接口的使用,弄懂这些接口的基本使用后,我们在去阅读spring源码的时候,就会更加得心应手。本章节要讲述的接口是BeanPostProcessor接口。

1. BeanPostProcessor的基本介绍

我们有一些开发经验的同学都知道,一个复杂的业务过程,我们往往会将他拆解成好几个步骤,也就是拆解成好几个方法,然后会新添加有一个调度方法来调度这几个步骤(从设计模式的角度来说,这种设计模式叫做模板模式,而刚刚那个调度方法,我们称为模板方法。其实我们经常在使用,只不过我们很少会用这样的术语来称呼自己的这个方法)。举个把大象塞冰箱的例子,方便大家的理解。我们创建一个业务处理类如下:

public class Context {

    public void templateMethod(Object elephant) {
        //1.打开冰箱门
        this.open();

        //2.把大象塞进去
        this.putIn(elephant);

        //3.关上冰箱门
        this.close();
    }

    public void open() {
        System.out.println("1.打开冰箱门");
    }

    public void putIn(Object elephant) {
        System.out.println("2.把大象塞进冰箱");
    }

    public void close() {
        System.out.println("3.关上冰箱门");
    }

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

        Context context = new Context();
        context.templateMethod("大象");

        return;
    }
}

那么如果你是一个在乎自己程序扩展性的开发者,那么你可能会多做一步,就是在其中的某个重要的业务方法前面和后面各添加一些扩展方法。那么我们改造一下上面的类,看看怎么样使得这个类的扩展性更强。

public class Context {

    public void templateMethod(Object elephant) {
        //1.打开冰箱门
        this.beforeOpen();
        this.open();
        this.afterOpen();

        //2.把大象塞进去
        this.putIn(elephant);

        //3.关上冰箱门
        this.close();
    }

    public void beforeOpen() {
        System.out.println("1.1开启冰箱内灯");
    }

    public void open() {
        System.out.println("1.2打开冰箱门");
    }

    public void afterOpen() {
        System.out.println("1.3因为开了门,所以将制冷温度右22度变成21度,防止食物变坏");
    }

    public void putIn(Object elephant) {
        System.out.println("2.1把大象塞进冰箱");
    }

    public void close() {
        System.out.println("3.1关上冰箱门");
    }

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

        Context context = new Context();
        context.templateMethod("大象");

        return;
    }
}

经过扩展之后,我们的开门动作变得使用体验更好了。开门冰箱内的灯自动打开了,防止暗光环境下看不清,开门后会将制冷温度变的更低,保证冰箱内的温度恒定。作为开发者,如果我们想自定义灯的亮度或者调低冰箱的温度,我们可以继承Context类,覆盖beforeOpen和afterOpen方法。

那么前面讲述的这些东西与我们的BeanPostProcessor有什么关系呢?聪明的你一定想到了我要表述什么。一个业务bean是被spring创建的,那么有可能我们存在某种需求,就是在创建这个bean的初始化之前,或者创建这个bean的初始化之后,开发者需要介入进来做某种处理,这个初始化bean的动作是不是和上面说的开冰箱门的操作非常的类似。而spring友好的提供了一个BeanPostProcessor接口给我们,只要我们实现BeanPostProcessor接口,我们就可以介入到每一个bean的创建的初始化过程种去,完成我们的个性化的需求功能。接下来我们看看BeanPostProcessor接口的基本使用。

2. BeanPostProcessor的基本使用

第一步:创建一个User类如下:

@Data
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class User implements Serializable {

    private static final long serialVersionUID = 4800166777883697833L;

    private Long id;
    private String name;
    private String identity;
    private String mobile;
    private String bankcard;
    private Integer age;
    private Integer gender;

}

第二步:创建一个接口和接口实现类如下:

public interface UserService {

    User findUserById(Long id);

}

@Service
public class UserServiceImpl implements UserService {

    private Map<Integer, String> blackListMap = new HashMap<>();

    @Override
    public User findUserById(Long id) {

        User user = new User();
        user.setId(id);
        user.setName("张山");
        user.setIdentity("张山");
        user.setBankcard("36457736355363");
        user.setMobile("16752652625");
        user.setGender(2);
        user.setAge(18);

        return user;
    }

}

第三步:创建一个实现BeanPostProcessor接口的类如下:

@Component
public class GlobalBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Execute postProcessBeforeInitialization." + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Execute postProcessAfterInitialization." + beanName);
        return bean;
    }
}

第四步:配置applicationContext.xml文件内容:

<context:component-scan base-package="com.minesoft.tutorial" />

第五步:最后我们运行下面的代码

    ClassPathXmlApplicationContext cac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    UserService userService = cac.getBean(UserService.class);
    User user = userService.findUserById(100L);
    System.out.println(JSONObject.toJSONString(user));

我们能看到控制台输出Execute postProcessBeforeInitialization.userServiceImpl,Execute postProcessAfterInitialization.userServiceImpl等日志,表明spring会在初始化UserServiceImpl这个bean之前和之后,会调用GlobalBeanPostProcessor类的postProcessBeforeInitialization方法和postProcessAfterInitialization方法。记住是初始化之前之后,不是创建这个bean之前和之后。

在这里有两个需要注意的点:
一、是我们的测试场景只有一个UserServiceImpl测试业务bean,所以只能看到一组打印结果。实际上如果我们的spring框架如果有N个业务bean的话,那么这组调用(也就是调用GlobalBeanPostProcessor类的postProcessBeforeInitialization方法)会被反复调用N次。

二、第五步的运行代码能不能不用ClassPathXmlApplicationContext作用spring容器,而使用BeanFactory作为容器行不行呢?答案是不行,因为BeanPostProcessor功能是属于spring的扩展功能,不是基础功能。我们之前说过BeanFactory是一个标配版的spring容器,提供spring的基础功能;而ApplicationContext是一个豪华版本的spring容器,在BeanFactory的基础上额外扩展了很多功能。所以当我们使用BeanFactory作为容器去测试的时候,会发现这个功能不能用。至于代码中是如何体现这一点的,在后面的章节中我们会具体提到。

beanfactory篇-(六)BeanPostProcessor接口的使用-九零后大叔的技术博客

更多知识请关注公众号

继续阅读
weinxin
资源分享QQ群
本站是一个IT技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: