在 B 站黑马程序员的 SSM 框架教程中,基于注解的开发模式是简化 Spring 配置、提升开发效率的核心实践。本文结合教程内容,系统解析注解驱动的 IoC(控制反转)、DI(依赖注入)及 Bean 管理,涵盖核心注解、实战技巧与企业级应用场景。
一、IoC 核心:注解声明 Bean 的三种方式
1. 基础注解:@Component 及其衍生注解
通过注解替代 XML 中的标签,直接在类上声明 Bean,实现 “组件化”:
// 通用组件(推荐细化为专用注解)
@Component("userService")
public class UserServiceImpl implements UserService { ... }
// 业务层(语义更明确)
@Service("userService")
public class UserServiceImpl implements UserService { ... }
// 数据访问层(自动参与MyBatis整合)
@Repository("userDao")
public class UserDaoImpl implements UserDao { ... }
// Web层控制器(自动被SpringMVC扫描)
@Controller("userController")
public class UserController { ... }
核心规则:
- value属性指定 Bean 的 id(可选,默认类名首字母小写)
- 需通过<context:component-scan base-package=“com”/>开启组件扫描
2. @Bean:手动定义 Bean(替代 XML 工厂方法)
在@Configuration类中通过方法返回 Bean,适合第三方组件或复杂对象创建:
@Configuration
public class AppConfig {
// 配置Druid数据源(替代XML的<bean>)
@Bean("dataSource")
public DataSource druidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return dataSource;
}
// 整合MyBatis的SqlSessionFactory
@Bean("sqlSessionFactory")
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
return factory;
}
}
适用场景:
- 第三方库组件(如 MyBatis、Redis 客户端)
- 需要编程式配置的 Bean(动态参数、条件判断)
3. 条件化 Bean:@Conditional
根据条件动态注册 Bean(如区分开发 / 生产环境):
// 开发环境启用本地缓存
@Conditional(DevEnvironmentCondition.class)
@Bean("cacheManager")
public LocalCacheManager devCacheManager() { ... }
// 生产环境启用分布式缓存
@Conditional(ProdEnvironmentCondition.class)
@Bean("cacheManager")
public RedisCacheManager prodCacheManager() { ... }
二、DI 核心:依赖注入的三种注解方式
1. 自动装配:@Autowired(byType 为主)
(1)字段注入(最简洁,但违背 “构造器注入优先” 原则)
@Service
public class UserServiceImpl implements UserService {
// 自动匹配UserDao类型的Bean(唯一实例)
@Autowired
private UserDao userDao;
}
(2)构造器注入(推荐有参依赖场景)
@Service
public class UserServiceImpl implements UserService {
private final UserDao userDao;
// 单构造器可省略@Autowired(Spring4.3+特性)
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
}
(3)Setter 方法注入(可选依赖场景)
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 可选依赖:允许userDao为null(需配合@Nullable)
@Autowired(required = false)
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
2. 精准匹配:@Qualifier 解决歧义
当同一类型存在多个 Bean 时,通过@Qualifier(“beanId”)指定具体实例:
@Service
public class OrderService {
// 存在多个DataSource时指定主库
@Autowired
@Qualifier("masterDataSource")
private DataSource dataSource;
}
3. 简单值注入:@Value
注入基本类型、字符串或 EL 表达式结果:
@Service
public class UserServiceImpl {
// 注入配置文件中的值
@Value("${jdbc.username}")
private String dbUser;
// 注入固定值
@Value("默认超时时间:3000ms")
private String defaultTimeout;
}
配置支持: 需在 XML 中添加<context:property-placeholder location=“classpath:config.properties”/>
三、Bean 高级特性:生命周期与作用域
1. 生命周期管理注解
(1)初始化回调:@PostConstruct(替代 XML init-method)
@Service
public class CacheService {
private Map<String, Object> cache;
// Bean创建并注入依赖后执行
@PostConstruct
public void init() {
cache = new ConcurrentHashMap<>();
}
}
(2)销毁回调:@PreDestroy(替代 XML destroy-method)
@Service
public class DataSourceService {
// Bean销毁前释放资源
@PreDestroy
public void close() {
// 关闭数据库连接等操作
}
}
2. 作用域控制:@Scope
替代 XML 的scope属性,支持 4 种作用域(新增 WebFlux 相关作用域):
// 原型模式:每次注入创建新实例
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service("userSession")
public class UserSession { ... }
// Web环境专用(需添加spring-web依赖)
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Service("sessionCache")
public class SessionCache { ... }
**proxyMode:**解决作用域 Bean 注入到单例 Bean 时的生命周期问题(推荐使用 TARGET_CLASS 代理)
3. 延迟初始化:@Lazy
推迟单例 Bean 的创建(首次使用时初始化):
@Service
@Lazy // 等价于XML的lazy-init="true"
public class HeavyService {
// 资源密集型Bean延迟加载
}
四、企业级实战:注解与 XML 混合开发
1. 传统 SSM 框架整合(注解为主 + XML 为辅)
(1)SpringMVC 配置(XML 保留核心组件)
<!-- web.xml -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<!-- springmvc.xml:保留视图解析器等XML配置 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 开启注解驱动:替代XML的<mvc:annotation-driven/> -->
<mvc:annotation-driven/>
(2)MyBatis 整合(全注解模式)
// 替代XML的MapperScannerConfigurer
@MapperScan("com.dao") // 扫描Mapper接口
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setTypeAliasesPackage("com.entity"); // 配置实体类别名
return factory;
}
}
2. 复杂场景:多条件 Bean 注册
通过@Profile实现环境隔离(开发 / 测试 / 生产环境不同配置):
// 开发环境配置
@Profile("dev")
@Configuration
public class DevConfig {
@Bean
public DataSource dataSource() {
// 配置本地开发数据库
}
}
// 生产环境配置
@Profile("prod")
@Configuration
public class ProdConfig {
@Bean
public DataSource dataSource() {
// 配置线上数据库集群
}
}
**激活方式:**通过 JVM 参数-Dspring.profiles.active=dev
或 XML<beans profile="dev">
五、注解开发的优缺点与适用场景
1. 核心优势
- 代码内聚性:Bean 定义与实现逻辑集中在类文件,避免 XML 与代码分离带来的上下文切换
- 类型安全:编译期检查依赖匹配错误(如 @Autowired 无匹配 Bean 时编译报错)
- 开发效率:消除大量 XML 模板代码,尤其适合快速迭代的互联网项目
2. 潜在问题
- 隐式配置:依赖关系通过注解隐含在代码中,复杂场景下可读性低于 XML
- 框架侵入性:业务代码依赖 Spring 注解(如 @Service),增加框架迁移成本
- 调试难度:动态代理生成的 Bean 实例,堆栈跟踪时需区分原始类与代理类
3. 最佳实践
- 分层使用:业务层(@Service)、持久层(@Repository)使用注解,基础设施层(数据源、事务管理器)保留 XML 或 @Bean 配置
- 组合使用:复杂条件配置用 @Conditional+@Profile,简单 Bean 用 @Component 系列注解
- 规范命名:Bean 的 id 统一使用@Component(“xxxService”)显式声明,避免依赖默认命名规则
六、从 XML 到注解:Spring 开发的演进逻辑
特性 | XML 配置模式 | 注解模式 |
---|---|---|
Bean 声明方式 | 集中式 XML 文件 | 分散在类文件中(@Component/@Bean ) |
依赖注入方式 | 显式 | 隐式@Autowired+@Qualifier |
第三方组件整合 | 必须 XML 配置 | @Bean + 编程式配置 |
团队协作友好度 | 配置可见性高(适合新手) | 依赖关系隐含(需 IDE 辅助) |
黑马教程核心启示: 注解开发的本质是 “约定大于配置”,通过注解简化重复劳动,但需掌握底层原理(如 Spring 如何解析 @Autowired、BeanPostProcessor 如何处理 @PostConstruct)。在企业级开发中,建议采用 “注解为主、XML 为辅” 的混合模式,兼顾效率与可维护性。
总结:注解时代的 Spring 核心能力
注解开发模式通过@ComponentScan、@Autowired等核心注解,将 Spring 的 IoC/DI 能力融入代码逻辑,实现 “零 XML” 配置的轻量化开发。掌握这套体系的关键在于:
- 精准选择注解:用 @Service/@Repository 明确分层,用 @Bean 处理第三方组件
- 理解自动装配规则:区分 byType(@Autowired)与 byName(@Qualifier)的适用场景
- 管理 Bean 生命周期:通过 @PostConstruct/@PreDestroy 替代 XML 的 init/destroy 方法
- 结合条件与作用域:利用 @Profile/@Scope 实现环境隔离与实例控制