Java:64-Spring概述以及IOC介绍

Spring概述以及IOC介绍

Spring是什么:
Spring是分层的 Java SE/EE应用 full-stack(全栈式) 轻量级开源框架

在这里插入图片描述

提供了表现层 SpringMVC和持久层 Spring JDBC Template以及 业务层 事务管理等众多的企业级应用技术
还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架
两大核心:以 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核

在这里插入图片描述

对方法增强,实际上就是使用了动态代理,动态代理也使用了一些反射操作,但单纯使用反射,是很难做出动态代理的
反射的操作:一般我们是可以通过反射,获得对应的类的构成,操作类的信息
动态代理:代理类里面有具体实现类的变量,当我们需要操作实现类时,通过代理类来进行对应实现
那么就会发现,我们执行了代理类的方法,实际上也执行了实现类的方法(操作了对应变量)
且代理类的对应方法可以进行扩展增强(使得实现类操作时,出现了其他操作),这就是静态代理
而动态代理需要一些反射来进行,所以说反射的出现,使得很多框架的出现,甚至可以通过反射,自己写出框架
代理其实也类似于回调模式,只是有以下区别
代理模式需要创建接口实现类,并放入代理类中,隔离性更好,扩展性好
回调函数不需要创建接口实现类,编写方便
Spring发展历程:
/*
EJB
 1997 年,IBM提出了EJB 的思想
    1998 年,SUN制定开发标准规范 EJB1.0
    1999 年,EJB1.1 发布
    2001 年,EJB2.0 发布
    2003 年,EJB2.1 发布
    2006 年,EJB3.0 发布
    
Spring
 Rod Johnson( Spring 之父)
 改变Java世界的大师级人物
 
    2002年编著《Expert one on one J2EE design and development》 
   指出了JavaEE和EJB组件框架中的存在的一些主要缺陷;提出普通java类依赖注入更为简单的解
决方案。
   
    2004年编著《Expert one-on-one J2EE Development without EJB》
   阐述了JavaEE开发时不使用EJB的解决方式(Spring 雏形)
   同年4月spring1.0诞生
 2006年10月,发布 Spring2.0
 2009年12月,发布 Spring3.0
 2013年12月,发布 Spring4.0
    2017年9月, 发布最新 Spring5.0 通用版(GA)
*/
一般对于数据库来说,我们设置的参数后,帮我们进行连接操作的是对于驱动的实现类,那么就说明以下耦合度的操作
package com.lagou.test;

import org.junit.Test;

import java.sql.*;

public class JDBCTest {

    @Test
    public void test1() throws SQLException, ClassNotFoundException {
        //依赖关系:程序之间的联系,当其中一个改变或出错,会使得与之依赖的可能也要改变或出错
        //也就是说两者之间有联系
        //如这里报错,使得这个类报错
        //1.注册驱动      存在编译期依赖:耦合重的体现,编译期就会报错,即依赖严重
       // DriverManager.registerDriver(new com.mysql.jdbc.Driver());

        // 编译期不依赖,运行期才依赖,因为编译器这只是字符串,运行时才会进行反射操作
        //少了一些联系
        Class.forName("com.mysql.jdbc.Driver");

        //所以我们将这些联系称作耦合
        //而解耦就是降低联系,且基本不会影响对应的功能

        //2.获取连接
        Connection connection = 
            DriverManager.getConnection("jdbc:mysql:///mybatis_db","root","123456");

        //3.定义sql
        String sql = "select * from user";

        //4.获取预编译对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //5.执行sql,得到结果
        ResultSet resultSet = preparedStatement.executeQuery();

        //6.遍历结果
        while (resultSet.next()){
            System.out.println(resultSet.getString("username"));
        }

        //7.关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();


    }


}

这是一个经典的说明,当然也有其他说明,如下
public class a {

    public void fa(){
        System.out.println("a类的执行");
    }
}

public class b {

    public a a;

    public b(a a){
        this.a = a;
    }
}
public class c {
    public static void main(String[] args) {
        b b = new b(new a());
        b.a.fa();
    }
}

可以发现,b类和a类也是有一定联系的,a类的改变,那么b类的a类变量也会进行改变
当然,也有接口之间的耦合
比如:设计模式的隔离原则,一般的一个接口中,定义了对应接口方法,但这些接口方法,归属不同,如狗和鸟
一个奔跑一个飞行,那么就需要将这个大接口,变成对应的小接口,否则,可以发现大接口中的其他接口
别人实现时,也是可以调用的(明显从意义上不能够调用),这就是一个联系
那么从这些例子,就知道:
耦合:可以理解为程序之间的一种联系
解耦:降低这些联系(要想完全不联系,基本不可以,比如代码和二进制之间,必须有联系)
虽然降低联系,但具体的功能基本也是不变的
框架(依赖)可以说成是封装好的一些程序(变量,方法等等)
Spring优势:
/*
1)方便解耦,简化开发
 Spring就是一个容器,可以将所有对象创建和关系维护交给Spring管理
 什么是耦合度?对象之间的关系,通常说当一个模块(对象)更改时也需要更改其他模块(对象),这就是
耦合,耦合度过高会使代码的维护成本增加。要尽量解耦
2)AOP编程的支持
 Spring提供面向切面编程,方便实现程序进行权限拦截,运行监控等功能。
3)声明式事务的支持
 通过配置完成事务的管理,无需手动编程
4)方便测试,降低JavaEE API的使用
 Spring对Junit4支持,可以使用注解测试
5)方便集成各种优秀框架
 不排除各种优秀的开源框架,内部提供了对各种优秀框架的直接支持
*/
图解:

在这里插入图片描述

Spring体系结构:

在这里插入图片描述

初识IOC:
概述:
控制反转(Inverse Of Control)不是什么技术,而是一种设计思想,它的目的是指导我们设计出更加松耦合的程序
控制:在java中指的是对象的控制权限(创建、销毁)
反转:指的是对象控制权由原来 由开发者在类中手动控制 反转到 由Spring容器控制(即对象的创建由Spring来创建)
举个栗子:
/*
传统方式
 之前我们需要一个userDao实例,需要开发者自己手动创建 new UserDao();
 
IOC方式
 现在我们需要一个userDao实例,直接从spring的IOC容器获得,对象的创建权交给了spring控制

*/
图解:

在这里插入图片描述

可以发现,使用IOC方式,降低了耦合(解耦),不在编译期进行依赖,只在运行时进行依赖(联系)
自定义IOC容器:
介绍:
需求
实现service层与dao层代码解耦合
步骤分析
创建java项目,导入自定义IOC相关坐标(依赖,此依赖是对应包,不是联系)
编写Dao接口和实现类
编写Service接口和实现类
编写测试代码
实现:
创建java项目,导入自定义IOC相关坐标:
<dependencies>
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
    <dependency>
        <!--dom4j是依赖jaxen项目的,所以不能省略-->
        <groupId>jaxen</groupId>
        <artifactId>jaxen</artifactId>
        <version>1.1.6</version>
    </dependency>
    <dependency>
        <!--测试@Test-->
        <groupId>junit</groupId>
        <artifactId>junit</artifactId><version>4.12</version>
    </dependency>
</dependencies

编写Dao接口和实现类:
public interface IUserDao {

    public void save();

}

public class UserDaoImpl implements IUserDao {

    public void save() {
        System.out.println("dao被调用了,保存成功...");
    }
}

编写Service接口和实现类 :
public interface IUserService {

    public void save();


}

public class UserServiceImpl implements IUserService {


    public void save(){
        // 调用dao层方法 传统方式 :存在编译期依赖:耦合重
        IUserDao userDao = new UserDaoImpl();

     
        userDao.save();

    }

}
编写测试代码:
public class SpringTest {

    @Test
    public void test1() throws IllegalAccessException, InstantiationException, ClassNotFoundException 
    {

        // 获取业务层对象
        IUserService userService = new UserServiceImpl();

        // 调用save方法
        userService.save();


    }



}

问题:
当前service对象和dao对象耦合度太高,而且每次new的都是一个新的对象,导致服务器压力过大
解耦合的原则是编译期不依赖,而运行期依赖就行了
我们可以使用反射
public class UserServiceImpl implements IUserService {
    public void save() throws ClassNotFoundException {
        // 调用dao层方法 传统方式 :存在编译期依赖:耦合重
        //IUserDao userDao = new UserDaoImpl();

        //反射
        //T newInstance(),用于创建该Class对象所表示类的新实例,无参构造,没有无参会报错
        IUserDao userDao = (IUserDao)Class.forName("com.lagou.dao.impl.UserDaoImpl").newInstance();
        userDao.save();

    }

}
说明一下:一般配置文件的读取,都会用到对应的xml解析方式
如dom4j方式(其实也是由IO流来进行读取,只是由各种程序操作而已,形成了一种方式)
这上面也是有问题的,如以下图解:

在这里插入图片描述

其中我们自己创建的map集合,可以说是我们自定义的IOC容器(就是自己创建一个类似于IOC容器的方式)
接下来我们根据上图来编写自定义的IOC容器
beans.xml配置文件(xml名称基本可以随便写):
<beans>
    <!--id:标识,class要生成实例的类的全路径-->
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl">

    </bean>

</beans>

<!--注意:这里我们并没有添加约束,也就是说,这个配置文件,是我们自己定义的
那么对应标签也是可以自己定义,具体操作,回顾xml的那个博客

这些标签也是自定义的
-->
核心代码(后面会多次提到):
package com.lagou.utils;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BeanFactory {

    //注意:静态代码的初始化,是有执行顺序的,即这个变量必须在static代码块前面,否则是识别不了的
    //当然,静态方法可以不做顺序要求,因为静态方法先于他们加载,基本上是最优先的
    private static Map<String,Object> iocmap = new HashMap<>();
    // 程序启动时,初始化对象实例,类加载时,正好调用一次
    static {

        //接下来我们使用dom4j包,来读取配置文件,具体操作,可以去看我写的xml博客

        //首先我们先使用类加载器来加载配置文件,获得对应的IO流
        //该方式在我对应博客的数据库连接池的Druid(德鲁伊)方式下使用过
        InputStream resourceAsStream = 
            BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");

        //获得解析xml的对象(dom4j提供)
        SAXReader saxReader = new SAXReader();

        //指定读取的配置文件,也可以传递输入流
        try {
            Document read = saxReader.read(resourceAsStream);

            //使用xpath表达式,定位
            String xpath = "//bean";

            //根据上面表达式,获取所有bean标签
            //在学习spring时,后面会有多个对应标签,也使用类似于这个操作来进行
            List<Element> list = read.selectNodes(xpath);

            //遍历并使用放射创建对应实例,存在map集合(充当IOC容器)中
            for (Element element : list) {
                //attributeValue(…) 获得指定属性名的属性值,...代表有多个重载方法
                String id = element.attributeValue("id");

                //aClass:com.lagou.dao.impl.UserDaoImpl
                String aClass = element.attributeValue("class");
                //不管怎么获取,本质上是使用IO流,找到对应位置
                //再进行判断读取的,而结束标签,就是为了规定IO流的读取区域
                //假如,什么都不操作获得,那么就一直读取到结束标签时
                //就相当于这个标签的读取完毕,然后再次进行后续操作

                //现在可以使用反射,进行对象的创建了
                //大多数框架的编写,都类似于这样的操作
                Object o = Class.forName(aClass).newInstance();
                //不进行强制,因为读取的aClass值是变化的(因为是编写的配置文件)
                //所以为了通用性,就不进行强转了

                //接下来我们将上面的对应数据,存放在map中,充当IOC容器
                iocmap.put(id, o);
            }

//            } catch (DocumentException ex) {
//            ex.printStackTrace();
//        } catch (ClassNotFoundException ex) {
//            ex.printStackTrace();
//        } catch (InstantiationException ex) {
//            ex.printStackTrace();
//        } catch (IllegalAccessException ex) {
//            ex.printStackTrace();
//        }
        }catch(Exception ex){
            ex.printStackTrace();
        }

    }
/*
     public static Object getBean(String beanId){
        Object o = iocmap.get(beanId);
        return o;
    }
    */


}

