Spring_two

Day-two

1.1 数据源(连接池)的作用

1、数据源(连接池)是提高程序性能如出现的

2、事先实例化数据源,初始化部分连接资源

3、使用连接资源时从数据源中获取

4、使用完毕后将连接资源归还给数据源

5、常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等

开发步骤

①导入数据源的坐标数据库驱动坐标

②创建数据源对象

③设置数据源的基本连接数据

④使用数据源获取连接资源和归还连接资源

1.2 数据源的手动创建【c3p0+druid】

导入数据源的坐标数据库驱动坐标

<!-- C3P0连接池 -->
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

<!-- Druid连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

<!-- mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.39</version>
</dependency>

②c3p0

@Test
public void testC3P0() throws Exception {
	//创建数据源
	ComboPooledDataSource dataSource = new ComboPooledDataSource();
	//设置数据库连接参数
    dataSource.setDriverClass("com.mysql.jdbc.Driver");  //注册驱动  
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");	//连接数据库(ip+端口+数据库名)	               	       
    dataSource.setUser("root");//数据库账户密码
    dataSource.setPassword("root");
	//获得连接对象
	Connection connection = dataSource.getConnection();
	System.out.println(connection);
}

③druid

@Test
public void testDruid() throws Exception {
    //创建数据源
    DruidDataSource dataSource = new DruidDataSource();
    //设置数据库连接参数
    dataSource.setDriverClassName("com.mysql.jdbc.Driver"); 
    dataSource.setUrl("jdbc:mysql://localhost:3306/test");   
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    //获得连接对象
    Connection connection = dataSource.getConnection();    
    System.out.println(connection);
}

以上方式来使用数据库不方便解耦,怎么不方便法?你看setXXX设置都是固定的,写死的,这些是不是能够可以抽取?所以,我们要把数据库的一些信息放在配置文件中,到时候直接修改配置文件就好了。

那么,怎么操作?

①提取到jdbc.properties配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root

②读取jdbc.properties配置文件创建连接池

@Test
public void testC3P0ByProperties() throws Exception {
    //1、加载类路径下的jdbc.properties
    ResourceBundle rb = ResourceBundle.getBundle("jdbc");	【加载配置文件的名称】
    //2、创建连接池对象
    ComboPooledDataSource dataSource = new ComboPooledDataSource(); 
    //3、设置数据库基本信息
    dataSource.setDriverClass(rb.getString("jdbc.driver"));   【提取并设置数据库信息】
    dataSource.setJdbcUrl(rb.getString("jdbc.url")); 
    dataSource.setUser(rb.getString("jdbc.username")); 
    dataSource.setPassword(rb.getString("jdbc.password"));
    //4、创建数据库连接对象
    Connection connection = dataSource.getConnection();   
    System.out.println(connection);
}
  • ResourceBundle是Spring的工具类,用于加载运行时的类文件下(classes)的配置文件,这个运行时的classes文件,就相当于resourse下的配置文件。
    在这里插入图片描述

1.3 Spring配置数据源

上边是使用配置文件来解决解耦问题,但是我们还可以脱离掉上边propocies配置文件,并且还可以继续解耦。

我们分析一下:
连接池对象获取数据库连接对象,是不是要依赖数据库的一些配置信息才能获取?
而且,你看下面的代码:

//3、设置数据库基本信息
    dataSource.setDriverClass(rb.getString("jdbc.driver"));
    dataSource.setJdbcUrl(rb.getString("jdbc.url")); 
    dataSource.setUser(rb.getString("jdbc.username")); 
    dataSource.setPassword(rb.getString("jdbc.password"));

是不是有很多setXXX的方法来设置数据库的配置信息呢?而这些方法正是连接池对象的,说明它的内部有setxxx方法和相关的成员变量。
所以,我们可以将创建连接池对象的控制权交给Spring,在配置bean的时候使用set方式来注入连接池对象依赖的信息。

具体怎么实现?

