FactoryMethod を使ってオブジェクトを生成する方法

まず、自分で書いたコードを掲載します。

public class User {
    private int userId;
    private String userName;
    private int userAge;
    private String userGender;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getUserAge() {
        return userAge;
    }

    public void setUserAge(int userAge) {
        this.userAge = userAge;
    }

    public String getUserGender() {
        return userGender;
    }

    public void setUserGender(String userGender) {
        this.userGender = userGender;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userAge=" + userAge +
                ", userGender='" + userGender + '\'' +
                '}';
    }
}

静的な工場メソッド:

public class UserStaticFactory {

    public static User createUser(String name){
        User user = new User();
        user.setUserId(1);
        user.setUserName(name);
        return user;
    }

    public static User createUser(int age){
        return new User();
    }

    public static User createUser(String name,int id){
        User user = new User();
        user.setUserId(1);
        user.setUserName(name);
        return user;
    }
}

インスタンス工場:

public class UserInstanceFactory {
    public User createUser(String name){
        User user = new User();
        user.setUserId(1);
        user.setUserName(name);
        return user;
    }
}

テストクラス:

public class TestFactoryMethod {

    public static void main(String[] args) {

        ApplicationContext ac = new ClassPathXmlApplicationContext("factoryMethod.xml");
        User user = ac.getBean("user", User.class);
        System.out.println(user);
        User user2 = ac.getBean("user2", User.class);
        System.out.println(user2);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--静的な工場メソッド-->    
<bean id="user" class="com.mashibing.factoryMethod.UserStaticFactory" factory-method="createUser">
        <!--constructor-arg:メソッドに引数を指定します-->
        <constructor-arg value="123"></constructor-arg>
    </bean>
<!--インスタンス工場--> 
    <bean id="userInstanceFactory" class="com.mashibing.factoryMethod.UserInstanceFactory"></bean>
    <bean id="user2" class="com.mashibing.factoryMethod.User" factory-bean="userInstanceFactory" factory-method="createUser">
        <constructor-arg value="wangwu"></constructor-arg>
    </bean>
</beans>

BeanFactoryの初期化が完了し、createBeanメソッドを経てdoCreateBean、createBeanInstanceに到達します。

createBeanInstanceのコード:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// クラス名を解決します
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		// 公開アクセス許可がない場合、例外をスローします
		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}

		// インスタンスサプライヤーがある場合、それを使用します
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}

		// 工場メソッドがある場合、それを使用します
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// コンストラクタ引数を解析します
		boolean resolved = false;
		boolean autowireNecessary = false;

		// 引数がない場合
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}

		// 解析済みのコンストラクタ引数がある場合
		if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				return instantiateBean(beanName, mbd);
			}
		}

		// コンストラクタ引数の解決が必要な場合
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// 最も適切なコンストラクタを検索します
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			return autowireConstructor(beanName, mbd, ctors, null);
		}

		// 引数なしコンストラクタを使用します
		return instantiateBean(beanName, mbd);
	}

上記の条件を通過し、工場メソッドの実装に到達します。

instantiateUsingFactoryMethodメソッド:

protected BeanWrapper instantiateUsingFactoryMethod(
			String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
		// 工場メソッドを使用してインスタンスを生成します
		return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
	}

ConstructorResolverのコンストラクタ:

public ConstructorResolver(AbstractAutowireCapableBeanFactory beanFactory) {
		this.beanFactory = beanFactory;
		this.logger = beanFactory.getLogger();
	}

工場メソッドを使用してインスタンスを生成するための基本的な流れは以下の通りです:

候補となるメソッドを取得します 各メソッドの引数の適合度を評価します 最も適合度の高いメソッドを選択します 選択したメソッドをキャッシュし、使用します

今回は、FactoryBean名が空のため、elseブロックに進みます。

getUserClassメソッド:

public static Class<?> getUserClass(Class<?> clazz) {
		// CGLIB生成の代理クラスかどうかを判断します
		if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
			Class<?> superclass = clazz.getSuperclass();
			if (superclass != null && superclass != Object.class) {
				return superclass;
			}
		}
		return clazz;
	}

このメソッドは、指定されたクラスがCGLIB生成の代理クラスかどうかを判断します。

isFactoryMethodメソッド:

public boolean isFactoryMethod(Method candidate) {
		return candidate.getName().equals(getFactoryMethodName());
	}

getCandidateMethodsメソッド:

private Method[] getCandidateMethods(Class<?> factoryClass, RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			return AccessController.doPrivileged((PrivilegedAction<Method[]>) () ->
					(mbd.isNonPublicAccessAllowed() ?
						ReflectionUtils.getAllDeclaredMethods(factoryClass) : factoryClass.getMethods()));
		}
		else {
			return (mbd.isNonPublicAccessAllowed() ?
					ReflectionUtils.getAllDeclaredMethods(factoryClass) : factoryClass.getMethods());
		}
	}

このメソッドは、指定されたクラスのメソッドを取得します。

instantiateメソッド:

@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Object factoryBean, final Method factoryMethod, Object... args) {

		try {
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					ReflectionUtils.makeAccessible(factoryMethod);
					return null;
				});
			}
			else {
				ReflectionUtils.makeAccessible(factoryMethod);
			}

			Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
			try {
				currentlyInvokedFactoryMethod.set(factoryMethod);
				Object result = factoryMethod.invoke(factoryBean, args);
				if (result == null) {
					result = new NullBean();
				}
				return result;
			}
			finally {
				if (priorInvokedFactoryMethod != null) {
					currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
				}
				else {
					currentlyInvokedFactoryMethod.remove();
				}
			}
		}
		catch (IllegalArgumentException ex) {
			throw new BeanInstantiationException(factoryMethod,
					"Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
					"args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
		}
		catch (IllegalAccessException ex) {
			throw new BeanInstantiationException(factoryMethod,
					"Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex);
		}
		catch (InvocationTargetException ex) {
			String msg = "Factory method '" + factoryMethod.getName() + "' threw exception";
			if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory &&
					((ConfigurableBeanFactory) owner).isCurrentlyInCreation(bd.getFactoryBeanName())) {
				msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " +
						"declaring the factory method as static for independence from its containing instance. " + msg;
			}
			throw new BeanInstantiationException(factoryMethod, msg, ex.getTargetException());
		}
	}

このメソッドは、工場メソッドをInvokerし、結果を返します。

タグ: Spring Framework 工場メソッド 依存性注入

7月16日 18:39 投稿