SpringDay2

一、IOC/DI配置管理第三方bean

1.1 Spring实现Druid管理

需求 : 使用 Spring IOC 容器来管理 Druid 连接池对象
1.pom.xml中添加依赖
<dependency> 
    <groupId>com.alibaba</groupId> 
    <artifactId>druid</artifactId> 
    <version>1.1.16</version> 
</dependency>

2.配置第三方bean
<?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对象-->
    <bean class="com.alibaba.druid.pool.DruidDataSource"> 
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/> 
        <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/> 
        <property name="username" value="root"/> 
        <property name="password" value="root"/> 
    </bean> 
</beans>

3.从IOC容器中获取对应的bean对象
public class App { 
    public static void main(String[] args) { 
        ApplicationContext ctx = 
new ClassPathXmlApplicationContext("applicationContext.xml"); 
        DataSource dataSource = (DataSource) ctx.getBean("dataSource"); 
        System.out.println(dataSource); 
    } 
}

4.运行程序打印结果: 说明第三方bean对象已经被spring的IOC容器进行管理
第三方的类指的是什么?DruidDataSource
如何注入数据库连接四要素?setter注入

1.2 加载properties文件

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 
    <property name="driverClass" value="com.mysql.jdbc.Driver"/> 
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/> 
    <property name="user" value="root"/> 
    <property name="password" value="root"/> 
    <property name="maxPoolSize" value="1000"/> 
</bean>

        像上面配置,某些bean使用到了一些固定的常量如数据库连接四要素,把这些值写在Spring的配置文件中不利于后期维护,需要将这些值提取到一个外部的properties配置文件中然后从配置文件中读取属性值。

需求:将数据库连接四要素提取到properties配置文件,spring来加载配置信息
并使用这些信息来完成属性注入。

1.在resources下创建一个jdbc.properties,将数据库连接四要素配置到配置文件中
jdbc.driver=com.mysql.jdbc.Driver 
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db 
jdbc.username=root 
jdbc.password=root

2.在Spring的配置文件中加载properties文件
//2.1在applicationContext.xml中开context命名空间
<?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"> 
</beans>

//2.2.在配置文件中使用context命名空间下的标签来加载properties配置文件
<context:property-placeholder location="jdbc.properties"/>

3.完成属性注入
<?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:property-placeholder location="jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> 
        <property name="driverClassName" value="${jdbc.driver}"/> 
        <property name="url" value="${jdbc.url}"/> 
        <property name="username" value="${jdbc.username}"/> 
        <property name="password" value="${jdbc.password}"/> 
    </bean>
</beans>

注意:在properties中配置键值对的时候,如果key设置为username,如:username=root666
控制台打印的却不是root666,而是自己电脑的用户名
出现问题的原因是<context:property-placeholder/>标签会加载系统的环境变量,而且环境
变量的值会被优先加载
//查看系统环境变量
public static void main(String[] args) throws Exception{ 
    Map<String, String> env = System.getenv(); 
    System.out.println(env); 
}

<context:property-placeholder location="jdbc.properties" 
system- properties-mode="NEVER"/>
system-properties-mode:设置为NEVER,表示不加载系统属性,就可以解决上述问题。

二、核心容器

这里所说的核心容器,大家可以把它简单的理解为 ApplicationContext
BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载。
ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载。
ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能。

2.1 容器的创建方式

方式一:
ApplicationContext ctx = new 
ClassPathXmlApplicationContext("applicationContext.xml");
这种方式翻译为:类路径下的XML配置文件

方式二:
ApplicationContext ctx = new 
FileSystemXmlApplicationContext("D:\\workspace\\applicationContext.xml");
这种方式翻译为:文件系统下的XML配置文件

2.2 Bean的三种获取方式

方式一:getBean("名称"):需要类型转换
BookDao bookDao = (BookDao) ctx.getBean("bookDao");

方式二:多了一个参数
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);

方式三:容器中不能有多个该类的bean对象
BookDao bookDao = ctx.getBean(BookDao.class);

2.3 容器类层次结构

(1)在IDEA中双击shift ,输入BeanFactory

(2) 点击进入BeanFactory类,ctrl+h,就能查看到如下结构的层次关系

2.4 BeanFactory的使用

