国家电网面试被问:FactoryBean与BeanFactory的区别和动态代理生成

一、核心概念对比

1.1 一句话总结

BeanFactory是Spring的"工厂总管",FactoryBean是"特殊产品生产线"

1.2 详细对比表

维度 BeanFactory FactoryBean
定位 Spring IoC容器的核心接口 创建复杂Bean的扩展接口
职责 管理所有Bean的生命周期 专门生产特定类型的复杂Bean
方法数量 20+个方法(getBean、containsBean等) 3个核心方法(getObject、getObjectType、isSingleton)
实现者 ApplicationContext、DefaultListableBeanFactory MyBatis的SqlSessionFactoryBean、Spring的ProxyFactoryBean
使用场景 所有Bean的获取和管理 需要复杂创建逻辑的对象(如动态代理)

二、源码级解析

2.1 BeanFactory接口结构

java

复制

下载

复制代码
public interface BeanFactory {
    // 核心方法:获取Bean
    Object getBean(String name) throws BeansException;
    
    // 检查是否包含Bean
    boolean containsBean(String name);
    
    // 是否是单例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    // 获取Bean类型
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
    // 还有20+个其他方法...
}

2.2 FactoryBean接口结构

java

复制

下载

复制代码
public interface FactoryBean<T> {
    // 核心:创建对象的方法
    T getObject() throws Exception;
    
    // 返回对象的类型
    Class<?> getObjectType();
    
    // 是否单例
    boolean isSingleton();
}

三、动态代理生成的实战案例

3.1 FactoryBean实现动态代理

java

复制

下载

复制代码
/**
 * 动态代理FactoryBean示例:为任意接口生成代理
 */
@Component
public class DynamicProxyFactoryBean implements FactoryBean<Object>, InitializingBean {
    
    private Class<?> targetInterface;  // 要代理的接口
    private Object targetObject;       // 真实对象(可选)
    private Object proxy;              // 代理对象
    
    /**
     * 核心方法:创建代理对象
     */
    @Override
    public Object getObject() throws Exception {
        if (proxy == null) {
            proxy = createProxy();
        }
        return proxy;
    }
    
    /**
     * 创建JDK动态代理
     */
    private Object createProxy() {
        return Proxy.newProxyInstance(
            targetInterface.getClassLoader(),
            new Class[]{targetInterface},
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 前置增强:记录日志
                    logBefore(method, args);
                    
                    // 方法执行
                    Object result;
                    long startTime = System.currentTimeMillis();
                    
                    try {
                        if (targetObject != null) {
                            // 调用真实对象的方法
                            result = method.invoke(targetObject, args);
                        } else {
                            // 无真实对象的情况(如MyBatis Mapper)
                            result = handleMethodWithoutTarget(method, args);
                        }
                        
                        // 后置增强:记录结果
                        logAfter(method, result, System.currentTimeMillis() - startTime);
                        
                        return result;
                        
                    } catch (InvocationTargetException e) {
                        // 异常增强
                        handleException(method, e.getTargetException());
                        throw e.getTargetException();
                    }
                }
            }
        );
    }
    
    /**
     * 记录方法调用日志
     */
    private void logBefore(Method method, Object[] args) {
        System.out.println("[" + new Date() + "] 调用方法: " + method.getName());
        if (args != null && args.length > 0) {
            System.out.println("参数: " + Arrays.toString(args));
        }
    }
    
    /**
     * 处理无实现类的方法调用(如MyBatis Mapper)
     */
    private Object handleMethodWithoutTarget(Method method, Object[] args) {
        String methodName = method.getName();
        
        // 这里可以根据方法名、参数等信息执行相应逻辑
        // 比如:转换SQL语句、调用远程服务等
        
        System.out.println("处理方法调用: " + methodName);
        
        // 返回默认值(实际项目中会有具体实现)
        if (method.getReturnType() == void.class) {
            return null;
        } else if (method.getReturnType() == String.class) {
            return "proxy-result";
        } else if (method.getReturnType() == List.class) {
            return Collections.emptyList();
        } else if (method.getReturnType().isPrimitive()) {
            return 0;
        }
        
        return null;
    }
    
    @Override
    public Class<?> getObjectType() {
        return targetInterface;
    }
    
    @Override
    public boolean isSingleton() {
        return true;  // 代理对象通常是单例
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        // 参数验证
        Assert.notNull(targetInterface, "目标接口不能为空");
        Assert.isTrue(targetInterface.isInterface(), "目标必须是接口");
    }
    
    // Getter和Setter
    public void setTargetInterface(Class<?> targetInterface) {
        this.targetInterface = targetInterface;
    }
    
    public void setTargetObject(Object targetObject) {
        this.targetObject = targetObject;
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

3.2 使用CGLIB创建类代理

java

复制

下载

复制代码
/**
 * CGLIB代理FactoryBean:可以为类创建代理(不需要接口)
 */
@Component
public class CglibProxyFactoryBean implements FactoryBean<Object> {
    
    private Class<?> targetClass;
    private Object target;
    private Object proxy;
    
    @Override
    public Object getObject() throws Exception {
        if (proxy == null) {
            proxy = createCglibProxy();
        }
        return proxy;
    }
    
    /**
     * 使用CGLIB创建代理
     */
    private Object createCglibProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, 
                                  MethodProxy proxy) throws Throwable {
                
                // 方法过滤:不代理Object类的方法
                if (method.getDeclaringClass() == Object.class) {
                    return proxy.invokeSuper(obj, args);
                }
                
                // 前置增强
                System.out.println("CGLIB代理 - 调用前: " + method.getName());
                
                // 执行原方法
                long start = System.currentTimeMillis();
                Object result = proxy.invokeSuper(obj, args);
                
                // 后置增强
                System.out.println("CGLIB代理 - 调用后: " + method.getName() + 
                                 ", 耗时: " + (System.currentTimeMillis() - start) + "ms");
                
                return result;
            }
        });
        
        return enhancer.create();
    }
    
    @Override
    public Class<?> getObjectType() {
        return targetClass;
    }
    
    @Override
    public boolean isSingleton() {
        return true;
    }
}

