手写 Spring 框架之 Bean 定义配置化:从 XML 到注解,打造优雅的配置体验
关键词:Spring 源码, Bean 定义, XML 配置, 注解配置, 组件扫描, ApplicationContext, 手写框架
写在前面
在前面的文章中,我们已经手写实现了 Spring 的 IoC、DI 和 AOP 功能。但是,我们在使用这些功能时发现,代码调用非常繁琐:
// 手动注册 Bean 定义,代码重复且容易出错
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(ABean.class);
List<Object> args = new ArrayList<>();
args.add("abean01");
args.add(new BeanReference("cbean"));
bd.setConstructorArgumentValues(args);
bf.registerBeanDefinition("abean", bd);
这种方式不仅重复代码多、容易出错,而且给使用者带来了很不好的体验。那么 Spring 是如何解决这个问题的呢?
本文将带你实现 Spring 的 Bean 定义配置化功能,支持 XML 配置 和 注解配置 两种方式,让你的手写 Spring 框架更加完善和易用。
目录
一、问题分析与方案设计
1.1 现有问题
回顾之前的手写 Spring 框架,我们在使用时有以下痛点:
// 创建 BeanFactory
DefaultBeanFactory factory = new DefaultBeanFactory();
// 注册 CBean
GenericBeanDefinition cDef = new GenericBeanDefinition();
cDef.setBeanClass(CBean.class);
List<Object> cArgs = new ArrayList<>();
cArgs.add("cbean01");
cDef.setConstructorArgumentValues(cArgs);
factory.registerBeanDefinition("cbean", cDef);
// 注册 ABean,依赖 CBean
GenericBeanDefinition aDef = new GenericBeanDefinition();
aDef.setBeanClass(ABean.class);
List<Object> aArgs = new ArrayList<>();
aArgs.add("abean01");
aArgs.add(new BeanReference("cbean")); // 依赖引用
aDef.setConstructorArgumentValues(aArgs);
factory.registerBeanDefinition("abean", aDef);
// 获取 Bean
ABean aBean = (ABean) factory.getBean("abean");
问题总结:
| 问题 | 说明 |
|---|---|
| 代码重复 | 每个 Bean 都需要创建 GenericBeanDefinition |
| 容易出错 | BeanReference 容易写错,类型容易不匹配 |
| 可读性差 | 大量 Java 代码淹没业务逻辑 |
| 维护困难 | 修改配置需要修改代码并重新编译 |
1.2 Spring 的解决方案
Spring 提供了两种优雅的配置方式:
方式一:XML 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="abean" class="com.study.spring.samples.ABean">
<constructor-arg type="String" value="abean01" />
<constructor-arg ref="cbean" />
</bean>
<bean id="cbean" class="com.study.spring.samples.CBean">
<constructor-arg type="String" value="cbean01" />
</bean>
</beans>
方式二:注解配置
@Component
public class AController {
@Autowired
private Acc acc;
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
@PostConstruct
public void init() {
// 初始化逻辑
}
}
1.3 我们要实现的功能
功能清单:
-
XML 配置支持
- 定义 XML 规范
- 实现 XML 解析器
- 完成 Bean 定义注册
-
注解配置支持
- 定义一套注解(
@Component、@Autowired等) - 实现包扫描
- 解析注解并注册 Bean 定义
- 定义一套注解(
-
ApplicationContext
- 使用外观模式封装底层细节
- 提供统一简洁的使用界面
二、XML 配置方式实现
2.1 XML 规范设计
XML 结构设计:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- 基本 Bean 定义 -->
<bean id="userDao" class="com.boge.dao.UserDao" />
<!-- 构造器注入 -->
<bean id="userService" class="com.boge.service.UserService">
<constructor-arg ref="userDao" />
</bean>
<!-- 属性注入 -->
<bean id="orderService" class="com.boge.service.OrderService">
<property name="userDao" ref="userDao" />
<property name="timeout" value="5000" />
</bean>
<!-- 带参数的构造器 -->
<bean id="dataSource" class="com.boge.DataSource">
<constructor-arg type="String" value="jdbc:mysql://localhost:3306/test" />
<constructor-arg type="String" value="root" />
<constructor-arg type="String" value="password" />
</bean>
<!-- Scope 和初始化方法 -->
<bean id="cache" class="com.boge.Cache"
scope="singleton"
init-method="init"
destroy-method="destroy" />
</beans>
XML 元素说明:
| 元素 | 属性 | 说明 |
|---|---|---|
<beans> |
- | 根元素,包含所有 Bean 定义 |
<bean> |
id |
Bean 的唯一标识 |
<bean> |
class |
Bean 的全限定类名 |
<bean> |
scope |
作用域(singleton/prototype) |
<bean> |
init-method |
初始化方法名 |
<bean> |
destroy-method |
销毁方法名 |
<constructor-arg> |
value |
构造参数值(直接值) |
<constructor-arg> |
ref |
构造参数引用(Bean 依赖) |
<constructor-arg> |
type |
参数类型 |
<property> |
name |
属性名 |
<property> |
value |
属性值 |
<property> |
ref |
属性引用 |
2.2 XML 解析器实现
解析流程:
读取 XML 文件
│
▼
解析 DOM 树
│
▼
遍历 <bean> 元素
│
├── 提取 id、class、scope 等属性
│
├── 解析 <constructor-arg> 元素
│ └── 创建构造参数列表
│
├── 解析 <property> 元素
│ └── 创建属性值列表
│
└── 创建 BeanDefinition 并注册
XMLBeanDefinitionReader 实现:
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* XML Bean 定义读取器
*/
public class XmlBeanDefinitionReader {
private BeanDefinitionRegistry registry; // Bean 定义注册中心
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
this.registry = registry;
}
/**
* 加载 XML 配置文件
* @param location XML 文件路径
*/
public void loadBeanDefinitions(String location) throws Exception {
// 1. 加载 XML 文件
InputStream is = this.getClass().getClassLoader().getResourceAsStream(location);
if (is == null) {
throw new RuntimeException("找不到配置文件: " + location);
}
// 2. 解析 XML
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(is);
// 3. 解析 <bean> 元素
Element root = doc.getDocumentElement();
NodeList beanNodes = root.getElementsByTagName("bean");
for (int i = 0; i < beanNodes.getLength(); i++) {
Node beanNode = beanNodes.item(i);
if (beanNode instanceof Element) {
parseBeanElement((Element) beanNode);
}
}
}
/**
* 解析 <bean> 元素
*/
private void parseBeanElement(Element beanElement) throws Exception {
// 1. 获取基本属性
String beanName = beanElement.getAttribute("id");
String className = beanElement.getAttribute("class");
String scope = beanElement.getAttribute("scope");
String initMethod = beanElement.getAttribute("init-method");
String destroyMethod = beanElement.getAttribute("destroy-method");
// 2. 创建 BeanDefinition
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(Class.forName(className));
// 3. 设置 Scope
if (!scope.isEmpty()) {
bd.setScope(scope);
}
// 4. 设置初始化和销毁方法
if (!initMethod.isEmpty()) {
bd.setInitMethodName(initMethod);
}
if (!destroyMethod.isEmpty()) {
bd.setDestroyMethodName(destroyMethod);
}
// 5. 解析构造参数
parseConstructorArgs(beanElement, bd);
// 6. 解析属性
parseProperties(beanElement, bd);
// 7. 注册 Bean 定义
registry.registerBeanDefinition(beanName, bd);
}
/**
* 解析构造参数
*/
private void parseConstructorArgs(Element beanElement, GenericBeanDefinition bd) {
NodeList argNodes = beanElement.getElementsByTagName("constructor-arg");
List<Object> args = new ArrayList<>();
for (int i = 0; i < argNodes.getLength(); i++) {
Element argElement = (Element) argNodes.item(i);
Object value = parseValue(argElement);
args.add(value);
}
if (!args.isEmpty()) {
bd.setConstructorArgumentValues(args);
}
}
/**
* 解析属性
*/
private void parseProperties(Element beanElement, GenericBeanDefinition bd) {
NodeList propNodes = beanElement.getElementsByTagName("property");
List<PropertyValue> propertyValues = new ArrayList<>();
for (int i = 0; i < propNodes.getLength(); i++) {
Element propElement = (Element) propNodes.item(i);
String propName = propElement.getAttribute("name");
Object value = parseValue(propElement);
propertyValues.add(new PropertyValue(propName, value));
}
if (!propertyValues.isEmpty()) {
bd.setPropertyValues(propertyValues);
}
}
/**
* 解析值(value 或 ref)
*/
private Object parseValue(Element element) {
String value = element.getAttribute("value");
String ref = element.getAttribute("ref");
String type = element.getAttribute("type");
if (!ref.isEmpty()) {
// Bean 引用
return new BeanReference(ref);
} else if (!value.isEmpty()) {
// 直接值,可能需要类型转换
return convertValue(value, type);
}
return null;
}
/**
* 类型转换
*/
private Object convertValue(String value, String type) {
if (type.isEmpty() || "String".equals(type)) {
return value;
} else if ("int".equals(type) || "Integer".equals(type)) {
return Integer.parseInt(value);
} else if ("long".equals(type) || "Long".equals(type)) {
return Long.parseLong(value);
} else if ("boolean".equals(type) || "Boolean".equals(type)) {
return Boolean.parseBoolean(value);
} else if ("double".equals(type) || "Double".equals(type)) {
return Double.parseDouble(value);
}
return value;
}
}
2.3 与 BeanFactory 集成
使用示例:
public class XmlConfigTest {
public static void main(String[] args) throws Exception {
// 1. 创建 BeanFactory
DefaultBeanFactory factory = new DefaultBeanFactory();
// 2. 创建 XML 读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
// 3. 加载 XML 配置
reader.loadBeanDefinitions("application-context.xml");
// 4. 获取 Bean 并使用
UserService userService = (UserService) factory.getBean("userService");
userService.save(new User("张三"));
}
}
XML 配置文件(application-context.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- Dao 层 -->
<bean id="userDao" class="com.boge.dao.UserDaoImpl" />
<!-- Service 层 -->
<bean id="userService" class="com.boge.service.UserServiceImpl">
<constructor-arg ref="userDao" />
</bean>
<!-- 带属性的 Bean -->
<bean id="config" class="com.boge.Config">
<property name="appName" value="MyApplication" />
<property name="maxConnections" value="100" />
<property name="timeout" value="5000" />
</bean>
</beans>
三、注解配置方式实现
3.1 注解体系设计
需要定义的注解清单:
| 注解 | 作用 | 目标 |
|---|---|---|
@Component |
标识类为 Bean | 类 |
@Service |
标识服务层 Bean | 类 |
@Repository |
标识数据层 Bean | 类 |
@Controller |
标识控制层 Bean | 类 |
@Scope |
设置作用域 | 类 |
@Primary |
设置主要 Bean | 类 |
@Bean |
工厂方法 | 方法 |
@PostConstruct |
初始化方法 | 方法 |
@PreDestroy |
销毁方法 | 方法 |
@Autowired |
自动注入(构造器、属性) | 构造器、字段 |
@Value |
注入直接值 | 字段 |
@Qualifier |
指定 Bean 名称 | 字段、参数 |
注解定义:
import java.lang.annotation.*;
/**
* 组件注解,标识类需要被 Spring 管理
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value() default ""; // Bean 名称,默认类名首字母小写
}
/**
* 服务层注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Service {
String value() default "";
}
/**
* 数据访问层注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Repository {
String value() default "";
}
/**
* 作用域注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
String value() default "singleton"; // singleton 或 prototype
}
/**
* 主要 Bean 注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Primary {
}
/**
* 自动注入注解
*/
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
boolean required() default true;
}
/**
* 值注入注解
*/
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
String value();
}
/**
* 初始化方法注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PostConstruct {
}
/**
* 销毁方法注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreDestroy {
}
/**
* 工厂方法注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
String value() default "";
}
/**
* 限定符注解
*/
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {
String value();
}
3.2 组件扫描实现
扫描流程:
指定扫描包路径
│
▼
递归扫描包目录
│
├── 找到所有 .class 文件
│
├── 加载 Class 对象
│
├── 判断是否有 @Component 注解
│
├── 解析类上的其他注解
│
├── 解析方法上的注解
│
├── 解析字段上的注解
│
└── 注册 Bean 定义
ClassPathBeanDefinitionScanner 实现:
import java.io.File;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
/**
* 类路径 Bean 定义扫描器
*/
public class ClassPathBeanDefinitionScanner {
private BeanDefinitionRegistry registry;
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
this.registry = registry;
}
/**
* 扫描指定包
* @param basePackages 基础包路径
*/
public void scan(String... basePackages) throws Exception {
if (basePackages == null || basePackages.length == 0) {
return;
}
for (String basePackage : basePackages) {
// 1. 递归扫描包目录下的 .class 文件
Set<File> classFiles = doScan(basePackage);
// 2. 加载 Class 并解析注解,注册 Bean 定义
readAndRegisterBeanDefinition(classFiles, basePackage);
}
}
/**
* 递归扫描 .class 文件
*/
private Set<File> doScan(String basePackage) {
Set<File> classFiles = new HashSet<>();
// 将包路径转换为文件路径
String path = basePackage.replace(".", "/");
URL url = this.getClass().getClassLoader().getResource(path);
if (url != null) {
File dir = new File(url.getFile());
if (dir.exists()) {
scanDirectory(dir, classFiles);
}
}
return classFiles;
}
/**
* 递归扫描目录
*/
private void scanDirectory(File dir, Set<File> classFiles) {
File[] files = dir.listFiles();
if (files == null) return;
for (File file : files) {
if (file.isDirectory()) {
scanDirectory(file, classFiles);
} else if (file.getName().endsWith(".class")) {
classFiles.add(file);
}
}
}
/**
* 读取并注册 Bean 定义
*/
private void readAndRegisterBeanDefinition(Set<File> classFiles, String basePackage)
throws BeanDefinitionRegistException {
for (File classFile : classFiles) {
String className = getClassNameFromFile(classFile, basePackage);
try {
// 加载类
Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
// 检查是否有 @Component 或其派生注解
Component component = clazz.getAnnotation(Component.class);
if (component == null) {
// 检查其他派生注解
component = checkComponentMetaAnnotation(clazz);
}
if (component != null) {
// 生成 Bean 名称
String beanName = component.value();
if (beanName.isEmpty()) {
beanName = generateBeanName(clazz);
}
// 创建 BeanDefinition
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(clazz);
// 解析类级别的注解
parseClassAnnotations(clazz, bd);
// 解析构造器
parseConstructor(clazz, bd);
// 解析方法(初始化、销毁、工厂方法)
parseMethods(clazz, bd, beanName);
// 解析字段(属性注入)
parseFields(clazz, bd);
// 注册 Bean 定义
registry.registerBeanDefinition(beanName, bd);
// 注册 @Bean 方法定义的 Bean
registerBeanMethods(clazz, bd);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/**
* 检查元注解
*/
private Component checkComponentMetaAnnotation(Class<?> clazz) {
// 检查 @Service
Service service = clazz.getAnnotation(Service.class);
if (service != null) {
return new Component() {
@Override
public String value() { return service.value(); }
@Override
public Class<? extends Annotation> annotationType() { return Component.class; }
};
}
// 检查 @Repository
Repository repository = clazz.getAnnotation(Repository.class);
if (repository != null) {
return new Component() {
@Override
public String value() { return repository.value(); }
@Override
public Class<? extends Annotation> annotationType() { return Component.class; }
};
}
// 检查 @Controller
Controller controller = clazz.getAnnotation(Controller.class);
if (controller != null) {
return new Component() {
@Override
public String value() { return controller.value(); }
@Override
public Class<? extends Annotation> annotationType() { return Component.class; }
};
}
return null;
}
/**
* 从文件获取类名
*/
private String getClassNameFromFile(File classFile, String basePackage) {
String fileName = classFile.getAbsolutePath();
String className = fileName.substring(fileName.indexOf(basePackage.replace(".", File.separator)))
.replace(File.separator, ".")
.replace(".class", "");
return className;
}
/**
* 生成 Bean 名称(类名首字母小写)
*/
private String generateBeanName(Class<?> clazz) {
String className = clazz.getSimpleName();
return className.substring(0, 1).toLowerCase() + className.substring(1);
}
/**
* 解析类级别注解
*/
private void parseClassAnnotations(Class<?> clazz, GenericBeanDefinition bd) {
// 解析 @Scope
Scope scope = clazz.getAnnotation(Scope.class);
if (scope != null) {
bd.setScope(scope.value());
}
// 解析 @Primary
Primary primary = clazz.getAnnotation(Primary.class);
if (primary != null) {
bd.setPrimary(true);
}
}
/**
* 解析构造器
*/
private void parseConstructor(Class<?> clazz, GenericBeanDefinition bd) {
// 查找带有 @Autowired 的构造器
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
Autowired autowired = constructor.getAnnotation(Autowired.class);
if (autowired != null) {
// 使用这个构造器
bd.setConstructor(constructor);
// 解析参数
List<Object> args = new ArrayList<>();
for (Parameter param : constructor.getParameters()) {
Object arg = resolveParameter(param);
args.add(arg);
}
bd.setConstructorArgumentValues(args);
break;
}
}
}
/**
* 解析参数
*/
private Object resolveParameter(Parameter param) {
// 解析 @Value
Value value = param.getAnnotation(Value.class);
if (value != null) {
return value.value();
}
// 解析 @Qualifier
Qualifier qualifier = param.getAnnotation(Qualifier.class);
if (qualifier != null) {
return new BeanReference(qualifier.value());
}
// 默认按类型引用
return new BeanReference(param.getType());
}
/**
* 解析方法
*/
private void parseMethods(Class<?> clazz, GenericBeanDefinition bd, String beanName) {
for (Method method : clazz.getDeclaredMethods()) {
// 初始化方法
if (method.isAnnotationPresent(PostConstruct.class)) {
bd.setInitMethodName(method.getName());
}
// 销毁方法
if (method.isAnnotationPresent(PreDestroy.class)) {
bd.setDestroyMethodName(method.getName());
}
}
}
/**
* 解析字段
*/
private void parseFields(Class<?> clazz, GenericBeanDefinition bd) {
List<PropertyValue> propertyValues = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
Autowired autowired = field.getAnnotation(Autowired.class);
Value value = field.getAnnotation(Value.class);
if (autowired != null || value != null) {
String fieldName = field.getName();
Object fieldValue;
if (value != null) {
// @Value 注入直接值
fieldValue = value.value();
} else {
// @Autowired 注入 Bean
Qualifier qualifier = field.getAnnotation(Qualifier.class);
if (qualifier != null) {
fieldValue = new BeanReference(qualifier.value());
} else {
fieldValue = new BeanReference(field.getType());
}
}
propertyValues.add(new PropertyValue(fieldName, fieldValue));
}
}
if (!propertyValues.isEmpty()) {
bd.setPropertyValues(propertyValues);
}
}
/**
* 注册 @Bean 方法定义的 Bean
*/
private void registerBeanMethods(Class<?> clazz, GenericBeanDefinition configBd) {
for (Method method : clazz.getDeclaredMethods()) {
Bean bean = method.getAnnotation(Bean.class);
if (bean != null) {
String beanName = bean.value();
if (beanName.isEmpty()) {
beanName = method.getName();
}
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setFactoryBeanName(configBd.getBeanClass().getName());
bd.setFactoryMethodName(method.getName());
bd.setBeanClass(method.getReturnType());
// 解析工厂方法参数
List<Object> args = new ArrayList<>();
for (Parameter param : method.getParameters()) {
Object arg = resolveParameter(param);
args.add(arg);
}
bd.setConstructorArgumentValues(args);
registry.registerBeanDefinition(beanName, bd);
}
}
}
}
3.3 注解解析与注册
使用注解的示例代码:
// ==================== Dao 层 ====================
@Repository
public class UserDao {
public User findById(Long id) {
// 查询数据库
return new User(id, "张三");
}
}
// ==================== Service 层 ====================
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Value("${app.name}")
private String appName;
@PostConstruct
public void init() {
System.out.println("UserService 初始化,应用名称:" + appName);
}
public User getUser(Long id) {
return userDao.findById(id);
}
}
// ==================== 配置类 ====================
@Component
public class AppConfig {
@Bean
public DataSource dataSource() {
DataSource ds = new DataSource();
ds.setUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("root");
ds.setPassword("123456");
return ds;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Autowired DataSource dataSource) {
return new SqlSessionFactory(dataSource);
}
}
// ==================== 测试 ====================
public class AnnotationTest {
public static void main(String[] args) throws Exception {
DefaultBeanFactory factory = new DefaultBeanFactory();
// 创建扫描器
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(factory);
// 扫描指定包
scanner.scan("com.boge");
// 获取并使用 Bean
UserService userService = (UserService) factory.getBean("userService");
User user = userService.getUser(1L);
System.out.println(user);
}
}
四、ApplicationContext 外观模式
虽然我们已经实现了 XML 和注解配置,但用户的使用体验还可以进一步简化。我们可以使用外观模式(Facade Pattern)为框架定义一个更简单统一的使用界面。
ApplicationContext 接口:
/**
* 应用上下文
* 提供统一简洁的 Bean 访问接口
*/
public interface ApplicationContext extends BeanFactory {
/**
* 刷新上下文
*/
void refresh() throws Exception;
/**
* 获取所有 Bean 定义名称
*/
String[] getBeanDefinitionNames();
/**
* 获取 Bean 数量
*/
int getBeanDefinitionCount();
}
ClassPathXmlApplicationContext 实现:
/**
* 类路径 XML 应用上下文
*/
public class ClassPathXmlApplicationContext implements ApplicationContext {
private DefaultBeanFactory beanFactory;
private String[] configLocations;
public ClassPathXmlApplicationContext(String... configLocations) throws Exception {
this.configLocations = configLocations;
refresh();
}
@Override
public void refresh() throws Exception {
// 1. 创建 BeanFactory
beanFactory = new DefaultBeanFactory();
// 2. 加载 Bean 定义
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
for (String location : configLocations) {
reader.loadBeanDefinitions(location);
}
// 3. 初始化所有单例 Bean
beanFactory.preInstantiateSingletons();
}
@Override
public Object getBean(String beanName) throws Exception {
return beanFactory.getBean(beanName);
}
@Override
public <T> T getBean(String beanName, Class<T> requiredType) throws Exception {
return beanFactory.getBean(beanName, requiredType);
}
@Override
public String[] getBeanDefinitionNames() {
return beanFactory.getBeanDefinitionNames();
}
@Override
public int getBeanDefinitionCount() {
return beanFactory.getBeanDefinitionCount();
}
}
AnnotationConfigApplicationContext 实现:
/**
* 注解配置应用上下文
*/
public class AnnotationConfigApplicationContext implements ApplicationContext {
private DefaultBeanFactory beanFactory;
private String[] basePackages;
public AnnotationConfigApplicationContext(String... basePackages) throws Exception {
this.basePackages = basePackages;
refresh();
}
@Override
public void refresh() throws Exception {
// 1. 创建 BeanFactory
beanFactory = new DefaultBeanFactory();
// 2. 扫描并注册 Bean 定义
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan(basePackages);
// 3. 注册 AOP 处理器
beanFactory.registerBeanPostProcessor(new AopBeanPostProcessor(beanFactory));
// 4. 初始化所有单例 Bean
beanFactory.preInstantiateSingletons();
}
@Override
public Object getBean(String beanName) throws Exception {
return beanFactory.getBean(beanName);
}
@Override
public <T> T getBean(String beanName, Class<T> requiredType) throws Exception {
return beanFactory.getBean(beanName, requiredType);
}
@Override
public String[] getBeanDefinitionNames() {
return beanFactory.getBeanDefinitionNames();
}
@Override
public int getBeanDefinitionCount() {
return beanFactory.getBeanDefinitionCount();
}
}
使用对比:
改进前(繁琐):
// 创建工厂
DefaultBeanFactory factory = new DefaultBeanFactory();
// 创建读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions("application-context.xml");
// 获取 Bean
UserService userService = (UserService) factory.getBean("userService");
改进后(简洁):
// XML 方式
ApplicationContext ctx = new ClassPathXmlApplicationContext("application-context.xml");
UserService userService = ctx.getBean("userService", UserService.class);
// 注解方式
ApplicationContext ctx = new AnnotationConfigApplicationContext("com.boge");
UserService userService = ctx.getBean("userService", UserService.class);
五、完整实现与测试
完整类图
┌─────────────────────────────────────────────────────────────────────┐
│ ApplicationContext │
│ + getBean(String): Object │
│ + refresh(): void │
└──────┬──────────────────────────────────────────────────────────────┘
│
├── ClassPathXmlApplicationContext
│ - configLocations: String[]
│ - beanFactory: DefaultBeanFactory
│ + refresh(): void
│
└── AnnotationConfigApplicationContext
- basePackages: String[]
- beanFactory: DefaultBeanFactory
+ refresh(): void
┌─────────────────────────────────────────────────────────────────────┐
│ XmlBeanDefinitionReader │
│ - registry: BeanDefinitionRegistry │
│ + loadBeanDefinitions(String): void │
│ - parseBeanElement(Element): void │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ ClassPathBeanDefinitionScanner │
│ - registry: BeanDefinitionRegistry │
│ + scan(String...): void │
│ - doScan(String): Set<File> │
│ - parseClassAnnotations(Class, BeanDefinition): void │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 注解体系 │
│ │
│ @Component @Service @Repository @Controller │
│ │ │
│ ├── @Scope │
│ ├── @Primary │
│ ├── @Autowired │
│ ├── @Value │
│ ├── @PostConstruct │
│ └── @PreDestroy │
└─────────────────────────────────────────────────────────────────────┘
测试用例
public class BeanConfigTest {
@Test
public void testXmlConfig() throws Exception {
// 使用 XML 配置
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"application-context.xml"
);
UserService userService = ctx.getBean("userService", UserService.class);
assertNotNull(userService);
User user = userService.getUser(1L);
assertNotNull(user);
assertEquals("张三", user.getName());
}
@Test
public void testAnnotationConfig() throws Exception {
// 使用注解配置
ApplicationContext ctx = new AnnotationConfigApplicationContext(
"com.boge"
);
// 验证扫描到的 Bean
String[] beanNames = ctx.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println("Bean: " + name);
}
UserService userService = ctx.getBean("userService", UserService.class);
assertNotNull(userService);
// 验证依赖注入
assertNotNull(userService.getUserDao());
}
@Test
public void testFactoryMethod() throws Exception {
ApplicationContext ctx = new AnnotationConfigApplicationContext(
"com.boge"
);
// 测试 @Bean 方法
DataSource dataSource = ctx.getBean("dataSource", DataSource.class);
assertNotNull(dataSource);
assertEquals("jdbc:mysql://localhost:3306/test", dataSource.getUrl());
}
}
六、总结
核心知识点回顾
1. XML 配置方式
- 定义 XML 规范(
<bean>、<constructor-arg>、<property>等) - 实现
XmlBeanDefinitionReader解析 XML - 支持构造器注入和属性注入
- 支持类型转换
2. 注解配置方式
- 定义注解体系(
@Component、@Autowired、@Value等) - 实现
ClassPathBeanDefinitionScanner包扫描 - 解析类、方法、字段上的注解
- 支持
@Bean工厂方法
3. ApplicationContext
- 外观模式封装底层细节
ClassPathXmlApplicationContext用于 XML 配置AnnotationConfigApplicationContext用于注解配置- 提供统一简洁的使用接口
与 Spring 源码的对比
| 功能 | 手写实现 | Spring 源码 |
|---|---|---|
| XML 解析 | XmlBeanDefinitionReader |
XmlBeanDefinitionReader |
| 注解扫描 | ClassPathBeanDefinitionScanner |
ClassPathBeanDefinitionScanner |
| 上下文 | ApplicationContext |
ApplicationContext |
| XML 上下文 | ClassPathXmlApplicationContext |
ClassPathXmlApplicationContext |
| 注解上下文 | AnnotationConfigApplicationContext |
AnnotationConfigApplicationContext |
| 注解定义 | 自定义注解 | @Component、@Autowired 等 |
| 类型转换 | 简单实现 | TypeConverter、PropertyEditor |
学习建议
- 动手实践:自己实现一遍 XML 解析和注解扫描
- 阅读源码:与 Spring 源码对比,学习处理细节
- 思考问题:
- XML 和注解配置各有什么优缺点?
- 如何处理循环依赖的 Bean 定义注册?
- 如何支持占位符(如
${jdbc.url})?
- 扩展练习:
- 实现
@Profile环境切换 - 实现
@Conditional条件装配 - 支持
import导入其他配置文件
- 实现
相关资源
- Spring Core 文档:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html
- Spring 源码:https://github.com/spring-projects/spring-framework
如果本文对你有帮助,欢迎点赞、收藏、关注!如有疑问,欢迎在评论区留言讨论。
关键词:Spring 源码, Bean 定义, XML 配置, 注解配置, 组件扫描, ApplicationContext, 手写框架, IoC 容器
本文是手写 Spring 框架系列第四篇,实现了 Bean 定义配置化功能。敬请期待后续文章…
更多推荐



所有评论(0)