使用 BeanFactory 来创建 IOC 容器的具体实现方式为 :
public class AppForBeanFactory { 
    public static void main(String[] args) { 
        Resource resources = new ClassPathResource("applicationContext.xml"); 
        BeanFactory bf = new XmlBeanFactory(resources); 
        BookDao bookDao = bf.getBean(BookDao.class); 
        bookDao.save(); 
    } 
}

BeanFactory是延迟加载,只有在获取bean对象的时候才会去创建
ApplicationContext是立即加载,容器加载的时候就会创建bean对象
ApplicationContext要想成为延迟加载,只需要按照如下方式进行配置:

<?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="bookDao" class="com.itheima.dao.impl.BookDaoImpl" lazy- init="true"/> 
</beans>

 三、IOC/DI注解开发

环境准备:

1.创建一个Maven项目;
2.pom.xml添加Spring的依赖;

3.resources下添加applicationContext.xml
<?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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/> 
</beans>

4.添加BookDao、BookDaoImpl、BookService、BookServiceImpl类
public interface BookDao { 
    public void save(); 
}
public class BookDaoImpl implements BookDao { 
    public void save() { 
        System.out.println("book dao save ..." ); 
    } 
}
public interface BookService { 
    public void save(); 
}
public class BookServiceImpl implements BookService { 
    public void save() { 
        System.out.println("book service save ...");
    }
}

5.创建运行App
public class App { 
    public static void main(String[] args) { 
        ApplicationContext ctx = new 
ClassPathXmlApplicationContext("applicationContext.xml"); 
        BookDao bookDao = (BookDao) ctx.getBean("bookDao"); 
        bookDao.save(); 
    } 
}

3.1 注解开发定义bean

步骤1:删除原XML配置
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>

步骤2:Dao上添加注解(@Component注解设置该类为spring管理的bean)
//注意:@Component注解不可以添加在接口上,因为接口是无法创建对象的。
@Component("bookDao") 
public class BookDaoImpl implements BookDao { 
    public void save() { 
        System.out.println("book dao save ..." ); 
    } 
}
@Component
//BookServiceImpl类没有起名称,所以在App中是按照类型来获取bean对象 
public class BookServiceImpl implements BookService { 
    private BookDao bookDao; 
    public void setBookDao(BookDao bookDao) { 
        this.bookDao = bookDao; 
    }
    public void save() { 
        System.out.println("book service save ..."); 
        bookDao.save(); 
    } 
}

步骤3:配置Spring的注解包扫描
//为了让Spring框架能够扫描到写在类上的注解,需要在配置文件上进行包扫描
<?xml version="1.0" encoding="UTF-8"?> 
<beans  xmlns="http://www.springframework.org/schema/beans" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation=" 
            http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd"> 
<context:component-scan base-package="com.itheima"/> 
</beans>

component-scan
component:组件,Spring将管理的bean视作自己的一个组件;    
scan:扫描
base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
包路径越多[如:com.itheima.dao.impl],扫描的范围越小速度越快
包路径越少[如:com.itheima],扫描的范围越大速度越慢
一般扫描到项目的组织名称即Maven的groupId下[如:com.itheima]即可。

4.App程序中获取BookServiceImpl对应的bean对象
//按类型获取bean 
//BookServiceImpl类没有起名称,所以在App中是按照类型来获取bean对象
BookService bookService = ctx.getBean(BookService.class); 
System.out.println(bookService);
//@Component注解如果不起名称,会有一个默认值就是当前类名首字母小写,
//所以也可以按照名称获取,如
BookService bookService = (BookService)ctx.getBean("bookServiceImpl"); 
System.out.println(bookService);

5.运行程序:打印观察结果,两个bean对象都已经打印到控制台

对于 @Component 注解,还衍生出了其他三个注解 @Controller @Service @Repository

方便我们后期在编写类的时候能很好的区分出这个类是属于表现层业务层还是数据层的类。

3.2 纯注解开发模式  

        上面已经可以使用注解来配置bean, 但是依然有用到配置文件,在配置文件中对包进行了扫描, Spring在 3.0版已经支持纯注解开发,使用 Java 类替代配置文件。
将配置文件applicationContext.xml删除掉,使用类来替换。
1.创建一个配置类SpringConfig
2.在配置类上添加@Configuration注解,将其标识为一个配置类,替换applicationContext.xml
3.在配置类上添加包扫描注解@ComponentScan替换<context:component-scan base-package=""/>
多个数据请用数组格式:@ComponentScan({"com.itheima.service","com.itheima.dao"})
@Configuration 
@ComponentScan("com.itheima")
public class SpringConfig { 
}