修改UserServiceImpl实现类 :

public class UserServiceImpl implements IUserService {


    public void save() throws ClassNotFoundException, IllegalAccessException, InstantiationException 
    {
        // 调用dao层方法 传统方式 :存在编译期依赖:耦合重
        //IUserDao userDao = new UserDaoImpl();

        //反射
        //T newInstance(),用于创建该Class对象所表示类的新实例
        // IUserDao userDao = (IUserDao) 
        //Class.forName("com.lagou.dao.impl.UserDaoImpl").newInstance();
        
         //这时,就出现了我们将配置文件配置的类存放好,我们自己来获得对应对象
        //而由于我们的配置文件的对应路径的类,是一个接口,那么只要是实现了这个接口的类,都可以进行获得对象
        //这就将我们的硬编码问题解决了,但是部署好后,我们修改配置文件时,不能随便的修改对应类
        //当然了就算不用配置文件,而用反射也是不能随便修改的,但配置文件也是给了点操作空间,且联系差不多
        //使得配置文件的反射类是这个强转(变量类型)的子类,或者本类,否则报错
        //但注意,不要是接口,因为反射创建接口类时,会报错
        //都可以进行操作,而不报错
        //即扩展性增加了,没有硬编码问题了
        IUserDao userDao = (IUserDao) BeanFactory.getBean("userDao");
        userDao.save();
        
        //这就是将对象交由map(IOC)集合管理

    }

}

知识小结:
/*
其实升级后的BeanFactory就是一个简单的Spring的IOC容器所具备的功能
之前我们需要一个userDao实例,需要开发者自己手动创建 new UserDao();
现在我们需要一个userdao实例,直接从spring的IOC容器获得,对象的创建权交给了spring控制(也就是上面的核心代码)
最终目标:代码解耦合
*/
上面就可以说是一个小的IOC容器了,即可以将IOC容器看成一个map集合,后面会多次用这个解释
注意:配置文件是会识别空格的,所以最好要严谨的写,否则可能就会报错,因为通常情况下,java文件名称是没有空格的
若有空格,需要特殊处理,具体去百度搜索
Spring快速入门:
Spring帮我们封装(编写)好了类似于上面的核心代码,那么我们只需要编写对应配置文件就可以操作了
介绍:
需求:借助spring的IOC实现service层与dao层代码解耦合:
步骤分析:
/*
1. 创建java项目,导入spring开发基本坐标
2. 编写Dao接口和实现类
3. 创建spring核心配置文件
4. 在spring配置文件中配置 UserDaoImpl
5. 使用spring相关API获得Bean实例
*/
实现:
创建java项目,导入spring开发基本坐标:
<dependencies>
    <dependency>
        <!--Spring容器,即IOC容器的使用需要这个,比如ApplicationContext-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

/*
Spring的配置文件我们一般写:applicationContext.xml
Mybatis的配置文件我们一般写:sqlMapConfig.xml
实际上这只是约定,也就是说,他们的名称其实也是可以改变的,只是大多数都约定这样写了
所以在你看到自己接手的项目时,有上面的对应配置文件,那么一般就是对应框架的对应配置文件
*/
package com.lagou.dao;

/**
 *
 */
public interface IUserDao {

    public void save();
}

package com.lagou.dao.impl;

import com.lagou.dao.IUserDao;

/**
 *
 */
public class UserDaoImpl implements IUserDao {
    @Override
    public void save() {
        System.out.println("dao被调用了");
    }
}

package com.lagou.test;

import com.lagou.dao.IUserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 *
 */
public class SpringTest {

    @Test
    public void text1(){

        //通过查找
        //可以知道ClassPathXmlApplicationContext中继承的类里有实现了继承了ApplicationContext的方法
        //获取到了spring上下文对象.借助上下文对象可以获取到IOC容器中的bean对象
        //即这个上下文对象,封装了核心代码,其中可以通过构造方法,进行配置文件的读取
        //内部进行了解析,将配置文件中的信息,进行反射实例化封装
        //即加载配置文件后的同时,就创建了bean对象存到容器中
        ApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        

        //由于创建上面的对象,就帮你封装好了,那么,我们可以通过上下文对象,获取到IOC容器中的bean对象
        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao"); 
        //返回的也是Object类型,但要注意地址的指向
        //map集合的key
        //也要注意:指向地址不要出现问题,否则会报错的,如a类指向创建b类的地址,子类指向创建父类的地址

        //调用方法
        userDao.save();


    }
}

很明显:创建上下文的这个对象时,该对象的内部就帮我们执行了类似于核心代码的所有操作
使得后面我们可以使用对应方法来获得IOC容器反射出来的对象
这个测试类,就可以当作service层的操作

在这里插入图片描述

<?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">
    <!--上面是约束,以及一些命名空间-->

    <!--在spring配置文件里配置UserDaoImpl
    id:唯一标识,注意不要重复,就如map集合的key不会重复一样
    若重复,则报错,而不是进行map的修改,因为Spring在进行读取配置文件时,有对应判断,重复了就会报错
    class:实现类的全路径
    -->
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
    <!--注意:不要写接口,因为反射创建接口对象时,会报错-->
</beans>

知识小结:
Spring的开发步骤:
/*
1. 导入坐标
2. 创建Bean
3. 创建applicationContext.xml
4. 在配置文件中进行Bean配置
5. 创建ApplicationContext对象,执行getBean
*/
Spring相关API:
API继承体系介绍:
Spring的API体系异常庞大,我们现在只关注两个BeanFactory和ApplicationContext

在这里插入图片描述

从上到下(上面是最底层的):
紫色:接口
浅绿色:抽象类
深绿色:实现类
BeanFactory:
从图看是底层接口
BeanFactory是 IOC 容器的核心接口,它定义了IOC的基本功能
特点:在第一次调用getBean()方法时,创建指定对象的实例
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
@Test
    public void text2(){
        //XmlBeanFactory的父类(接口也算一个类,特殊的抽象类)中有BeanFactory
        //核心接口:不会创建bean对象存到容器中的
        BeanFactory xmlBeanFactory = new XmlBeanFactory(new 
                                                        ClassPathResource("applicationContext.xml"));

        //getBean的时候,才真正创建bean对象,并存到容器中
        IUserDao userDao = (IUserDao) xmlBeanFactory.getBean("userDao");

        //那么从上面可知
        //ClassPathXmlApplicationContext创建的对象,执行了所有的核心代码操作
        //XmlBeanFactory创建的对象,只执行了读取配置文件的操作,并解析
        //但没有执行核心代码中类似于获得对应节点属性的值的操作,即attributeValue方法
        //而是在执行对应getBean时,才进行获得对应节点属性的值的操作
        //并通过反射实例化对象,将内存和对应id放入容器
        //然后根据参数获得该对象并返回,其中执行getBean时,会判断是否有对应参数的key值
        //没有,则获得对应节点来进行操作,有,直接取出对应对象并返回
        
     

        userDao.save();

    }
从上可以得出(可能会有改变,所以具体只看实现类,而不看下面的说明):
/*
ApplicationContext:基本使用这个接口的对应实现类时,创建对应对象就会创建bean对象
BeanFactory:基本使用这个接口的对应实现类时,且一般不是ApplicationContext接口的实现类,那么执行getBean方法才创建bean对象(相当于默认操作延迟加载)
*/
ApplicationContext:
代表应用上下文对象,可以获得spring中IOC容器的Bean对象
特点:在spring容器启动时,加载并创建所有对象的实例
常用实现类:
/*
1. ClassPathXmlApplicationContext 
      它是从类的根路径下加载配置文件 推荐使用这种,也就是说给出磁盘路径,基本就会报错
      相当于默认在路径前面加上项目路径/
      
2. FileSystemXmlApplicationContext 
      它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置,必须指定对应盘,否则报错
      /和\\都可以,基本所有的指定路径都是可以的
      因为windows的文件路径,就可以是/和\(程序里需要\\进行转义),但windows的/和\最后都会变成\
      网页路径的/和\则就是都变成/,且可以多写
      而windows不可以多写
      主要是找不到,因为文件名不可以写\和/,还有其他符号也不可以写
      如英文的":",*,?,",<,>,|等等,否则提示报错(并不会加上这些)
      
3. AnnotationConfigApplicationContext 后面会说明
      当使用注解配置容器对象时,需要使用此类来创建 spring 容器,它用来读取注解
*/
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
常用方法:
/*
1. Object getBean(String name);
 根据Bean的id从容器中获得Bean实例,返回是Object,通常需要强转
2. <T> T getBean(Class<T> requiredType);
 根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错
3. <T> T getBean(String name,Class<T> requiredType);
 根据Bean的id和类型获得Bean实例,解决容器中相同类型Bean有多个情况
 补充:
 配置bean的时候,可以不设置id,也可以不设置name,Spring会默认的使用类的全限定名作为bean的唯一标识符
 如果配置bean时,只设置了id属性,那么id就是作为bean的唯一标识符,且id在spring容器中必须唯一
 如果配置bean时,没有设置id属性,只设置了name属性,那么name属性就是作为bean的唯一标识符,且name的值在spring容器中必须唯一
 如果同时设置了id和name属性,那么id设置的即为唯一标识符,name设置的是别名
 如果同时设置了id和name属性,并且id和name的值相同,spring容器会自动检测并消除冲突,这个bean就只会有标识符,而没有别名
 如果没有设置id,而name属性设置了多个值,那么name的第一个值(没有id的,有id会认为是别名)会被用作标识符,其他的值被视为别名,如果设置了id,那么name的所有值都是别名
 具体可以看看这个博客:https://blog.csdn.net/SEVENY_/article/details/88029611
  默认对应的别名存放在数组(是String[])中,如果没有别名,那么数组长度为0(好像一般只有一个长度),自己测试就知道了,不能在一个标签中设置两个name,否则报错(不同的标签自然可以)
*/
package com.lagou.test;

import com.lagou.dao.IUserDao;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;

/**
 *
 */
public class SpringTest {

    @Test
    public void text1(){


        ApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");

        //可以凭空创建对象
        /*
        new FileSystemXmlApplicationContext(
        "D:\\sss\\sss\\Desktop\\sss\\sss\\sss\\
        spring_quicstart\\src\\main\\resources\\applicationContext.xml");
		*/
        //根据beanid,即key,在容器中找对应的bean对象
        //IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        //根据类型在容器中进行查询
        //也可以是UserDaoImpl.class,也当作返回类型
        //只能是对应bean对象是指定参数实例的子类或者本类且只有一个的,才可
        //多个(这个就是下面的解释)会报错,没找到也会报错,bean对象是指定参数实例的父类也会报错
        //IUserDao userDao = classPathXmlApplicationContext.getBean(IUserDao.class);

        //为了防止上面出现多个报错,可以加上key,但只是解决了出现多个报错的问题,其他问题还在
        IUserDao userDao = classPathXmlApplicationContext.getBean("userDao", IUserDao.class);
        
        //所以通常使用根据beanid,即key的方式,来找对应bean对象
        //调用方法
        userDao.save();

//可以发现多个类的错误基本是在getBean出现的(虽然可以避免),是否多个对配置文件没有什么关系,因为map集合中有些map集合(具体可以百度)是可以存放相同的id的,然而spring是单纯的map,所以不能存放相同的id,所以在解析时,即
        /*
         ApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
            时,若存在相同id,那么会报错,虽然类实例是可以多个
        */




    }
}

