Spring是一个分层的Java SE/EE full-stack轻量级开源框架,以IoC(Inverse Of Control,即控制反转)和AOP(Aspect Oriented Programming,即面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还整合了开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业级应用框架。
通过Spring提供的IOC容器,可以将对象间的依赖关系交给Spring进行控制,避免了硬编码所造成的过度程序耦合,或者说大大降低了组件之间的耦合性。
用户也不必再为单例模式类、属性解析等这些很底层的需求编写代码,可以更加专注于上层应用。
通过Spring提供的AOP功能,方便进行面向切面编程,允许将一些通用的任务,如安全、事务、日志等进行集中式处理,许多不容易使用传统OOP实现的功能可以通过AOP轻松应付。
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活进行事务管理,提高开发效率和质量。
Spring提供了对Junit4的支持,可以通过注解的方式方便的测试Spring程序,即可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
Spring可以降低各种框架的使用难度,提供了对各种框架的直接支持。
Spring对JavaEE API(如JSDBC、JavaMail、远程调用等)进行了薄薄的封装层,使得这些API的使用难度大大降低
Spring框架采用的是分层架构,它一系列功能要素被分成20个模块,这些目模块从下到上主要是:Test、Core Container、AOP、Aspects、Instrumentation、Mesaaging、Data Access/Integration、Web等
上图中包含了Spring框架的所有模块,接下来分别对体系结构中的模块作用进行简单的介绍:
Core Container(核心容器)
Spring核心容器是其他模块建立的基础,主要由Beans模块、Core模块、Context模块、Context-support模块以及SpEL模块组成。
Beans模块:
提供了BeanFactory,是工厂模式的经典实现,Spring将管理对象称为Bean。由org.springframework.beans.factory.BeanFactory接口定义,是基础类型的IoC容器,提供了完整的IoC服务支持。简单来说,BeanFactory就是一个管理Bean的工厂,负责初始化各种Bean,并调用他们的声明周期方法,什么时候使用什么时候创建对象。ApplicationContext是BeanFactory的子接口,也被称为应用上下文,是另一种常用的Spring核心容器,只要一读取配置文件,默认情况下就会创建对象。提供了ClassPathXmlApplicationContext和FileSystemApplicationContext两种实现方式,ClassPathApplicationContext是从类路径下加载配置文件,目前推荐使用这种方式;FileSystemXmlApplicationContext是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置;AnnotationConfigApplicationContext是当我们使用注解配置容器对象时,需要使用此类来 创建spring容器。
Core核心模块:
提供了Spring框架的基本组成部分,包括IoC和DI功能。
Context上下文模块:
建立在Core和Beans模块的基础之上,它是访问定义和配置的任何对象的媒介。其中applicationContext接口是上下文模块的焦点。
Context-support模块:
提供了第三方库嵌入Spring应用的集成支持,比如缓存、邮件服务、任务调度和模块引擎。
SpEL模块:
提供了Spring Expression Language支持,是运行时查询和操作对象图的强大语言表达式。
AOP模块:
提供了面向切面编程的实现,允许定义方法拦截器和切入点,将代码按照功能进行分离以降低耦合性。
Aspects模块:提供了与AspectJ的集成功能,AspectJ是一个功能强大且成熟的面向切面编程(AOP)的框架。
Instrumentation模块:提供了工具类的支持和类加载器的实现,可以在特定的应用服务器中使用。
Messaging模块:Spring4.0以后新增的模块,提供了对消息传递体系结构和协议的支持
Test模块:提供了对单元测试和集成测试的支持。
Data Access/Integration(数据访问/集成):数据访问/集成层包括JDBC、ORM、OXM、JMS和Transaction模块,具体介绍如下:
JDBC模块:
提供了一个JDBC的抽象层,大幅度地减少了在开发过程中对数据库操作的编码。
ORM模块:对流行的关系映射API,包括JPA、JDO和Hibernate提供了集成层支持。
OXM模块:提供了一个支持对象/XML映射的抽象层实现,如JAXB、Castor、XMLBeans、JiBX和XStream。
JMS模块:
指Java消息传递服务,包含使用和产生信息的特性。
Transaction事务模块:支持对实现特殊接口以及所有POJO类的编程和声明式事务管理。
WEB
Spring的Web层包括WebSocket、Servlet、Web和Portlet模块,具体的介绍如下:
WebSocket模块:Spring 4.0以后新增的模块,它提供了WebSocket和SockJS的实现,以及对STOMP的支持。
Servlet模块:也称为Spring-webmvc模块,包含了Spring的模型—视图—控制器(MVC)和REST Web Services实现的Web应用程序。
Web模块:提供了基本的Web开发集成特性,例如:多文件上传功能、使用Servlet监听器来初始化IoC容器以及Web应用上下文。
Portlet模块:提供了在Portlet环境中使用MVC实现,类似Servlet模块的功能。
Spring可以看作是一个大型工厂,这个工厂的作用就是生产和管理Spring容器中的Bean。在Spring中,XML配置文件的根元素是 beans,beans中包含多个bean子元素,每个baen子元素定义了一个Bean,并描述了该Bean如何装配到Spring容器中。bean中包含了多个属性以及子元素,其常用的属性以及子元素如下:
属性或子元素名称 | 描述 |
---|---|
id | Bean的唯一标识符,Spring容器对Bean的配置和管理都是通过该属性来完成 |
name | Spring可以通过此属性对容器中的Bean进行配置和管理,name属性可以为Bean指定多个名称,每个名称使用逗号分隔 |
class | 该属性指定了Bean的具体实现类,必须是一个完整的类名,使用类的全限定名 |
scope | 用来设定Bean实例的作用域,其属性值有:singleton(单例)、prototype(多例)、request、session、global session等,默认值是singleton |
property | bean元素的子元素,用于调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入。该元素的name指定bean实例中相应属性的名称,ref属性或者value属性用于指定参数值 |
ref | property或者constructor-arg等元素的属性或者子元素,可以用于指定对Bean工厂中的某一个Bean实例引用 |
value | property、constructor-arg等元素的属性或者子元素,可以用于指定一个常量值 |
list | 用于封装List或者数组类型的依赖注入 |
set | 用于封装set类型的属性的依赖注入 |
map | 用于封装map类型属性的依赖注入 |
entry | map元素的子元素,用于设置一个键值对,其key属性用于指定字符串类型的键值,ref或者value子元素用于指定其值,也可以通过value-ref或者value属性指定其值 |
在配置文件中,通常一个普通的Bean只需与定义id和class两个属性即可。
耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合。在我们的开发中,有些依赖关系是必须的,有些依赖关系是可以通过代码来消除的。接下来看Servcie层的业务代码:
public class IAccountServiceImpl implements IAccountService {
private IAccountDao iAccountDao = new IAccountDaoImpl();
@Override
public List findAll() {
return iAccountDao.findAll();
}
}
上述代码中,业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类,如果此时将持久层实现类删除,则编译不能通过。再比如JDBC操作,在注册驱动的时候,我们为啥不用DriverManager的register方法,而是采用Class.forName()的方式呢?
public class JDBCDemo1 {
public static void main(String[] args) throws SQLException {
//1.注册驱动
/*
* 方式1:直接通过new的方式来注册,这时候我们依赖了数据库的具体驱动类(MySql),
* 这个时候如果我们更换了数据库的品牌(比如Oracel),就需要修改源码来冲洗更新数据库的驱动,
* 这显然不是我们想要的
* */
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.获取连接
//3.获取预处理sql语句对象
//4.获取结果集
//5.遍历结果集
}
}
一般我们注册驱动的时候,都是使用反射的方式,代码如下:
public class JDBCDemo1 {
public static void main(String[] args) throws SQLException {
//1.注册驱动
/*
* 方式1:直接通过new的方式来注册,这时候我们依赖了数据库的具体驱动类(MySql),
* 这个时候如果我们更换了数据库的品牌(比如Oracel),就需要修改源码来冲洗更新数据库的驱动,
* 这显然不是我们想要的
* */
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");//此处只有一个字符串
//2.获取连接
//3.获取预处理sql语句对象
//4.获取结果集
//5.遍历结果集
}
}
这里的好处是我们不再依赖于具体的驱动类,此时就算删除了mysql的驱动jar包,依然可以编译通过(即不会编译时就不通过),运行时就不要想了,没有驱动类是不能运行成功的。同时也产生了一个新的问题,mysql驱动的全限定类名字符串是在Java类中写死的,一旦要修改还要修改源码,解决这个问题比较简单,就是使用配置文件。在实际开发过程中,我们可以把三层的对象都是用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法读取配置文件,把这些对象创建出来并保存起来,在接下来的使用的时候直接拿过来用就好了,那么这个读取配置文件、创建和获取三层对象的类就是工厂。
控制反转(IoC):把对象的创建权利交给框架,这是框架的重要特征,并非面向对象编程的专用术语,包括依赖注入和依赖查找。
4.0.0
com.itheima
spring_test001
1.0-SNAPSHOT
jar
8
8
org.springframework
spring-context
5.2.9.RELEASE
org.springframework
spring-beans
5.2.9.RELEASE
mysql
mysql-connector-java
8.0.22
public interface IAccountDao {
//查找所有
List findAll();
//根据id查找Account账户
Account findAccountById(Integer id);
//根据name进行查找
Account findAccountByName(String name);
//新增账户
void addAccount(Account account);
//修改账户
void updateAccount(Account account);
//删除账户
void deleteAccount(Account account);
}
public class IAccountDaoImpl implements IAccountDao {
private QueryRunner runner;
@Override
public List findAll() {
try {
return runner.query("select * from account",new BeanListHandler(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public Account findAccountById(Integer id) {
try {
return runner.query("select * from account where id=?",new BeanHandler(Account.class),id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public Account findAccountByName(String name) {
try {
return runner.query("select * from account where name=?",new BeanHandler(Account.class),name);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
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);
}
}
@Override
public void addAccount(Account account) {
try {
runner.update("insert into account(name,money) values(?,?);",account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public void deleteAccount(Integer id) {
try {
runner.update("delete account where id=?",id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
在类路径下(resources)创建bean.xml配置文件,并在bean.xml中配置IAccountDao
public class AccountTest {
@Test
public void test1(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountDao iAccountDao = (IAccountDao) ac.getBean("iAccountDao");
iAccountDao.findAll();
}
}
用于配置对象交由Spring来创建。默认情况下调用的是类中无参构造函数,如果没有无参构造函数则不能创建成功。
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB项目中,Spring创建一个Bean对象,将对象存入到request域中 |
session | WEB项目中,Spring创建一个Bean对象,将对象存入session域中 |
global session | WEB项目中,应用在Porlet环境,如果没有Porlet环境那么global Session相当于session |
(1)当scope的取值为singleton时
(2)当scope取值为prototype时
Spring可以管理singleton作用域的生命周期,在此作用域下,Spring能够精确地知道该Bean何时被创建,何时完成初始化以及被销毁。对于prototype作用域的Bean,Spring只负责创建,当容器创建了Bean实例之后,Bean的实例就交给客户端来管理,Spring容器就不再跟踪其生命周期。每次客户端请求prototype作用域的Bean时,Spring容器都会创建一个新的实例,并且不会管那些被配置成prototype作用域的Bean的生命周期。了解Bean生命周期的意义在于,可以在某个Bean生命周期的某些指定时刻完成一些相关的操作。这种时刻可能很多,但是一般情况下,常会在Bean的postinitiation(初始化后)和predestrudtion(销毁前)执行一些相关操作。当一个Bean被加载到Spring容器的时候,它就具有了生命,而Spring容器在保证一个Bean能够使用之前,会做很多的工作。Singleton类型的Bean的生命周期的配置如下:
(1)使用无参构造方法实例化
它会根据默认无参构造方法来创建对象,如果Bean中没有默认的无参构造方法,就会创建失败。
(2)使用静态工厂实例化。
使用工厂的静态方法返回Bean实例
public class StaticFactoryBean {
public static IAccountDao getIAccoutDao(){
return new IAccountDaoImpl();
}
}
(3)使用工厂实例方法实例化
工厂的非静态方法返回Bean实例
public class DynamicFactoryBean {
public IAccountDao getAccountDao(){
return new IAccountDaoImpl();
}
}
Dependency Injection,它是spring框架核心ioc的具体实现,与控制反转IoC的含义相同,只不过这两种称呼是从两个角度描述的同一个概念。在编写程序时,通过控制反转把对象的创建交给spring容器(即控权发生了反转),但是代码中不可能出现没有依赖的情况。ioc解耦只是降低了他们的依赖关系,但是不会消除,业务层的方法还是会调用持久化层的方法。这种业务层和持久化层的依赖关系在使用spring之后,就让spring来维护了。依赖注入的作用就是在使用Spring框架创建对象的时候,动态的将所依赖的对象注入Bean组件中,其实现的方式通常有两种:一种是属性setter方法注入;另一种是构造方法注入。
IoC容器使用setter方法注入被依赖的对象实例,通过调用无参构造器或者无参静态工厂方法实例化Bean之后,调用该Bean的setter方法,即可实现基于setter方法的依赖注入。
public class IAccountServiceImpl implements IAccountService {
private IAccountDao iAccountDao;
public void setiAccountDao(IAccountDao iAccountDao) {
this.iAccountDao = iAccountDao;
}
@Override
public List findAll() {
return iAccountDao.findAll();
}
}
IoC容器使用构造方法注入被依赖的实例,基于构造方法的依赖注入通过调用带参数的构造方法来实现,每一个参数代表一个依赖。
要求:类中需要提供一个对应参数列表的构造函数
public class IAccountServiceImpl implements IAccountService {
public IAccountServiceImpl() {
}
private Integer id ;
private String name;
private Double money;
private IAccountDao iAccountDao;
public IAccountServiceImpl(Integer id, String name, Double money, IAccountDao iAccountDao) {
this.id = id;
this.name = name;
this.money = money;
this.iAccountDao = iAccountDao;
}
}
constructor-arg
属性:
index:指定参数在构造函数列表的索引位置
type:指定参数在构造函数中的数据类型
name:指定参数在构造函数中的名称
value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型
注入集合属性就是给类中的集合成员传值,用的是set方法注入的方式,变量的数据类型是集合,这里介绍数组、List、Set、Map、Properties。具体的代码如下:
public class IAccountDaoImpl implements IAccountDao {
private QueryRunner runner;
private String[] myStrs;
private List myList;
private Set mySet;
private Map myMap;
private Properties properties;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List myList) {
this.myList = myList;
}
public void setMySet(Set mySet) {
this.mySet = mySet;
}
public void setMyMap(Map myMap) {
this.myMap = myMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
@Override
public List findAll() {
try {
System.out.println(myStrs);
System.out.println(myList);
System.out.println(myMap);
System.out.println(mySet);
return runner.query("select * from account",new BeanListHandler(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public Account findAccountById(Integer id) {
try {
return runner.query("select * from account where id=?",new BeanHandler(Account.class),id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public Account findAccountByName(String name) {
try {
return runner.query("select * from account where name=?",new BeanHandler(Account.class),name);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
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);
}
}
@Override
public void addAccount(Account account) {
try {
runner.update("insert into account(name,money) values(?,?);",account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public void deleteAccount(Integer id) {
try {
runner.update("delete account where id=?",id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
aaa
bbb
ccc
张三
李四
王五
ddd
eee
fff
create table account(id int primary key auto_increment,name varchar(100),money int);
insert into account(name,money) values('汤显祖',23.32),('捷克李',34.56);
package com.itheima.domain;
public class Account {
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
}
package com.itheima.dao;
import com.itheima.domain.Account;
import java.util.List;
public interface IAccountDao {
//查找所有
List findAll();
//根据id查找Account账户
Account findAccountById(Integer id);
//根据name进行查找
Account findAccountByName(String name);
//新增账户
void addAccount(Account account);
//修改账户
void updateAccount(Account account);
//删除账户
void deleteAccount(Integer id);
}
public class IAccountDaoImpl implements IAccountDao {
private QueryRunner runner;
private String[] myStrs;
private List myList;
private Set mySet;
private Map myMap;
private Properties properties;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List myList) {
this.myList = myList;
}
public void setMySet(Set mySet) {
this.mySet = mySet;
}
public void setMyMap(Map myMap) {
this.myMap = myMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
@Override
public List findAll() {
try {
System.out.println(myStrs);
System.out.println(myList);
System.out.println(myMap);
System.out.println(mySet);
return runner.query("select * from account",new BeanListHandler(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public Account findAccountById(Integer id) {
try {
return runner.query("select * from account where id=?",new BeanHandler(Account.class),id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public Account findAccountByName(String name) {
try {
return runner.query("select * from account where name=?",new BeanHandler(Account.class),name);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
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);
}
}
@Override
public void addAccount(Account account) {
try {
runner.update("insert into account(name,money) values(?,?);",account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public void deleteAccount(Integer id) {
try {
runner.update("delete account where id=?",id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
package com.itheima.service;
import com.itheima.domain.Account;
import java.util.List;
public interface IAccountService {
//查找所有
List findAll();
//根据id查找Account账户
Account findAccountById(Integer id);
//根据name进行查找
Account findAccountByName(String name);
//新增账户
void addAccount(Account account);
//修改账户
void updateAccount(Account account);
//删除账户
void deleteAccount(Account account);
}
public class IAccountServiceImpl implements IAccountService {
public IAccountServiceImpl() {
}
private Integer id ;
private String name;
private Double money;
private IAccountDao iAccountDao;
public IAccountServiceImpl(Integer id, String name, Double money, IAccountDao iAccountDao) {
this.id = id;
this.name = name;
this.money = money;
this.iAccountDao = iAccountDao;
}
public void setiAccountDao(IAccountDao iAccountDao) {
this.iAccountDao = iAccountDao;
}
@Override
public List findAll() {
return iAccountDao.findAll();
}
@Override
public Account findAccountById(Integer id) {
return null;
}
@Override
public Account findAccountByName(String name) {
return null;
}
@Override
public void addAccount(Account account) {
}
@Override
public void updateAccount(Account account) {
}
@Override
public void deleteAccount(Account account) {
}
}
aaa
bbb
ccc
张三
李四
王五
ddd
eee
fff
public class AccountTest {
@Test
public void test1(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService iAccountService = (IAccountService) ac.getBean("iAccountService");
iAccountService.findAll();
System.out.println(iAccountService);
IAccountDao iAccountDao = (IAccountDao) ac.getBean("iAccountDao");
List accounts = iAccountDao.findAll();
}
}
相当于:
他们三个注解都是针对一个衍生的注解,他们的作用和属性都是一摸一样的,他们只不过是提供了更加明确的语意化。下面的三个注解中,如果注解中有且只有一个属性要赋值的时候,且名称是value时,那么value在赋值的时候是可以不写的。
相当于:
或者:
作用:自动按照类型注入。当使用注解注入属性的时候,set方法可以省略。它只能注入其他的bean类型。当有多个类型匹配的时候,使用要注入的对象变量名称作为bean的id,在spring容器中查找,找到了可以注入成功,找不到就报错。
作用:在自动按照类型注入的基础上,再按照Bean的id注入。它在给字段注入的时候,必须和@Autowire一起使用,但是在给方法注入的时候,可以单独使用。
属性:value指定Bean的id
作用:直接按照Bean的id注入,它也只能注入其他bean的类型
属性:name指定bean的id
作用:注入基本数据类型和String类型的数据
属性:value用于指定值
相当于:
作用:指定bean的作用范围
属性:value指定范围的值。取值有:singleton、prototype、request、session、globalsession
相当于:
作用:指定初始化方法
作用:用于指定销毁方法
基于XML的配置 | 基于注解的配置 | |
---|---|---|
Bean定义 | < bean id="…" class="…" /> | @Component衍生类@Repository、@Service、@Controller |
Bean名称 | 通过id或者name指定 | @Component(“person”) |
Bean注入 | 或者通过p命名空间 | @Autowired按照类型注入、@Qualifier按照名称注入 |
生命过程、Bean作用范围 | init-method、destory-method、范围的scope属性 | @PostConstruct初始化、@PreSestory销毁、@Scope设置作用范围 |
适合场景 | Bean来自第三方,使用其他 | Bean的实现由用户自己开发 |
基于注解的Spring IoC配置中,bean对象特点和基于XML配置是一摸一样的。
@Configuration注解用于代替配置文件的,加载了这个注解之后就不需要再用bean.xml配置文件了
@org.springframework.context.annotation.Configuration
public class Configuration {
}
是一样的
@org.springframework.context.annotation.Configuration
@ComponentScan("com.itheima")
public class Configuration {
}
我们已经配置好了扫描包,要将数据源和JdbcTemplate对象从配置文件中移除,需要使用@Bean注解。
/*
* 连接数据库的配置类
* */
public class JdbcConfig {
/*
* 创建一个数据源,并存入spring容器中
* */
@Bean
public DataSource getDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser("root");
ds.setDriverClass("com.jdbc.mysql.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
ds.setPassword("Shezeq1,");
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
/*
* 连接数据库的配置类
* */
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
/*
* 创建一个数据源,并存入spring容器中
* */
@Bean
public DataSource getDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser("root");
ds.setDriverClass("com.jdbc.mysql.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
ds.setPassword("Shezeq1,");
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
/*
* 连接数据库的配置类
* */
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
/*
* 创建一个数据源,并存入spring容器中
* */
@Bean
public DataSource getDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser("root");
ds.setDriverClass("com.jdbc.mysql.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
ds.setPassword("Shezeq1,");
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
在原始测试类中,每个测试方法都要有下面的两行代码:
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService iAccountService = ac.getBean("iAccountService",IAccountService.class);
这些代码的作用是获取容器,如果不写的话,会直接提示空指针异常,所以不能删掉。
让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它,将需要测试的Bean直接在测试类中注入。
org.springframework
spring-test
5.2.9.RELEASE
junit
junit
4.13.1
test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AnnoAccountTest {
@Autowired
private IAccountService as=null;
@Test
public void testFindAll(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService iAccountService = ac.getBean("iAccountService",IAccountService.class);
List accounts = as.findAll();
for (Account account :accounts){
System.out.println(account);
}
}
@Test
public void testFindAccountById(){
Account account = as.findAccountById(5);
System.out.println(account);
}
@Test
public void testFindAccountByName(){
Account account = as.findAccountByName("王二麻子");
System.out.println(account);
}
@Test
public void testAddAccount(){
Account account = new Account();
account.setName("赵敏");
account.setMoney(2345.6789);
as.addAccount(account);
}
@Test
public void testDeleteAccount(){
as.deleteAccount(7);
}
@Test
public void testUpdateAccount(){
Account account = new Account();
account.setName("赵敏");
account.setMoney(2345.6789);
account.setId(5);
as.updateAccount(account);
}
}
4.0.0
com.itheima
spring_test02
1.0-SNAPSHOT
jar
8
8
org.springframework
spring-context
5.2.9.RELEASE
junit
junit
4.13.1
test
mysql
mysql-connector-java
8.0.22
commons-dbutils
commons-dbutils
1.7
c3p0
c3p0
0.9.1.2
org.springframework
spring-test
5.2.9.RELEASE
org.springframework
spring-test
5.2.9.RELEASE
test
package com.itheima.domain;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
@Override
public String toString() {
return "IAccount{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
}
package com.itheima.dao;
import com.itheima.domain.Account;
import java.util.List;
public interface IAccountDao {
//查找所有
List findAll();
//根据id查找
Account findAccountById(Integer id);
//根据name查找
Account findAccountByName(String name);
//新增account
void addAccount(Account account);
//删除账户
void deleteAccount(Integer id);
//修改账户
void updateAccount(Account account);
}
@Repository("accountDao")
public class IAccountDaoImpl implements IAccountDao {
@Autowired
private QueryRunner runner;
@Override
public List findAll() {
try {
return runner.query("select * from account",new BeanListHandler(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public Account findAccountById(Integer id) {
try {
return runner.query("select * from account where id=?",new BeanHandler(Account.class),id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public Account findAccountByName(String name) {
try {
List accounts = runner.query("select * from account where name=?",new BeanListHandler(Account.class),name);
if (accounts.isEmpty()) return null;
if (accounts.size()>1){
throw new RuntimeException("结果集大于1,数据有误");
}
return accounts.get(0);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public void addAccount(Account account) {
try {
runner.update("insert into account(name,money) values(?,?)",account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public void deleteAccount(Integer id) {
try {
runner.update("delete from account where id=?",id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
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);
}
}
}
package com.itheima.service;
import com.itheima.domain.Account;
import java.util.List;
public interface IAccountService {
//查找所有
List findAll();
//根据id查找
Account findAccountById(Integer id);
//根据name查找
Account findAccountByName(String name);
//新增account
void addAccount(Account account);
//删除账户
void deleteAccount(Integer id);
//修改账户
void updateAccount(Account account);
}
@Service("accountService")
public class IAccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Override
public List findAll() {
return accountDao.findAll();
}
@Override
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
@Override
public Account findAccountByName(String name) {
return accountDao.findAccountByName(name);
}
@Override
public void addAccount(Account account) {
accountDao.addAccount(account);
}
@Override
public void deleteAccount(Integer id) {
accountDao.deleteAccount(id);
}
@Override
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
}
package com.itheima.config;
public class JdbcConfiguration {
@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(name = "datasource")
public DataSource getDataSource(){
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 = "runner")
public QueryRunner getQueryRunner(@Qualifier("datasource")DataSource dataSource){
return new QueryRunner(dataSource);
}
}
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
@ComponentScan("com.itheima")
@Import(JdbcConfiguration.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}
import com.itheima.config.SpringConfiguration;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AnnoAccountTest {
@Autowired
private IAccountService as=null;
@Test
public void testFindAll(){
List accounts = as.findAll();
for (Account account :accounts){
System.out.println(account);
}
}
@Test
public void testFindAccountById(){
Account account = as.findAccountById(5);
System.out.println(account);
}
@Test
public void testFindAccountByName(){
Account account = as.findAccountByName("王二麻子");
System.out.println(account);
}
@Test
public void testAddAccount(){
Account account = new Account();
account.setName("赵敏");
account.setMoney(2345.6789);
as.addAccount(account);
}
@Test
public void testDeleteAccount(){
as.deleteAccount(7);
}
@Test
public void testUpdateAccount(){
Account account = new Account();
account.setName("赵敏");
account.setMoney(2345.6789);
account.setId(5);
as.updateAccount(account);
}
}