4.创建一个新的运行类AppForAnnotation
public class AppForAnnotation { 
    public static void main(String[] args) { 
        ApplicationContext ctx = new 
AnnotationConfigApplicationContext(SpringConfig.class); 
        BookDao bookDao = (BookDao) ctx.getBean("bookDao"); 
        System.out.println(bookDao); 
        BookService bookService = ctx.getBean(BookService.class); 
        System.out.println(bookService); 
    } 
}

5.运行AppForAnnotation,可以看到两个对象依然被获取成功

3.3 注解开发Bean

环境准备:

创建一个Maven项目
pom.xml添加Spring的依赖
添加一个配置类SpringConfig

@Configuration 
@ComponentScan("com.itheima") 
public class SpringConfig { 
}

//添加BookDao、BookDaoImpl类
public interface BookDao { 
    public void save(); 
}
@Repository 
public class BookDaoImpl implements BookDao { 
    public void save() { 
        System.out.println("book dao save ..." ); 
    } 
}

创建运行App
public class App { 
    public static void main(String[] args) { 
        AnnotationConfigApplicationContext ctx = new 
AnnotationConfigApplicationContext(SpringConfig.class); 
        BookDao bookDao1 = ctx.getBean(BookDao.class); 
        BookDao bookDao2 = ctx.getBean(BookDao.class); 
        System.out.println(bookDao1); 
        System.out.println(bookDao2); 
    } 
}

3.2.1 Bean的作用范围

1.先运行App类,在控制台打印两个一摸一样的地址,说明默认情况下bean是单例

2.要想将BookDaoImpl变成非单例,只需要在其类上添加@scope注解
@Repository 
//@Scope设置bean的作用范围 
@Scope("prototype") 
public class BookDaoImpl implements BookDao { 
    public void save() { 
        System.out.println("book dao save ..."); 
    } 
}

3.2.2 Bean的生命周期

1.在BookDaoImpl中添加两个方法,init和destroy ,方法名可以任意


2.在对应的方法上添加@PostConstruct和@PreDestroy注解,标识初始化方法和销毁方法
@Repository 
public class BookDaoImpl implements BookDao { 
    public void save() { 
        System.out.println("book dao save ..."); 
    }
    @PostConstruct //在构造方法之后执行,替换 init-method
    public void init() { 
        System.out.println("init ..."); 
    }
    @PreDestroy //在销毁方法之前执行,替换 destroy-method
    public void destroy() { 
        System.out.println("destroy ..."); 
    } 
}

3.destroy只有在容器关闭的时候,才会执行,所以需要修改App的类
public class App { 
    public static void main(String[] args) { 
        AnnotationConfigApplicationContext ctx = new 
AnnotationConfigApplicationContext(SpringConfig.class); 
        BookDao bookDao1 = ctx.getBean(BookDao.class); 
        BookDao bookDao2 = ctx.getBean(BookDao.class); 
        System.out.println(bookDao1); 
        System.out.println(bookDao2); 
        ctx.close(); //关闭容器 
    } 
}

4.运行App,类查看打印结果,证明init和destroy方法都被执行了。
init...
两个对象地址
destory...

5.@PostConstruct和@PreDestroy注解如果找不到,需要导入下面的jar包
//原因是从JDK9以后jdk中的javax.annotation包被移除了,这两个注解刚好就在这个包中。
<dependency> 
    <groupId>javax.annotation</groupId> 
    <artifactId>javax.annotation-api</artifactId> 
    <version>1.3.2</version> 
</dependency>

 3.4 注解开发依赖注入

        Spring为了使用注解简化开发,并没有提供 构造函数注入 setter 注入 对应的注解,只提供了自动装配的注解实现。
环境准备:
创建一个Maven项目
pom.xml添加Spring的依赖
添加一个配置类SpringConfig

@Configuration 
@ComponentScan("com.itheima") 
public class SpringConfig { 
}

//添加BookDao、BookDaoImpl、BookService、BookServiceImpl类
public interface BookDao { 
    public void save(); 
}
@Repository 
public class BookDaoImpl implements BookDao { 
    public void save() { 
        System.out.println("book dao save ..." ); 
    } 
}

