spring入门实战&源码解读(1):spring IOC入门实战

一、Spring概述

    Spring是一个分层的Java SE/EE full-stack轻量级开源框架,以IoC(Inverse Of Control,即控制反转)和AOP(Aspect Oriented Programming,即面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还整合了开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业级应用框架。

1.1Spring的优势

  • 方便解耦、简化开发:
通过Spring提供的IOC容器,可以将对象间的依赖关系交给Spring进行控制,避免了硬编码所造成的过度程序耦合,或者说大大降低了组件之间的耦合性。
用户也不必再为单例模式类、属性解析等这些很底层的需求编写代码,可以更加专注于上层应用。
  • AOP编程支持
通过Spring提供的AOP功能,方便进行面向切面编程,允许将一些通用的任务,如安全、事务、日志等进行集中式处理,许多不容易使用传统OOP实现的功能可以通过AOP轻松应付。
  • 声明式事务支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活进行事务管理,提高开发效率和质量。
  • 方便程序的测试
Spring提供了对Junit4的支持,可以通过注解的方式方便的测试Spring程序,即可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
  • 方便集成各种优秀的框架
Spring可以降低各种框架的使用难度,提供了对各种框架的直接支持。
  • 降低JavaEE API的使用难度
Spring对JavaEE API(如JSDBC、JavaMail、远程调用等)进行了薄薄的封装层,使得这些API的使用难度大大降低

1.2Spring体系结构

Spring框架采用的是分层架构,它一系列功能要素被分成20个模块,这些目模块从下到上主要是:Test、Core Container、AOP、Aspects、Instrumentation、Mesaaging、Data Access/Integration、Web等
spring入门实战&源码解读(1):spring IOC入门实战_第1张图片
    上图中包含了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模块的功能。

1.3Spring中的Bean

    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两个属性即可。

二、使用Spring IoC解决程序耦合

2.1之前程序中出现的耦合

    耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合。在我们的开发中,有些依赖关系是必须的,有些依赖关系是可以通过代码来消除的。接下来看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.遍历结果集
    }
}

2.2解决程序耦合的思路

一般我们注册驱动的时候,都是使用反射的方式,代码如下:

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):把对象的创建权利交给框架,这是框架的重要特征,并非面向对象编程的专用术语,包括依赖注入和依赖查找。

2.3Spring快速入门

2.3.1Spring程序开发步骤

  • 导入Spring开发的基本坐标
  • 编写Dao接口和实现类
  • 编写Spring核心配置文件
  • 在Spring配置文件中配置UserDaoImpl
  • 使用Spring的API获取Bean

2.3.2导入Spring开发的基本包坐标



    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
        
    


2.3.3编写Dao接口和实现类

  • 接口类IAccountDao
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);
}

  • 接口实现类IAccountDaoImpl
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);
        }
    }
}

2.3.4创建Spring核心配置文件

在类路径下(resources)创建bean.xml配置文件,并在bean.xml中配置IAccountDao



    
    

2.3.5使用Spring的API获取Bean的实例

public class AccountTest {
    @Test
    public void test1(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountDao iAccountDao = (IAccountDao) ac.getBean("iAccountDao");
        iAccountDao.findAll();
    }
}

三、Spring配置文件

3.1bean标签的基本配置

3.1.1Bean标签作用

用于配置对象交由Spring来创建。默认情况下调用的是类中无参构造函数,如果没有无参构造函数则不能创建成功。

3.1.2Bean标签配置

  • id:给对象在容器中提供一个唯一的标识,用于获取对象;
  • class:指定类的全限定类名,用于反射创建对象,默认情况下调用的是无参构造函数。
  • scope:指定对象的作用范围。singleton默认值,单例的;prototype是多例的。
取值范围 说明
singleton 默认值,单例的
prototype 多例的
request WEB项目中,Spring创建一个Bean对象,将对象存入到request域中
session WEB项目中,Spring创建一个Bean对象,将对象存入session域中
global session WEB项目中,应用在Porlet环境,如果没有Porlet环境那么global Session相当于session

(1)当scope的取值为singleton时

  • Bean的实例化个数:1个
  • Bean的实例化时机: 当Spring核心配置文件被加载时,实例化配置Bean实例
  • 对象创建:当应用加载,创建容器时,对象就被加载了;
  • 对象运行:只要容器在,对象就会一直活着
  • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了

(2)当scope取值为prototype时

  • Bean的实例化个数:多个
  • Bean的实例化时机:当调用getBean()方法时,实例化Bean
  • 对象创建:当使用对象时,创建新的对象实例
  • 对象运行:只要对象在使用中,就一直活着
  • 对象销毁:当对象长时间不使用时,就被Java的垃圾回收器回收了

3.1.3Bean对象的生命周期

    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的生命周期的配置如下:

  • init-method:指定类中的初始化方法
  • destory-method:指定类中的销毁方法

3.1.4Bean实例化的三种方法

