文章源自JAVA秀-https://www.javaxiu.com/19877.html
回顾:通过前面的章节的了解,我们又了解了InitializingBean接口的使用,当spring创建一个业务bean之后,只要该业务bean实现了InitializingBean接口,那么spring框架会自动的调用该业务bean的afterPropertiesSet方法。本章节开始,我们将开始了解一些重要的spring接口的使用,弄懂这些接口的基本使用后,我们在去阅读spring源码的时候,就会更加得心应手。本章节要讲述的接口是BeanPostProcessor接口。 文章源自JAVA秀-https://www.javaxiu.com/19877.html
文章源自JAVA秀-https://www.javaxiu.com/19877.html
1. BeanPostProcessor的基本介绍
我们有一些开发经验的同学都知道,一个复杂的业务过程,我们往往会将他拆解成好几个步骤,也就是拆解成好几个方法,然后会新添加有一个调度方法来调度这几个步骤(从设计模式的角度来说,这种设计模式叫做模板模式,而刚刚那个调度方法,我们称为模板方法。其实我们经常在使用,只不过我们很少会用这样的术语来称呼自己的这个方法)。举个把大象塞冰箱的例子,方便大家的理解。我们创建一个业务处理类如下:文章源自JAVA秀-https://www.javaxiu.com/19877.html
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;
}
}
那么如果你是一个在乎自己程序扩展性的开发者,那么你可能会多做一步,就是在其中的某个重要的业务方法前面和后面各添加一些扩展方法。那么我们改造一下上面的类,看看怎么样使得这个类的扩展性更强。文章源自JAVA秀-https://www.javaxiu.com/19877.html
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方法。文章源自JAVA秀-https://www.javaxiu.com/19877.html
那么前面讲述的这些东西与我们的BeanPostProcessor有什么关系呢?聪明的你一定想到了我要表述什么。一个业务bean是被spring创建的,那么有可能我们存在某种需求,就是在创建这个bean的初始化之前,或者创建这个bean的初始化之后,开发者需要介入进来做某种处理,这个初始化bean的动作是不是和上面说的开冰箱门的操作非常的类似。而spring友好的提供了一个BeanPostProcessor接口给我们,只要我们实现BeanPostProcessor接口,我们就可以介入到每一个bean的创建的初始化过程种去,完成我们的个性化的需求功能。接下来我们看看BeanPostProcessor接口的基本使用。文章源自JAVA秀-https://www.javaxiu.com/19877.html
2. BeanPostProcessor的基本使用
第一步:创建一个User类如下:文章源自JAVA秀-https://www.javaxiu.com/19877.html
@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;
}
第二步:创建一个接口和接口实现类如下:文章源自JAVA秀-https://www.javaxiu.com/19877.html
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接口的类如下:文章源自JAVA秀-https://www.javaxiu.com/19877.html
@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文件内容:文章源自JAVA秀-https://www.javaxiu.com/19877.html
<context:component-scan base-package="com.minesoft.tutorial" />
第五步:最后我们运行下面的代码文章源自JAVA秀-https://www.javaxiu.com/19877.html
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之前和之后。文章源自JAVA秀-https://www.javaxiu.com/19877.html
在这里有两个需要注意的点:
一、是我们的测试场景只有一个UserServiceImpl测试业务bean,所以只能看到一组打印结果。实际上如果我们的spring框架如果有N个业务bean的话,那么这组调用(也就是调用GlobalBeanPostProcessor类的postProcessBeforeInitialization方法)会被反复调用N次。文章源自JAVA秀-https://www.javaxiu.com/19877.html
二、第五步的运行代码能不能不用ClassPathXmlApplicationContext作用spring容器,而使用BeanFactory作为容器行不行呢?答案是不行,因为BeanPostProcessor功能是属于spring的扩展功能,不是基础功能。我们之前说过BeanFactory是一个标配版的spring容器,提供spring的基础功能;而ApplicationContext是一个豪华版本的spring容器,在BeanFactory的基础上额外扩展了很多功能。所以当我们使用BeanFactory作为容器去测试的时候,会发现这个功能不能用。至于代码中是如何体现这一点的,在后面的章节中我们会具体提到。文章源自JAVA秀-https://www.javaxiu.com/19877.html
文章源自JAVA秀-https://www.javaxiu.com/19877.html
更多知识请关注公众号文章源自JAVA秀-https://www.javaxiu.com/19877.html

评论