public interface BookService { 
    public void save(); 
}
@Service 
public class BookServiceImpl implements BookService { 
    private BookDao bookDao; 
    public void setBookDao(BookDao bookDao) { 
        this.bookDao = bookDao; 
    }
    public void save() { 
        System.out.println("book service save ..."); 
        bookDao.save(); 
    } 
}

创建运行App
public class App { 
    public static void main(String[] args) { 
        AnnotationConfigApplicationContext ctx = new 
AnnotationConfigApplicationContext(SpringConfig.class); 
        BookService bookService = ctx.getBean(BookService.class); 
        bookService.save();
    } 
}

运行App出现问题的原因是,在BookServiceImpl类中添加了BookDao的属性,并提供了setter方法,
但是目前是没有提供配置注入BookDao,所以bookDao对象为Null,调用其save方法就会报控指针异常。

3.4.1 注解实现按类型注入

在BookServiceImpl类的bookDao属性上添加@Autowired注解
@Service 
public class BookServiceImpl implements BookService { 
    @Autowired 
    private BookDao bookDao; 
    // public void setBookDao(BookDao bookDao) { 
    // this.bookDao = bookDao; 
    // } 
    public void save() { 
        System.out.println("book service save ..."); 
        bookDao.save(); 
    } 
}

@Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将 
setter方法删除掉(对比非注解的自动装配byType和byName必须要setter方法)

为什么setter方法可以删除呢?
自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
普通反射只能获取public修饰的内容
暴力反射除了获取public修饰的内容还可以获取private修改的内容
所以此处无需提供setter方法

@Autowired是按照类型注入,那么对应BookDao接口如果有多个实现类,比如添加BookDaoImpl2
@Repository 
public class BookDaoImpl2 implements BookDao { 
    public void save() { 
        System.out.println("book dao save ...2"); 
    } 
}
这个时候再次运行App,就会报错
此时,按照类型注入就无法区分到底注入哪个对象,解决方案:按照名称注入
先给两个Dao类分别起个名称
@Repository("bookDao") 
public class BookDaoImpl implements BookDao { 
    public void save() { 
        System.out.println("book dao save ..." ); 
    } 
}
@Repository("bookDao2") 
public class BookDaoImpl2 implements BookDao { 
    public void save() { 
        System.out.println("book dao save ...2" );
    } 
}
此时就可以注入成功,但是得思考个问题:
@Autowired是按照类型注入,给BookDao的两个实现起了名称,它还是有两个bean对象,为什么不报错?
@Autowired默认按照类型自动装配,若IOC容器中同类的Bean找到多个,就按照变量名Bean的名称匹配。因为变量名叫bookDao而容器中也有一个booDao,所以可以成功注入。

若将第一个实现名称bookDao改成bookDao1则不能完成注入,因为按照类型会找到多个bean对象,
此时会按照bookDao名称去找,IOC容器只有名称叫bookDao1和bookDao2 ,
所以找不到,会报NoUniqueBeanDefinitionException

3.4.2 注解实现按名称注入

        当根据类型在容器中找到多个bean, 注入参数的属性名又和容器中 bean 的名称不一致,这个时候该如 何解决,就需要使用到@Qualifier 来指定注入哪个名称的 bean 对象。
@Service 
public class BookServiceImpl implements BookService { 
    @Autowired 
    @Qualifier("bookDao1") 
    //注意:@Qualifier不能独立使用,必须和@Autowired一起使用
    private BookDao bookDao; 
    public void save() { 
        System.out.println("book service save ..."); 
        bookDao.save(); 
    } 
}

3.4.3 简单数据类型注入

@Repository("bookDao") 
public class BookDaoImpl implements BookDao { 
    private String name; 
    public void save() { 
        System.out.println("book dao save ..." + name); 
    } 
}

数据类型换了,对应的注解也要跟着换,这次使用@Value注解,直接将值写入注解的参数中
注意数据格式要匹配,如将"abc"注入给int值,这样程序就会报错。
@Repository("bookDao") 
public class BookDaoImpl implements BookDao { 
    @Value("itheima")
    private String name; 
    public void save() { 
        System.out.println("book dao save ..." + name); 
    } 
}

        介绍完后,会有一种感觉就是这个@Value注解好像没什么用,跟直接赋值是一个效果,还没有直接赋值简 单,所以这个注解存在的意义是什么?

3.4.4 注解读取配置文件

@Value一般会被用在从properties配置文件中读取内容进行使用,具体如何实现?

1.resource下准备properties文件
name=itheima888

