前面一篇文章花大量内容,重点学习了 Spring入门 的一些思想,以及简单的学习了 IOC基础 以及基于基于 XML 的配置方式,你们应该清楚,XML与注解经常是如影随行的,他们就像一对双胞胎,但兄弟两个的想法都是一致的,那就是帮助开发者实现想要的功能,咱们所说的IOC 技术,无疑是为了下降程序间的耦合,那么,今天就来聊一聊,基于注解的IOC配置,固然为了你们有对比学习,两种配置同时讲解,同时我把例举得尽可能完整一些,就来完成一个对单表进行 CURD 的案例java
说明:因为我这里建立的是一个Maven项目,因此在这里修改 pom.xml 添加一些必要的依赖坐标就能够mysql
若是建立时没有使用依赖的朋友,去下载咱们所须要的 jar 包导入就能够了spring
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
简单看一下,spring核心的一些依赖,以及数据库相关的依赖等就都导入进来了sql
<div align="center">
<img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618a159a64af?w=535&h=368&f=png&s=152529" style="zoom:80%">
</div>数据库
-- ---------------------------- -- Table structure for account -- ---------------------------- CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32), `balance` float, PRIMARY KEY (`id`) )
没什么好说的,对应着咱们的表创出实体apache
public class Account implements Serializable { private Integer id; private String name; private Float balance; ......补充 get set toString 方法
public interface AccountService { void add(Account account); void delete(Integer accpuntId); void update(Account account); List<Account> findAll(); Account findById(Integer accountId); }
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void add(Account account) { accountDao.addAccount(account); } public void delete(Integer accpuntId) { accountDao.deleteAccount(accpuntId); } public void update(Account account) { accountDao.updateAccount(account); } public List<Account> findAll() { return accountDao.findAllAccount(); } public Account findById(Integer accountId) { return accountDao.findAccountById(accountId); } }
public interface AccountDao { void addAccount(Account account); void deleteAccount(Integer accountId); void updateAccount(Account account); List<Account> findAllAccount(); Account findAccountById(Integer accountId); }
因为今天要完成的是一个增删改查的操做,因此咱们引入了 DBUtils 这样一个操做数据库的工具,它的做用就是封装代码,达到简化 JDBC 操做的目的,因为之后整合 SSM 框架的时候,持久层的事情就能够交给 MyBatis 来作,而今天咱们重点仍是讲解 Spring 中的知识,因此这部分会用就能够了,重点看 XML 与 注解 两种配置方式安全
用到的内容基本讲解:微信
QueryRunner 提供对 sql 语句进行操做的 API (insert delete update)session
ResultSetHander 接口,定义了查询后,如何封装结果集(仅提供了咱们用到的)框架
public class AccountDaoImpl implements AccountDao { private QueryRunner runner; public void setRunner(QueryRunner runner) { this.runner = runner; } public void addAccount(Account account) { try { runner.update("insert into account(name,balance)values(?,?)", account.getName(), account.getBalance()); } catch (Exception e) { throw new RuntimeException(e); } } public void updateAccount(Account account) { try { runner.update("update account set name=?,balance=? where id=?", account.getName(), account.getBalance(), account.getId()); } catch (Exception e) { throw new RuntimeException(e); } } public void deleteAccount(Integer accountId) { try { runner.update("delete from account where id=?", accountId); } catch (Exception e) { throw new RuntimeException(e); } } 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); } } }
在这里有两基本的方式,一是经过构造函数注入,另外一种就是经过Set注入,实际上所作的就是,使用类的构造函数或者Set给成员变量进行赋值,但特别的是,这里是经过配置,使用 Spring 框架进行注入首先就是头部的依赖信息,顺便提一句,固然咱们能够去官网查找贴过来
先把针对上面功能的具体配置代码贴出来
<?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="cn.ideal.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <!--配置Dao--> <bean id="accountDao" class="cn.ideal.dao.impl.AccountDaoImpl"> <property name="runner" ref="runner"></property> </bean> <!--配置 QueryRunner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"> <!--注入数据源--> <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/ideal_spring"></property> <property name="user" value="root"></property> <property name="password" value="root99"></property> </bean> </beans>
分析一下:
配置 Bean 标签的时候,咱们见到了两种形式 property、constructor-arg 也就是对应着 set 方式 与构造函形式,先说一下比较常见的 set 方式,用上面的代码中距离:
顾名思义,就是经过去找你给出对应的 Set 方法,而后对成员变量进行赋值,先看下类中的代码
public class AccountServiceImpl implements AccountService { //成员 private AccountDao accountDao; //Set方法 public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } ...... 下面是增删改查的方法 }
这是 bean.xml 中的配置
<!--配置Service--> <bean id="accountService" class="cn.ideal.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean>
而后 property 配置的过程当中,有一些属性须要说一下
固然,之后可能会见到一种方式就是 使用 p名称空间注入数据 (本质仍是set)
头部中须要修改引入这一句
xmlns:p="http://www.springframework.org/schema/p"
我直接拿之前文章中的一个例子:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" 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"> <bean id="accountService" class="cn.ideal.service.impl.AccountServiceImpl" p:name="汤姆" p:age="21" p:birthday-ref="nowdt"/> <bean id="nowdt" class="java.util.Date"></bean> </beans>
下面就是使用构造函数的一种方式,这一种的前提就是:类中必须提供一个和参数列表相对应的构造函数
因为咱们选择的是 DBUtils 这样一个工具,而它为咱们提供了两种构造函数,即带参和无参,因此咱们能够在其中注入数据源,也可使得每一条语句都独立事务
还有一点须要说明的就是:咱们下面的数据源使用了 c3p0 这只是一种选择方式,并非必定的,是由于使用 DBUtils 的时候须要手动传递一个 Connection 对象
<!--配置 QueryRunner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"> <!--注入数据源--> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean>
来讲一下所涉及到的标签:
constructor-arg(放在 bean 标签内) 再说一说其中的一些属性值
给谁赋值:
赋什么值:
为了演示这些方式,咱们在一个实体成员中将常见的一些集合都写出来,而后补充其 set 方法
private String[] strs; private List<String> list; private Set<String> set; private Map<String,String> map; private Properties props;
在配置中也是很简单的,只须要按照下列格式写标签就能够了,因为增删改查中并无涉及到集合的相关信息,这里就是简单提一下,能够本身测试一下
<bean id="accountService" class="cn.ideal.service.impl.AccountServiceImpl"> <property name="strs"> <array> <value>张三</value> <value>李四</value> <value>王五</value> </array> </property> <property name="list"> <list> <value>张三</value> <value>李四</value> <value>王五</value> </list> </property> <property name="set"> <set> <value>张三</value> <value>李四</value> <value>王五</value> </set> </property> <property name="map"> <map> <entry key="name" value="张三"></entry> <entry key="age" value="21"></entry> </map> </property> <property name="props"> <props> <prop key="name">张三</prop> <prop key="age">21</prop> </props> </property> </bean>
public class AccountServiceTest { private ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); private AccountService as = ac.getBean("accountService", AccountService.class); @Test public void testAdd(){ Account account = new Account(); account.setName("jack"); account.setBalance(1000f); as.add(account); } @Test public void testUpdate(){ Account account = as.findById(4); account.setName("杰克"); account.setBalance(1500f); as.update(account); } @Test public void testFindAll(){ List<Account> list = as.findAll(); for(Account account : list) { System.out.println(account); } } @Test public void testDelete(){ as.delete(4); } }
添加,修改(包含了查询指定id),删除
<div align="center">
<img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618a27d84ba4?w=259&h=153&f=png&s=5014" style="zoom:80%"> <img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618a841155ed?w=266&h=156&f=png&s=5381" style="zoom:80%"> <img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618adeefa5e7?w=263&h=136&f=png&s=4654" style="zoom:80%">
</div>
查询全部
<div align="center">
<img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618aeedd590b?w=437&h=90&f=png&s=31302" style="zoom:80%">
</div>
首先,咱们先将上面的例子使用注解来实现一下,再来具体的讲解:
首先须要为 Dao 和 Service 的实现类中 添加注解
@Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; 下面的原封不动 }
@Repository("accountDao") public class AccountDaoImpl implements AccountDao { @Autowired private QueryRunner runner; 下面的原封不动 }
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--开启扫描--> <context:component-scan base-package="cn.ideal"></context:component-scan> <!--配置 QueryRunner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"> <!--注入数据源--> <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/ideal_spring"></property> <property name="user" value="root"></property> <property name="password" value="root99"></property> </bean> </beans>
到这里,一个最基本的注解改造就完成了,你们能够用前面的测试类进行一下测试
下面咱们正式说一下注解配置相关的知识
@Component
<bean id="" class=""
>@Controller @Service @Repository
对于建立对象的注解,Spring 还提供了三种更加明确的说法,做用是彻底相同的,可是针对不一样的场景起了不一样的叫法罢了
<property name="" ref="">
或者 <property name="" value="">
容器中有一个惟一的 bean 对象类型和注入的变量类型一致,则注入成功
@Autowired private AccountDao accountDao; @Repository("accountDao") public class AccountDaoImpl implements AccountDao {......}
@Autowired private AccountDao accountDaoA; @Repository("accountDaoA") public class AccountDaoImplA implements AccountDao {......} @Repository("accountDaoB") public class AccountDaoImplB implements AccountDao {......}
它有时候必须配合别的注解使用,有没有一个标签能够解决这个问题呢?答案就是 @Resource
前面三个都是用来注入其余的 bean 类型的数据,下面来讲一说,基本类型以及String的实现
(特别说明:集合类型的注入只能经过 XML 来实现)
<bean id="" class="" scope
至关于:<bean id="" class="" init-method="" destroy-method="" />
通常来讲,咱们两种配置方式都是有人使用的,不过我我的更习惯使用注解的方式
XML
注解:
XML配置 | 注解配置 | |
---|---|---|
建立对象 | <bean id="" class="" > |
@Controller @Service @Repository@Component |
指定名称 | 经过 id 或者 name 值指定 | @Controller("指定的名称") |
注入数据 | <property name="" ref=""> |
@Autowired @Qualifier @Resource @Value |
做用范围 | <bean id="" class="" scope> |
@Scope |
生命周期 | <bean id="" class="" init-method="" destroy-method=""/> |
@PostConstruct @PreDestroy |
为何要补充新注解呢? 在咱们使用注解时,在书写代码时,简化了不少,可是咱们在 bean.xml 文件中 仍然须要 开启扫描、 进行配置QueryRunner 以及 数据源,如何完全摆脱 xml 配置全面使用注解呢?
这也就是咱们将要补充的几个新注解,做用就是让咱们全面使用注解进行开发
private ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
依旧使用上方的 CURD 的案例代码进行修改,首先与cn同级建立了一个名为 config 的包,而后编写一个名为 SpringConfiguration 的类,固然实际上这两个名字无所谓的,添加注解
@Configuration public class SpringConfiguration { }
@Configuration 至关于已经帮咱们把 bean.xml 文件创立好了,按照咱们往常的步骤,应该指定扫描的包了,这也就是咱们这个注解的做用
<!--开启扫描--> <context:component-scan base-package="cn.ideal"></context:component-scan>
具体使用:
@Configuration @ComponentScan("cn.ideal") public class SpringConfiguration { }
写好了配置类,以及指定了扫描的包,下面该作的就是配置 QueryRunner 以及数据源了,在 XML 中咱们会经过书写 bean 标签来配置,而 Spring 为咱们提供了 @Bean 这个注解来替代原来的标签
具体使用:
package 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 javax.sql.DataSource; public class JdbcConfig { /** * 建立一个 QueryRunner对象 * @param dataSource * @return */ @Bean(name = "runner") public QueryRunner creatQueryRunner(DataSource dataSource){ return new QueryRunner(dataSource); } /** * 建立数据源,而且存入spring * @return */ @Bean(name = "dataSource") public DataSource createDataSource() { try { ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setUser("root"); ds.setPassword("1234"); ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql:///spring_day02"); return ds; } catch (Exception e) { throw new RuntimeException(e); } } }
上面在建立数据源的时候,都是直接把配置信息写死了,若是想要使用 properties 进行内容的配置,在这时候就须要,使用 @PropertySource 这个注解
@Configuration @ComponentScan("cn.ideal") @PropertySource("classpath:jdbcConfig.properties") public class SpringConfiguration { }
public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * 建立一个 QueryRunner对象 * @param dataSource * @return */ @Bean(name = "runner") public QueryRunner creatQueryRunner(DataSource dataSource){ return new QueryRunner(dataSource); } /** * 建立数据源,而且存入spring * @return */ @Bean(name = "dataSource") public DataSource createDataSource() { try { ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setUser(username); ds.setPassword(password); ds.setDriverClass(driver); ds.setJdbcUrl(url); return ds; } catch (Exception e) { throw new RuntimeException(e); } } }
这样看来一个 JdbcConfig 就基本写好了,咱们在其中配置了 QueryRunner 对象,以及数据源,这个时候,实际上咱们原先的 bean.xml 就能够删掉了,可是咱们虽然写好了 JdbcConfig 可是如何将两个配置文件联系起来呢?这也就是这个注解的做用
@Configuration @ComponentScan("cn.ideal") @Import(JdbcConfig.class) @PropertySource("classpath:jdbcConfig.properties") public class SpringConfiguration { }
修改获取容器的方式后,就能够进行测试了
private ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); private AccountService as = ac.getBean("accountService", AccountService.class);
因为咱们须要经过上面测试中两行代获取到容器,为了避免每一次都写这两行代码,因此咱们在前面将其定义在了成员位置,可是有没有办法能够省掉这个步骤呢?
也就是说,咱们想要程序自动建立容器,可是原来的 junit 很显然是实现不了的,由于它并不会知道咱们是否使用了 spring ,不过 junit 提供了一个注解让咱们替换它的运行器,转而由 spring 提供
首先须要导入 jar 包 或者说导入依赖坐标
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency>
使用 @RunWith 注解替换原有运行器 而后使用 @ContextConfiguration 指定 spring 配置文件的位置,而后使用 @Autowired 给变量注入数据
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) public class AccountServiceTest { @Autowired private AccountService as; }
到这里,这篇文章就结束了,这篇文章主要讲解的内容接着咱们上一篇文章讲解,上一篇更多的是从传统按部就班的提到 Spring,更多的是帮助你们理解为何用Spring,而这篇文章是基于上一篇的基础,更多的提到的是如何去用 Spring,因此看起来更像是一个工具书同样的文章,并无提到不少的思想原理,而是帮助你们快速的上手用 XML 以及注解的方式,固然大神天然不少,不敢说什么有技术,但总归是但愿能给不太熟悉 spring 的朋友一些帮助,或者临时当作一篇工具文来查找,再次感谢你们的访问与赞,谢谢朋友们的支持!再次感谢!
若是文章中有什么不足,欢迎你们留言交流,感谢朋友们的支持!
若是能帮到你的话,那就来关注我吧!若是您更喜欢微信文章的阅读方式,能够关注个人公众号
在这里的咱们素不相识,却都在为了本身的梦而努力 ❤一个坚持推送原创开发技术文章的公众号:理想二旬不止