單例工廠是工廠模式的一種,表示生產出的產品爲單例產品。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; } }
這樣咱們獲取的的就是同一個對象了。