2.使用注解加载properties配置文件:在配置类上添加@PropertySource注解
@Configuration 
@ComponentScan("com.itheima") 
@PropertySource("jdbc.properties") 
public class SpringConfig { }

3.使用@Value读取配置文件中的内容
@Repository("bookDao") 
public class BookDaoImpl implements BookDao { 
    @Value("${name}") 
    private String name; 
    public void save() { 
        System.out.println("book dao save ..." + name); 
    } 
}

如果读取的properties配置文件有多个,可以使用@PropertySource的属性来指定多个
@PropertySource({"jdbc.properties","xxx.properties"})

@PropertySource注解属性中不支持使用通配符* ,运行会报错
@PropertySource({"*.properties"})

@PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件
@PropertySource({"classpath:jdbc.properties"})

3.5 注解管理第三方bean

        前面定义bean的时候都是在自己开发的类上面写个注解就完成了,但如果是第三方的类,这些类都是在jar包中,我们没有办法在类上面添加注解,这个时候该怎么办?

        遇到上述问题,我们就需要有一种更加灵活的方式来定义bean,这种方式不能在原始代码上面书写注解,一样能定义bean,这就用到了一个全新的注解@Bean。

1.添加一个配置类SpringConfig
@Configuration 
public class SpringConfig { }

2.添加BookDao、BookDaoImpl类
public interface BookDao { 
    public void save(); 
}
@Repository 
public class BookDaoImpl implements BookDao { 
    public void save() { 
        System.out.println("book dao save ..." ); 
    } 
}

3.创建运行类
public class App { 
    public static void main(String[] args) { 
        AnnotationConfigApplicationContext ctx = new 
AnnotationConfigApplicationContext(SpringConfig.class); 
    } 
}
在上述环境中完成对 Druid 数据源的管理,具体的实现步骤为 :
步骤1:导入对应的jar包
<dependency> 
    <groupId>com.alibaba</groupId> 
    <artifactId>druid</artifactId> 
    <version>1.1.16</version> 
</dependency>

步骤2:在配置类中添加一个方法并在方法上添加@Bean注解
//如果有多个bean要被Spring管理,直接在配置类中多些几个方法,方法上添加@Bean注解。
//注意:不能使用DataSource ds = new DruidDataSource()
//因为DataSource接口中没有对应的setter方法来设置属性。
@Configuration 
public class SpringConfig { 
    @Bean
    public DataSource dataSource(){ 
        DruidDataSource ds = new DruidDataSource(); 
        ds.setDriverClassName("com.mysql.jdbc.Driver"); 
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); 
        ds.setUsername("root"); 
        ds.setPassword("root"); 
        return ds; 
    } 
}

步骤3:从IOC容器中获取对象并打印
public class App { 
    public static void main(String[] args) { 
        AnnotationConfigApplicationContext ctx = new
AnnotationConfigApplicationContext(SpringConfig.class); 
        DataSource dataSource = ctx.getBean(DataSource.class); 
        System.out.println(dataSource); 
    } 
}

3.6 引入外部配置类

        如果把所有的第三方bean 都配置到 Spring 的配置类 SpringConfig 中,虽然可以,但是不利于代码阅 读和分类管理,所有我们就想能不能按照类别将这些bean 配置到不同的配置类中 ?
        对于数据源的bean,我们新建一个JdbcConfig配置类,并把数据源配置到该类下。
public class JdbcConfig { 
    @Bean
    public DataSource dataSource(){ 
        DruidDataSource ds = new DruidDataSource(); 
        ds.setDriverClassName("com.mysql.jdbc.Driver"); 
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); 
        ds.setUsername("root"); 
        ds.setPassword("root"); 
        return ds; 
    } 
}
现在的问题是,这个配置类如何能被 Spring 配置类加载到,并创建 DataSource 对象在 IOC 容器中 ?
针对这个问题,有两个解决方案 :
方式一:使用包扫描引入
1.:在Spring的配置类上添加包扫描
@Configuration 
@ComponentScan("com.itheima.config") 
public class SpringConfig { }

2.在JdbcConfig上添加配置注解
@Configuration
public class JdbcConfig { 
    @Bean
    public DataSource dataSource(){ 
        DruidDataSource ds = new DruidDataSource(); 
        ds.setDriverClassName("com.mysql.jdbc.Driver"); 
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); 
        ds.setUsername("root"); 
        ds.setPassword("root"); 
        return ds; 
    } 
}

