单例工厂是工厂模式的一种,表示生产出的产品为单例产品。java
在上古web开发中,后端是servlet、service、dao这三层。servlet依赖service,service依赖dao,为何说依赖,由于该层持有另外一层的对象:web
//UserServlet public class UserServlet extends HttpServlet { //一个UserServlet中持有一个UserService的实现类对象 //这里UserService为接口,指向其实现类 private UserService userService = new UserServiceImpl(); //其余代码 ... } //Userservice public interface UserService{ private UserDao userDao = new UserDaoImpl(); ... } //UserDao public interface UserDao{ //do something }
这里为何将UserService和UserDao声明为接口,并让其引用实现类呢?后端
咱们要知道的是,Java语言是编译型语言,.java
源代码文件是须要编译成.class
以后才能运行,若是咱们须要切换业务模式,好比须要使用UserService2
的业务模式,对于上面用new
的方式去建立对象,咱们就须要去修改源代码,而后从新编译才能运行。而在项目大的时候,编译是很是耗费时间的。服务器
对于时间就是金钱的互联网行业,在你编译的时候,你的对手可能就把你的顾客给抢走了,那么有没有一种方法能够快速切换呢?多线程
有,就是使用反射 + 配置文件
的方式来获取对象,当须要不一样的对象时只须要修改配置文件,而不用从新编译:工具
public class UserServlet extends HttpServlet throws Exception { //1.读取配置,参数是.properties文件的文件名 ResourceBundle rb = ResourceBundle.getBundle("service"); //获取配置文件中的类路径 String classPath = rb.getString("UserService"); //经过类路径来得到实例,也就是建立对象 private UserService userService = Class.forName(classPath).newInstance(); //其余代码 ... }
service.properties文件:优化
UserService = com.bilibili.service.impl.UserServiceImpl
这样当咱们想获取不一样的UserServiceImpl实例时只须要修改配置文件并重启服务器便可。线程
这也是为何上面使用接口引用其实现类,若是直接使用实现类去引用的话则不能达到这种效果。code
上面的优化后虽然不用从新编译了,可是问题又来了:除了UserServlet中这样反射得到了一个对象,另外一个Servlet中——ProductServlet也须要一个相似的ProductService对象,甚至Service中也须要dao对象,这些代码都很像,那么咱们可不能够把这一部分给单独抽取出来,做为工具类使用能?对象
尝试一下:
public class BeansFactory { public static Object getInstance(String beanName){ //1.读取配置 ResourceBundle rb = ResourceBundle.getBundle("beans"); String classPath = rb.getString(beanName); //2.反射建立对象 Object o = null; try { o = Class.forName(classPath).newInstance(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } return o; } }
咱们把这一部分逻辑类似的代码做为一个类的静态方法来使用,这个方法是专门用来生产对象的,咱们把这种模式叫作工厂模式。
上面的方式虽然很不错,但仍是存在一些问题的:若是不止一个Servlet中须要使用UserService的实现类对象,好比Produce中也须要使用,是否是每一个须要使用的地方都要用这种方式持有一个对象呢?
对于每一个须要使用到UserService的地方来讲,咱们只关注UserService中的方法,咱们只是想使用它的功能,咱们不关注这个对象怎么建立,也不关注是不是同一个对象,若是每一个须要使用到的地方都去 getgetInstance来得到一个对象,这样会形成资源的浪费,那么能不能只建立一个对象来供许多地方使用能?
能够,这就是单例工厂:
public class BeansFactory { //咱们把建立过的对象使用Map保存起来 private static Map<String,Object> map = new HashMap<>(); //这里使用synchronized修饰能够防止多线程问题 public static synchronized Object getInstance2(String beanName){ //当有地方须要得到对象时,优先从map中获取 Object o = map.get(beanName); //o为null表明map中没有这种对象,此时咱们就去建立这个对象并保存在map中 if(o == null){ //若是获取不到,再去反射机制建立,而且保存到map。 //1.读取配置 ResourceBundle rb = ResourceBundle.getBundle("beans"); String classPath = rb.getString(beanName); //2.反射建立对象 try { o = Class.forName(classPath).newInstance(); //保存 map.put(beanName,o); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } return o; } }
这样咱们获取的的就是同一个对象了。