Day01——快速入门
一、简介
Spring:轻量级的、开源的JavaEE解决方案
二、工厂模式
2.1 概述
工厂设计模式是一种创建型设计模式,其核心思想是通过一个工厂类来封装对象的创建过程,客户端无需直接实例化具体类,而是通过工厂获取对象。这种模式解耦了对象的创建与使用,提升了系统的灵活性和可维护性。
例如
Dao层中存在多个UserDaoImpl实现,以UserDaoMybatisImpl举例
public class UserDaoMybatisImpl implements UserDao {
public UserDaoMybatisImpl() {
}
@Override
public void save() {
System.out.println("Mybatis implements");
}
}
在bean.xml文件中有如下定义:
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id="userDao" class="com.zzy.user.UserDaoMybatisImpl"/>
</beans>
创建类XmlBeanFactory读取xml文件并利用反射生产类
public class XmlBeanFactory {
public static Object getBean(String fileName, String key) {
SAXReader reader = new SAXReader();
try {
Document doc = reader.read(fileName);
Element root = doc.getRootElement();
List<Element> beans = root.elements("bean");
for (Element bean : beans) {
if (key.contentEquals(bean.attributeValue("id"))) {
String className = bean.attributeValue("class");
//使用类名反射创建Class对象
Class<?> aClass = Class.forName(className);
return aClass.getConstructor().newInstance();
}
}
} catch (DocumentException | ClassNotFoundException | NoSuchMethodException | InstantiationException |
IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
在Service层中定义save方法
实现类中如下
public class UserServiceImpl implements UserService {
public UserServiceImpl() {
}
@Override
public void save() {
UserDao dao = (UserDao) XmlBeanFactory.getBean("G:\\Spring\\spring_core\\day01\\target\\classes\\bean.xml", "userDao");
if (dao != null) {
dao.save();
}
}
}
在测试类中调用Serviceimpl的save方法后,控制台输出
Mybatis implements
2.2 Spring工厂模式的核心实现
1.BeanFactory
Spring的BeanFactory
是工厂模式的典型代表,它是IoC容器的核心接口,负责管理Bean的生命周期和依赖注入。其核心方法getBean()
通过唯一标识符动态创建或获取Bean实例,例如:
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
UserService userService = (UserService) factory.getBean("userService");
延迟加载:BeanFactory
默认延迟初始化Bean,仅在首次调用getBean()
时创建实例,减少启动时的资源占用。
2.ApplicationContext
作为BeanFactory
的子接口,ApplicationContext
扩展了更多功能(如事件发布、国际化支持等),并支持预加载所有单例Bean。常见实现类包括:
-
ClassPathXmlApplicationContext
:加载类路径下的XML配置文件。 -
AnnotationConfigApplicationContext
:基于Java注解配置Bean。 -
FileSystemXmlApplicationContext
:从文件系统加载XML配置。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = context.getBean("user", User.class);
3.FactoryBean
当Bean的创建逻辑复杂时(如依赖外部资源或动态代理),可通过实现FactoryBean
接口自定义实例化过程。例如:
public class CarFactoryBean implements FactoryBean<Car> {
@Override
public Car getObject() {
return new Car("Tesla", "red"); // 复杂初始化逻辑
}
@Override
public Class<?> getObjectType() { return Car.class; }
}
-
特点:通过
getBean("carFactoryBean")
获取的是Car
实例而非工厂本身。
三、控制反转(Ioc)与依赖注入(DI)
一、IoC与DI的本质:控制权的转移
1. 传统开发模式:程序员掌控一切
在传统编码中,对象的创建、依赖关系由开发者显式控制。例如:
// 传统方式:直接在代码中创建依赖对象
public class Car {
private Engine engine = new GasolineEngine(); // 直接绑定具体实现
public void start() {
engine.ignite();
}
}
-
问题:
Car
类与GasolineEngine
类强耦合。若想改用ElectricEngine
,必须修改Car
的源码,违反开闭原则(OCP)。
2. IoC(控制反转):将控制权交给容器
IoC的核心是反转对象创建和依赖绑定的控制权,由容器(如Spring)统一管理。例如:
public class Car {
private Engine engine; // 不再直接创建对象
public Car(Engine engine) { // 依赖通过外部传入
this.engine = engine;
}
}
-
关键点:
Car
不再关心Engine
的具体实现,只需声明依赖接口,由容器决定注入GasolineEngine
还是ElectricEngine
。
3. DI(依赖注入):实现IoC的具体手段
DI是IoC的一种实现方式,通过外部注入依赖而非内部创建。这里先简单讲讲Spring支持三种注入方式,下一节中将重点讲解注入(injection):
-
构造函数注入(最推荐,保证依赖不可变):
@Component public class Car { private final Engine engine; @Autowired // Spring自动注入Engine实例 public Car(Engine engine) { this.engine = engine; } }
-
Setter方法注入(适合可选依赖):
public class Car { private Engine engine; @Autowired public void setEngine(Engine engine) { this.engine = engine; } }
-
字段注入(简洁但隐藏依赖关系,不推荐):
public class Car { @Autowired private Engine engine; }
二、IoC与DI的核心价值:解耦与灵活性
1. 解耦的实战意义
-
场景1:替换数据库驱动
若项目从MySQL迁移到PostgreSQL,只需修改Spring配置中的JDBC连接参数,无需改动业务层代码。 -
场景2:Mock测试
单元测试时,可用Mock对象(如Mockito)注入到被测试类,无需启动真实数据库:@Test public void testCar() { Engine mockEngine = Mockito.mock(Engine.class); Car testCar = new Car(mockEngine); // 注入Mock对象 testCar.start(); Mockito.verify(mockEngine).ignite(); }
2. Spring容器的实现机制
-
BeanFactory:基础容器,提供Bean的创建与获取。
-
ApplicationContext:扩展了BeanFactory,支持国际化、事件传播等企业级功能。
-
工作流程:
-
容器启动时,扫描配置(XML或注解)并初始化Bean定义。
-
根据依赖关系(如
@Autowired
)构建Bean之间的关联图。 -
通过反射或CGLIB动态代理创建Bean实例,完成依赖注入。
-
三、IoC与DI的进阶理解
1. IoC的两种实现方式对比
方式 | 依赖注入(DI) | 依赖查找(DL) |
---|---|---|
代表框架 | Spring、Guice | EJB(通过JNDI查找) |
代码侵入性 | 低(通过注解/配置声明依赖) | 高(需主动调用查找API) |
灵活性 | 依赖关系由容器管理,透明化 | 需手动管理对象生命周期 |
2. Spring中的DI高级特性
-
条件化注入:使用
@Conditional
根据环境动态注入Bean(如区分开发与生产环境配置)。 -
多Bean冲突解决:
-
@Primary
:标记优先注入的Bean。 -
@Qualifier
:按名称指定具体Bean。
-
-
延迟注入:
@Lazy
注解延迟初始化Bean,优化启动性能。
3. IoC与设计模式的关系
-
工厂模式:Spring容器本质上是一个超级工厂,负责生产和管理Bean。
-
策略模式:通过注入不同的接口实现,动态切换算法(如支付方式:支付宝 vs 微信支付)。
-
模板方法模式:Spring JdbcTemplate通过依赖注入DataSource,封装了数据库操作的通用流程。
四、总结:为什么IoC/DI是Spring的灵魂?
-
代码层面:通过解耦提升可维护性和可测试性。
-
架构层面:使模块化开发成为可能,支持微服务、云原生等现代架构。
-
哲学层面:体现了“好莱坞原则”(Don’t call us, we’ll call you),框架掌控流程,开发者专注业务逻辑。
Day02 03——注入
我们创建一个测试类Student,其中包含如下属性(这里使用Lombok注解自动生成Getter Setter)
@Data
public class Student {
private String name;
private int age;
}
还有一个更复杂的TeacherService类,包含更多更全面的测试属性
@Data
public class TeacherService {
private TeacherDao dao;
private String name;
private String major;
private boolean isSingle;
private String[] addr;
private Set<Student> students;
private List<String> list;
private Map<String, Double> map;
private Properties properties;
}
现在我们需要在Spring的配置文件中开始对这些属性进行注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="teacherService" class="com.zzy.inject.TeacherService">
<!-- 针对基本数据类型的操作 -->
<property name="name" value="Eric"/>
<!-- 赋空值 -->
<!-- <property name="name"><null/></property> -->
<property name="major" value="Computer Science"/>
<property name="single" value="true"/>
<!-- 针对数组的操作 -->
<property name="addr">
<list>
<value>SH</value>
<value>BJ</value>
<value>LZ</value>
<value>CZ</value>
</list>
</property>
<!-- 针对Set<Student>的操作 -->
<property name="students">
<set>
<!-- 写法一 -->
<!--<bean id="student1" class="com.zzy.inject.Student">
<property name="name" value="Stu1"/>
<property name="age" value="18"/>
</bean>
<bean id="student2" class="com.zzy.inject.Student">
<property name="name" value="Stu2"/>
<property name="age" value="81"/>
</bean>-->
<!-- 写法二 -->
<ref bean="student1"/>
<ref bean="student2"/>
</set>
</property>
<!-- 针对List集合 -->
<property name="list">
<list>
<value>list value 1</value>
<value>list value 2</value>
<value>list value 3</value>
<value>list value 4</value>
</list>
</property>
<!-- 针对Map<K,V> -->
<property name="map">
<