3.3 XML配置示例

xml

复制

下载

运行

复制代码
<!-- 配置FactoryBean -->
<bean id="userServiceProxy" class="com.example.DynamicProxyFactoryBean">
    <property name="targetInterface" value="com.example.UserService"/>
    <property name="targetObject" ref="userServiceImpl"/>
</bean>

<bean id="productDaoProxy" class="com.example.CglibProxyFactoryBean">
    <property name="targetClass" value="com.example.ProductDao"/>
</bean>

3.4 注解配置示例

java

复制

下载

复制代码
@Configuration
public class ProxyConfig {
    
    @Bean
    public FactoryBean<Object> userMapperProxy() {
        DynamicProxyFactoryBean factoryBean = new DynamicProxyFactoryBean();
        factoryBean.setTargetInterface(UserMapper.class);
        // 可以设置自定义的InvocationHandler
        return factoryBean;
    }
    
    @Bean
    public UserService userServiceProxy(@Autowired UserServiceImpl userService) {
        // 或者使用@Bean方法直接返回代理
        return (UserService) Proxy.newProxyInstance(
            UserService.class.getClassLoader(),
            new Class[]{UserService.class},
            new UserServiceInvocationHandler(userService)
        );
    }
}

四、实际框架中的应用

4.1 MyBatis的MapperFactoryBean

java

复制

下载

复制代码
/**
 * MyBatis MapperFactoryBean的简化版实现
 */
public class MyBatisMapperFactoryBean<T> implements FactoryBean<T> {
    
    private SqlSession sqlSession;
    private Class<T> mapperInterface;
    
    @Override
    public T getObject() throws Exception {
        // MyBatis的核心:为接口创建动态代理
        return sqlSession.getMapper(mapperInterface);
    }
    
    @Override
    public Class<?> getObjectType() {
        return mapperInterface;
    }
    
    @Override
    public boolean isSingleton() {
        return true;
    }
}

4.2 Spring AOP的ProxyFactoryBean

java

复制

下载

复制代码
/**
 * Spring早期AOP的ProxyFactoryBean
 */
public class AopProxyFactoryBean implements FactoryBean<Object>, BeanClassLoaderAware {
    
    private Object target;
    private Advisor advisor;
    
    @Override
    public Object getObject() throws Exception {
        // 创建AOP代理
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(advisor);
        
        // 根据情况选择JDK代理或CGLIB代理
        if (target.getClass().getInterfaces().length > 0) {
            proxyFactory.setInterfaces(target.getClass().getInterfaces());
            return proxyFactory.getProxy();
        } else {
            return proxyFactory.getProxy(target.getClass().getClassLoader());
        }
    }
}

4.3 缓存代理FactoryBean

java

复制

下载

复制代码
/**
 * 带缓存功能的代理FactoryBean
 */
@Component
public class CacheableProxyFactoryBean implements FactoryBean<Object> {
    
    private Object target;
    private Map<String, Object> cache = new ConcurrentHashMap<>();
    