知识小结:
/*
ApplicationContext app = new ClasspathXmlApplicationContext("xml文件");
 app.getBean("id");
 app.getBean(Class);

*/
Spring配置文件:
Bean标签基本配置:
/*
<bean id="" class=""></bean>
用于配置对象交由Spring来创建。
基本属性:
id:Bean实例在Spring容器中的唯一标识
class:Bean的全限定名(因为反射)
默认情况下它调用的是类中的 无参构造函数,如果没有无参构造函数则不能创建成功,那么通常会在配置文件里报错的
因为反射中的Class的newInstance()方法就是通过无参来创建对应的对象的
*/
Bean标签范围配置:
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" scope="singleton"></bean>
<!--或者下面的配置-->
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" scope="prototype"></bean>
scope属性指对象的作用范围,取值如下:

在这里插入图片描述

singleton:单例的(类似于单例模式),那么只有一个对应对象,也就是说
我们根据id取值时,就是使用一个对象,会将对应的对象,放入map集合中
prototype:多例的,那么会获得多个对象,也就是说,当你获得一个对象时,就会创建一个新的对象并返回
也就是说,不会放入集合中
每次请求都会去创建对象并返回,虽然调用对应方法时
不会去map集合找,但也会根据参数和id来进行判断是否一致,不一致还是会报错的
下面的request和session不止会存在容器里,还会存在对应域中
当然了,只有在服务器里才可以设置,否则是会提示没有对应属性值的
测试:
/*
测试scope="singleton"
 */
    @Test
    public void text3(){

        ApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");

        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        IUserDao userDao1 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        System.out.println(userDao); //com.lagou.dao.impl.UserDaoImpl@25fb8912

        System.out.println(userDao1); //com.lagou.dao.impl.UserDaoImpl@25fb8912


    }

    /*
测试scope="prototype"
 */
    @Test
    public void text4(){

        ApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");

        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        IUserDao userDao1 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        System.out.println(userDao); //com.lagou.dao.impl.UserDaoImpl@1e04fa0a

        System.out.println(userDao1); //com.lagou.dao.impl.UserDaoImpl@1af2d44a

    }
/*
1. 当scope的取值为singleton时
  Bean的实例化个数:1个
      Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
      Bean的生命周期:
        对象创建:当应用加载,创建容器时,对象就被创建了
        对象运行:只要容器在,对象一直活着
        对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2. 当scope的取值为prototype时
  Bean的实例化个数:多个
      Bean的实例化时机:当调用getBean()方法时实例化Bean
      Bean的生命周期:
        对象创建:当使用对象时,创建新的对象实例
        对象运行:只要对象在使用中,就一直活着
        对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了(这个回收,在于引用为null或者后面的代码中没有该引用处理)

一般来说,后面的@Autowired只是操作map里面的对象,无论你是单例还是多例,最终只会存在一个(多例一般多个),但是为什么多例也会只存在一个呢,注意看版本的原因,因为@Autowired也是操作getBean的得到的,所以多例也是不同的,但是为什么他也可以是同一个呢:
答:是因为可以当操作@Autowired时,多例或给创建的对象来个关联关系,在以后继续操作@Autowired时,会直接获取对应关系的对象,所以@Autowired也可以只有一个
这里大多数都是需要看源码的,但是我们也不用刻意的去记住,因为别人修改一下,你记住的就是错的了,且也只是知道与否,与实际能力并没有重要关系,所以了解即可,我们只要记住多例一般都是多个的而已,可以认为,当是多例时,getBean()方法并不是从map中获取(可以有其他变量存在来判断多例,从而考虑其他判断,就如变量保留了代表多例的值),最后从而得到全限定名,而创建对象,只是可以操作成一个对象而已(但一般也不是放在map中,就算可以,那需要考虑一直覆盖,一般不会的)

*/
上面两个就代表了实例方式不同,而ApplicationContext和BeanFactory代表操作不同(按照上面的来进行)
所以BeanFactory最后的获得对象,是按照上面实例方式的获得
也就是说当scope的取值为singleton时,ApplicationContext全部操作,BeanFactory获得操作(map)
当scope的取值为prototype时,ApplicationContext获得操作(无map),BeanFactory获得操作(无map)
也就是说,使用了多例的bean,不会在全部操作中进行放入map集合
而是再调用对应方法时,直接获取新创建的对象
Bean生命周期配置:
/*
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy">
</bean>
init-method:指定类中的初始化方法名称
destroy-method:指定类中销毁方法名称
*/
测试:
package com.lagou.dao.impl;

import com.lagou.dao.IUserDao;

/**
 *
 */
public class UserDaoImpl implements IUserDao {

    public void init(){
        System.out.println("初始化");
    }
    public void destroy(){
        System.out.println("销毁");
    }

    @Override
    public void save() {
        System.out.println("dao被调用了");
    }
}

    /*
测试init-method="init" destroy-method="destroy"
*/
    @Test
    public void text5(){

        ClassPathXmlApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");

        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");


        System.out.println(userDao);

        classPathXmlApplicationContext.close();

        //注意:这里需要将ApplicationContext改成classPathXmlApplicationContext
        //因为只有classPathXmlApplicationContext才有对应close()方法
        //若不调用close()方法,按道理来说,程序执行完,也会进行销毁,那么对应销毁的方法内容应该会打印出来
        //但实际上并没有打印出来,是因为必须手动销毁(一般情况下注解@Autowired会判断程序的结束来提取执行方法,从而出现打印信息)
        //实际上就是容器自身销毁才会操作对应的方法(而虚拟机回收(销毁)操作是不会这样的,所以没有打印出来),实际上我们设置的生命周期方法是在真的生命周期中的补充而已
        //并不是不设置就没有

        //一般来说多例没有销毁,因为他不算容器里面来销毁的,所以说如果是多例,那么对应的close并不会使得执行对应的方法(就如变量保留了代表多例的值)


    }
Bean实例化三种方式:
无参构造方法实例化
工厂静态方法实例化
工厂普通方法实例化
无参构造方法实例化:
它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败
即报错(idea通常也会有提示报红)
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
工厂静态方法实例化 :
应用场景
依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象
如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器
以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得
我们可以通过如下操作,进行配置
public class StaticFactoryBean {
    
    public static IUserDao createUserDao(){    
   return new UserDaoImpl();
        //将这个创建对象,交给IOC容器创建,以后我们可以直接去IOC容器里进行获取
        //其实反射由于是需要很多的解释(底层操作)
        //那么性能也是会降低的(但现在通常性能都非常高,所有有些是可以忽略)
        
        //如果是没有使用配置文件的反射,基本上降低耦合度的只有编译器的耦合(联系)
        
        //所以并没有降低很多的耦合度,且与new对象一样,也有硬编码
        //而配置文件加反射,耦合度就降低许多
        //来个硬编码和不是硬编码的对比:100个类,每个类里面都有一个new a()对象
        //如果使用new对象或者没有配置文件的反射
        //那么就要创建100次new a()对象,突然有一天,我们要将a对象改成b对象
        //那么我们需要在100个类里面进行修改,如果是1000个,10000个,很明显是非常痛苦的
        //而如果使用配置文件加反射(配置文件基本只能与反射来实现创建对象)
        //那么我们在new a()的这个操作时,进行读取配置文件,来实现创建对象(通常是存放在一个集合中的)
        //因为不可能全部在对应位置写上读取配置文件和反射的操作,来进行获得对应对象,那样太麻烦,且浪费性能
        //所以我们需要集合来进行存放,IOC容器基本就是这样操作的
        //那么如果要改成b对象(使用配置文件加反射的话,对应获得,就是使用对应方法,来进行获得对象)
        //但这里也有对应获得限制(变量必须是对应对象的父类或者本类),后面会说明
        //我们只需要修改一次配置文件即可,且可以进行单例和多例的切换操作(IOC),这是其中一个对比
        
        //其他对比:部署时,硬编码问题需要重新部署
        //而使用配置文件的地方则基本不需要(因为有些时候,需要修改配置文件的读取位置,或者大修改)
        //修改对应配置文件即可,注意:是使用配置文件的地方
        //但是要注意,在没有自动重新读取xml的,也是需要部署的,因为只会读取一次,这里是认为自动的重新读取,我们也以这个自动的重新读取为例
        
        //最重要的对比:我们可以在配置文件里随意的编写对应类的全路径,那么对应IOC容器就有对应的类来给我们使用
        //相当于以后的创建对象,都可以在配置文件里写了,而不用在类里面写了
        //且可以根据反射的操作进行配置文件的相应编写,如设置值(后面的依赖注入)等等
        
        //即修改问题解决,部署问题解决,新建对象也可以解决(其实也算是一个修改问题)
        //因为修改问题主要导致配置文件的编写,即新建
        
        //上面是硬编码的缺点,但性能高,非硬编码缺点是性能低(底层操作多),但效率高
        //对于性能,现在我们基本不会到达拼性能的地步,所以还是以效率为主
        
        //对于耦合度的对比:耦合度基本可以说是为了好的扩展,上面就可以说是一种解耦
        //即100个类与100个a对象的联系,全部放在了配置文件一个全路径名的联系,有100耦合度变成了1耦合度
        
       
        //再次举个例子:100个类里面,当a类方法需要对b类,c类,d类起作用时
        //使用配置文件加反射创建a类对象
        //那么a类就与b类,c类,d类产生了联系,那么b类,c类,d类使用a类对象时,必然要手动获得
        //这时并没有出现很大问题,因为与对象并没有太大关系,与获得有关,但我们也要进行思考
        //如何可以像上面的修改配置文件一样,修改某些位置,会帮我们自动获得对象呢
        //后面的依赖注入实现了让b类,c类,d类获得a类对象(实际上需要对应变量,但获得操作不用我们操作了)
        
        //而要实现修改方法内容,反射基本上不能凭空修改,基本只能获得结构
        //而不能进行修改(可能需要特定技术,可以百度找)
        //所以有些情况,反射也是做不到的,如修改方法体内容
        
        //可以看出,使用反射,主要的为了方便,以及好修改,即好维护
   }
}

<bean id="userDao" class="com.lagou.factory.StaticFactoryBean" factory-method="createUserDao"></bean>
<!--这样,我们在读取配置文件时,会根据factory-method属性,来进行相应的操作
这里会将反射出来的Class类,使用静态方式,调用createUserDao方法,如果不是静态,就会报错,如普通方法
将他的返回值放入IOC容器里,而不是反射出来对应的对象
既然调用了createUserDao方法,那么里面的其他结果也是会执行的(如打印),只是返回值当作map集合的value而已
-->
工厂普通方法实例化 :
应用场景
依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象,如果我们频繁使用B对象
此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得
与上面的工厂静态方法实例化类似,只是不是静态的而已
public class DynamicFactoryBean {  
    
 public IUserDao createUserDao(){        
 return new UserDaoImpl(); 
 }
}

  <bean id="dynamicFactoryBean" class="com.lagou.factory.DynamicFactoryBean" ></bean>
    <!--使用factory-bean属性,指定对应id的class反射出来时,调用的对应方法为普通方法(静态方法就会报错)
    并获得普通方法的返回值放入IOC容器里
    -->
    <bean id="userDao" factory-bean="dynamicFactoryBean" factory-method="createUserDao"></bean>
    <!--factory-method只是指定方法名称,当没有factory-bean时,默认操作静态,否则操作普通方法,且对应的factory-method值的方法名称要完全对应,否则自然报错(没有什么忽略大小写哦)-->