3.运行程序,能获取到bean对象并打印控制台。
这种方式虽然能够扫描到,但是不能很快的知晓都引入了哪些配置类,所有这种方式不推荐使用。

方式二:使用@Import引入
方案一实现起来有点小复杂,Spring早就想到了这一点,于是又给我们提供了第二种方案。
这种方案可以不用加@Configuration注解,但是必须在Spring配置类上使用@Import注解手动
引入需要加载的配置类
1.:在Spring配置类中引入
@Configuration 
//@ComponentScan("com.itheima.config") 
@Import({JdbcConfig.class}) 
public class SpringConfig { }

注意:
扫描注解可以移除
@Import参数需要的是一个数组,可以引入多个配置类

3.7 注解为第三方bean注入资源

3.7.1 简单数据类型

        对于下面代码关于数据库的四要素不应该写死在代码中,应该是从properties 配置文件中读取。如何来优化下面的代码?
public class JdbcConfig { 
    @Bean
    public DataSource dataSource(){ 
        DruidDataSource ds = new DruidDataSource(); 
        ds.setDriverClassName("com.mysql.jdbc.Driver"); 
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); 
        ds.setUsername("root"); 
        ds.setPassword("root"); 
        return ds; 
    } 
}

优化:
1.准备properties文件
jdbc.driver=com.mysql.jdbc.Driver 
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db 
jdbc.username=root 
jdbc.password=root

2.在主配置类上添加@PropertySource注解
@PropertySource("jdbc.properties") 

3.更改JDBC配置类
public class JdbcConfig { 
    @Value("${jdbc.driver}") 
    private String driver; 
    @Value("${jdbc.url}") 
    private String url; 
    @Value("${jdbc.username}") 
    private String userName; 
    @Value("${jdbc.password}") 
    private String password;
    @Bean
    public DataSource dataSource(){ 
        DruidDataSource ds = new DruidDataSource(); 
        ds.setDriverClassName(driver); 
        ds.setUrl(url); 
        ds.setUsername(userName); 
        ds.setPassword(password); 
        return ds; 
    } 
}

3.7.1 引用数据类型

        假设在构建DataSource 对象的时候,需要用到 BookDao 对象,该如何把 BookDao 对象注入进方法内 让其使用呢?
步骤1:在SpringConfig中扫描BookDao
扫描的目的是让Spring能管理到BookDao,也就是说要让IOC容器中有一个bookDao对象
@Configuration 
@ComponentScan("com.itheima.dao") 
@Import({JdbcConfig.class}) 
public class SpringConfig { }

步骤2:在JdbcConfig类的方法上添加参数
public class JdbcConfig {
    @Bean
    public DataSource dataSource(BookDao bookDao){
        System.out.println(bookDao); 
        DruidDataSource ds = new DruidDataSource(); 
        ds.setDriverClassName(driver); 
        ds.setUrl(url); 
        ds.setUsername(userName); 
        ds.setPassword(password); 
        return ds; 
    } 
}

引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象。

步骤3:运行程序

四、Spring整合Mybatis

        在进行企业级开发的时候,其实除了将自己写的类让Spring 管理之外,还有一部分重要的

工作就是使用第三方的技术

4.1 环境准备

1.创建数据库表
create database spring_db character set utf8; 
use spring_db; 
create table tbl_account( 
    id int primary key auto_increment, 
    name varchar(35), 
    money double 
);

2.导入pom.xml相关依赖(略)

3.根据表创建模型类
public class Account implements Serializable { 
    private Integer id; 
    private String name; 
    private Double money; 
    //setter...getter...toString...方法略 
}

4.创建Dao接口(Mybatis注解方式)
public interface AccountDao { 
    @Insert("insert into tbl_account(name,money)values(#{name},#{money})") 
    void save(Account account); 
    @Delete("delete from tbl_account where id = #{id} ") 
    void delete(Integer id); 
    @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
    void update(Account account); 
    @Select("select * from tbl_account") 
    List<Account> findAll(); 
    @Select("select * from tbl_account where id = #{id} ") 
    Account findById(Integer id); 
}

5.创建Service接口和实现类
public interface AccountService { 
    void save(Account account); 
    void delete(Integer id); 
    void update(Account account); 
    List<Account> findAll(); 
    Account findById(Integer id); 
}