(1)使用无参构造方法实例化

它会根据默认无参构造方法来创建对象,如果Bean中没有默认的无参构造方法,就会创建失败。

    

(2)使用静态工厂实例化。

使用工厂的静态方法返回Bean实例

  • 静态工厂类StaticFactoryBean:
public class StaticFactoryBean {
    public static IAccountDao getIAccoutDao(){
        return new IAccountDaoImpl();
    }
}
  • 配置beans.xml配置文件
    

(3)使用工厂实例方法实例化

工厂的非静态方法返回Bean实例

  • 工厂非静态方法实例化Bean
public class DynamicFactoryBean {
    public IAccountDao getAccountDao(){
        return  new IAccountDaoImpl();
    }
}
  • Beans.xml配置文件


3.2依赖注入

3.2.1 依赖注入的概念

Dependency Injection,它是spring框架核心ioc的具体实现,与控制反转IoC的含义相同,只不过这两种称呼是从两个角度描述的同一个概念。在编写程序时,通过控制反转把对象的创建交给spring容器(即控权发生了反转),但是代码中不可能出现没有依赖的情况。ioc解耦只是降低了他们的依赖关系,但是不会消除,业务层的方法还是会调用持久化层的方法。这种业务层和持久化层的依赖关系在使用spring之后,就让spring来维护了。依赖注入的作用就是在使用Spring框架创建对象的时候,动态的将所依赖的对象注入Bean组件中,其实现的方式通常有两种:一种是属性setter方法注入;另一种是构造方法注入。

3.2.2属性setter方法依赖注入的实现

IoC容器使用setter方法注入被依赖的对象实例,通过调用无参构造器或者无参静态工厂方法实例化Bean之后,调用该Bean的setter方法,即可实现基于setter方法的依赖注入。

  • 在IAccountServiceImpl类中添加setiAccountDao方法
public class IAccountServiceImpl implements IAccountService {
    private IAccountDao iAccountDao;

    public void setiAccountDao(IAccountDao iAccountDao) {
        this.iAccountDao = iAccountDao;
    }

    @Override
    public List findAll() {
        return iAccountDao.findAll();
    }
}    
  • 在spring配置文件中配置Spring容器调用set方法进行注入

    
        
    
    

3.2.3构造方法注入

IoC容器使用构造方法注入被依赖的实例,基于构造方法的依赖注入通过调用带参数的构造方法来实现,每一个参数代表一个依赖。

  • 在IAccountServiceImpl类中添加带参数的构造方法
要求:类中需要提供一个对应参数列表的构造函数
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;
    }
}
  • 在spring配置文件中配置Spring容器调用有参构造的形式进行注入。涉及的标签:
constructor-arg
属性:
	index:指定参数在构造函数列表的索引位置
	type:指定参数在构造函数中的数据类型
	name:指定参数在构造函数中的名称
	value:它能赋的值是基本数据类型和String类型
	ref:它能赋的值是其他bean类型

    
        
        
        
        
    
 
        
 
 
        
    
    
        
        
        
        
       

3.2.4注入集合属性

注入集合属性就是给类中的集合成员传值,用的是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);
        }
    }
}

  • spring主配置文件的注入配置

    
        
        
        
        
    
    
        
		
        
            
                
                
                
            
        
        
        
            
                aaa
                bbb
                ccc
            
        
        
        
            
                张三
                李四
                王五
            
        
        
        
            
                ddd
                eee
                fff
            
        
        
            
                
                
                
            
        
    
    


    
        
    
    
        
        
        
        
    

3.3使用Spring的IoC实现账户的CRUD

3.3.1创建数据库

  • 创建数据库表并插入数据
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();
    }
}

3.4 常用注解

3.4.1用于创建对象的注解

相当于:
3.4.1.1 @Component
  • 作用:把资源让spring来管理,相当于在xml中配置一个bean。
  • 属性:value,指定bean的id,如果不指定value属性,默认bean的id是当前类的类名,首字母小写。
3.4.1.2 @Controller、@Service、@Repository

他们三个注解都是针对一个衍生的注解,他们的作用和属性都是一摸一样的,他们只不过是提供了更加明确的语意化。下面的三个注解中,如果注解中有且只有一个属性要赋值的时候,且名称是value时,那么value在赋值的时候是可以不写的。

  • @Controller:一般用于表现层的注解
  • @Service:一般用于业务层的注解
  • @Repository:一般用于持久层的注解

3.4.2用于数据注入的注解

相当于:
或者:
3.4.2.1 @Autowired

作用:自动按照类型注入。当使用注解注入属性的时候,set方法可以省略。它只能注入其他的bean类型。当有多个类型匹配的时候,使用要注入的对象变量名称作为bean的id,在spring容器中查找,找到了可以注入成功,找不到就报错。

3.4.2.2@Qualifier