①pom中配置Spring基本包坐标(Spring)
②pom中配置相关的jar包坐标(数据库驱动包)
③配置applicationContext的Bean+注入依赖
<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/test"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
</bean>

解析:

  • c3p0的连接池对象ComboPooledDataSource的全类名:class="com.mchange.v2.c3p0.ComboPooledDataSource
  • c3p0连接池对象的id唯一标识:id="dataSource"
  • ComboPooledDataSource中去掉setXXX的属性名:property name="driverClass"
  • ComboPooledDataSource中去掉setXXX的属性名的值:value="com.mysql.jdbc.Driver"

对比一下:

    dataSource.setDriverClass(rb.getString("jdbc.driver"));
    dataSource.setJdbcUrl(rb.getString("jdbc.url")); 
    dataSource.setUser(rb.getString("jdbc.username")); 
    dataSource.setPassword(rb.getString("jdbc.password"));
④使用Spring容器获取连接池对象。
    @Test
    //测试:从Spring容器获取连接池对象
    public void test02() throws Exception {
        //1、加载配置文件,创建Spring容器
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2、获取连接池对象
        //DataSource dataSource = (DataSource) app.getBean("dataSource");
        //ComboPooledDataSource dataSource = app.getBean(ComboPooledDataSource.class);
        DataSource dataSource = app.getBean(DataSource.class);
        //3、获取数据库连接对象
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    }

我之前就很疑惑,为什么哪个连接池对象的类型不是ComboPooledDataSource而是DataSource ?

哦,原来前者是厂商提供实现标准接口的实现类,后者是标准的接口。(接口是sun公司的,实现类是不同数据库厂商来写)
这俩都可以,但是一般使用DataSource,因为接口嘛,比较通用,使用ComboPooledDataSource只代表c3p0的厂商,如果使用别的还得改代码。

1.4 抽取jdbc配置文件

<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/test"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
</bean>

从这里可以看出Spring配置Bean对象时,注入依赖的数据库信息都是写死的。因为JDBC的配置不是通过加载配置文件来进行动态获取的,如果我们使用其他非Mysql数据,我们还得修改Bean配置
最主要是,一般开发的习惯是:把Spring配置和一些配置文件分开操作。

因此,这里的解决方案是: 把JDBC的信息提取出来,写到配置文件中。
然后通过SPEL(类似JSP的EL表达式)使用键值对的KEY获取值。

代码演示:

①配置resource下的jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/travel
jdbc.username=root
jdbc.password=root

②【applicationContext.xml引入jdbc配置文件】+【配置连接池对象】

在这里插入图片描述
③从Spring容器获取对象

 @Test
    //测试:从Spring容器获取连接池对象
    public void test02() throws Exception {
        //1、加载配置文件,创建Spring容器
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2、获取连接池对象
        //DataSource dataSource = (DataSource) app.getBean("dataSource");
        ComboPooledDataSource dataSource = app.getBean(ComboPooledDataSource.class);
        //3、获取数据库连接对象
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    }

2.1 Spring原始注解

Spring是个轻代码、重配置的的框架,而我们都知道使用XML配置的过程是十分繁琐的,所以我们使用注解代替XML标签来提高开发效率。

什么是原始注解?

原始注解主要是代替XML的Bean配置。 替换就意味着,不需要写XML配置了。

原始注解图:
注解说明解释参数
@Component使用在类上用于实例化Bean代替XML的Bean的作用该bean的ID标识(自定义)
@Controller使用在web层类上用于实例化Bean语义化区分👆该bean的ID标识 (自定义)
@Service使用在service层类上用于实例化Bean语义化区分👆该bean的ID标识(自定义)
@Repository使用在dao层类上用于实例化Bean语义化区分👆该bean的ID标识(自定义)
@Autowired使用在字段上用于根据类型依赖注入在成员变量位置上无。表示自动注入
@Qualifier结合@Autowired一起使用用于根据名称进行依赖注入在成员变量位置上被注入的对象的ID标识
@Resource相当于@Autowired+@Qualifier,按照名称进行注入在成员变量位置上被注入的对象的ID标识
@Value注入普通属性在成员变量上需要注入的值,一般会跟SPEL使用
@Scope标注Bean的作用范围在类上,表示Spring容器能够创建单个还是多个对象prototype:多例;singleton:单例
@PostConstruct使用在方法上标注该方法是Bean的初始化方法在成员方法上表示构造方法执行后执行某方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法在成员方法上表示在容器关闭时执行某方法