@Service 
public class AccountServiceImpl implements AccountService { 
    @Autowired 
    private AccountDao accountDao; 
    public void save(Account account) { 
        accountDao.save(account); 
    }
    public void update(Account account){ 
        accountDao.update(account); 
    }
    public void delete(Integer id) { 
        accountDao.delete(id); 
    }
    public Account findById(Integer id) { 
        return accountDao.findById(id); 
    }
    public List<Account> findAll() { 
        return accountDao.findAll(); 
    } 
}

6.添加jdbc.properties文件:resources目录下添加,用于配置数据库连接四要素
//useSSL:关闭MySQL的SSL连接
jdbc.driver=com.mysql.jdbc.Driver 
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false 
jdbc.username=root 
jdbc.password=root

7.添加Mybatis核心配置文件
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPE configuration
    PUBLIC"-//mybatis.org//DTDConfig3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--读取外部properties配置文件--> 
    <properties resource="jdbc.properties"></properties> 
    <typeAliases> 
        <!-- 包扫描方式,自动注册该包下所有类的别名 -->
        <!--MyBatis会自动为指定包下的所有类注册别名,别名默认为类名-->
        <package name="com.itheima.domain"/> 
    </typeAliases>
    <!--数据源--> 
    <environments default="mysql"> 
        <environment id="mysql"> 
            <transactionManager type="JDBC"></transactionManager> 
            <dataSource type="POOLED"> 
                <property name="driver" value="${jdbc.driver}"></property> 
                <property name="url" value="${jdbc.url}"></property> 
                <property name="username" value="${jdbc.username}"></property> 
                <property name="password" value="${jdbc.password}"></property> 
            </dataSource> 
        </environment> 
    </environments>
    <!--映射文件扫描包路径-->
    <mappers>
       <package name="com.itheima.dao"></package>
    </mappers>
</configuration>

8.编写应用程序
public class App { 
    public static void main(String[] args) throws IOException { 
        // 1. 创建SqlSessionFactoryBuilder对象 
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = 
new SqlSessionFactoryBuilder(); 
        // 2. 加载SqlMapConfig.xml配置文件(核心配置文件)
        InputStream inputStream = 
Resources.getResourceAsStream("SqlMapConfig.xml"); 
        // 3. 创建SqlSessionFactory对象 
        SqlSessionFactory sqlSessionFactory = 
sqlSessionFactoryBuilder.build(inputStream); 
        // 4. 获取SqlSession 
        SqlSession sqlSession = sqlSessionFactory.openSession(); 
        // 5. 执行SqlSession对象执行查询,获取结果User 
        AccountDao accountDao = sqlSession.getMapper(AccountDao.class); 
        Account ac = accountDao.findById(1); 
        System.out.println(ac); 
        // 6. 释放资源 
        sqlSession.close(); 
    } 
}

9.运行程序
Account{id=1, name='Tom',money=1000.0}

4.2 整合思路分析

Mybatis的基础环境准备好后,接下来分析下在上述的内容中,哪些对象可以交给Spring来管理?

从图中可以获取到,真正需要交给Spring管理的是SqlSessionFactory
整合Mybatis,就是将Mybatis用到的内容交给Spring管理,分析下配置文件

第一行读取外部配置文件,Spring有提供具体的解决方案@PropertySource ,需要交给Spring。
第二行起别名包扫描,为SqlSessionFactory服务的,需要交给Spring。
第三行主要用于做连接池,Spring之前我们已经整合了Druid连接池,这块也需要交给Spring。
        前面三行一起都是为了创建SqlSession对象用的,那么用Spring管理SqlSession对象吗?
        SqlSession是由SqlSessionFactory创建出来的,所以只需要管理SqlSessionFactory即可。
第四行是Mapper接口和映射文件[如果使用注解就没有该映射文件],这个是在获取到SqlSession以后执行具体操作的时候用,所以它和SqlSessionFactory创建的时机都不在同一个时间,可能需要单独管理。

4.3 Spring整合MyBatis步骤

前面我们已经分析了Spring与Mybatis的整合,大体需要做两件事:

第一件事是:Spring要管理MyBatis中的SqlSessionFactory。

第二件事是:Spring要管理Mapper接口的扫描。

1.项目中导入整合需要的jar包
<dependency> 
    <!--Spring操作数据库需要该jar包--> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-jdbc</artifactId> 
    <version>5.2.10.RELEASE</version>