    @Override
    public Object getObject() throws Exception {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    
                    // 检查是否有@Cacheable注解
                    if (method.isAnnotationPresent(Cacheable.class)) {
                        String cacheKey = generateCacheKey(method, args);
                        
                        // 从缓存获取
                        if (cache.containsKey(cacheKey)) {
                            System.out.println("缓存命中: " + cacheKey);
                            return cache.get(cacheKey);
                        }
                        
                        // 执行方法
                        Object result = method.invoke(target, args);
                        
                        // 存入缓存
                        cache.put(cacheKey, result);
                        System.out.println("缓存写入: " + cacheKey);
                        
                        return result;
                    }
                    
                    // 没有缓存注解,直接执行
                    return method.invoke(target, args);
                }
                
                private String generateCacheKey(Method method, Object[] args) {
                    StringBuilder key = new StringBuilder(method.getName());
                    if (args != null) {
                        for (Object arg : args) {
                            key.append(":").append(arg);
                        }
                    }
                    return key.toString();
                }
            }
        );
    }
}

五、面试回答要点

5.1 回答结构

第一层:概念区别

  • BeanFactory:Spring容器核心,管理所有Bean

  • FactoryBean:创建复杂对象的工厂,本身也是一个Bean

第二层:设计目的

  • 分离关注点:BeanFactory负责通用逻辑,FactoryBean负责特殊创建

  • 开闭原则:通过扩展而不是修改来增加功能

第三层:动态代理实战

  • FactoryBean的getObject()方法是创建代理的最佳位置

  • 可以封装JDK Proxy、CGLIB等代理创建逻辑

  • 在代理中可以加入日志、缓存、事务等横切关注点

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

5.2 高频问题应对

Q:什么时候用FactoryBean?

A:三种典型场景:

  1. 对象创建过程复杂(如需要多步初始化)

  2. 需要动态生成对象(如动态代理)

  3. 对象需要根据配置变化(如不同环境的配置)

Q:FactoryBean和@Bean方法选哪个?

A:

  • 简单对象:用@Bean

  • 复杂创建逻辑:用FactoryBean

  • 需要复用创建逻辑:用FactoryBean

Q:如何获取FactoryBean本身?

A:使用"&"前缀:

java

复制

下载

复制代码
// 获取产品
MyProduct product = context.getBean("myFactoryBean");
// 获取工厂本身  
MyFactoryBean factory = context.getBean("&myFactoryBean");

六、性能优化建议

6.1 代理对象缓存

java

复制

下载

复制代码
public class CachedProxyFactoryBean implements FactoryBean<Object> {
    
    private volatile Object proxy;  // volatile保证可见性
    
    @Override
    public Object getObject() throws Exception {
        if (proxy == null) {
            synchronized (this) {
                if (proxy == null) {  // 双重检查锁
                    proxy = createProxy();
                }
            }
        }
        return proxy;
    }
}

6.2 懒加载支持

java

复制

下载

复制代码
@Component
@Lazy  // 配合@Lazy实现懒加载
public class LazyInitProxyFactoryBean implements FactoryBean<Object>, SmartFactoryBean<Object> {
    
    @Override
    public boolean isEagerInit() {
        return false;  // 不是急切初始化
    }
    
    @Override  
    public Object getObject() throws Exception {
        // 第一次调用时才会创建代理
        return createProxy();
    }
}

七、总结

FactoryBean是Spring框架中一个强大的扩展点,特别适合:

  1. 动态代理生成:封装代理创建逻辑

  2. 复杂对象创建:多步骤初始化过程

  3. 资源池管理:如数据库连接池、线程池

  4. 协议适配:如RPC客户端、消息队列消费者

关键要点

  • FactoryBean本身也是Bean,由BeanFactory管理

  • getObject()方法返回的是"产品",不是工厂本身

  • 动态代理是FactoryBean最经典的应用场景

  • 理解这个设计有助于掌握Spring的扩展机制

相关推荐
电商API&Tina2 小时前
Python请求淘宝商品评论API接口全指南||taobao评论API
java·开发语言·数据库·python·json·php
小北方城市网2 小时前
Redis 分布式锁与缓存三大问题解决方案
spring boot·redis·分布式·后端·缓存·wpf·mybatis
若鱼19192 小时前
SpringBoot4.0新特性-Resilience之失败重试
java·spring
txinyu的博客2 小时前
static_cast、const_cast、dynamic_cast、reinterpret_cast
linux·c++
摩西蒙2 小时前
业务监控和常用产品
java·大数据·人工智能
哪里不会点哪里.2 小时前
Spring 核心原理解析:它到底解决了什么问题?
java·后端·spring
“αβ”2 小时前
TCP相关实验
运维·服务器·网络·c++·网络协议·tcp/ip·udp
qq_254674412 小时前
Cisco Nexus 9504交换机上
java·linux·服务器
咕噜企业分发小米2 小时前
腾讯云在多云管理工具上如何实现合规性要求?
java·云计算·腾讯云