其实第二个bean标签是可以写在第一个前面的,即可以知道,Spring读取配置文件的方式
基本是全部读取(一个对象),然后根据对象,操作信息依次放入或者将对应信息(后面的解释多以这种形式)
依次放入(一般是拿出进行操作,在后面的"认为是map之后的操作"有具体说明)map集合(其中也有其他操作,就与Mybatis的对应MappedStatement对象类似)
也就相当于一个bean标签可以看成就是一个对象
以此来说,那么我们将factory-bean=“dynamicFactoryBean” factory-method="createUserDao"写在第一个标签里
那么会操作不了factory-bean="dynamicFactoryBean"的(因为只能操作其他对象的,不操作本对象)
即factory-method="createUserDao"会显示找不到对应静态方法(因为只有普通方法)
所以由于jar包的内容,基本上与工厂静态方法实例化和工厂普通方法实例化的类的操作类似,即返回对应类的对象
所以也就通常用于jar包的类
我们自己写的类,一般就会使用无参构造方法实例化,因为我们通常不会这样操作
Bean依赖注入概述:
依赖注入 DI(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况
IOC 解耦只是降低他们的依赖关系,但不会消除
例如:业务层仍会调用持久层的方法
那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了
简单的说,就是通过框架把持久层对象传入业务层,而不用我们自己去获取
测试说明:

    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
    <!--配置UserService-->
    <bean id="UserService" class="com.lagou.service.impl.UserServiceImpl"></bean>
package com.lagou.service;

/**
 *
 */
public interface IUserService {

    public void save();
}

package com.lagou.service.impl;

import com.lagou.dao.IUserDao;
import com.lagou.service.IUserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 *
 */
public class UserServiceImpl implements IUserService {
    @Override
    public void save() {

        //调用dao层的save方法
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");
        userDao.save();
		
        //可以看到,虽然使用了配置文件加上反射来操作,但是userDao是我们手动去获取的,即联系还是有点
        //虽然我们不能完全的消除联系,但是这样的联系还是可以消除的
        //我们可以通过某种方式,使得userDao不用我们手动去获取
        //看后面的知识


    }
}

  @Test
    public void text6(){

        ClassPathXmlApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        IUserService userService = (IUserService) 
            classPathXmlApplicationContext.getBean("UserService");
        userService.save();



    }

为了解决上述情况,我们有如下方式
Bean依赖注入方式:
构造方法(有参,不设置默认无参):
在UserServiceImpl中创建有参构造:
 <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
    <!--配置UserService-->
    <bean id="UserService" class="com.lagou.service.impl.UserServiceImpl">
        <!--当看到constructor-arg标签后,会认为采用有参构造
        index:指定构造参数的索引,从0开始,即第一个参数,从左到右
        type:指定构造参数的类型(可以不写,就像Mybatis的parameterType类似),只是用来好维护的,最终还是按照
ref的类型来
        但是也不能是错误的类型,与Mybatis不同,编译时就会检查,所以不符合的就会报错
        ref:指向index的位置的参数是上面,即这里获得的是UserDaoImpl对象
        理解:当读取配置文件时,进行全部读取,然后根据节点对象,来将对应信息放入IOC容器里
      在读取时,这里的对象中,会被找到constructor-arg标签(也可以看成对象),根据对象属性ref获得对应对象
        即获得UserDaoImpl对象,那么就使用有参构造
通过反射将对应信息判断出来
如下面index的位置,或者类型,类型可以不写,但可以确定变量属性,降低自己判断ref的类型错误
最后将UserDaoImpl对象传入,最后使用有参构造创建UserServiceImpl对象最后放入map集合中的value,key自然是id
所以被引用时,是有参对象
        -->
        <!--也就是说,这个标签使得原来对象进行赋值了-->
     <!--<constructor-arg index="0" type="com.lagou.dao.IUserDao" ref="userDao"></constructor-arg>-->
        <!--我们可以不通过反射来确定位置和类型,而直接确定参数名称
注意:名称必须一致,因为反射是很严谨的(实际上这里并没有进行拼接操作)-->
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
        
        <!--这两个只能使用一个,同时存在会报错-->
    </bean>

<!--若对应的参数是三个,那么你必须三个(写三个constructor-arg标签),多了或者少了都会报错
index不能相同或者超过,否则报错
-->

package com.lagou.service.impl;

import com.lagou.dao.IUserDao;
import com.lagou.service.IUserService;

/**
 *
 */
public class UserServiceImpl implements IUserService {

    private IUserDao userDao; //需要提取定义好变量,但获取对象,由配置文件来获取

    public UserServiceImpl(IUserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {

        //调用dao层的save方法
     
        userDao.save();
        //由于上面被赋值,那么这里就不用自己获取对应对象了
        //也就解决了前面留下的问题,手动获取问题



    }
}

  @Test
    public void text6(){

        ClassPathXmlApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        IUserService userService = (IUserService) 
            classPathXmlApplicationContext.getBean("UserService");
        userService.save();



    }

}
这就是反射的功能强大之处,基本上可以对构造信息进行操作,如何操作,那就看你的程序是怎么操作的
set方法:
package com.lagou.service.impl;

import com.lagou.dao.IUserDao;
import com.lagou.service.IUserService;

/**
 *
 */
public class UserServiceImpl implements IUserService {

    private IUserDao userDao;

    //注意:范围,如这里写上public,如果不写public的话,操作不到这个方法
    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {

        //调用dao层的save方法

        userDao.save();



    }
}

 @Test
    public void text6(){

        ClassPathXmlApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        IUserService userService = (IUserService) 
            classPathXmlApplicationContext.getBean("UserService");
        userService.save();



    }
<?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">
    
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
<bean id="UserService" class="com.lagou.service.impl.UserServiceImpl">
        <!--注意:现在我来说明一下,前面遇到的set和get对应名称
		为什么有些可以大小写忽略,有些可以是对应的小写
        主要是因为反射的拼接,我们知道反射中有个Method类,传递对应字符串当参数,可以执行对应方法
        而由于是字符串,那么就会有拼接的可能
		一般的我们会取出对应方法名,将对应的set和get后面的名称与自己写的名称做比较
        这个比较就是主要的作用
        可能是忽略大小写比较,或者将不同的进行小写比较
		即Us(原来的),与us(自己写的),那么U与u不同,所以U小写比较
        或者是首字母不同小写比较等等,这使得我们写名称时,一般会出现这些情况,只有满足,那么就会调用对应的方法
        而这里是首字母不同小写比较,所以name的首字母是可以小写的(反过来也是一样,即首字母忽略大小写)
        经过测试,其他必须相同,而首字母忽略大小写
		若对应的set是USerDao,而这里是userDao,那么会报错
        因为S不会进行小写比较,而uSerDao不会(会报提示错误,实际上运行时不会错)
        因为首字母不同小写比较后,就是正确的

        这就是前面我们说过的名称底层原理
        -->
    <!--调用方法之前,也会检查ref或者value的类型是否一致的,不一致会报错-->
            <property name="userDao" ref="userDao"></property>
    <!--这里还有个value,指定将字符串当作参数传递,若通过反射看到对应方法类型是整型
会转化为整型,但要注意不要有除了整型的其他数,如符号或字母,否则报错-->
    </bean>
</beans>

<!--实际上无论他是否操作了对于的忽略大小写,在java类中只需要我们按照命名规范编写即可,就不会出现问题,因为框架的编写基本都是建立在命名规范下的,所以并不需要特意去记住,只需要了解即可,当然,java类虽然规范了,但是如果有,比如sql(mybatis),不怎么规范,可能也会出现问题,比如#{id}中写成Id,那么就会报错,因为按照规范在java类中是id,而不是Id,他必须对方也是Id才可,具体自己测试,所以只要在对应操作中的双方都规范,那么基本不会出错-->

上面两个方式,set方式更加常用
P命名空间注入:
P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下
首先,需要引入P命名空间:
<!--
xmlns:p="http://www.springframework.org/schema/p"
-->
其次,需要修改注入方式:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
    
     <bean id="UserService" class="com.lagou.service.impl.UserServiceImpl" p:userDao-ref="userDao">
         
         <!--            <property name="userDao" ref="userDao"></property>-->
    </bean>
    
    <!--
p:userDao-ref=userDao:分开写就是name="userDao"对应p:后面的,所以也是有首字母不同小写比较的操作
-ref=userDao,对应ref="userDao"

实际上使用property被识别后就是根据当前bean反射出来的对象再次进行操作,Method可以帮这个对象进行调用方法
即使用Method的invoke方法来调用参数对象的对应方法
可以加对应参数,即加上了userDao对象,名称判断也是这样调用的
最后存放在map集合中(大多数情况下,是map先放,然后取出进行操作,当然,无论哪种方式都是一种实现,这里考虑不是构造,认为是map之后的操作,大多数的这样的说明都是如此的),key自然为UserService
实际上在创建实例后就会放入map,那么按照这样的说明对应的操作是在map后还是前就非常明了了,比如构造必然在前面,而依赖注入必然在后面(实例没有出来我怎么操作,这里就有两个考虑,在将实例放入map之前可以操作注入,但是源码中是先放入然后操作的,这里为了保证分工,但无论哪种方式都可,具体看Spring版本了(通常是先注入,后放入map),这里我们认为是map之前,当然,就算后面我说成是map之后也是可以的,因为最终都是操作map,或者说最终都是map中保留了对应的实例)

而p:userDao-ref="userDao"被识别后,将-进行分开,前面的userDao当成name来操作
ref="userDao"则按照自己来操作
但无论使那种方式,都是赋值给对应变量,来进行反射罢了(因为将标签看成对象)
-->
</beans>
所以说p命名空间注入实际上也就是set方式的注入
Bean依赖注入的数据类型 :
依赖注入,一般只能注入一个参数,所以我们通常写上多个property标签,来进行依赖注入
因为Spring是符合对应set方法的规则的,对应set方法一般就是一个参数,如果你非要写两个参数,那不用说,就会报错
打印出不匹配对应规则的错误
上面操作,都是注入Bean对象,除了对象的引用可以注入,普通数据类型和集合都可以在容器中进行注入
注入数据的三种数据类型
普通数据类型
引用数据类型
集合数据类型
其中引用数据类型,此处就不再说明(赘述)了,之前的操作都是对UserDao对象的引用进行注入的
下面将以set方法注入为例,演示普通数据类型和集合数据类型的注入
注入普通数据类型(注意修改前面代码):
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl">
        <!--ref是引用数据类型,若是其他类型,就报错,如a(对应值) instanceof Object判断为true
        value是普通数据类型,若是其他类型,就报错,如a(对应值) instanceof Object判断为false(我是你这个类,或者你的子类吗)
        -->
        <property name="username" value="哈哈哈"></property>
        <property name="age" value="18"></property>
    </bean>
<bean id="UserService" class="com.lagou.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>
package com.lagou.dao.impl;

import com.lagou.dao.IUserDao;

/**
 *
 */
public class UserDaoImpl implements IUserDao {

    private String username;

    private Integer age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }


    @Override
    public void save() {
        System.out.println(username);
        System.out.println(age);

        System.out.println("dao被调用了");
    }
}

注入集合数据类型 :
List集合注入,Set集合注入 ,Array数组注入 ,Map集合注入,Properties配置注入:
package com.lagou.dao.impl;

import com.lagou.dao.IUserDao;

import java.util.*;

/**
 *
 */
public class UserDaoImpl implements IUserDao {
    private String username;

    private Integer age;

    private List<Object> list;
    private Set<Object> set;
    private Object[] array;
    private Map<String,Object> map;
    private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    public void setArray(Object[] array) {
        this.array = array;
    }

    public void setSet(Set<Object> set) {
        this.set = set;
    }

