Spring核心之注解实践
我们会以一个案例贯穿Spring的整个常用的注解开发流程,全程不会使用到以前的配置文件applicationContext.xml
,首先我们先看一下我们的项目结构:
其中,entity
包是实体类所在的包,dao
包是数据持久化层,service
包是业务逻辑层,aop
包是AOP开发的切面类所在的包,其它三个AppConfig
、MyBatisConfig
、TransactionConfig
分别是主配置类、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
| @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); } }
|
注意的第一点,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
| @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); }
|