Spring核心之注解实践

Spring核心之注解实践

我们会以一个案例贯穿Spring的整个常用的注解开发流程,全程不会使用到以前的配置文件applicationContext.xml,首先我们先看一下我们的项目结构:

image-20231101152800331

其中,entity包是实体类所在的包,dao包是数据持久化层,service包是业务逻辑层,aop包是AOP开发的切面类所在的包,其它三个AppConfigMyBatisConfigTransactionConfig分别是主配置类、MyBatis配置类、事务配置类。

我们需要先明确一下,对于大部分程序员自定义的类型我们都是采用@Component及其衍生注解如@Repository、@Service、@Controller等完成Bean的装配;对于框架和第三方提供的一些类型如DruidDataSource、SqlSessionFactoryBean等我们采用@Bean注解完成Bean的装配;对于我们不满意的Bean对象我们可以声明XML配置文件,通过@ImportResource注解的方式覆盖Bean对象(id属性必须保持一致)。

首先,我们先看实体类User的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Component
public class User {
@Value("101")
private Integer id;
@Value("tom")
private String name;
@Value("123456")
private String password;


public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}

注意,@Value注解不能作用于静态变量上,不能注入集合类型,并且@Value注解是直接通过反射注入的。

接着,我们编写数据持久层的UserDAO接口:

1
2
3
public interface UserDAO {
public void register(User user);
}

接着,我们编写MyBatis的配置类,完善我们的数据持久层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// MyBatis的配置类,配置信息存放在mybatis.properties配置文件中
@Configuration
@MapperScan("com.huling.dao")
@PropertySource("classpath:mybatis.properties")
public class MyBatisConfig {
@Value("${jdbc.driverClassName}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${mybatis.typeAliasesPackage}")
private String typeAliasesPackage;
@Value("${mybatis.mapperLocations}")
private String mapperLocations;


@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}

@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(mapperLocations);
sqlSessionFactoryBean.setMapperLocations(resources);
return sqlSessionFactoryBean;
}

}

其中,@MapperScan注解中填写DAO接口所在的包路径,@PropertySource注解中填写配置文件路径,下面是resources目录下的MyBatis配置文件mybatis.properties的内容:

1
2
3
4
5
6
jdbc.driverClassName = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/huling?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
jdbc.username = root
jdbc.password = root
mybatis.typeAliasesPackage = com.huling.entity
mybatis.mapperLocations = mapper/*Mapper.xml

并且,针对UserDAO编写的UserDAOMapper.xml文件的内容如下:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huling.dao.UserDAO">
<insert id="register" parameterType="User">
insert into t_user(name,password) values (#{name},#{password})
</insert>
</mapper>

接着,编写业务逻辑层的UserService接口及其实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface UserService {
public void register(User user);
}

// 涉及到数据库的增删改,增加事务注解
@Transactional
@Service("userService")
public class UserServiceImpl implements UserService {
// 跨配置类注入
@Autowired
private UserDAO userDAO;

public UserDAO getUserDAO() {
return userDAO;
}

public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}

@Override
public void register(User user) {
userDAO.register(user);
// throw new RuntimeException("测试异常");
}
}

注意的第一点,UserServiceImpl中使用@Autowired注解注入的userDAO对象来自于之前的MyBatisConfig配置类,会在AppConfig主配置类中通过@Import注解导入,因此属于跨配置类注入。

注意的第二点,Autowired注解基于类型注入,可以配合Qualifier注解基于名称注入,并且@Autowired注解放在字段上是直接通过反射注入,放在set方法是通过调用set方法注入。

接着,编写我们的事务配置类,使得被@Transactional标注的UserServiceImpl事务生效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
@EnableTransactionManagement
public class TransactionConfig {

// 跨配置类注入
@Autowired
private DataSource dataSource;

@Bean
public DataSourceTransactionManager dataSourceTransactionManager() {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}

为了完善我们的业务逻辑层的功能,我们又引入了日志功能,并且通过切面类的形式进行AOP开发:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 切面类,增加额外日志功能
@Component
@Aspect
public class MyAspect {
// 切入点表达式复用
@Pointcut("execution(* *..UserServiceImpl.register(com.huling.entity.User))")
public void myPointCut() {}

// 额外功能
@Around("myPointCut()")
public Object invoke(ProceedingJoinPoint joinPoint) {
Object ret = null;
System.out.println("-----日志记录:正常开启-----");
try {
ret = joinPoint.proceed();
} catch (Throwable e) {
System.out.println("-----日志记录:异常结束-----");
throw new RuntimeException(e);
}
System.out.println("-----日志记录:正常结束-----");
return ret;
}

}

最终,我们在主配置文件中开启AOP、导入其他配置类并且扫描可能的组件:

1
2
3
4
5
6
7
8
// 导入其他配置类,开启AOP
@Configuration
@ComponentScan(basePackages = "com.huling")
@Import({MyBatisConfig.class, TransactionConfig.class})
@EnableAspectJAutoProxy
public class AppConfig {

}

测试程序如下:

1
2
3
4
5
6
7
8
9
10
@Test
public void test() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = ctx.getBean("userService", UserService.class);
User user = new User();
user.setId(null);
user.setName("bubu");
user.setPassword("123456");
userService.register(user);
}

image-20231101155009176

image-20231101155041393