    public void setList(List<Object> list) {
        this.list = list;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public void save() {
        System.out.println(list);
        System.out.println(set);
        System.out.println(Arrays.toString(array));
        System.out.println(map);
        System.out.println(properties);
        System.out.println(age);
        System.out.println(username);
        System.out.println("dao被调用了");
    }
    
    /*
    执行结果:
    [aaa, User{username='哈哈好', age=20}]
	[bbb, User{username='哈哈好', age=20}]
	[ccc, User{username='哈哈好', age=20}]
	{k1=ddd, k2=User{username='哈哈好', age=20}}
	{k1=v1, k2=v2, k3=v3}
	18
	哈哈哈
	dao被调用了
    */

}

package com.lagou.domain;

/**
 *
 */
public class User {

    private String username;

    private Integer age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

package com.lagou.service.impl;

import com.lagou.dao.IUserDao;
import com.lagou.service.IUserService;

/**
 *
 */
public class UserServiceImpl implements IUserService {

    private IUserDao userDao;
    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;

    }

    @Override
    public void save() {
        //调用dao层的save方法
        userDao.save();
    }
}

<bean id="user" class="com.lagou.domain.User">
    <property name="username" value="哈哈好"></property>
    <property name="age" value="20"></property>
</bean>
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl">
        <property name="username" value="哈哈哈"></property>
        <property name="age" value="18"></property>

        <!--进行集合数据类型的注入,若使用value和ref的话,默认添加到第一个位置
        有泛型的话,会根据泛型来判断对应注入的类型是否匹配
        即 a(对应值) instanceof Integer(Object)
        但是这样只能添加一个,而要想添加多个
        需要添加对应标签,添加后,value和ref就不可以添加了,约束的作用-->
        <property name="list">
            <!--进行list集合数据类型的注入-->
         <list>
                <!--从上到下,依次为对应下标,即aaa就是list集合的第一个元素-->
                <!--对应标签,指定对应类容,即ref不能有内容,只能进行指定,否则报约束的错误-->
                <!--那么我们反射进行设置值时,会根据这里内容,找到对应是否有list标签,来进行对应list的添加值
                value直接添加对应内容里,ref根据bean来获得对应对象,再添加到对应内容里
                这里说明一下,当对应程序执行完(都放入map集合中),那么bean对象基本都销毁
                -->
                <value>aaa</value>
                <ref bean="user"></ref>
            </list>

        </property>
        <!--进行set集合数据类型的注入-->
        <property name="set">
            <!--其实用list标签也是可以的,因为会判断set的类型,从而进行转化,然后执行对应set方法-->
            <set>
            <value>bbb</value>
            <ref bean="user"></ref>
            </set>
        </property>
        <!--进行array数组数据类型的注入-->
        <property name="array">
            <!--通过实验,基本上所有集合和数组都可以进行互相转化
		那么就可以知道,array标签也是可以是list和set等标签-->
            <array>
                <value>ccc</value>
                <ref bean="user"></ref> <!--需要符合数组类型,否则添加或者转化时就会出错-->
            </array>
        </property>
        <!--进行map集合数据类型的注入-->
        <property name="map">
            <map>
                <entry key="k1" value="ddd"></entry>
                <entry key="k2" value-ref="user"></entry>
                <!--对应key和value加上-ref,那么就是知道一个对象,没找到的话,自然会报错-->
                <!--map集合基本上是不能与其他集合和数组进行转化的,所以很明显的将对应标签进行替换后会报错-->
            </map>
        </property>
        <!--进行properties集合数据类型的注入-->
        <property name="properties">
            <props>
                <!--也是key,value形式,value的值是对应标签内容,且基本上不能使用对象的形式-->
                <prop key="k1">v1</prop>
                <prop key="k2">v2</prop>
                <prop key="k3">v3</prop>
            </props>
        </property>
    </bean>
<bean id="UserService" class="com.lagou.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>
</beans>

<!--一般情况下,相同结构的可以替换,比如map的操作可以放到properties集合对应的操作中,具体自己可以测试,但是最好不要,因为可能版本的不同,会优化掉的-->

@Test
    public void text6(){

        ClassPathXmlApplicationContext classPathXmlApplicationContext = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        IUserService userService = (IUserService) 
            classPathXmlApplicationContext.getBean("UserService");
        userService.save();



    }
package com.lagou.dao;

/**
 *
 */
public interface IUserDao{

    public void save();
}

package com.lagou.service;

/**
 *
 */
public interface IUserService {


    public void save();
}

配置文件模块化:
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大
所以,可以将部分配置拆解到其他配置文件中,也就是所谓的配置文件模块化
并列的多个配置文件:
ApplicationContext act =
   new 
ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");
//可以加载多个,反正也是形成对应对象
主从配置文件:
<!--直接的写,就是相对路径,本目录下-->
<import resource="applicationContext-xxx.xml"/>
<!--或者是如下的操作-->
  <!--借助import标签标签,进行再次根据路径读取-->
    <!--classpath:意思是在项目路径下(也就是在编译后的classes的文件),可以在左边目录下的target里看到
之所以可以说是项目路径,因为项目路径下的文件,最终会被编译到classes的
所以也可以说是项目路径(src和resources等资源对应文件放在项目路径,他们本身只是用来存放,被忽略的)
    一般idea生成的是out文件,而maven生成的是target文件,若maven和服务器一起操作(也就是web操作)
    那么服务器基本会帮maven再次生成out文件
    你在classes文件的配置文件里看时,都会出现红色错误,实际上不用管他,只有在运行时,才会进行真正的读取到
    实际上就是他那里基本不受idea的正常检查影响,导致不会进行编译期的正确识别
    -->
    <import resource="classpath:applicationContext-user.xml"></import>
<!--也可以加上classpath*:基本代表所有目录,因为classpath只会在classes目录下,不会深入的去其他目录
可能会在配置文件中看到报红,其实是idea的检查还不够完美,因为不可能所有的都会被解释
所有当你设置时不管他,且运行时是基本没有错误的
-->

注意:
同一个xml中不能出现相同名称的bean,如果出现会报错
多个xml如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean(一般来说导入的通常相当于在对应位置的顺序,所以如果你的标签在导入的代码前面,那么被导入的覆盖)
因为同样id只会在同一个配置文件里进行检查,即在对象里进行检查
而之所以会覆盖,是因为在进行依次放入map集合中,后放入的一定是修改操作,也就是覆盖,这是map集合自身的作用
知识小结:
Spring的重点配置
/*
<bean>标签:创建对象并放到spring的IOC容器
   id属性:在容器中Bean实例的唯一标识,不允许重复
   class属性:要实例化的Bean的全限定名
   scope属性:Bean的作用范围,常用是Singleton(默认)和prototype
    
<constructor-arg>标签:构造 属性注入 即有参
 name属性:属性名称
   value属性:注入的普通属性值
   ref属性:注入的对象引用值
<property>标签:set 属性注入
   name属性:属性名称
   value属性:注入的普通属性值
   ref属性:注入的对象引用值
    <list>
    <set>
    <array>
    <map>
    <props>
<import>标签:导入其他的Spring的分文件
*/
DbUtils(IOC实战):
DbUtils是什么?
DbUtils是Apache的一款用于简化Dao代码的工具类,它底层封装了JDBC技术
核心对象:
QueryRunner queryRunner = new QueryRunner(DataSource dataSource);
//DataSource基本是一个连接池,前面博客第41章有解释
核心方法:
/*
int update(); 执行增、删、改语句
T query(); 执行查询语句
 ResultSetHandler<T> 这是一个接口,主要作用是将数据库返回的记录封装到实体对象(看具体情况使用)
*/
举个栗子:
查询数据库所有账户信息到Account实体中
public class DbUtilsTest {
    @Test
    public void findAllTest() throws Exception {
        // 创建DBUtils工具类,传入连接池
        //这一步就可以使用Spring的IOC(控制反转)容器来操作
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        //会话只是操作对应的数据,就如一个程序有多个窗口类似
        // 编写sql
        String sql = "select * from account";
        // 执行sql
        List<Account> list = queryRunner.query(sql, new BeanListHandler<Account>
(Account.class));
        // 打印结果
        for (Account account : list) {
            System.out.println(account);
       }
   }
}
Spring的xml整合DbUtils :
介绍 :
需求:
基于Spring的xml配置实现账户的CRUD案例
步骤分析:
/*
1. 准备数据库环境
2. 创建java项目,导入坐标
3. 编写Account实体类
4. 编写AccountDao接口和实现类
5. 编写AccountService接口和实现类
6. 编写spring核心配置文件
7. 编写测试代码
*/
实现:
准备数据库环境:
CREATE DATABASE `spring_db`;
USE `spring_db`;
CREATE TABLE `account` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(32) DEFAULT NULL,
 `money` double DEFAULT NULL,
 PRIMARY KEY (`id`)
) ;
insert  into `account`(`id`,`name`,`money`) values (1,'tom',1000),
(2,'jerry',1000);

创建java项目,导入坐标:
<dependencies>
    <dependency>
        <!--mysql驱动-->
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <!--连接池,如DruidDataSource-->
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.9</version>
    </dependency>
    <dependency>
        <!--使用方式,即QueryRunner-->
        <groupId>commons-dbutils</groupId>
        <artifactId>commons-dbutils</artifactId>
        <version>1.6</version>
    </dependency>
    <dependency>
         <!--Spring容器,即IOC容器的使用需要这个,比如ApplicationContext-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <!--使用@Test的-->
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>
编写Account实体类:
package com.lagou.domain;

/**
 *
 */
public class Account {
    private Integer id;
    private String name;
    private Double money;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

编写AccountDao接口和实现类 :
package com.lagou.dao;

import com.lagou.domain.Account;

import java.util.List;

/**
 *
 */
public interface AccountDao {

    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);

}

package com.lagou.dao.impl;

import com.lagou.dao.AccountDao;
import com.lagou.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.SQLException;
import java.util.List;

/**
 *
 */
public class AccountDaoImpl implements AccountDao {

    //使用Spring的set注入来操作
    private QueryRunner qr;

    public void setQr(QueryRunner qr) {
        this.qr = qr;
    }

    @Override
    public List<Account> findAll() {

        List<Account> query = null;

        //sql语句
        String sql = "select * from account";

        try {

            //执行sql语句
            query = qr.query(sql, new BeanListHandler<Account>(Account.class));


        } catch (SQLException e) {
            e.printStackTrace();
        }

        return query;
    }

    @Override
    public Account findById(Integer id) {
        Account account = null;

        String sql = "select * from account where id = ?";

        try {
            account = qr.query(sql, new BeanHandler<Account>(Account.class), id);
        } catch (SQLException e) {
            e.printStackTrace();
        }


        return account;
    }