</dependency> 
<dependency> 
    <!--Spring与Mybatis整合的jar包 这个jar包mybatis在前面,是Mybatis提供的 -->         
    <groupId>org.mybatis</groupId> 
    <artifactId>mybatis-spring</artifactId> 
    <version>1.3.0</version> 
</dependency>

2.:创建Spring的主配置类
//配置类注解 
@Configuration 
//包扫描,主要扫描的是项目中的AccountServiceImpl类 
@ComponentScan("com.itheima") 
public class SpringConfig { }

3.创建数据源的配置类
public class JdbcConfig { 
    @Value("${jdbc.driver}") 
    private String driver; 
    @Value("${jdbc.url}") 
    private String url; 
    @Value("${jdbc.username}") 
    private String userName; 
    @Value("${jdbc.password}") 
    private String password;
    @Bean
    public DataSource dataSource(){ 
        DruidDataSource ds = new DruidDataSource(); 
        ds.setDriverClassName(driver); 
        ds.setUrl(url); 
        ds.setUsername(userName); 
        ds.setPassword(password); 
        return ds; 
    } 
}


4.创建Mybatis配置类并配置SqlSessionFactory
public class MybatisConfig { 
    //定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象 
    @Bean 
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); 
        //设置模型类的别名扫描 
        ssfb.setTypeAliasesPackage("com.itheima.domain"); 
        //设置数据源 
        ssfb.setDataSource(dataSource); 
        return ssfb; 
    }
    //定义bean,返回MapperScannerConfigurer对象 
    @Bean 
    public MapperScannerConfigurer mapperScannerConfigurer(){
        //MapperScannerConfigurer对象也是MyBatis提供的专用于整合的jar包中的类,用来
        //处理原始配置文件中的mappers相关配置,加载数据层的Mapper接口类 
        MapperScannerConfigurer msc = new MapperScannerConfigurer(); 
        msc.setBasePackage("com.itheima.dao"); 
        return msc; 
    } 
}

5.主配置类中读properties并引入数据源配置类
  主配置类中引入Mybatis配置类
@Configuration 
@ComponentScan("com.itheima") 
@PropertySource("jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class}) 
public class SpringConfig { }

6.编写运行类
public class App2 { 
    public static void main(String[] args) { 
        ApplicationContext ctx = 
new AnnotationConfigApplicationContext(SpringConfig.class); 
        AccountService accountService = ctx.getBean(AccountService.class); 
        Account ac = accountService.findById(1); 
        System.out.println(ac); 
    } 
}

支持Spring与Mybatis的整合就已经完成了,其中主要用到的两个类分别是:
SqlSessionFactoryBean和MapperScannerConfigurer

4.4 Spring整合Junit

        整合Junit 与整合 Druid MyBatis 差异比较大,为什么呢? Junit 是一个搞单元测试用的工具,它不是我们程序的主体,也不会参加最终程序的运行,从作用上来说就和之前的东西不一样,它不是做功能的,看做是一个辅助工具就可以了。
环境准备:直接使用 Spring Mybatis 整合的环境
1.引入依赖(lue)

2.编写测试类
在test\java下创建一个AccountServiceTest,这个名字任意
//设置类运行器 
@RunWith(SpringJUnit4ClassRunner.class) 
//设置Spring环境对应的配置类 
@ContextConfiguration(classes = {SpringConfiguration.class}) //加载配置类
//@ContextConfiguration(locations={"applicationContext.xml"})//加载配置文件 
public class AccountServiceTest { 
//支持自动装配注入bean 
    @Autowired 
    private AccountService accountService;
    @Test 
    public void testFindById(){ 
        System.out.println(accountService.findById(1)); 
    }
    @Test 
    public void testFindAll(){ 
        System.out.println(accountService.findAll()); 
    } 
}

单元测试,如果测试的是注解配置类,则使用@ContextConfiguration(classes = 配置 类.class)
单元测试,如果测试的是配置文件,则使用@ContextConfiguration(locations={配置文件 名,...})
Junit运行后是基于Spring环境运行的,所以Spring提供了一个专用的类运行器,这个务必要设
置,这个类运行器就在Spring的测试专用包中提供的,导入的坐标就是SpringJUnit4ClassRunner
上面两个配置都是固定格式,当需要测试哪个bean时,使用自动装配加载对应的对象,下面的工
作就和以前做Junit单元测试完全一样了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值