设计模式应用(二)—Spring IOC(反射)+配置文件改造简单工厂
一、简单工厂概述
简单工厂并不属于设计模式,因为它不满足开放封闭原则,不过它展现了软件设计另外一个重要原则——单一职责,即这个类只有一个创建对象的职责,也体现了代码编写的一个重要思想——统一管理。下面将以改造实际项目中的代码为主线,改造简单工厂,让它接近符合开闭原则。
二、项目中"简单工厂"代码
2.1代码展示
简单工厂是根据用户传入的参数(searchType),实例化对应的类,最具代表性就是使用了switch-case语句,如下代码所示
@Service
public class TypeFactory {
@Autowired
private RestHighLevelClient restHighLevelClient;
public List<Map<String, Object>> createType(String searchType, String keyword, int pageNo, int pageSize) throws IOException {
AbsElasticSearch absElasticSearch = null;
ArrayList<Map<String, Object>> searchList = null;
switch (searchType) {
case "全部":
absElasticSearch = new ElasticSearchImpl(keyword, pageNo, pageSize);
searchList = (ArrayList<Map<String, Object>>) absElasticSearch.searchPageHighlight(restHighLevelClient);
break;
case "任务":
absElasticSearch = new TaskGroupElasticSearchImpl(keyword, pageNo, pageSize);
searchList = (ArrayList<Map<String, Object>>) absElasticSearch.searchPageHighlight(restHighLevelClient);
break;
case "事件":
absElasticSearch = new EventsGroupElasticSearchImpl(keyword, pageNo, pageSize);
searchList = (ArrayList<Map<String, Object>>) absElasticSearch.searchPageHighlight(restHighLevelClient);
break;
case "学生信息":
absElasticSearch = new StudentInfoElasticSearchImpl(keyword, pageNo, pageSize);
searchList = (ArrayList<Map<String, Object>>) absElasticSearch.searchPageHighlight(restHighLevelClient);
break;
default:
searchList=new ArrayList<>();
}
return searchList;
}
}
然而,上述代码有如下不足之处
1.不符合开闭原则
- 这里详细说明下,开闭原则允许对一个软件实体(类、函数等)扩展,但是禁止修改。也就是说假如新添加一个搜索——“模板信息”,就得新加一个case分支,此时就是修改了一个类。
2.不符合单一职责
单一职责,就一个类而言,应该仅有一个引起它变化的原因。这个引起它变化的原因就是职责,更直接的理解是一个类只有一个职责。
而上面类中包含了两个职责
- 职责1:根据用户参数创建对象
- 职责2:调用方法得到返回值
如果职责1因为需求的更改而导致职责2不能正常使用,这时就说职责1和职责2耦合到一起了
2.2修改代码
1.用反射+配置文件改进简单工厂
对于if或者switch语句,经典的改进方法是反射加配置文件,然而我们知道Spring作为一个巨大工厂,存放各种各样的bean实例,底层实现也是使用的反射。所以代码优化可以直接从Spring IOC容器中取
2.对于单一职责
将调用方法的多余参数从代码中移出,将这个类的职责简化到只创建对象
三、优化后代码
ApplicationContextAware接口是Spring创建bean生命周期的一个扩展点,该接口含有一个回调方法,从这个方法中可以拿到ApplicationContext(IOC容器)。当一个Spring项目启动后,会将有标记@Component或者它的别名(@Controller、@Service等等)的类扫描到Spring单例池中或者IOC容器中,通过getBean()方法就可以从IOC容器中拿到一个bean实例
如下
//yml文件
beantype:
all: elasticSearchImpl
event: eventsGroupElasticSearchImpl
task: taskGroupElasticSearchImpl
//工厂代码
@Service
public class TypeFactory implements ApplicationContextAware {
public static final String PREFIX="beantype.";
private ApplicationContext applicationContext;
//通过实现ApplicationContextAware接口的回调方法拿到IOC容器
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public IElasticSearch createType(String searchType){
//获取spring应用环境配置
Environment environment = applicationContext.getEnvironment();
//获取yml文件中的beanName
String searchBeanName = environment.getProperty(PREFIX+searchType);
//获取接口类型的bean实例
IElasticSearch searchBeanInstance = (IElasticSearch)applicationContext.getBean(searchBeanName);
return searchBeanInstance;
}
}
此时根据用户传入的参数就可以拿到对应的bean实例
四、基于SpringIOC与手写反射方式优势所在
在写优势之前,先了解下反射的过程,在我之前的博客中有详细解释
4.1反射的过程
1.类加载器(classLoader)通过一个类的全限定类名在主机硬盘找到A.class文件
2.转换为A字节码:将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(包含字段方法等)
3.生成A的class对象:在堆中生成一个代表这个类的java.lang.Class对象,可以通过这个对象访问方法区的A字节码(字段方法等)
从这三个步骤可以看出来,反射创建一个实例需要经过IO操作,而IO操作是程序性能的瓶颈
4.2优势点
1.基于SpringIOC的性能比反射好
虽然spring底层也是通过反射创建并完成依赖注入,但是bean实例在spring项目启动的过程已经创建好了,并放入单例池中,如果用直接从单例池中去,不用了返还给单例池,不用反复创建。
2.节省内存
这个也是单例池的作用,反射创建出来的对象是多例的,一个用户请求下就创建一个,如果将其改成单例模式,又需要考虑线程安全问题,增加系统的复杂性
3.易于管理,便于垃圾回收
spring创建的bean由spring统一管理,也由spring统一回收。降低内存溢出和内存泄漏发生的概率
上一篇: QMap 之 swap 特性
下一篇: CentOS 7 添加Swap分区