    @Override
    public void save(Account account) {

        String sql = "insert into account values(null,?,?)";

        try {
            qr.update(sql, account.getName(), account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }


    }

    @Override
    public void update(Account account) {

        String sql = "update account set name = ?,money = ? where id = ?";
        try {
            qr.update(sql, account.getName(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void delete(Integer id) {

        String sql = "delete from account where id = ?";
        try {
            qr.update(sql, id);
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }
}

编写AccountService接口和实现类:
package com.lagou.service;

import com.lagou.domain.Account;

import java.util.List;

/**
 *
 */
public interface AccountService {

    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);

}

package com.lagou.service.impl;

import com.lagou.dao.AccountDao;
import com.lagou.domain.Account;
import com.lagou.service.AccountService;

import java.util.List;

/**
 *
 */
public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }

    @Override
    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    @Override
    public void save(Account account) {
        accountDao.save(account);
    }

    @Override
    public void update(Account account) {
        accountDao.update(account);

    }

    @Override
    public void delete(Integer id) {
        accountDao.delete(id);
    }
}

编写spring核心配置文件:
applicationContext.xml:
注意:我们在使用set注入时,必须有对应的set方法,通常jar包都是有对应set方法的
<?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">

<!--配置连接池对象DruidDataSource对象,名称是dataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--都有对应的set方法和对应方法名称-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///spring_db?characterEncoding=utf8"/>
        <!--注意:java程序与数据库之间的编码设置,我们的数据过去,也会被编码的,编码:字符串变二进制
实际上都是文件的数据操作,所有的数据都可以看成文件的存放的,然后写入文件和读取文件等等
-->
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--配置使用连接池的QueryRunner对象,名称是qr-->
    <bean id="qr" class="org.apache.commons.dbutils.QueryRunner">
        <!--使用有参构造,创建对应的QueryRunner对象-->
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

    <!--配置AccountDaoImpl对象,并注入qr对应的对象-->
    <bean id="accountdao" class="com.lagou.dao.impl.AccountDaoImpl">
        <property name="qr" ref="qr"/>
    </bean>
    <!--配置AccountService对象,并注入accountdao对应的对象-->
    <bean id="accountservice" class="com.lagou.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountdao"/>
    </bean>
</beans>
编写测试代码:
package com.lagou.test;

import com.lagou.domain.Account;
import com.lagou.service.AccountService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

/**
 *
 */
public class AccountServiceTest {

    ClassPathXmlApplicationContext classPathXmlApplicationContext = new 
        ClassPathXmlApplicationContext("applicationContext.xml");
    AccountService accountService = (AccountService) 
        classPathXmlApplicationContext.getBean("accountservice");


    //测试添加
    @Test
    public void testsave(){
        Account account = new Account();
        account.setName("张三");
        account.setMoney(1000.0);
        accountService.save(account);


    }

    //测试查询
    @Test
    public void testFindById(){
        Account account = accountService.findById(3);
        System.out.println(account);
    }

    //测试查询所有
    @Test
    public void testFindAll(){

        List<Account> all = accountService.findAll();
        for (Account account : all) {
            System.out.println(account);
        }
    }

    //测试更新
    @Test
    public void testUpdate(){
        Account account = new Account();
        //虽然什么的account可以通过查询id来获得对应数据,但是会影响性能
        //即多进行了一次数据库的操作所以不建议使用
        account.setId(3);
        account.setName("李四");
        account.setMoney(2000.0);
        accountService.update(account);
    }

    //测试删除
    @Test
    public void testDelete(){
        accountService.delete(3);
    }


}

抽取jdbc配置文件 :
applicationContext.xml加载jdbc.properties配置文件获得连接信息
首先,需要引入context命名空间和约束路径:
<!--
 命名空间:
 xmlns:context="http://www.springframework.org/schema/context"
 约束路径:
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context.xsd
-->
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
# #是注释符号,当上面的数据被读取时,当作键值对来操作读取
# 一般其他配置文件都是使用${对应变量},也就是读取key的value的值来获得后面的数据
# 一般value的值都是直接替换的,若加上""肯定会报错,不能有两个"",且底层无限循环
# 因为java会判断变化失败,继续变化
# 这里之所以不像java在字符串需要""来包括,那是因为使用""来包括,是java需要判断对应类型而使用的
# 最终的结果,还是文件是的二进制的显示,也就是字节,java只是用来判断类型,所以可以看到类型的报错
# 但实际上在文件里显示,就不会加上所谓的"",即没有类型的概念,单纯的替换字节显示
# 但被java获得时,基本都会按照字符串来进行包括
# 实际上打印时,都会变成字节来打印,变成字符图案显示,也就是解码后的显示
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- xsi:schemaLocation约束路径,其他的是文档命名空间,使用对应操作的
    命名空间让我们实现了对应标签在配置文件中起到对应作用-->

    <!--xmlns:context="http://www.springframework.org/schema/context"这个可以使用命名空间的操作
即可以使用context这个命名空间的操作
一般就是xmlns:这个值,使用这个值:的操作,那么这里就是context:来进行对应标签的使用操作
可以说context:就表示引用context命名空间的标签了,所以这里引用了property-placeholder标签
上面基本需要写,因为beans的检查
对应的导入xml操作一般会识别这些标签的,就如spring会识别bean标签一样,若不能识别的一般都会报错,因为有约束,或者不操作这个标签
最后要注意:命名空间也是依赖得到的,没有对应的依赖,那么也就获取不了命名空间,即会报错
即命名空间也是与依赖有关系的
-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置连接池对象DruidDataSource对象,名称是dataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--都有对应的set方法和对应方法名称-->
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--配置使用连接池的QueryRunner对象,名称是qr-->
    <bean id="qr" class="org.apache.commons.dbutils.QueryRunner">
        <!--使用有参构造,创建对应的QueryRunner对象-->
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

    <!--配置AccountDaoImpl对象,并注入qr对应的对象-->
    <bean id="accountdao" class="com.lagou.dao.impl.AccountDaoImpl">
        <property name="qr" ref="qr"/>
    </bean>
    <!--配置AccountService对象,并注入accountdao对应的对象-->
    <bean id="accountservice" class="com.lagou.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountdao"/>
    </bean>
</beans>
知识小结:
/*
DataSource的创建权交由Spring容器去完成
QueryRunner的创建权交由Spring容器去完成,使用构造方法传递DataSource
Spring容器加载properties文件
 <context:property-placeholder location="xx.properties"/>
 <property name="" value="${key}"/>
*/
Spring注解开发:
xml和注解,基本都是通过反射来简便的,所以说,对于运行的效率
实际上就是启动时间,因为启动后,他们的作用已经作用完了,如IOC容器,都创建好了,之后就不会再次读取
所以说主要看启动时间,但启动时间的快慢,并不影响程序,所以他们的运行效率都是差不多的
即读取xml和读取注解运行效率差不多
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势
注解代替xml配置文件可以简化配置,提高开发效率
Spring常用注解:
介绍:
Spring常用注解主要是替代 < bean> 的配置

在这里插入图片描述

图解:
下图中,前面四个@Component(其他三个可以认为是这个的别名),@Controller,@Service,@Repository作用都是创建对应对象放在IOC容器中
只是我们可以根据名称来进行判断这是哪个层,所以在不规范的情况下,这四个可以随便用
而在后面四个,虽然作用都是依赖注入,但是需要对应条件来使用,如类型,如需要结合

在这里插入图片描述

其中类似于property的是直接赋值,而不是调用set方法
无论是什么框架,注解和xml的作用基本类似,且谁后配置,谁进行覆盖(对应操作的地方都会有唯一标识,使得可以进行覆盖)
一般的框架都会自动扫描对应注解位置
也就基本得到了对应类的位置(这样完全受注解位置影响,也就是硬编码问题)
所以这就是可以使用反射进行获取全路径名,而创建对象的原因
从而实现注解的作用(内容有其他作用),并使用反射进行操作
而xml一般需要自己指定读取,从而实现xml的作用(内容有其他作用,且有全路径名的内容),并使用反射进行操作
说明:
JDK11以后完全移除了javax扩展导致不能使用@resource注解
需要maven引入依赖
 <dependency>
     <!--使得可以使用上面的@resource注解,注意:该注解一般java的jdk本身就有处理他的操作,也就会使得他会注入jdk操作的一些组件,具体可以百度,当然,大多数我们并不会使用他,而是使用spring里面的-->
       <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
       <version>1.3.2</version>
 </dependency>
注意:
再Spring里,需要自己进行扫描注解并使用
所以使用注解进行开发时,需要在applicationContext.xml中配置组件扫描
作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法
<!--注解扫描,使用context的component-scan标签,进行对包的扫描
    其中base-package用来指定对应包,指定后,读取到这个位置
    就会扫描指定包,以及子包的所有类,并操作对应注解
    -->
    <context:component-scan base-package="com.lagou" />
实现:
Bean实例化(IOC):
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
使用@Component,@Controller,@Service,@Repository标识UserDaoImpl进行Spring实例化
//相当于配置了bean标签 既然注解在这里,就可以通过扫描,知道这个注解所在类的全路径
//即class属性知道了,而id属性需要我们自己进行设置
//    @Controller
//    @Repository
//    @Component  也可以写这三个,获得都写上,都没问题,因为最后反正都是一样的覆盖
@Service(value = "accountservice") //id属性就是accountservice
// 如果没有写value属性值,Bean的id为:类名首字母小写
public class AccountServiceImpl implements AccountService {
}
属性依赖注入(DI):
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDaoImpl"/>
</bean>

使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入
  //根据类型去完成注入,也就是说,当通过扫描包下的类找到这个注解
    //将这些操作存放在一个对象里,最后进行注入,注意是最后
    //即通过查找当前类的对象(有唯一标识可以识别),只操作本类类型的其他拥有对象
    //所以使得xml的配置文件创建对象时
    //这里使用注解,也是可以操作的
    //若没有当前类的对象(必须一致类),是不会去注入,即注解不起作用
    //若有当前类的对象,且没有指定下面变量类型(可以多态操作),或者有多个类型
    //若有多个类型,则默认根据变量名来查找,所以就算是多个
    //若有key与变量名相同,那么就不会报错,而是获得对象,否则就不知道获得谁,那么就会报错
    //而由于@Value注解,是不需要去map集合中获得值的,所以,map集合没有对应变量类型,不会报错
    //@Autowired注解的操作方式是:
    //通过反射获得注解操作的对应结构,最后再获得注解的内容
    //通过注解信息,那么就知道是去找对应对象,然后放到IOC容器里面(有地址指向,所以可以嵌套注入)
    //但也要注意:对应的该类实例基本需要存在,否则相当于没有该注解,即没有注入,那么对应的值就是初始化的值(比如null,注入一般会覆盖初始化)
    //所以就相当于对这个变量进行赋值,且是根据类型(可以多态操作)匹配的(由于直接赋值,那么就不用set方法了)
    //就如前面说过的IUserDao userDao = classPathXmlApplicationContext.getBean(IUserDao.class)类似
    //即也可以说是相当于调用一次这个操作进行赋值
    //所以也就有了不能够有多个类型,否则会报错
    //当然,若对应的实例id名称有与这个变量名称相同的(完全相同,没有忽略大小写)
	//那么会使用对应的类型,而不是操作多个,即这时不会报错了
    //@Autowired //不看上面的话,就理解为从map集合中取得对象进行赋值
    //为了防止上面的多个类型,扫描时,扫描到Qualifier注解
     //那么就指定了key名称的value来进行赋值,若没有,直接报错(如果是多个类型的话),不会再次根据变量名来进行了
//当然,可能这个时候会按照类型来处理(多个报错),当然,他可能只会看名称,这些细节都只是spring规定操作的,记住并没有很大的意义,因为可能随着版本而发生改变(虽然通常只是在新版本中处理,旧版本通常不会更改)
    //这两个位置可以互换,不影响
    //且不能单独使用,必须要和上面的@Autowired一起使用(否则相当于没有操作,即得到null值),即读取这两个后,变成(得到)一个对象
    //@Qualifier("accountdao")
    @Resource(name = "accountdao")
    //他们也是会进行覆盖的,但只要有一个错误,那么错误导致程序结束,所以最后就会报错,覆盖不了了
    //注解与注释:操作内容之间有注释是不影响的
    private AccountDao aDao;

/*
 @Resource的细节:
如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不到则抛出异常
如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异常
如果指定了 type(Class的),则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个,都会抛出异常
如果既没有指定name,⼜没有指定type,则⾃动按照当前类型Class来找相当于直接的(@Autowired)


这里实际上在Spring中,大多数都是相同的说明,所以了解即可
*/



@Value:
使用@Value进行字符串的注入,结合SPEL表达式获得配置参数
@Value("注入普通属性")
private String str;
//由于我们进行扫描时,那个配置文件进行了导入
//那么使用注解也是会获得对应map集合内容的,即对应map集合是全局的
//使用${}符号,与配置文件一样,在java相关代码对应初始化时期,就会被替换
@Value("${jdbc.driverClassName}")
private String driver;
@Scope:
<bean scope=""/>
使用@Scope标注Bean的范围:
@Service(value = "accountservice") //id属性就是accountservice
@Scope("prototype")
//默认是单例,这里改成多例,每次调用都是一个新的对象
//而要单例也是不同对象,只有重新读取一下配置文件,也就是对应配置文件的map集合的获取了
//即不同map集合中,不同对应对象的获取
Bean生命周期 :
<bean init-method="init" destroy-method="destory" />
使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法
@PostConstruct
    public void init(){
        System.out.println("初始化方法....");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("销毁方法.....");
    }
    //按照上面注解的解释,很明显,也是最后将对应类的对应方法设置成初始化方法和销毁方法
    //初始化方法,在放入map集合之前,就进行执行了
//而销毁,关闭最开始的对象,即读取配置文件获取的对象,就可以看到执行销毁方法
//否则的话,虽然程序结束,也会执行销毁方法,但是还没打印,就中断了,如无限循环的中断,后续不执行了
Spring常用注解整合DbUtils :
步骤分析:
/*
拷贝xml配置项目,改为注解配置项目
修改AccountDaoImpl实现类
修改AccountServiceImpl实现类
修改spring核心配置文件
编写测试代码
*/
将前面进行修改之后,如下
package com.lagou.dao;

import com.lagou.domain.Account;

import java.util.List;

/**
 *
 */
public interface AccountDao {

    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);

}

package com.lagou.dao.impl;

import com.lagou.dao.AccountDao;
import com.lagou.domain.Account;
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.Repository;

import java.sql.SQLException;
import java.util.List;

/**
 *
 */
@Repository //相当于配置了bean标签
public class AccountDaoImpl implements AccountDao {

    //使用Spring的set注入来操作
    @Autowired
    private QueryRunner qr;


    @Override
    public List<Account> findAll() {

        List<Account> query = null;

        //sql语句
        String sql = "select * from account";

        try {

            //执行sql语句
            query = qr.query(sql, new BeanListHandler<Account>(Account.class));


        } catch (SQLException e) {
            e.printStackTrace();
        }

        return query;
    }

    @Override
    public Account findById(Integer id) {
        Account account = null;

        String sql = "select * from account where id = ?";

        try {
            account = qr.query(sql, new BeanHandler<Account>(Account.class), id);
        } catch (SQLException e) {
            e.printStackTrace();
        }


        return account;
    }

    @Override
    public void save(Account account) {

        String sql = "insert into account values(null,?,?)";

        try {
            qr.update(sql, account.getName(), account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }


    }

    @Override
    public void update(Account account) {

        String sql = "update account set name = ?,money = ? where id = ?";
        try {
            qr.update(sql, account.getName(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void delete(Integer id) {

        String sql = "delete from account where id = ?";
        try {
            qr.update(sql, id);
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }
}

package com.lagou.domain;

/**
 *
 */
public class Account {
    private Integer id;
    private String name;
    private Double money;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    public Integer getId() {
        return id;
    }

    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.lagou.service;

import com.lagou.domain.Account;

import java.util.List;

/**
 *
 */
public interface AccountService {

    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);

}

package com.lagou.service.impl;

import com.lagou.dao.AccountDao;
import com.lagou.domain.Account;
import com.lagou.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 *
 */

@Service(value = "accountservice")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao aDao;


    public void setAccountDao(AccountDao accountDao) {
        this.aDao = accountDao;
    }

    @Override
    public List<Account> findAll() {

        return aDao.findAll();
    }

    @Override
    public Account findById(Integer id) {
        return aDao.findById(id);
    }

    @Override
    public void save(Account account) {
        aDao.save(account);
    }

    @Override
    public void update(Account account) {
        aDao.update(account);

    }

    @Override
    public void delete(Integer id) {
        aDao.delete(id);
    }
}

package com.lagou.test;

import com.lagou.domain.Account;
import com.lagou.service.AccountService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

/**
 *
 */
public class AccountServiceTest {

    ClassPathXmlApplicationContext classPathXmlApplicationContext = new 
        ClassPathXmlApplicationContext("applicationContext.xml");
    AccountService accountService = (AccountService) 
        classPathXmlApplicationContext.getBean("accountservice");


    //测试添加
    @Test
    public void testsave(){
        Account account = new Account();
        account.setName("张三");
        account.setMoney(1000.0);
        accountService.save(account);


    }

    //测试查询
    @Test
    public void testFindById(){
        Account account = accountService.findById(3);
        System.out.println(account);
    }

    //测试查询所有
    @Test
    public void testFindAll(){

        List<Account> all = accountService.findAll();
        for (Account account : all) {
            System.out.println(account);
        }
    }

    //测试更新
    @Test
    public void testUpdate(){
        Account account = new Account();
        //虽然什么的account可以通过查询id来获得对应数据,但是会影响性能
        //即多进行了一次数据库的操作所以不建议使用
        account.setId(3);
        account.setName("李四");
        account.setMoney(2000.0);
        accountService.update(account);
    }

    //测试删除
    @Test
    public void testDelete(){
        accountService.delete(3);
    }


}

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
                           ">

<!--注解扫描记得一定要添加上-->
    <context:component-scan base-package="com.lagou" />

<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">

        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>


    <bean id="qr" class="org.apache.commons.dbutils.QueryRunner">

        <constructor-arg name="ds" ref="dataSource"/>
    </bean>





</beans>
从这里可以发现,上面并没有完全的使用注解的方式,也就是说上面的方式是注解和xml的结合使用
为了完全的只使用注解,看如下内容
Spring新注解:
使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
/*
非自定义的Bean的配置:<bean> 即配置不到的jar包的类
加载properties文件的配置:<context:property-placeholder>
组件扫描的配置:<context:component-scan>
引入其他文件:<import>
*/

在这里插入图片描述

由于前面的xml配置文件,不好进行配置,那么我们就引出了替代对应xml的文件,也就是我们所说的配置类
这个配置类相当于上面的xml文件,只是这个是以类来保存的,即可以全部使用注解来开发了
之所以配置类可以与xml一样进行读取,是因为我们不止可以读取xml配置文件,也可以读取配置类
即扫描被@Configuration注解的类
被扫描后,那么会根据自身信息进行操作,扫描到一定的注解,那么就会根据这个注解的信息来进行操作
如创建对象放入map集合,或者加载properties到全局,或者扫描对应包,又或者导入其他配置类
使得我们读取xml获得的信息,变得在注解获得的信息是类似的,即实现了纯注解开发
图解:

在这里插入图片描述

Spring纯注解整合DbUtils:
步骤分析:
/*
编写Spring核心配置类
编写数据库配置信息类
编写测试代码
*/
package com.lagou.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {


    @Value("${jdbc.driverClassName}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;


    @Bean("dataSource")
    public DataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }

   /*
    这里需要注意:spring会拦截被注解@Bean的调用,使得如果他以及被调用过了,即其他@Bean调用过了,那么直接返回对应保留的对象,虽然这个对象也是给对应@Bean操作的,具体例子如下:
    @Configuration
public class MyConfig {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }

    @Bean
    public AnotherBean anotherBean() {
        MyBean myBean = myBean(); // 调用同一个@Bean方法创建的对象
        return new AnotherBean(myBean);
    }

}

在上述示例中,myBean()方法使用@Bean注解声明并返回一个MyBean对象(他在前面,他先操作,有先后顺序,xml都有,他自然也有),然后,anotherBean()方法中通过调用myBean()方法来获取MyBean对象,并将其传递给AnotherBean的构造函数,在这种情况下,anotherBean()方法中创建的AnotherBean对象的MyBean对象和myBean()方法中创建的MyBean对象是同一个实例

这是因为Spring容器会拦截@Bean方法的调用,并管理这些方法返回的实例,当多次调用同一个@Bean方法时,Spring会在容器中查找并返回已创建的单例对象

需要注意的是,如果在@Bean方法中手动创建对象而不是通过其他@Bean方法调用,那么每次调用@Bean方法时都会创建一个新的实例,如果需要确保多个地方获取的对象是同一个实例,需要通过方法调用来实现(因为是拦截方法)

简单来说,由于拦截的存在,在调用时会有一个地方保留其对象,然后就只返回这个保留的对象来确定是同一个实例
    */



}

package com.lagou.config;

import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;

import javax.sql.DataSource;

/**
 *
 */
//实际上只要我们有@Component、@ComponentScan、@Import、@ImportResource
//这四个注解,那么这个配置类注解可以不写
@Configuration //将这个类变成配置类,使得被扫描,形成对应操作
@ComponentScan("com.lagou")
//被读取后,根据对应包,进行注解扫描,而不用判断xml的节点了
@PropertySource("classpath:jdbc.properties")
//读取配置文件,指定文件路径,也不用判断xml的节点了
@Import(DataSourceConfig.class) 
//引入对应配置类,这个类不是配置类也是可以的(对应版本必须要配置类,但现在基本可以不是配置类了)
//也是相当于去扫描,然后放入对应对象
//大多数情况下,引入的也需要是配置类,还有,如果需要这个配置被使用,那么需要加上配置注解,因为是当成xml的,所以里面的注解通常也会需要的,否则的话,随便处理,不起作用就不要怪配置了
//通常来说,如果导入的不是配置类,一般是因为实现了 ImportSelector 或 ImportBeanDefinitionRegistrar 接口的类,这些接口允许你在运行时动态地注册 Bean 定义
public class SpringConfig {
    //从上面看,可以知道,本来读取xml放入对应对象以及全局内容的
    //变成了读取配置类,放入对应对象以及全局内容的
    //但都是读取,只是方式不同

    //由于对应properties是全局配置的(这是因为他是最先获取,所以认为是全局的(对于后面的内容来说是这样的)),那么不会存在对应对象里,最后依次执行,所以这里可以直接获得
    //而对应的对象,在变成配置类时,就反射出来了,所以是有对应类的
    //当然了注解也不要写错了,虽然有提示

//    @Value("${jdbc.driverClassName}")
//    private String driver;
//    @Value("${jdbc.url}")
//    private String url;   在引入的类里面
//    @Value("${jdbc.username}")
//    private String username;
//    @Value("${jdbc.password}")
//    private String password;

  //要知道,对应的${jdbc.driverClassName}在xml中,并非是在对应的固定地方,在任何地方都可以进行获取,比如类似的:
    /*
    
    <bean id="accountdao" class="com.lagou.dao.impl.AccountDaoImpl2">
<property name="a" value="${jdbc.driverClassName}"></property>
    </bean>

所以这里自然也是可以的
    */

    //有value,那么就需要key值,即dataSource就是key值
    //若不加key,则默认为方法名,即getDataSource就是key值
    //@Bean("dataSource")
    //由于我们需要创建对象,那么必须要有对应值
    //所以@Bean注解,就是获得方法返回值的方式,用来当作map集合对象
    //当扫描到这个注解时,一定是通过这个方法来进行获得注解信息的
    //那么就会通过反射,调用这个方法,获得对象,然后像xml一样,放在对应对象里
    //最后放在map集合中
    //实际上,在读取这个配置类时,就会反射出对应对象(类名首字母小写),最后操作时,先操作变量,才操作方法
    //然后依次放入IOC容器,与Mybatis的多表类似的操作,但对象中对象是在外面依次操作(如放入map集合依次操作)
//    public DataSource getDataSource() {
//        DruidDataSource druidDataSource = new DruidDataSource();
//        druidDataSource.setDriverClassName(driver);
//        druidDataSource.setUrl(url);  在引入的类里面
//        druidDataSource.setUsername(username);
//        druidDataSource.setPassword(password);
//        return druidDataSource;
//    }

    //由于@Autowired注解,是可以对操作的变量进行注入的
    //即不止可以写在对应的变量上,也是可以写在参数上
    //所以,有些注解有多种方式,实际上是元注解的作用,修饰注解可以用在那些地方上
    @Bean("queryRunner")
    public QueryRunner getQueryRunner(@Autowired DataSource dataSource) { //默认情况下,他的这个变量会自动的操作@Autowired的注解的,所以@Autowired可以不加,有参数的情况是后将对象放入map
        //由于他们最后都是依次进行放入map集合的,那么在放入这个时,会先进行注入然后放入,而因为后放入,所以如果在这之前操作注入是得不到对应的实例的,也就会报错
        QueryRunner queryRunner = new QueryRunner(dataSource);
        //很明显,在放入时,是注解先操作,因为注解在内部
        return queryRunner;
 //注意: //配置类和注解操作的实例类,那么我们注入的变量(单词,注意是单词哦,应该有个单词表的)首字母只能小写,其余需要比对方类名的字母大写或者相同,不能小写,否则得不到
        //由于@Bean是后操作的,那么他是可以覆盖原来的map的对象,或者说重新赋值了对应的对象(一般是重新赋值,前提是id相同)
         //然而,大多数默认的id配置,可能是会随着版本的改变而改变,所以我们在操作相关注解时,都有尽量的进行id的确定,而不是操作默认,比如类似于@Bean("queryRunner")的操作等等,这样就不会使得突然有一天版本改变而出现错误的情况了
    }

}

//当然,具体情况是因为依赖关系,因为依赖的关系,所以存在先后顺序,以后说明源码时可能会提到吧

package com.lagou.dao;

import com.lagou.domain.Account;

import java.util.List;

/**
 *
 */
public interface AccountDao {

    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);

}

package com.lagou.dao.impl;

import com.lagou.dao.AccountDao;
import com.lagou.domain.Account;
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.Repository;

import java.sql.SQLException;
import java.util.List;

/**
 *
 */
@Repository //相当于配置了bean标签
public class AccountDaoImpl implements AccountDao {

    //使用Spring的set注入来操作
    @Autowired
    private QueryRunner qr;


    @Override
    public List<Account> findAll() {

        List<Account> query = null;

        //sql语句
        String sql = "select * from account";

        try {

            //执行sql语句
            query = qr.query(sql, new BeanListHandler<Account>(Account.class));


        } catch (SQLException e) {
            e.printStackTrace();
        }

        return query;
    }

    @Override
    public Account findById(Integer id) {
        Account account = null;

        String sql = "select * from account where id = ?";

        try {
            account = qr.query(sql, new BeanHandler<Account>(Account.class), id);
        } catch (SQLException e) {
            e.printStackTrace();
        }


        return account;
    }

    @Override
    public void save(Account account) {

        String sql = "insert into account values(null,?,?)";

        try {
            qr.update(sql, account.getName(), account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }


    }

    @Override
    public void update(Account account) {

        String sql = "update account set name = ?,money = ? where id = ?";
        try {
            qr.update(sql, account.getName(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void delete(Integer id) {

        String sql = "delete from account where id = ?";
        try {
            qr.update(sql, id);
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }
}

package com.lagou.domain;

/**
 *
 */
public class Account {
    private Integer id;
    private String name;
    private Double money;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    public Integer getId() {
        return id;
    }

    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.lagou.service;

import com.lagou.domain.Account;

import java.util.List;

/**
 *
 */
public interface AccountService {

    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);

}

package com.lagou.service.impl;

import com.lagou.dao.AccountDao;
import com.lagou.domain.Account;
import com.lagou.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 *
 */

@Service(value = "accountservice")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao aDao;


    public void setAccountDao(AccountDao accountDao) {
        this.aDao = accountDao;
    }

    @Override
    public List<Account> findAll() {

        return aDao.findAll();
    }

    @Override
    public Account findById(Integer id) {
        return aDao.findById(id);
    }

    @Override
    public void save(Account account) {
        aDao.save(account);
    }

    @Override
    public void update(Account account) {
        aDao.update(account);

    }

    @Override
    public void delete(Integer id) {
        aDao.delete(id);
    }
}

package com.lagou.test;

import com.lagou.config.SpringConfig;
import com.lagou.domain.Account;
import com.lagou.service.AccountService;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.sql.DataSource;
import java.util.List;

/**
 *
 */
public class AccountServiceTest {

    //ClassPathXmlApplicationContext classPathXmlApplicationContext = new 
   // ClassPathXmlApplicationContext("applicationContext.xml");
    //AccountService accountService = (AccountService) 
   // classPathXmlApplicationContext.getBean("accountservice");

    //而由于使用纯注解方式,那么就不能去读取配置文件了,而是读取配置类
    //但无论是什么读取,最后都是一样的数据,放在一样的地方(map集合),只是方式不同而已
    //在扫描时,这里就已经创建了对应的SpringConfig对象了,并放入一个对象中
    //最后依次放入map集合,对应key是类首字母小写,且相当于对bean的全局配置
    //即当配置类时,也是有对应对象的,只是在反射时,会根据这个对象内容,来进行其他对象获得操作
    //变成了对象,然后最后依次放入map集合
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new 
        AnnotationConfigApplicationContext(SpringConfig.class); //可以给多个class,实际上xml也是可以的,简称为合并,因为反正前面class和xml中间再怎么操作都是map获取
    AccountService accountService = 
        (AccountService)annotationConfigApplicationContext.getBean("accountservice");
    //一般在类里面有些是没有提示的,所以可以先在方法里写完代码后,再复制到类里面

    //测试添加
    @Test
    public void testsave(){
        Account account = new Account();
        account.setName("张三");
        account.setMoney(1000.0);
        accountService.save(account);


    }

    //测试查询
    @Test
    public void testFindById(){
        Account account = accountService.findById(3);
        System.out.println(account);
    }

    //测试查询所有
    @Test
    public void testFindAll(){

        List<Account> all = accountService.findAll();
        for (Account account : all) {
            System.out.println(account);
        }
        //当然了,局部也是可以定义类的对应变量的,但是不建议这么做
        //之所以可以,是因为最后都会被消除,即只会作用与当前方法,而不会作用于其他方法
//SpringConfig accountService=
//        (SpringConfig)annotationConfigApplicationContext.getBean("springConfig"); 
//        System.out.println(accountService.getDataSource());   没使用@Import导入前的
        DataSource accountService1 = 
            (DataSource)annotationConfigApplicationContext.getBean("dataSource");
        System.out.println(accountService1);
        //可以得出,两个值是一样的,那么就知道,是SpringConfig对象,在扫描时,会将对应对象,放入其他对象里
        //最后依次放入map集合
    }

    //测试更新
    @Test
    public void testUpdate(){
        Account account = new Account();
        //虽然什么的account可以通过查询id来获得对应数据,但是会影响性能
        //即多进行了一次数据库的操作所以不建议使用
        account.setId(3);
        account.setName("李四");
        account.setMoney(2000.0);
        accountService.update(account);
    }

    //测试删除
    @Test
    public void testDelete(){
        accountService.delete(3);
    }


}

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
Spring整合Junit:
普通Junit测试问题:
在普通的测试类中,需要开发者手动加载配置文件并创建Spring容器,然后通过Spring相关API获得Bean实例
如果不这么做,那么无法从容器中获得对象
ApplicationContext applicationContext =
   new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = 
applicationContext.getBean(AccountService.class);
我们可以让SpringJunit负责创建Spring容器来简化这个操作,开发者可以直接在测试类注入Bean实例
但是需要将配置文件的名称告诉它
操作如下:
步骤分析:
/*
1. 导入spring集成Junit的坐标
2. 使用@Runwith注解替换原来的运行器
3. 使用@ContextConfiguration指定配置文件或配置类
4. 使用@Autowired注入需要测试的对象
5. 创建测试方法进行测试
*/
导入spring集成Junit的坐标:
<!--此处需要注意的是,spring5 及以上版本要求 junit 的版本必须是 4.12 及以上-->
<dependency>
    <!--使得可以使用测试注解来帮外面指定配置,但需要@Test,所以下面的依赖也要-->
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
使用@Runwith注解替换原来的运行器:
@RunWith(SpringJUnit4ClassRunner.class)
//@RunWith知道junit即测试的运行环境,即这个类的测试类(@Test)有对应作用了
//不加上的话,使用下面注解,就会报错(空指针异常)
//即SpringJUnit4ClassRunner是Spring提供的作为junit运行环境的类
//当加上这个注解后,在运行测试方法时,会创建这个类的对象
//放入IOC容器里,那么其中注解就会起作用,如注入,使得可以使用注入方式(而不用扫描),当然一般是不允许得到的
//配置类也是如此,但可以得到
public class AccountServiceTest {
}
使用@ContextConfiguration指定配置文件或配置类:
//加上上面的注解时,这个注解就会起作用了(不是空指针异常了)
//@ContextConfiguration({"classpath:applicationContext.xml"}) 这是xml的加载配置方式
//无论怎么配置,都需要加载某些文件,如配置类,xml等,作为初始加载对象
@ContextConfiguration(classes = SpringConfig.class)
//这里单个,可以不加{},若有多个包都是这个类,但最终只会看注解的操作
//所以多个这个类,就相当于结合,即不会报错
//这是配置类的加载方式,也可以加载多个,即{SpringConfig.class}的操作
public class AccountServiceTest {

}
使用@Autowired注入需要测试的对象 :
public class AccountServiceTest {
    @Autowired
    private AccountServiceImpl accountService;
}
创建测试方法进行测试(结合了前面的代码) :
 //测试查询所有,通过上面的注解,那么执行这个方法时,实际上就是执行这个类的测试方式,也就是IOC容器的实例方法
//因为要执行方法,一般都是IOC容器的实例来执行的,而不是自己创建(因为没有IOC容器里的操作,如注入)
    @Test
    public void testFindAll(){

        List<Account> all = accountService.findAll();
        for (Account account : all) {
            System.out.println(account);
        }

    }
最后:其实Spring是存在循环依赖的(多例的情况下,一般单例没有,或者说本来就解决的),我们知道,要放入map中,必然是需要对方都操作完毕,如果没有操作完毕,那么不会放入,所以如果他们之间存在一个循环,那么就都不会操作完毕了,这就是循环依赖的理论原因,具体可以看看这个博客:https://blog.csdn.net/qq_41421997/article/details/119993334
一般来说,当配置文件,注解,new,一起时,他们的优先级是(配置文件,注解),new,即若存在(配置文件,注解),那么就是(配置文件,注解),否则就是new,本质上就是替换,因为new在类出现时就操作了,而注入在类之后进行赋值,配置文件虽然也操作注入,但是与注解注入还是有区别的,其中在产生实例时,注解优先配置,然后注入也是一样,注解优先配置,比如当一个类里面的变量同时由注解和配置进行注入时,那么配置的优先(我后执行),既然配置的优先,那么配置就是最后操作的
执行顺序:new,注解,配置
最终结果出现:配置,注解,new
当然,随着版本的变化,其中注解和配置的先后顺序可能发生改变,了解并注意即可
注意:配置类也可以被mvc直接的指定,也可以使用xml来扫描他,他是可以被扫描的,就如xml引入xml一样,只不过由于xml不能引入类,所以交给了扫描来处理了
最后需要注意:操作对应的如@Component将一个类变成实例时,若其构造方法只有一个,并且是有参构造,通常会考虑对参数的注入,否则默认情况下基本都是无参构造,或者当构造方法足够多以及没有对应的bean时,那么就会报错,若要解决构造方法足够多,也就是一个类有多个构造函数,为了让 Spring 明确知道该使用哪个构造函数进行依赖注入,你可以在其中一个构造函数上加上 @Autowired注解(存在无参默认是无参的,否则需要指定)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值