目录:
一、通过注解定义Bean
1.1、启用注解支持
<context:annotation-config />
若要以注解方式注入 Bean,则需要开启扫描包中的组件,并用 base-package
属性指定扫描的基本包路径:
<context:component-scan base-package="com.xxx" />
使用 <context:annotation-scan>
标签时,默认将隐式启用对注解的支持。
1.2、定义Bean
在 XML 配置中如果要将一个 Bean 交给 Spring IoC 容器管理,需要进行如下配置:
<bean id="userService" class="com.wlplove.service.impl.UserServiceImpl" />
若用注解要实现相同目的,只需要在要被管理的 Java 类上添加 @Component
注解,并在后面的括号中指定 Bean 的名称:
// 注解后面的括号里是 bean 的名称
@Component("userServiceImpl")
public class UserServiceImpl implements UserService {
// ...
}
@Component
注解也可以不指定 Bean 名称,Spring 会自动为该 Bean 生成一个默认的名称,通常是类名的首字母小写形式:
@Component
// 可以用名称“userServiceImpl”来获取这个Bean
public class UserServiceImpl implements UserService {
// ...
}
所以只需要在 Java 类上加入 @Component
注解,用于标识这个类是 Spring 容器中的组件(即 Bean),就可以实现与 XML 配置中的 <bean>
标签相同的作用。
1.3、@Component的派生注解
此外,Spring 还提供了 @Component
的几个派生注解:@Repository
、@Service
、@Controller
、@Configuration
,这几个注解的功能和用法与 @Component
相同,但它们在使用场景上有所不同。
1.3.1、@Repository
用于数据访问层(Dao 层)中与数据库交互的类。
Spring 会对使用 @Repository
注解的类进行特定的异常处理(将数据访问层的异常,如 SQLException
等转换为 Spring 的 DataAccessException
异常,方便统一处理和异常传播)。
@Repository
public interface UserMapper {
// ...
}
1.3.2、@Service
用于标识业务逻辑层(Service 层)中处理核心业务逻辑的类。
使用 @Service
注解可以将一个类声明为业务逻辑组件,将其对象存入 Spring 容器中,以便在其他组件(如Controller)中通过注入该 service 类的实例来使用其业务逻辑。
@Service
public class UserServiceImpl implements UserService {
// ...
}
1.3.3、@Controller
用于标识控制层(Controller 层)中处理响应和请求的类。
被 @Controller
标记的类实际上就是一个 Spring MVC Controller 对象,它可以处理请求,并通过 @RequestMapping
等注解将不同的请求分发到对应的方法上。
@Controller
public class UserController {
// ...
}
1.3.4、@Configuration
用于标记替换 XML 配置文件的配置类,这个注解的用法将在 1.4 中详细说明。
1.4、用注解代替XML配置文件
Spring 支持完全去除 XML 配置文件实现纯注解开发,实现的方式就是使用一个 Java 类来代替原本的 XML 配置文件。
1.4.1、配置类
创建一个 Java 类,类上面添加 @Configuration
注解标识这是一个配置类:
@Configuration
public class SpringConfig {
// ...
}
在 XML 配置文件中会用到 <context:annotation-config base-package="">
标签来设置扫描注解的路径,在配置类上添加 @ComponentScan
注解也可以实现相同的效果:
@Configuration
+@ComponentScan("com.xxx")
public class SpringConfig {
// ...
}
如果有多个包路径,可以用数组格式:
@ComponentScan({"com.xxx.dao","com.xxx.service"})
1.4.2、初始化IoC容器
当创建出一个配置类之后,初始化 Spring IoC 容器的方式也需要改变。
原来是通过 ClassPathXmlApplicationContext
类加载 XML 配置文件初始化得到 IoC 容器:
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
现在改用 AnnotationConfigApplicationContext
类来加载配置类得到 Spring IoC 容器:
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
二、通过注解使用Bean
2.1、自动装配
2.1.1、@Autowired
在要引用的依赖上面使用 @Autowired
注解开启自动装配注入该依赖:
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.insert();
}
}
@Autowired
注解默认按照类型(byType)进行自动装配。
2.1.2、@Qualifier
如果 Spring IoC 容器中存在多个类型相同的 Bean 时,仅仅使用 @Autowired
根据类型自动装配时就导致 Spring 无法确定应该自动注入哪一个 Bean,便会抛出 NoUniqueBeanDefinitionException
异常。那么此时使用 @Qualifier
注解来指定要装配的 Bean 名称:
@Repository
public class BookDao1 implements BookDao {
// ...
}
@Repository
public class BookDao2 implements BookDao {
// ...
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
// 存在两个BookDao类型的Bean,这里指定注入名称为bookDao1的Bean
@Qualifier("bookDao1")
private BookDao bookDao;
}
需要注意的是,@Qualifier
不能单独使用,必须与 @Autowired
一起使用。因为 @Autowired
主要是根据类型进行自动装配,而 @Qualifier
则在此基础上匹配 Bean 名称,从而进行更精确的限制。
2.1.3、@Resource
还有一个与 @Autowired
类似的注解是 @Resource
,默认按照名称(byName)进行自动装配,通过其 name
属性指定要注入的 Bean 名称:
@Service
public class BookServiceImpl implements BookService
@Resource(name = "bookDao2")
private BookDao bookDao;
}
不像 @Autowired
是 Spring 框架提供的用于按类型(byType)自动装配的注解,@Resource
是 JavaEE 标准中(javax.annotation.Resource)提供的用于按名称(byName)自动装配的注解,并非 Spring 框架所特有的。
2.1.4、@Primary
与 @Qualifier
类似,@Primary
注解也可以用来解决当存在多个相同类型的 Bean 时应该注入哪一个 Bean 的问题。
如果 Spring IoC 容器中存在多个类型相同的 Bean,给该 Bean 上面添加 @Primary
注解,则 Spring 会优先使用标注了 @Primary
的 Bean。
2.1.5、@Value
使用 @Value
注解实现简单类型的注入:
@Service
public class UserServiceImpl implements UserService {
@Value(30)
private int age;
@Value("root")
private String userName;
}
@Value
注解除了用于给简单类型的成员注入固定的值以外,也可以用于将配置文件中的属性注入给简单类型的成员,配置文件中的属性名写在占位符 ${}
里面:
@Service
public class UserServiceImpl implements UserService {
@Value("${user.name}")
private String userName;
}
2.2、读取properties文件配置
在 src/main/resources 目录下新建一个配置数据库连接信息的配置文件 jdbc.properties
:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db?useSSL=false&setUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
使用 @PropertySource
注解在 Spring 配置类上加载外部的 properties 文件,在要注入值的成员上添加 @Value
注解指定注入哪个属性:
/**
* Spring配置类
*/
@Configuration
@ComponentScan({"com.xxx"})
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
// ...
}
/**
* 使用属性
*/
public class DataBaseConnInfo {
// 注入配置文件中的属性
@Value("${jdbc.driverName}")
private String driverName;
@Value("${jdbc.url}")
private String url;
}
若要加载多个配置文件应使用数组:
@PropertySource({"classpath:jdbc.properties","classpath:appConfig.properties"})
2.3、作用域
使用注解 @Scope
定义 Bean 的作用域是 prototype
(原型/非单例)或者 singleton
(单例)。
public class AppConfig {
@Bean
// @Scope("prototype")
@Scope("singleton")
public MyBean myBean() {
return new MyBean();
}
}
2.4、自定义Bean初始化/销毁操作
使用 @PostConstruct
注解定义初始化方法(方法名不限),将在构造方法之后执行该方法。
使用 @PreDestroy
注解定义销毁方法(方法名不限),在容器销毁前执行该方法,一般用来释放占用的资源等。
public class UserServiceImpl implements UserService {
@PostConstruct
publid void init() {
System.out.println("init ....");
}
@PreDestroy
public void destory() {
System.out.println("destory ...");
}
}
2.5、总结
三、整合框架
3.1、数据源对象(以Druid为例)
导入 Druid 数据源的依赖坐标:
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.22</version>
</dependency>
新建一个配置数据库连接的类 JdbcConfig
,里面引入配置文件中的数据库信息,然后用一个方法构造并返回一个 DataSource
对象,最后用 @Bean
注解设置这个方法的返回值成为 Spring IoC 容器的一个对象:
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 数据源
* @return
*/
@Bean
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
Spring 配置类中用 Import
注解引入 JdbcConfig
这个类:
@Configuration
@ComponentScan("com.xxx")
+@Import({JdbcConfig.class})
public class SpringConfig {
// ...
}
3.2、整合Mybatis
导入 MyBatis 的依赖坐标:
<!-- spring-jdbc -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.1.4</version>
</dependency>
<!-- mybatis -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<!-- mybatis-spring -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<!-- mysql -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
新建一个 MyBatis 配置类 MybatisConfig
,配置 SqlSessionFactoryBean
和 MapperScannerConfigurer
这两个 Bean:
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setDataSource(dataSource);
ssfb.setTypeAliasesPackage("com.xxx.xxx");
return ssfb;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer msc = new MapperScannerConfigurer();
// Mapper接口所在的包路径
msc.setBasePackage("com.xxx.mapper");
return msc;
}
}
Spring 配置类中用 Import
注解引入 MybatisConfig
这个类:
@Configuration
@ComponentScan("com.xxx")
+@Import({JdbcConfig.class, MybatisConfig.class})
public class SpringConfig {
// ...
}
3.3、整合Log4j
导入 Log4j 的依赖坐标:
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
</dependency>
在 "src/main/java/resources" 下新建一个 Log4j 的配置文件 Log4j2.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Configuration 具有 Appenders 和 Loggers 这两种子节点,每个子节点可以定义多个 -->
<configuration>
<!-- Appender节点,具有 Console(控制台)、File(文件)、RoolingFile(滚动文件)这三种类型的子节点 -->
<Appenders>
<!-- 输出日志信息到控制台 -->
<Console name="console" target="SYSTEM_OUT">
<!--指定控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
</Console>
<!-- File 节点用来定义输出到指定位置的文件的 Appender,会将所有内容写入到同一个文件中 -->
<!-- append属性设置写入新的日志时是追加在原内容后面,还是清除所有内容之后再写入 -->
<!-- <File name="allLog" fileName="logs/AlliInOne.log" append="true">-->
<!-- <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>-->
<!-- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{9.9.9.1}(%L) %m%n"/>-->
<!-- </File>-->
<!-- RollingFile 节点,将日志写入文件,但是允许日志文件根据时间或大小进行滚动,从而避免单个文件过大 -->
<!-- fileName 生成的初始日志文件 -->
<RollingFile name="rollingFileInfo" fileName="logs/${date:yyyy-MM}/log-info-${date:yyyy-MM-dd}.log" filePattern="logs/${date:yyyy-MM}/log-info-%d{yyyy-MM-dd}-%i.log">
<!-- ThresholdFilter 只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<!-- PatternLayout 指定控制日志输出的格式,不设置默认为:%m%n -->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
<!-- Policies 滚动策略 -->
<Policies>
<!-- 按时间滚动 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 按大小滚动 -->
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy 设置一个文件下保存的日志文件数量,不设置则默认为同一文件夹下7个文件,超过这个数量后,最老的文件将被删除 -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
</Appenders>
<!-- 在 Loggers 中引入上面定义好的 Appender -->
<loggers>
<!-- level指定日志级别,从低到高的优先级:
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
-->
<!-- 设置org.mybatis包下的日志只打印WARN及以上级别 -->
<Logger name="org.mybatis" level="WARN" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="rollingFileInfo"/>
</Logger>
<!-- 设置org.springframework包下的日志只打印WARN及以上级别 -->
<Logger name="org.springframework" level="WARN" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="rollingFileInfo"/>
</Logger>
<root level="DEBUG">
<appender-ref ref="console"/>
<appender-ref ref="rollingFileInfo"/>
</root>
</loggers>
</configuration>
Log4j 配置文件详解:彻底掌握Log4j2 - 蚂蚁小哥 - 博客园
修改 Mybatis 配置类中的 SqlSessionFactoryBean
,设置其 Configuration
属性:
public class MybatisConfig {
// ...
@Bean
public SqlSessionFactoryBean getSqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.xxx.xxx");
ssfb.setDataSource(dataSource);
+ Configuration configuration = new Configuration();
+ // 修改MyBatis使用的日志框架为Log4j2
+ configuration.setLogImpl(Log4j2Impl.class);
+ ssfb.setConfiguration(configuration);
return ssfb;
}
// ...
}
3.4、整合Junit
导入 Junit 和 spring-test 的依赖坐标:
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
新建一个测试类:
// 在 JUnit 5 中集成 Spring 功能,如果是Junit4则换成@RunWith(SpringJunit4ClassRunner.class)
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = SpringConfig.class)
public class SpringTest {
@Autowired
private UserMapper userMapper;
@Test
public void find() {
User user = userMapper.select();
}
}
四、Spring事务
导入 Spring 事务的依赖坐标:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
在 Spring 的配置类上添加注解 @EnableTransactionManagement
开启 Spring 的事务管理功能:
@Configuration
@ComponentScan("com.xxx")
@Import({JdbcConfig.class, MybatisConfig.class})
+@EnableTransactionManagement
public class SpringConfig {
// ...
}
配置类中添加 PlatformTransactionManager
这个 Bean:
@Bean
public PlatformTransactionManager getTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dstm = new DataSourceTransactionManager();
dstm.setDataSource(dataSource);
return dstm;
}
最后在 service 方法或类上面用 @Transactional
注解设置添加事务管理,在执行该方法或类中的方法时便会开启事务管理,如果程序执行时发生异常就回滚之前的数据库操作。
需要注意的是,当在同一类中调用有 @Transactional
注解的方法时,可能不会触发事务管理,因为它绕过了 Spring 的代理机制,解决办法是将这个方法也标记为 @Transactional
。
评论 (0)