20201124 王维
学习总结
1 使用 spring 的 IoC 的实现账户的CRUD
使用 spring 的 IoC 实现对象的管理
使用 DBUtils 作为持久层解决方案
使用 c3p0 数据源
2 基于注解的 IOC 配置
2.1 环境搭建
2.2.1 添加 jar 包
注意:在基于注解的配置中,我们还要多一个 aop 的 jar 包
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2.1</version>
</dependency>
<!-- 数据库工具类 -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2.1.2 使用@Component 注解配置管理的资源
package com.itlaobing.spring.service.impl;
import com.itlaobing.spring.dao.IAccountDao;
import com.itlaobing.spring.model.Account;
import com.itlaobing.spring.service.IAccountService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service("service")
@Scope("prototype")
public class AccountServiceImpl implements IAccountService {
// @Autowired
// @Qualifier("accountDaoImpl")
@Resource
private IAccountDao accountDao;
@Override
public void saveAccount(Account account) {
accountDao.save(account);
}
@Override
public void updateAccount(Account account) {
accountDao.update(account);
}
@Override
public void deleteAccount(Integer accountId) {
accountDao.delete(accountId);
}
@Override
public Account findAccountById(Integer accountId) {
return accountDao.findById(accountId);
}
@Override
public List<Account> findAllAccount() {
return accountDao.findAll();
}
}
package com.itlaobing.spring.dao.impl;
import com.itlaobing.spring.dao.IAccountDao;
import com.itlaobing.spring.model.Account;
import lombok.Data;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import java.sql.SQLException;
import java.util.List;
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private QueryRunner runner;
@Override
public void save(Account account) {
String sql = "insert into tb_account(name , money) values( ? , ? )";
try {
runner.update(sql , account.getName() , account.getMoney());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public void update(Account account){
String sql = "update tb_account set name = ? , money = ? where id = ?";
try {
runner.update(sql , account.getName() , account.getMoney() , account.getId());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public void delete(Integer accountId) {
String sql = "delete from tb_account where id = ?";
try {
runner.update(sql , accountId);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public Account findById(Integer accountId) {
String sql = "select * from tb_account where id = ?";
try {
return runner.query(sql , new BeanHandler<Account>(Account.class) , accountId);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public List<Account> findAll() {
String sql = "select * from tb_account";
try {
return runner.query(sql , new BeanListHandler<Account>(Account.class) );
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
}
注意:当我们使用注解注入时,set 方法可以不用写,使用注解导入
2.1.3 创建 spring 的 xml 配置文件并开启对注解的支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 告知 spring 创建容器时要扫描的包 -->
<context:component-scan base-package="com.itlaobing.spring" />
<!-- <!– 配置service –>-->
<!-- <bean id="accountService" class="com.itlaobing.spring.service.impl.AccountServiceImpl">-->
<!-- <property name="accountDao" ref="accountDao" />-->
<!-- </bean>-->
<!-- <!– 配置Dao –>-->
<!-- <bean id="accountDao" class="com.itlaobing.spring.dao.impl.AccountDaoImpl">-->
<!-- <property name="runner" ref="runner" />-->
<!-- </bean>-->
<!-- 配置runner 此处我们只注入数据源,多例模式-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype" >
<constructor-arg index="0" ref="dataSource" />
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8" />
<property name="user" value="root" />
<property name="password" value="root" />
</bean>
</beans>
context:component-scan
: 告知spring在初始化容器时扫描包 base-package:要扫描的包名,多个包之间使用逗号隔开
2.1.4 测试
package com.itlaobing.spring.service.impl;
import com.itlaobing.spring.config.SpringConfig;
import com.itlaobing.spring.model.Account;
import com.itlaobing.spring.service.IAccountService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
public class AccountServiceImplTest {
private ApplicationContext context;
private IAccountService service;
@Before
public void setUp() throws Exception {
context = new ClassPathXmlApplicationContext("spring.xml");
service = context.getBean("accountServiceImpl" , IAccountService.class);
}
@Test
public void saveAccount() {
Account account = new Account();
account.setName("www");
account.setMoney(2000.5f);
service.saveAccount(account);
}
@Test
public void updateAccount() {
Account account = service.findAccountById(1);
account.setMoney(2020f);
service.updateAccount(account);
}
@Test
public void deleteAccount() {
service.deleteAccount(4);
}
@Test
public void findAccountById() {
Account account = service.findAccountById(1);
System.out.println(account);
}
@Test
public void findAllAccount() {
List<Account> list = service.findAllAccount();
if (list == null){
System.out.println("空");
return;
}
for (Account account : list){
System.out.println(account);
}
}
}
2.2 常用注解
2.2.1 创建对象
相当于: <bean id="" class="">
@Component
作用:把资源让 spring 来管理。相当于在 xml 中配置一个 bean。 属性: value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小 写。
语义化衍生:@Controller @Service @Repository
它们三个注解都是针对一个 @Component 的衍生注解,他们的作用及属性都是一模一样的。
他们只不过是提供了更加明确的语义化。
- @Controller:一般用于表现层的注解。
- @Service:一般用于业务层的注解。
- @Repository:一般用于持久层的注解。
2.2.2 注入数据
相当于:<property name="" ref="" />
@Autowired
作用: 自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型(spring中 有的)。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id(大小写敏感),在 spring 容器查找,找到 了也可以注入成功。找不到就报错
@Qualifier
作用: 在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和 @Autowired
一起使用,@Autowired
多个类型时不再使用要注入的对象变量名称作为 bean 的 id,只使用@Qualifier
指定的id;但是给方法参数注入时,可以独立使用。
属性: value:指定 bean 的 id。
@Resource
作用: 默认按 byName自动注入。@Resource有两个属性是比较重要的,分是name和type,Spring将 @Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用 name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName要注入的对象变量名称自动注入策略。
属性: name:指定 bean 的 id。type:指定bean的类型
装配顺序
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛 出异常
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
- 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则按照byType方式进行匹配,如果没有匹配则回退为一个原始类型进行匹配,如果匹配则自动装配
@Value
作用: 注入基本数据类型和 String 类型数据的
属性: value:用于指定值
@Autowired和@Resource的区别
@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。
@Autowired默认按类型装配(这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果 我们想使用名称装配可以结合@Qualifier注解进行使用
@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指 定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在 setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注 意的是,如果name属性一旦指定,就只会按照名称进行装配。
推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。
2.2.3 改变作用范围
相当于: <bean id="" scope="">
@Scope
作用: 指定 bean 的作用范围。
属性: value:指定范围的值。
取值:singleton prototype request session globalsession
2.2.4 生命周期相关(了解)
相当于: <bean id="" class="" init-method="" destroy-method="">
@PostConstruct
作用: 用于指定初始化方法
@PreDestroy
作用: 用于指定销毁方法
2.3 关于 Spring 注解和 XML 的选择问题
注解的优势: 配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
XML 的优势: 修改时,不用改源码。不涉及重新编译和部署。
Spring 管理 Bean 方式的比较:
基于xml配置 | 基于注解配置 | |
---|---|---|
bean定义 | <bean id="" class=""> |
@Component 语义化衍生:@Controller @Service @Repository |
bean名称 | 通过id或name指定 | @Component("id") 如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小 写。 |
bean注入 | <property name="" ref="" /> 或者p命名空间 |
@Autowired 按类型注入@Qualifier 按名称注入 |
生命周期 | <bean id="" class="" init-method="" destroy-method=""> |
@PostConstruct @PreDestroy |
适合场景 | bean的实现类来自第三方 | bean的实现类由用户自己开发 |
2.4 spring 的纯注解配置
package com.itlaobing.spring.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
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 javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration
@ComponentScan("com.itlaobing.spring")
@PropertySource("classpath:db.properties")
public class SpringConfig {
@Value("${driver}")
private String driver;
@Value("${url}")
private String url;
@Value("${user}")
private String username;
@Value("${pass}")
private String password;
@Bean(name = "dataSource")
public DataSource createDataSource(){
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return null;
}
@Bean(name = "runner")
public QueryRunner createDBAssit(DataSource dataSource){
return new QueryRunner(dataSource);
}
}
@Configuration
作用: 用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用 AnnotationConfigApplicationContext(有@Configuration 注解的类.class)。
属性: value:用于指定配置类的字节码
@ComponentScan
作用: 用于指定 spring 在初始化容器时要扫描的包。作用和在 spring 的 xml 配置文件中的: context:component-scan 是一样的
属性: basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。
@Bean
作用: 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。
属性: name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。
@PropertySource
作用: 用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
属性: value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:
@Import
作用: 用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问 题。
属性: value[]:用于指定其他配置类的字节码。
3 Spring 整合 Junit[掌握]
3.1 测试类中的问题和解决思路
问题:在测试类中,每个测试方法都要获取容器和获取实例
ApplicationContext contextnew = AnnotationConfigApplicationContext(SpringConfig.class);
IAccountService service = context.getBean("accountServiceImpl" , IAccountService.class);
解决思路分析:
针对上述问题,我们需要的是程序能自动帮我们创建容器。一旦程序能自动为我们创建 spring 容器, 我们就无须手动创建了,问题也就解决了。
我们都知道,junit 单元测试的原理,但显然,junit 是无法实现的,因为它自己都无法知晓我们是否使 用了 spring 框架,更不用说帮我们创建 spring 容器了。不过好在,junit 给我们提供了一个注解,可以 让我们替换掉它的运行器。 这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容 器。我们只需要告诉它配置文件在哪就行了。
3.2 配置步骤
第一步:导入jar包
注意,JUnit包的版本要是4.12及以上才可以
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
<scope>test</scope>
</dependency>
第二步:使用@RunWith 注解替换原有运行器
第三步:使用@ContextConfiguration 指定 spring 配置文件 的位置
@ContextConfiguration
作用:加载spring配置类
属性:locations:用于指定配置文件的位置。如果是类路径下,需要用
classpath:表明 classes 属性:用于指定注解的类。当不使用 xml 配置时,需要用此属性指定注解类的位置。
第四步:使用@Autowired 给测试类中的变量注入数据
package com.itlaobing.spring.service.impl;
import com.itlaobing.spring.config.SpringConfig;
import com.itlaobing.spring.model.Account;
import com.itlaobing.spring.service.IAccountService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceImplTest {
@Autowired
private ApplicationContext context;
@Autowired
private IAccountService service;
// @Before
// public void setUp() throws Exception {
//
//// context = new ClassPathXmlApplicationContext("spring.xml");
// context = new AnnotationConfigApplicationContext(SpringConfig.class);
// service = context.getBean("accountServiceImpl" , IAccountService.class);
// }
@Test
public void saveAccount() {
Account account = new Account();
account.setName("www");
account.setMoney(2000.5f);
service.saveAccount(account);
}
@Test
public void updateAccount() {
Account account = service.findAccountById(1);
account.setMoney(2020f);
service.updateAccount(account);
}
@Test
public void deleteAccount() {
service.deleteAccount(4);
}
@Test
public void findAccountById() {
Account account = service.findAccountById(1);
System.out.println(account);
}
@Test
public void findAllAccount() {
List<Account> list = service.findAllAccount();
if (list == null){
System.out.println("空");
return;
}
for (Account account : list){
System.out.println(account);
}
}
}
心得体会
今天将spring的基础操作基本学完了,对他的实际开发中的使用也有了一定的了解,使用起来还是比较的简单的,不过需要记忆的注解确实有点多,还是需要多熟悉。
近期评论