2.1.1 Spring原始注解:代码演示

XML方式:

此处省略了ApplicationContext.xml对于UserDaoIpml和UserServiceImpl的Bean配置,以及UserServiceImpl的依赖注入

UserDaoIpml

public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("I'm dao。。。。。");
    }
}

UserServiceIpml

public class UserServiceIpml implements UserService {
    //成员变量
    private UserDao userDao;
    //成员变量的set方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    //利用dao成员变量调用方法
    @Override
    public void save() {
        userDao.save();
    }
}

UserController(假设是web的控制层)

public class UserController {
    //假设为web控制层
    public static void main(String[] args) {
        //1、加载配置文件,创建Spring容器
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2、从容器内获取UserServiceImpl对象
        UserService service = (UserService) app.getBean("userService");
        //3、调用方法
        service.save();
    }
}

输出:

System.out.println("I'm dao。。。。。");

原始注解方式:

UserDaoImpl

使用@Compont或@Repository标识UserDaoImpl需要Spring进行实例化。
(告诉Spring,这个类你帮我创建)

/*
XML:
<!-- 配置UserDaoImpL-->
<bean id="userDao" class="cn.itcast.dao.Ipml.UserDaoImpl"></bean>*/

@Component("userDao")	//自定义id标识
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("I'm dao。。。。。");
    }
}

UserServiceIpml

/*
XML:
<!--配置UserService,并注入UserDao依赖-->
<bean id="userService" class="cn.itcast.service.Ipml.UserServiceIpml">
<property name="userDao" ref="userDao"></property>
</bean>
 */
 
@Component("userService")
public class UserServiceIpml implements UserService {
    @Autowired
    @Qualifier("userDao")	
    //成员变量
    private UserDao userDao;
    
    //成员变量的set方法
  	/* public void setUserDao(UserDao userDao) {	【XML使用set方法注入必须要有set方法,但是注解可以不用set方法来完成注入】
        this.userDao = userDao;
    }*/
    //利用dao成员变量调用方法
    @Override
    public void save() {
        userDao.save();
    }
}

  • 告诉Spring帮我进行实例化:

使用 【@Compont 】或 【@Service】标识UserServiceImpl

  • userDao的注入:

使用【@Autowired】或者【@Autowired+@Qulifier】或者【@Resource】

那么问题来了,【@Autowired】、【@Autowired+@Qulifier】和【@Resource】

有什么区别:

  • @Autowired:表示会按照数据类型从Spring容器进行匹配。

注意:它会先根据数据类型去容器里找对应的对象注入,如果该对象只有一个就直接注入成功;如果有多个同对象类型时,它会根据成员变量引用名称去容器里找,找到与之唯一匹配的对象id标识就注入成功。

  • @Autowired+@Qulifier:对于前面的补充,即,能够分得清多个不同的同对象类型具体是什么。
  • @Resource:相当于@Autowired+@Qulifier
    使用方法是:Resource(name=”被注入的对象ID标识“)

有什么相同点?

被依赖注入的对象必须得在spring容器中存在:可以是注解配置,也可以是xml文件配置。
如果容器中没有被依赖注入的Bean就会报错

控制层

public class UserController {
    //假设为web控制层
    public static void main(String[] args) {
        //1、加载配置文件,创建Spring容器
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2、从容器内获取UserServiceImpl对象
        UserService service = (UserService) app.getBean("userService");
        //3、调用方法
        service.save();
    }
}

