基于注解开发,注解xml整合,纯注解,spring整合c3p0和dbUtils
使用之前的基于xml配置的项目进行一步步改造为纯注解
再bean.xml中引入注解所需的约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 告知spring在创建容器的时候要扫描的包,配置所需要的标签不是在beans中,而是在一个叫context的名称空间中 -->
<context:component-scan base-package="com.itheima"/>
</beans>
之前的纯xml用<bean>标签来注入accountDao和accountService,现在只需加入component-scan标签
- 曾经XML的配置:
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="" value="" | ref=""></property>
</bean>
常用注解名词解释
用于创建对象的
他们的作用就和在XML配置文件中编写一个标签实现的功能是一样的
- Component:
作用:用于把当前类对象存入spring容器中
属性:value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
Controller:一般用在表现层
Service:一般用在业务层
Repository:一般用在持久层
-
以上三个注解他们的作用和属性与Component是一模一样。
它们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
用于注入数据的
他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的
- Autowired:
作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
如果Ioc容器中有多个类型匹配时:
出现位置:
可以是变量上,也可以是方法上
细节:
- 在使用注解注入时,之前使用的xml注入的set方法就不是必须的了。
- Qualifier:
作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(在Autowired下)
属性:
value:用于指定注入bean的id。 - Resource
作用:直接按照bean的id注入。它可以独立使用
属性:
name:用于指定bean的id。 - 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
- 另外,集合类型的注入只能通过XML来实现。
- Value
作用:用于注入基本类型和String类型的数据
属性:
value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
SpEL的写法:${表达式}
-
用于改变作用范围的
他们的作用就和在bean标签中使用scope属性实现的功能是一样的
Scope
作用:用于指定bean的作用范围
属性:
value:指定范围的取值。常用取值:singleton prototype -
和生命周期相关
他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
PreDestroy
作用:用于指定销毁方法
PostConstruct
作用:用于指定初始化方法
AccountServiceImpl:
@Component
//把当前类对象存入spring容器中
public class AccountServiceImpl implements IAccountService {
@Autowired
@Qualifier("accountDao2")//按照名称注入
private IAccountDao accountDao;
//public void setAccountDao(IAccountDao accountDao) {
// this.accountDao = accountDao;
//}
public void saveAccount(){
int i = 1;
accountDao.saveAccount();
System.out.println(i);
i++;
}
}
在开发中业务层就直接用@Service来注解了
Dao包中有两个accountDao和accountDao2,有多个对象,则按照名称注入,不然会报错
此时的accountDao的set方法就不需要了
@Repository("accountDao1")
public class AccountDaoImpl implements IAccountDao {
public void saveAccount(){
System.out.println("保存了账户1111111111111111");
}
}
在持久层中使用Repository将dao注入spring容器中
@Repository("accountDao2")
public class AccountDaoImpl2 implements IAccountDao {
public void saveAccount(){
System.out.println("保存了账户22222222222");
}
}
spring整合c3p0和dbUtils **************
首先引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
其中spring-test标签就是spring和JUnit整合的依赖
添加此标签就在测试类上使用spring的注解了
先使用xml配置
bean.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 注入service-->
<bean id="accountService" class="com.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 提供dao的ref -->
<bean id="accountDao" class="com.dao.impl.AccountDaoImpl">
<property name="runner" ref="runner"></property>
</bean>
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!-- constructor-arg用于注入其他bean类型 -->
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 连接数据库的信息 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
</beans>
复习:constructor-arg用于注入其他bean类型
然后在实现CRUD以及通过id查询的方法
(其他方法省略,这里只贴dao的实现类)
AccountDaoImpl:
public class AccountDaoImpl implements AccountDao {
private QueryRunner runner;
//注意此处使用set注入
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
public List<Account> findAllAccount() {
try{
return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
public Account findAccountById(Integer accountId) {
try{
return runner.query("select * from account where id = ?",new BeanHandler<Account>(Account.class),accountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void saveAccount(Account account) {
try{
runner.update("insert into account(name,money) values(?,?)",account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try{
runner.update("update account set name=?,money=? where id = ?",account.getName(),account.getMoney(),account.getId());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void deleteAccount(Integer acccountId) {
try{
runner.update("delete from account where id = ?", acccountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
测试类:AccountServiceTest
此处是经过注解改造的,目的是减少代码的重复,
//获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
不需要再重复的使用getBean()来得到bean.xml配置文件了
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
private AccountService as = null;
@Test
public void testFindAll(){
List<Account> accounts = as.findAllAccount();
for(Account account:accounts){
System.out.println(account);
}
}
@Test
public void testFindOne(){
Account account = as.findAccountById(3);
System.out.println(account);
}
@Test
public void testSave(){
Account account = new Account();
account.setMoney(1000.0);
account.setName("name");
as.saveAccount(account);
}
@Test
public void testUpdate(){
Account account = new Account();
account.setMoney(10100.0);
account.setName("name2");
account.setId(4);
as.updateAccount(account);
}
@Test
public void testDelete(){
as.deleteAccount(2);
}
}
- 使用Junit单元测试:测试我们的配置
- Spring整合junit的配置
1、导入spring整合junit的jar(坐标)
2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的 - @Runwith
3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置 - @ContextConfiguration
locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
classes:指定注解类所在地位置
当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
使用注解方法
使用注解,首先将bean.xml的约束换成注解的约束,然后告诉spring要扫描的包
AccountServiceImpl中要使用AccountDao,所以要注入,使用@Service和@Autowried来注入(因为只需在AccountServiceImpl注入一个对象,就只用Autowried)
AccountDaoImpl需要使用QueryRunner,因为是在持久层,所以使用@Repository和@Autowried来注入
此时的测试类并没有用注解来优化,所以有很多的重复代码
public class AccountServiceTest {
@Test
public void testFindAll(){
//获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
List<Account> accounts = as.findAllAccount();
for(Account account:accounts){
System.out.println(account);
}
}
@Test
public void testFindOne(){
//获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
Account account = as.findAccountById(3);
System.out.println(account);
}
@Test
public void testSave(){
Account account = new Account();
account.setMoney(1000.0);
account.setName("欧美");
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
as.saveAccount(account);
}
@Test
public void testUpdate(){
Account account = new Account();
account.setMoney(10100.0);
account.setName("欧美2");
account.setId(4);
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
as.updateAccount(account);
}
@Test
public void testDelete(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
as.deleteAccount(2);
}
}
纯注解实现
要改造成纯注解,就是要不使用bean.xml,而单纯的用注解实现,但是现在先不删除bean.xml,因为里面的很多东西还有用(等一下用到了再复制粘贴,用完再删)
目光放到bean.xml中数据源的注入,此时已经将各个字段写死了,按照以往的经验,可以将其写到一个properties文件中,创建一个jdbcConfig.properties文件,按照key,value格式来取数据
jdbc.Driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy
jdbc.username=root
jdbc.password=123456
然后创建一个springConfiguration类
@ComponentScan("com")
@Import(JDBCConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class springConfiguration {
}
- 该类是一个配置类,它的作用和bean.xml是一样的
- spring中的新注解
- Configuration
作用:指定当前类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。 - ComponentScan
作用:用于通过注解指定spring在创建容器时要扫描的包
属性:
value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
我们使用此注解就等同于在xml中配置了:
<context:component-scan base-package=“com.itheima”></context:component-scan> - Bean
作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
属性:
name:用于指定bean的id。当不写时,默认值是当前方法的名称
细节:
当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
查找的方式和Autowired注解的作用是一样的 - Import
作用:用于导入其他的配置类
属性:
value:用于指定其他配置类的字节码。
当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类 - PropertySource
作用:用于指定properties文件的位置
属性:
value:指定文件的名称和路径。
关键字:classpath,表示类路径下
用import导入其他的配置类
public class JDBCConfig {
@Value("${jdbc.Driver}")
private String driver;
//此处使用了SpEL的写法:${表达式}
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 创建一个queryRunner对象
* @return
*/
@Bean(name = "runner")
public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource dataSource){
return new QueryRunner(dataSource);
}
//调用多个数据库
@Bean(name = "ds2")
public DataSource createDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Bean(name = "ds1")
public DataSource createDataSource1(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy02");
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
此处使用了SpEL的写法:${表达式}
现在就可以删掉bean.xml文件了
如果有调用多个数据库的需求,这时候的数据源就有两个,这时候就可以在获取QueryRunner的createQueryRunner()方法的参数中加入注解@Qualifier(“ds1”) ,用来指定数据源。