目录
1.为了方便后期增加新的handler,先定义一个Person枚举。
3.headMasterHandler实现类 ,@Compont 需要加上让spring管理
4.teacherHandler实现类,@Compont 需要加上让spring管理
前言
对于程序员来说,23种设计模式最常用的估计就是工厂模式,不管是简单工厂还是抽象工厂。例如:在日常的业务编码handler类必不可少,为了易维护,好扩展,方便管理等。一般都会用抽象工厂对同一组handler进行统一封装。
一、没用spring之前,一般我的工厂类是这么实现的 。
public class Factory {
/**
* Object 就是抽象出来的父接口
*/
private static final Map<Integer,Object> MAP = new HashMap<>();
/**
* 通过静态代码块来填充MAP
*/
static {
}
public static Object getHandle(Integer code){
return MAP.get(code);
}
}
1.这样实现可以不可以?
肯定是可以的。用的时候只要用调用静态方法就行。
2.有什么缺点?
- 一般这么做,具体的handler都不会给spring管理。有可能我们的handler要用到spring管理的类。这种情况下就需要借助工具类。
- 现在的java程序员,大半都可以叫spring工程师。既然我们用了spring这么写就违背了IOC思想。
- 如果我们需要新增handler,需要改动工厂类。在static代码块中增加新增的handler。
二、如何使用SpringBoot如何改造?
我们先假设,我们需要为学校里不同职位的人写处理器,先抽出顶层接口PersonHandler,具体实现类先假设有两个
- HeadMasterHandler(校长处理器)
- TeacherHandler (老师处理器)
1.为了方便后期增加新的handler,先定义一个Person枚举。
package com.dreamland.springstudy.enums;
import lombok.Getter;
import lombok.ToString;
/**
* @author zhangyifeng
* @date 2021-10-16 16:52
*/
@Getter
@ToString
public enum PersonEnum {
/**
* 老师
*/
TEACHER(10),
/**
* 校长
*/
HEADMASTER(20),
private final int code;
PersonEnum(int code) {
this.code = code;
}
}
2.PeronHandler接口
package com.dreamland.springstudy.handler;
import com.dreamland.springstudy.enums.PersonEnum;
/**
* @author zhangyifeng
* @date 2021-10-16 16:50
*/
public interface PersonHandler {
/**
* 枚举
* @return {@link PersonEnum}
*/
PersonEnum getPersonEnum();
/**
* <pre>
* 具体的校验逻辑,由子类实现
* 一般都会携带入参,根据具体的业务需求定义。
* 这里主要是为了让spring管理,所以不带入参。
* </pre>
*
* @return true or false
*/
boolean handle();
}
3.headMasterHandler实现类 ,@Compont 需要加上让spring管理
package com.dreamland.springstudy.handler.impl;
import com.dreamland.springstudy.enums.PersonEnum;
import com.dreamland.springstudy.handler.PersonHandler;
import org.springframework.stereotype.Component;
/**
* @author zhangyifeng
* @date 2021-10-16 16:59
*/
@Component
public class HeadmasterHandler implements PersonHandler {
@Override
public PersonEnum getPersonEnum() {
return PersonEnum.HEADMASTER;
}
@Override
public boolean handle() {
System.out.println("校长校验器工作~~~~");
// 写自己的校验逻辑
return true;
}
}
4.teacherHandler实现类,@Compont 需要加上让spring管理
package com.dreamland.springstudy.handler.impl;
import com.dreamland.springstudy.enums.PersonEnum;
import com.dreamland.springstudy.handler.PersonHandler;
import org.springframework.stereotype.Component;
/**
* @author zhangyifeng
* @date 2021-10-16 16:58
*/
@Component
public class TeacherHandler implements PersonHandler {
@Override
public PersonEnum getPersonEnum() {
return PersonEnum.TEACHER;
}
@Override
public boolean handle() {
System.out.println("老师校验器工作~~~~");
// 写自己的校验逻辑
return true;
}
}
5. PersonHandlerFactory工厂,@Compont 需要加上让spring管理
最重要的工厂类如下 有两个重要的接口需要实现,有两个重要的方法
- InitializingBean 接口中 afterPropertiesSet() 作用:用于初始化替代 静态代码块
- ApplicationContetAware 接口中 setApplicationContext(ApplicationContext var1) 作用:获取ApplicationContext对象
这两个接口中的方法就是关键,想知道这两个接口中方法的作用,有兴趣的可用去看下源码,官方的解释更好。
package com.dreamland.springstudy.handler;
import lombok.NonNull;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
/**
* @author zhangyifeng
* @date 2021-10-16 17:03
*/
@Component
public class PersonHandlerFactory implements InitializingBean, ApplicationContextAware {
private static final Map<Integer, Supplier<PersonHandler>> MAP = new HashMap<>();
private ApplicationContext applicationContext;
public PersonHandler createHandler(Integer personCode) {
Supplier<PersonHandler> p = MAP.get(personCode);
if (p != null) {
return p.get();
}
throw new IllegalArgumentException("No such PersonHandler by code:" + personCode);
}
@Override
public void afterPropertiesSet() throws Exception {
applicationContext.getBeansOfType(PersonHandler.class)
.values()
.forEach(c -> MAP.put(c.getPersonEnum().getCode(), () -> c));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
6.测试
检验是否可用通过工厂拿到具体的handler,我写个接口测试,方便一点:
package com.dreamland.springstudy.Controller;
import com.dreamland.springstudy.handler.PersonHandler;
import com.dreamland.springstudy.handler.PersonHandlerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zhangyifeng
* @date 2021-10-16 17:25
*/
@RestController
@Slf4j
public class TestController {
@Autowired
PersonHandlerFactory personHandlerFactory;
@GetMapping("/handler")
public void testHandler(@RequestParam("code")Integer code){
log.info("/handler code:{}",code);
PersonHandler handler = personHandlerFactory.createHandler(code);
handler.handle();
}
}
测试结果证明可用从工厂类中拿到不同的handler:
总结
- 这么实现很好扩展,例如我们在加一个学生处理器(studentHandler),只要写2步,就可以使用了。
- 枚举中增加相关定义
- 实现父接口
- 具体handler是给spring管理的,非常方面我们用srping管理的其他类