【记得】ApplicationContext.XML
一定要写注解扫描,不然Spring不知道解析哪里的注解。

    <!--注解的组件扫描-->
<context:component-scan base-package="cn.itcast"/>

意思:
	告诉Spring要去知道的目录去寻找注解去解析,不写组件扫描会报错。

输出:

System.out.println("I'm dao。。。。。");

你使用注解的方法跟XML配置的效果一样的,但是使用注解会比较更简单一点,我们加个注解就好了,不需要指定什么类的全类名。XML写对象的全类名是为了告诉Spring去创建指定路径的对象,而注解就不需要了,因为它在那个类上了,Spring能找到注解,自然也知道它的全类名。

2.1.2 Spring原始注解:代码演示

@Value:

前面对成员变量的注入都是对象引用类型的,Value注入的是普通属性的。

位置:放在成员变量上,
参数: 可以直接传具体的值,也可以是通过SPEL的KEY获得值

    @Value("这是具体的值")
    private String str1;

    @Value("${jdbc.driver}")        //这个是jdbc配置文件中的key
    private String str2;

前提:

  • 这个成员变量的类也被Spring容器创建
  • 使用SPEL获取值,是从Spring容器中获取的,也就说,你得引入其他外部配置文件。(案列背景是:我们在此之前已经在applicationContext.xml引入jdbc的配置文件。)
@Scope:

位置:类上
参数:prototype:多例;singleton:单例
表示Spring容器是否可以创建单个或者多个Bean对象

//@Scope("prototype")
@Scope("singleton")
public class UserDaoImpl implements UserDao {
   //此处省略代码
}
@PostConstruct与 @PreDestroy(了解)

位置:成员方法上。
作用: 指定某个方法在构造器执行之后,还是Spring容器结束之后执行

@PostConstruct
public void init(){
	System.out.println("初始化方法....");
}
@PreDestroy
public void destroy(){
	System.out.println("销毁方法.....");
}

2.2 Spring新注解

在这里插入图片描述

新注解:汇总:
注解说明解释参数
@Configuration用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解意思:如果注解在该类上,表示该类就变成配置类,这个类替代了XML配置文件
@ComponentScan用于指定 Spring 在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package=“com.itheima”/>一样代替XML中注解的组件扫描“指定要扫描注解的目录”
@Bean使用在方法上,标注将该方法的返回值存储到 Spring 容器中代替XML中Bean标签,用于创建对象“给对象起个名”
@PropertySource用于加载.properties 文件中的配置代替XML中的加载配置文件的标签“"classpath:配置文件名+类型”
@Import用于导入其他配置类向主配置类引入其他分配置类。“其他类.class”
代码演示:

演示了配置类代替了XML的:

- 非自定义的Bean的配置:<bean>
- 加载properties文件的配置:<context:property-placeholder>
- 组件扫描的配置:<context:component-scan>

SpringCofiguration(代替了jdbc.properties)

         //标志着该类为Spring的核心配置类
@Configuration
        /*注解的组件扫描
<context:component-scan base-package="cn.itcast"/>*/
@ComponentScan("cn.itcast")

        /*加载外部的properties配置文件
<context:property-placeholder location="classpath:jdbc.properties"/>*/
@PropertySource("classpath:jdbc.properties")

public class SpringCofiguration {
        //定义成员变量并赋值
    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;
    
         //定义方法,返回连接池对象。【相当于XML的bean,这个Bean不是我们自己写的是别人写的(数据库厂商)】
    @Bean("dataSource")
    public DataSource getDataSource() throws Exception {//返回值是你要创建的Bean
        //1、创建连接池对象
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        //2、配置连接池信息
        //dataSource.setDriverClass("com.mysql.jdbc.Driver");//注册mysql驱动包
        dataSource.setDriverClass(driver);
        //dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/travel");//连接数据库
        dataSource.setJdbcUrl(url);
        //dataSource.setUser("root");//数据库登陆账户密码
        dataSource.setUser(username);
       // dataSource.setPassword("root");
        dataSource.setPassword(password);
        return dataSource;
    }

测试类

