我们看一下简单工厂在JDK源码的一些体现,我们看一个比较熟悉的类,Calendar这么一个类,我们找一个getInstance这么一个方法,

    public static Calendar getInstance()
    {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }
	
我们看一下这个方法,createCalendar后面传来一些参数,而这个是一个静态方法,我们进来

    private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }
	
现在这个方法是private static方法,我们看一下他的具体实现,这里面有一个if判断,语言国家

            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }

else就会返回另外一种实现,下面又返回日本的一个实现,所以我们看一下这里面的代码,我们在看一下VideoFactory

原来的一个写法,那我们会发现这两个方法是异曲同工的,都是进行简单的if判断,然后把对应的子类实现,Calendar也是

这么做的,只不过他这里是使用了static方法,他这里并不是考虑Calendar的扩展性,因为现在应该是满足的,非常简单

这个源码,我们看一下这个类相关的一个类图,我们看一下具体的实现,现在Calendar可以看出来,他实现了三个接口,

***接口,比较还有克隆,下面就是继承Calendar类的一个子类,Calendar是一个抽象类

简单工厂 jdk源码解析

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {

是abstract,抽象类会是这样的一个C,头标后面是一个权限符,锁打开就是public,点就是默认的default,

通过这种方式学习JDK源码,是非常有效果的,包括我们去学习Spring的一些开源框架,通过UML先对整体有一个认识,

这样有助于我们对源码和框架结构关系的全局的理解,这里面我们再扩展一个,我们在写JDBC的时候,JDK来实现的时候,

Class.forName("com.mysql.jdbc.Driver");通过Class.forName把mysql的驱动加载进来,那如果写ORACLE的驱动呢,

这里就变成对应的ORACLE的JDBC的jar包,ORACLE的driver类,然后调用DriverManager的getConnection方法,

    @CallerSensitive
    public static Connection getConnection(String url)
        throws SQLException {

        java.util.Properties info = new java.util.Properties();
        return (getConnection(url, info, Reflection.getCallerClass()));
    }
	
获取对应的数据库连接,JDBC的过程也是非常简单的,

    //  Worker method called by the public getConnection() methods.
    private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection(\"" + url + "\")");

        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;

        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }
	
建议大家看一下这个源码,通过Class.forName这种方式,直接通过反射拿到对应的Video,只不过MSYQL这里面还是需要通过注册的,

        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }
		
因为这个可以看出来它是一个for循环,在遍历注册的一个驱动,

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

并且它是CopyOnWriteArrayList,里面是DriverInfo,初始化的时候他是一个空的,具体是什么时候完成注册的呢,

我们把mysql的驱动增加进来,



<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.6</version>
</dependency>

我们就用5.1.6这个版本,我们就看一下com.mysql.jdbc.Driver这么一个类

一旦我们调用Class.forName,开始执行静态的DriverManager.registeredDriver

static {  
	try {  
		java.sql.DriverManager.registerDriver(new Driver());  
	} catch (SQLException E) {  
		throw new RuntimeException("Can't register driver!");  
	}  
}  

这个时候就会在registerDriver(new Driver())这个方法里面直接注册这个Driver,那里面的Driver自然就是MySQL的Driver,

public static synchronized void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);
}
 
如果不存在就往里放

if(driver != null) {
	registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
}
	
	public boolean addIfAbsent(E e) 这个方法使用了重入锁, 
	
final ReentrantLock lock = this.lock;这里面我们就不深入来讲解了,我们主要还是来关注设计模式,

讲到这些呢,主要是分享JDK源码中,一些相关的应用,JDK源码的分析就讲到这里,刚刚MYSQL需要扩展一下
我们看一下pom文件,我们把一些开源的项目导入到我们的设计模式当中,拿这些包主要是为了我们看源码的时候方便一些,

把版本声明也拿过来,这样这些包就导入进来了,关于logback一些简单的使用,

public final class LoggerFactory 

public static ILoggerFactory getILoggerFactory() {
    
}

最里面有一个getLogger方法,
	
public static Logger getLogger(Class clazz) {
	return getLogger(clazz.getName());
}

public static Logger getLogger(String name) {
	ILoggerFactory iLoggerFactory = getILoggerFactory();
	return iLoggerFactory.getLogger(name);
}

这个还有个重载,一个是String name,还有一个是Class clazz,clazz是干嘛的,是clazz.getName(),

首先getLogger根据我们传来的name,从iLoggerFactory里面getLogger,先看一下

public interface ILoggerFactory

package org.slf4j;
//抽象产品工厂
public interface ILoggerFactory {
    //抽象工厂方法
    public Logger getLogger(String name);
}

很明显ILoggerFactory它是一个接口,下面有一个方法,那这个呢是工厂方法,那在后面我们也会讲,这里先过去,

后面我们学习工厂方法的时候,再单独来说,然后通过iLoggerFactory.getLogger,因为它是一个接口,肯定有多个实现,

LoggerContext

//具体工厂实现类
public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
...
    //具体工厂方法
    @Override
    public final Logger getLogger(final String name) {
    	...
    }
...

这里面我们看一下,传入的一个name,这里面要返回Logger,这里面对name进行了判断,很明显这个方法就是一个简单工厂方法,

根据传入的入参进行选择哪个Logger,那这个还是非常简单的,刚刚也说了,在我们的LoggerFactory里面,既存在了工厂方法,

又存在了简单工厂,所以设计模式在使用的时候,不一定要局限在使用一种,例如这里就是一个组合的使用,这个简单工厂比较简单,

在很多源码中也能够找到他的影子,在前面的JDK,Logback开源框架的,对于学习设计模式的讲解呢,我们在阅读源码的时候呢,

还可以以设计模式的角度,去聚焦源码,这样对我们理解源码也是有益处的

 

相关文章:

  • 2021-05-30
  • 2022-01-05
  • 2022-12-23
  • 2021-12-14
  • 2021-08-25
  • 2021-05-04
  • 2021-04-13
  • 2021-11-30
猜你喜欢
  • 2021-04-12
  • 2021-09-25
  • 2021-10-29
  • 2021-12-14
  • 2021-12-08
  • 2021-04-30
相关资源
相似解决方案