FactoryBean
FactoryBean是spring框架定义的一个接口. 用于自定义bean的整个实例化过程;
说到实例化,默认spring bean只有给beanFactory注册beanDefinition就可以自动完成实例化, 这种方式能满足绝大多数的实例化需求. FactoryBean是为了满足复杂实例化的需求,比如aop生成代理对象, 自定义接口给接口生成代理对象等。
image.png
用法.
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
// 定义beanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserComponentFactoryBean.class);
// 自动注入
builder.setAutowireMode(2);
annotationConfigApplicationContext.registerBeanDefinition("userComponentFactoryBean", builder.getBeanDefinition());
annotationConfigApplicationContext.refresh();
public class UserComponentFactoryBean implements FactoryBean<UserComponent> {
private UserComponent userComponent;
@Override
public UserComponent getObject() throws Exception {
if (userComponent == null) {
userComponent = new UserComponent();
System.out.println("------------ factoryBean----" + userComponent.hashCode());
}
return userComponent;
}
@Override
public Class<?> getObjectType() {
return UserComponent.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
这里使用注册beanDefinition的方式让beanFactory感知到有FactoryBean,当然也可以用包扫描的方式加@Component。
这里UserComponentFactoryBean的getObject()方法获取对象实例只是示范,并不规范。
注意:如果UserCompontFactoryBean有依赖其他bean,FactoryBean不会自动完成注入。
如果需要获取bean关联的FactoryBean需要在beanName前加上 &
ProxyFactoryBean
spring中使用ProxyFactoryBean创建代理。
ProxyFactoryBean可以配置的属性有.
- proxyTargetClass: 如果要对类进行代理,这个属性设置为true
- optimize: 针对CGLIB优化.
- frozen: 冻结配置。如果为true有俩个作用,一个是不允许CGLIB优化, 另一个是代理创建后不允许手动进行增强的添加修改。默认是false, 所以一般情况下可以进行增强的手动添加修改;
- exposeProxy:是否暴露代理对象。如果是true, 则在增强点/目标对象可以通过AopContext.currentProxy()拿到代理对象;
- proxyInterfaces: string对象数组, 用于指定代理对象实现的接口。如果有值且-proxyTargetClass=false将启用jdk代理.
- interceptoreNames: 数组, 声明增强点集合。可以用* 一次匹配多个增强点;
- singleton: ProxyFactoryBean的getObject方法是否返回单例;
示例
- 定义被代理对象
public class TargetBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getProxy() {
// 在被代理对象里获取 代理
return AopContext.currentProxy();
}
@Override
public String toString() {
return "TargetBean{" +
"name='" + name + '\'' +
'}';
}
}
- 定义ProxyFactoryBean, 并设置一个增强点
@Configuration
public class FactoryBeanConfig {
@Bean("debugInterceptor")
public DebugInterceptor getInterceptor() {
return new DebugInterceptor();
}
@Bean
public ProxyFactoryBean getProxyFactoryBean() {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setInterceptorNames("debugInterceptor");
pfb.setTarget(new TargetBean());
// 暴露代理对象
pfb.setExposeProxy(true);
return pfb;
}
}
- 启动
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.scan("com.xie.java.aop.factorybean");
applicationContext.refresh();
TargetBean targetBean = applicationContext.getBean(TargetBean.class);
System.out.println("target bean :" + targetBean);
System.out.println("proxy bean :" + targetBean.getProxy());
// 任何代理对象都可以强制转换成 Advised对象
Advised advised = (Advised) targetBean;
System.out.println("advised :" + advised);
for (Advisor ad : advised.getAdvisors()) {
System.out.println("ad :" + ad );
System.out.println(" count = " + ((DebugInterceptor) ad.getAdvice()).getCount());
// 如果frozen = false, 可动态添加修改增强点
}
以上示例可以看出, ProxyFactoryBean是spring bean对象,目标类TargetBean不能是spring bean, 不然 getBean(TargetBean.class)会报异常。
getBean(TargetBean.class)查找ProxyFactoryBean的流程如下
1、根据类型TargetBean.class 查询spring容器中的所有bean Definition; 如果是FactoryBean则触发getType方法。
代码来自 DefaultListableBeanFactory
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
List<String> result = new ArrayList<String>();
// Check all bean definitions.
for (String beanName : this.beanDefinitionNames) {
// Only consider bean as eligible if the bean name
// is not defined as alias for some other bean.
if (!isAlias(beanName)) {
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Only check bean definition if it is complete.
if (!mbd.isAbstract() && (allowEagerInit ||
(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
// In case of FactoryBean, match object created by FactoryBean.
// 判断bean Definition是否是FactoryBean类型
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
boolean matchFound =
(allowEagerInit || !isFactoryBean ||
(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
(includeNonSingletons ||
(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
isTypeMatch(beanName, type);
if (!matchFound && isFactoryBean) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
}
if (matchFound) {
result.add(beanName);
}
}
}
..... 此处省略
// Check manually registered singletons too.
// 检查手动注册的单例
for (String beanName : this.manualSingletonNames) {
try {
// In case of FactoryBean, match object created by FactoryBean.
// 又一个FactoryBean判断
if (isFactoryBean(beanName)) {
if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
result.add(beanName);
// Match found for this bean: do not match FactoryBean itself anymore.
continue;
}
// In case of FactoryBean, try to match FactoryBean itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
}
// Match raw bean instance (might be raw FactoryBean).
if (isTypeMatch(beanName, type)) {
result.add(beanName);
}
}
... 此处省略
}
return StringUtils.toStringArray(result);
}
2、 找到ProxyFactoryBean后, 根据ProxyFactoryBean的 bean name 触发getBean(name, type args)
找到ProxyFactoryBean的实例后 触发getObjectForBeanInstance方法,从bean里获取bean
代码来着 AbstractBeanFactory
/**
* Get the object for the given bean instance, either the bean
* instance itself or its created object in case of a FactoryBean.
* @param beanInstance the shared bean instance
* @param name name that may include factory dereference prefix
* @param beanName the canonical bean name
* @param mbd the merged bean definition
* @return the object to expose for the bean
*/
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null;
if (mbd == null) {
// 此处 触发从FactoryBean 缓存 获取 对象
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 触发直接从FactoryBean 获取对象
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
3、触发ProxyFactory的 getObject()方法
public Object getObject() throws BeansException {
initializeAdvisorChain();
if (isSingleton()) {
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}
总结ProxyFactoryBean到目标代理对象的流程。
1、spring根据getBean的目标类型查找BeanDefinition
2、如果BeanDefinition是FactoryBean,则触发其getType方法
3、拿到ProxyFactoryBean bean后, 最终触发FactoryBean的getObject方法拿到代理对象.
MyBatise在FactoryBean上的应用
MapperFactoryBean<T>
程序急需要定义Mapper接口,MapperFactoryBean就会为其生成代理对象.
Dubbo在FactoryBean上的应用
ReferenceBean<T>
dubbo会为标识为@Reference的注入使用javassist生成具体的bean.
Spring Context在FactoryBean上的应用
ScopedProxyFactoryBean
给声明了@RefreshScope的类生成代理, 处理配置实时刷新同步的问题