spring配置数据源和注解开发
今天学习内容是如何使用spring核心配置文件配置数据源,以及学习如何进行spring注解开发。通过使用spring配置数据源,可以将一些参数以key-value的形式保存在properties文件中,将代码和一些参数解耦,修改参数时不需要修改代码。spring注解开发的目的是通过在代码中使用注解,来避免繁杂的xml文件配置。注解开发分为原始注解和新注解,原始注解无法替代某些xml配置,而新注解可以解决这些问题使得开发可以完全脱离xml文件配置。
配置数据源(数据库连接池)
- pom.xml配置文件添加mysql和c3p0依赖
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.5</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.5</version> </dependency>
手动获取数据源
- 测试手动连接c3p0数据源和druid数据源
package com.example.datasource; import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import java.sql.Connection; public class DataSourceTest { @Test // 手动测试c3p0数据源示例 public void testC3P0() throws Exception { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass("com.mysql.cj.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test"); // 需要提前创建好本地test数据库 dataSource.setUser("root"); dataSource.setPassword("root"); Connection connection = dataSource.getConnection(); // 获取一个连接 System.out.println(connection); connection.close(); // 释放连接 } @Test // 手动测试druid数据源示例 public void testDruid() throws Exception { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/test"); dataSource.setUsername("root"); dataSource.setPassword("root"); Connection connection = dataSource.getConnection(); System.out.println(connection); connection.close(); } }
- 使用properties文件配置数据库连接相关参数,对代码和参数进行解耦,更改参数不需要更改代码
// jdbc.properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test jdbc.username=root jdbc.password=root
@Test public void testPropertyFile() throws Exception { ResourceBundle rb = ResourceBundle.getBundle("jdbc"); String driver = rb.getString("jdbc.driver"); String url = rb.getString("jdbc.url"); String username = rb.getString("jdbc.username"); String password = rb.getString("jdbc.password"); ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass(driver); dataSource.setJdbcUrl(url); dataSource.setUser(username); dataSource.setPassword(password); Connection connection = dataSource.getConnection(); System.out.println(connection); connection.close(); }
spring配置数据源
- 将DataSource创建权交给spring容器
- 配置xml文件
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/> <property name="user" value="root"/> <property name="password" value="root"/> </bean>
- 继续将xml中dataSource的属性与其对应值解耦,更改参数值只需更改数据源的jdbc.properties文件
- 需要在applicationContext文件中引入context命名空间和约束路径
xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- 修改xml文件
<!-- 导入properties文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
- 测试代码
@Test public void testSpringConfig() throws Exception{ ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); ComboPooledDataSource dataSource = (ComboPooledDataSource) app.getBean("dataSource"); Connection connection = dataSource.getConnection(); System.out.println(connection); connection.close(); }
spring注解开发
- spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率
spring原始注解
注解类型
- 实例化注解
- @Component
- 可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可
- @Repository
- 用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同
- @Service
- 通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同
- @Controller
- 通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同
- @Component
- 依赖注入注解
- @Autowired
- 用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配
- @Qualifier
- 与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定
- @Resource
- 相当于@Autowired+@Qualifier,其作用与@Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配
- @Resource 中有两个重要属性:name 和 type
- Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配
- 如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常
- @Value
- 注入普通属性
- @Autowired
- Scope
- 标注Bean的作用范围
- PostConstruct
- 使用在方法上标注该方法是Bean的初始化方法
- PreDestroy
- 使用在方法上标注该方法是Bean的销毁方法
注解组件扫描
- 使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法
<!-- 注解组件扫描 --> <context:component-scan base-package="com.example"></context:component-scan>
注解开发简单示例
- 定义Dao接口和实现类
package com.example.anno.dao; public interface UserDao { public void save(); } package com.example.anno.dao.impl; import com.example.anno.dao.UserDao; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; // <bean id="userDao" class="com.example.anno.dao.impl.UserDaoImpl"></bean> // 括号内字符串相当于id @Repository("userDao") public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("UserDaoImpl save running"); } }
- 定义Service接口和实现类
package com.example.anno.service; public interface UserService { public void save(); } // --------------------------------------- package com.example.anno.service.impl; import com.example.anno.dao.UserDao; import com.example.anno.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; // <bean id="userService" class="com.example.anno.service.impl.UserServiceImpl"/> @Service("userService") @Scope("prototype") @PropertySource("classpath:jdbc.properties") public class UserServiceImpl implements UserService { // <property name="userDao" ref="userDao"> // 依赖注入,@Qualifier指定注入类型名称 @Autowired // 按照数据类型自动从spring容器中进行匹配注入 @Qualifier("userDao") // 按照id值在spring中进行匹配,仅按照类型注入可以省去,按照名称注入不可省去 // @Resource(name = "userDao") // 相当于@Autowired + @Qualifier private UserDao userDao; // 采用注解开发的方法,可以不写setter方法 @Value("${jdbc.url}") // 从properties文件中找到key对应的value赋给字段 private String url; @PostConstruct public void init(){ System.out.println("initializing..."); } @PreDestroy public void destroy(){ System.out.println("destroying..."); } @Override public void save() { userDao.save(); System.out.println(url); System.out.println("UserServiceImpl save running"); } }
- 定义测试类
package com.example.anno; import com.example.anno.service.UserService; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AnnoTest { @Test public void test(){ ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) app.getBean("userService"); userService.save(); app.close(); } }
spring新注解
- 使用原始注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
- 非自定义的Bean的配置:
<bean>
- 加载的properties文件的配置:
<context:property-placeholder>
- 组件扫描的配置:
<context:conponent:scan>
- 引入其他xml配置文件:
<import>
- 非自定义的Bean的配置:
新注解类型
- @Configuration
- 指定当前类时一个spring配置类,当创建容器时会从该类上加载注解
- @ComponentScan
- 用于指定spring在初始化容器时要扫描的包
- 作用和用在spring的xml配置文件中的
<context:conponent:scan base-package="...">
一样
- @Bean
- 使用在方法上,标注将该方法的返回值存储到spring容器中。它就相当于xml配置中的factory-bean和factory-method。name属性:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)
- @PropertySource
- 用于加载.properties文件中的配置,相当于
<context:property-placeholder>
- 用于加载.properties文件中的配置,相当于
- @Import
- 用于导入其他配置类
新注解开发示例
- 编写新注解数据源配置类,代替xml配置
package com.example.springconfig; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import java.beans.PropertyVetoException; @Configuration // 标志该类是一个spring配置类 @ComponentScan("com.example.springconfig") // 相当于<context:component-scan base-package="com.example.springconfig"/> @PropertySource("classpath:jdbc.properties") // 相当于<context:property-placeholder location="classpath:jdbc.properties"/> public class DataSourceConfig { /* 以下代码相当于 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> */ @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean("dataSource") // 将方法返回值以指定名称(dataSource)加入spring容器中 public ComboPooledDataSource getDataSource() throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass(driver); dataSource.setJdbcUrl(url); dataSource.setUser(username); dataSource.setPassword(password); return dataSource; } }
- 将上述数据源配置类导入到配置汇总类
package com.example.springconfig; import org.springframework.context.annotation.Import; @Configuration @ComponentScan("com.example.springconfig") @Import(DataSourceConfig.class) // 将配置好的类加载到本类中,相当于将其他子xml文件import到主xml文件 public class SpringConfig { }
- 测试类
package com.example.springconfig; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import java.sql.Connection; import java.sql.SQLException; public class SpringConfigTest { @Test public void test() throws SQLException { // 使用注解配置的ApplicationContext,加载主配置类SpringConfig AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class); ComboPooledDataSource dataSource = (ComboPooledDataSource) app.getBean("dataSource"); Connection connection = dataSource.getConnection(); System.out.println(connection); connection.close(); } }
spring集成Junit
- 让spring Junit负责创建spring容器,但是需要将配置文件的名称告诉Junit
- 将需要进行测试的Bean直接在测试类中进行注入
集成Junit步骤
- 导入spring集成Junit的坐标
- 使用@Runwith注解替换原来的运行期
- 使用@Autowired注入要测试的对象
- 创建测试方法进行测试
示例
- pom.xml导入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> </dependency>
- 集成Junit测试代码
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) //@ContextConfiguration("classpath:applicationContext.xml") // 使用配置文件 @ContextConfiguration(classes = {UserServiceImpl.class, UserDaoImpl.class}) // 使用全注解方式,注入要测试的对象 public class SrpingJunitTest { @Autowired private UserServiceImpl userService; @Autowired private UserDaoImpl userDao; @Test public void test(){ userService.save(); userDao.save(); } }