    @Test
    //测试:从Spring容器获取连接池对象
    public void test02() throws Exception {
        //1、加载配置文件,创建Spring容器
              //ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        ApplicationContext app = new AnnotationConfigApplicationContext(SpringCofiguration.class);
        //2、获取连接池对象
        //DataSource dataSource = (DataSource) app.getBean("dataSource");
        ComboPooledDataSource dataSource = app.getBean(ComboPooledDataSource.class);
        //3、获取数据库连接对象
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    	输出:connection地址
    }

在这里插入图片描述

这里要说的是,既然配置类可以代替XML来配置Spring容器信息,那么也可以代替XML引入其他配置文件。同样都是为了解决这个问题:当所有乱七八糟的类别配置文件信息堆在一起会很乱,所以我们需要把1个配置类作为主配置文件,把分配置文件引入其中。

下面演示主配置类和分配置类的样子:

主配置类:SpringCofiguration
         //标志着该类为Spring的核心配置类
@Configuration
        //注解的组件扫描
@ComponentScan("cn.itcast")
        //导入分配置类
@Import(DataSourceConfiguration.class)  //参数:配置类.class


public class SpringCofiguration {

}
分配置类:DataSourceConfiguration
//加载外部的properties配置文件
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {
    @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("dataSource")
    public DataSource getDataSource() throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);
        return dataSource;
    }

测试类不变啊,跟上边一样

但是我这里有个疑问:
你这里是导入1个配置类啊,那我向导入多个配置类怎么办呢?

看这里:

参数变成数组:
@Import({DataSourceConfiguration.class,xxx.class.......}) 

3. Spring整合Junit

3.1 原始Junit测试Spring的问题

在测试类中,每个测试方法都有以下两行代码:

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

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

3.2 上述问题解决思路

让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它 【指:指定加载配置文件或者配置类的名称,不然它不知道你测试哪个配置文件的对象】

将需要进行测试Bean直接在测试类中进行注入【】

3.3 Spring集成Junit步骤

①导入spring集成Junit的坐标 				【pom.xml导入Spring集成的Junit+基本包Junit】

②使用@Runwith注解替换原来的运行期			【runwith用谁来运行的意思。意思是:测试类的运行交给Spring的内核,Spring再交给Junit处理操作。以前我们测试时是直接找Junit的】

③使用@ContextConfiguration指定配置文件或配置类		【1、配置文件路径(classpath:xxx)  2、name:(classes:{配置类.class}):你得告诉我Spring容器在哪里】

④使用@Autowired注入需要测试的对象		【不需要创建对象了,直接@就可以注入对象】

⑤创建测试方法进行测试

代码实现:

①pom.xml导入Spring集成的Junit+基本包Junit

为什么导入Spring集成的Junit,还有导入基本包Junit?
因为前者依赖后者。

<!--此处需要注意的是,spring5 及以上版本要求 junit 的版本必须是 4.12 及以上-->
<!--Spring集成Junit-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

<!-- junit基本包-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
②使用@Runwith注解替换原来的运行期
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunitTest {
}
③使用@ContextConfiguration指定配置文件或配置类
@RunWith(SpringJUnit4ClassRunner.class)
	//加载spring核心配置文件
//@ContextConfiguration(value = {"classpath:applicationContext.xml"})
	//加载spring核心配置类
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
}
④使用@Autowired注入需要测试的对象

注入依赖对象就不需要New对象了,使用注解自动注入后直接可以使用(.)的方法是调用方法

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
    @Autowired
    private UserService userService;
}
⑤创建测试方法进行测试

@RunWith(SpringJUnit4ClassRunner.class)

//加载spring核心配置类
@ContextConfiguration(classes = {SpringConfiguration.class})

public class SpringJunitTest {
    @Autowired
    private UserService userService;
    @Test
    public void testUserService(){
   	 userService.save();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值