作用:在自动按照类型注入的基础上,再按照Bean的id注入。它在给字段注入的时候,必须和@Autowire一起使用,但是在给方法注入的时候,可以单独使用。

属性:value指定Bean的id

3.4.2.3 @Resource

作用:直接按照Bean的id注入,它也只能注入其他bean的类型

属性:name指定bean的id

3.4.2.4 @Value

作用:注入基本数据类型和String类型的数据

属性:value用于指定值

3.4.3用于改变作用范围的

相当于:
3.4.3.1@Scope

作用:指定bean的作用范围

属性:value指定范围的值。取值有:singleton、prototype、request、session、globalsession

3.4.4和生命周期相关的

相当于:
3.4.4.1 @postConstructor

作用:指定初始化方法

3.4.4.2 @PreDestory

作用:用于指定销毁方法

3.4.5Spring注解和xml选择的问题

  • 注解的优势:配置简单,维护方便(找到了类,就相当于找到了对应的配置)。
  • XML的优势:修改的时候,不用修改源码。不涉及重新编译和部署
  • Spring管理Bean方式的比较:
基于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的实现由用户自己开发

3.4.6 Spring管理对象细节

基于注解的Spring IoC配置中,bean对象特点和基于XML配置是一摸一样的。

3.4.6.1@Configuration
  • 作用:用于指定当前类是一个spring配置类,当创建容器的时候会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(有@Configuration注解类的.class)
  • 属性:value用于指定配置类的字节码
@Configuration注解用于代替配置文件的,加载了这个注解之后就不需要再用bean.xml配置文件了
@org.springframework.context.annotation.Configuration
public class Configuration {
}
3.4.6.2 @ComponentScan
  • 作用:用于指定spring在初始化容器的时候需要扫描的包。作用和在bean.xml配置文件中的:
是一样的
  • 属性:basepackages用于指定要扫描的包。和该注解中的value属性作用一样。
@org.springframework.context.annotation.Configuration
@ComponentScan("com.itheima")
public class Configuration {
}
3.4.6.3@Bean

我们已经配置好了扫描包,要将数据源和JdbcTemplate对象从配置文件中移除,需要使用@Bean注解。

  • 作用:该注解只能写在方法上,表示使用此方法创建一个对象,并将这个对象放进spring容器中。
  • 属性:name表示给当前@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);
        }
    }
}

3.4.6.4@PropertySource
  • 作用:主要用于加载.properties文件中的配置。例如我们配置数据源的时候,可以把连接数据库的信息写进properties配置文件中,然后使用此注解指定properties配置文件的位置。
  • 属性:value[]:用于指定properties文件的位置。如果是在列路径下,需要写上classpath:
/*
* 连接数据库的配置类
* */
@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);
        }
    }
}

3.4.6.5@Import
  • 作用:用于导入其配置类,在引入其他配置类的时候,可以不用再写@Configuration注解,当然写上也没有问题。
  • 属性:value[]:用于指定其他配置类的字节码
/*
* 连接数据库的配置类
* */
@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);
        }
    }
}

3.5Spring整合Junit

3.5.1原始Junit测试Spring

在原始测试类中,每个测试方法都要有下面的两行代码:

ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService iAccountService =  ac.getBean("iAccountService",IAccountService.class);        

这些代码的作用是获取容器,如果不写的话,会直接提示空指针异常,所以不能删掉。

3.5.2上述问题的解决思路

让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它,将需要测试的Bean直接在测试类中注入。

3.5.3Spring集成Junit步骤

  • 导入spring集成junit坐标

            org.springframework
            spring-test
            5.2.9.RELEASE
        
        
            junit
            junit
            4.13.1
            test
        
  • 使用RunWith注解替代原来的运行期
  • 使用@ContextConfiguration指定配置文件或者配置类
@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);
    }
}

  • 使用@Autowired注入需要测试的对象
  • 创建测试方法进行测试

3.6使用spring IoC注解的方式实现账户的CRUD

3.6.1Spring程序开发步骤

  • 导入Spring开发的基本坐标
  • 编写Account实体类
  • 编写Dao接口和实现类
  • 编写Service接口和实现类
  • 编写Spring核心配置类JdbcConfig.java和Configuration.java配置类
  • 编写测试类

3.6.2导入Spring开发的基本坐标到pom.xml文件中



    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
        
    

3.6.2编写Account实体类

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;
    }
}

3.6.3编写AccountDao接口和实现类

  • IAccountDao接口
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);
}
  • IAccountDao实现类
@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);
        }
    }
}

3.6.4编写AccountService接口和实现类

  • 编写AccountService接口
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);
}
  • 编写AccountService接口的实现类
@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);
    }
}

3.6.5编写Spring核心配置类JdbcConfig.java和Configuration.java配置类

  • Spring核心配置类JdbcConfig.java
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);
    }
}
  • Spring核心配置类SpringConfiguration.java
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 {
}

3.6.6编写测试类


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);
    }
}

你可能感兴趣的:(Spring)