框架---Spring基础代码(上)(IOC)

spring完全笔记 详见4个文档。

在这里插入图片描述控制反转=IOC=Inversion Of Control=削减耦合

解决程序耦合的思路?
当是我们讲解 jdbc 时,是通过反射来注册驱动的,代码如下:
Class.forName(“com.mysql.jdbc.Driver”);//此处只是一个字符串
此时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译(运
行就不要想了,没有驱动不可能运行成功的)。
同时,也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改
源码。
解决这个问题也很简单,使用配置文件配置。

工厂模式解耦?
在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的
方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。
那么,这个读取配置文件,创建和获取三层对象的类就是工厂。

上一小节解耦的思路有 2 个问题:

1、存哪去?存到map=容器
分析:由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。
到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。
所以我们的答案就是
在应用加载时,创建一个 Map,用于存放三层对象。
我们把这个 map 称之为容器。

2、还是没解释什么是工厂?工厂=类
工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
原来:
我们在获取对象时,都是采用 new 的方式。是主动的。
在这里插入图片描述

现在:
我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。
这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。
在这里插入图片描述
一.用 IOC 解耦

【xml配置的方式】

1.在类的根路径下创建一个任意名称的 xml 文件(不能是中
文)
在这里插入图片描述
2.开始写xml
2.1 复制粘贴 约束
【在某个文件夹的网址里。
根据不同情况 复制不同约束。】
在这里插入图片描述

<?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">
</beans>

2.2让 spring 管理资源,在配置文件中配置 service 和 dao

<!-- 
bean 标签:
	用于配置让 spring 创建对象,并且存入 ioc 容器之中
	id 属性:对象的唯1标识。
    class 属性:指定要创建对象的全限定类名
-->

<!-- 配置 service --> 
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
</bean>

<!-- 配置 dao -->
 <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>

3.创建测试类:

/**
* 模拟一个表现层
*/
public class Client {
/**
 * 使用 main 方法获取容器测试执行
 */
public static void main(String[] args) {

//1.使用 ApplicationContext 接口,就是在获取 spring 容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

//2.根据 bean 的 id 获取对象【aService  、aDao】
IAccountService aService = (IAccountService) ac.getBean("accountService");
//(IAccountService) ac.getBean("accountService",IAccountService.class);
System.out.println(aService);

IAccountDao aDao = (IAccountDao) ac.getBean("accountDao");
System.out.println(aDao);
									} 

查看运行结果:
在这里插入图片描述
扩展:

set 方法注入=在类中提供需要注入成员的 set 方法。

A:
等待注入属性值 的 类:

public class AccountServiceImpl implements IAccountService {

private String name;
private Integer age;
private Date birthday;

public void setName(String name) {
this.name = name;
	}
public void setAge(Integer age) {
this.age = age;
	}
public void setBirthday(Date birthday) {
this.birthday = birthday;
	}
@Override
public void saveAccount() {
System.out.println(name+","+age+","+birthday);
	}
}

xml文件:

<!-- 
通过配置文件给 bean 中的属性传值:使用 set 方法的方式

name:找的是类中 set 方法后面的部分
ref:给属性赋值是其他 bean 类型的
value:给属性赋值是基本数据类型和 string 类型的

【实际开发中,此种方式用的较多】
-->

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
	<property name="name" value="test"></property>
	<property name="age" value="21"></property>
	<property name="birthday" ref="now"></property>
</bean>

<bean id="now" class="java.util.Date"></bean>

B:
xml 中 注入集合属性 =就是给类中的 数据类型是集合 的成员属性【也是set方法注入】
我们这里介绍注入数组,List,Set,Map,Properties。

public class AccountServiceImpl implements IAccountService {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
@Override
public void saveAccount() {
System.out.println(Arrays.toString(myStrs));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
System.out.println(myProps);
}
}

xml中:

<!-- 注入集合数据
List 结构的:
array,list,set
Map 结构的
map,entry,props,prop
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<!-- 在注入集合数据时,只要结构相同【 单个  或  键值对】,标签可以互换  -->

	<!-- 给  数组  注入数据 -->
	<property name="myStrs">
		<set>
			<value>AAA</value>
			<value>BBB</value>
			<value>CCC</value>
		</set>
	</property>
	
	<!-- 注入 set 、list   集合数据 -->
	<property name="mySet">
		<list>
			<value>AAA</value>
			<value>BBB</value>
			<value>CCC</value>
		</list>
	</property>
	
	<!-- 注入 Map 、properties 数据 -->
	<property name="myMap">
		<props>
			<prop key="testA">aaa</prop>
			<prop key="testB">bbb</prop>
		</props>
	</property>
	
</bean>

二.用 spring 的 IoC 的实现账户的CRUD
A.【xml配置的方式】

技术要求:

使用 spring 的 IoC 实现对象的管理
使用 DBAssit 作为持久层解决方案
使用 c3p0 数据源
  1. 创建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">
 
	<!-- 配置 service -->
	<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> 
		 <property name="accountDao" ref="accountDao"></property>
	</bean>
	
	<!-- 配置 dao --> 
	<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
		 <property name="dbAssit" ref="dbAssit"></property>
	</bean>
	
	<!-- 配置 dbAssit 此处我们只注入了数据源,表明每条语句独立事务-->
	 <bean id="dbAssit" class="com.itheima.dbassit.DBAssit">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置数据源 --> 
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 	
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///spring_day02"></property> <property name="user" value="root"></property> <property name="password" value="1234"></property>
	</bean>

</beans>

2.创建测试类:

/**
* 测试类
*/
public class AccountServiceTest {
/**
* 测试保存
*/
@Test
public void testSaveAccount() {
	Account account = new Account();
	account.setName("黑马程序员");
	account.setMoney(100000f);
	
	ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
	IAccountService as = ac.getBean("accountService",IAccountService.class);
	
	as.saveAccount(account);
}
/**
* 测试查询一个
*/
@Test
public void testFindAccountById() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
传智播客——专注于 Java、.Net 和 Php、网页平面设计工程师的培训
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
IAccountService as = ac.getBean("accountService",IAccountService.class);
Account account = as.findAccountById(1);
System.out.println(account);
}
/**
* 测试更新
*/
@Test
public void testUpdateAccount() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = ac.getBean("accountService",IAccountService.class);
Account account = as.findAccountById(1);
account.setMoney(20301050f);
as.updateAccount(account);
}
/**
* 测试删除
*/
@Test
public void testDeleteAccount() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = ac.getBean("accountService",IAccountService.class);
as.deleteAccount(1);
}
/**
* 测试查询所有
*/
@Test
public void testFindAllAccount() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = ac.getBean("accountService",IAccountService.class);
List<Account> list = as.findAllAccount();
for(Account account : list) {
System.out.println(account);
} } }

然后写了测试类后,我们可以发现,
如果要CURD
每个测试方法都要重新获取了一次 spring 的核心容器,造成了不必要的重复代码,增加了我们开发的工作量。这种情况,在开发中应该避免发生。
或者也能把容器的定义 提到类中,每个方法都能用。
这种方式虽然能解决问题,但是仍需要我们自己写代码来获取容器。

能不能测试时直接就 编写测试方法,而 不需要手动编码来获取容器呢?

怎么做?

B.【注解 配置IOC】
常用注解:
1.类上的 @Component =此类把资源让 spring 来管理

<bean id="" class="">
相当于

@Component
作用:
	表示 此类把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性:
	value:指定 bean 的 id。
	【如果不指定 value 属性,
	默认 bean 的 id 是当前类的类名首字母小写。】


@Controller @Service @Repository
他们三个注解都是针对 @Component的衍生注解,
作用及属性都是一模一样的。

只不过是提供了更加明确的语义化。
@Controller:一般用于表现层的注解。
@Service:一般用于业务层的注解。
@Repository:一般用于持久层的注解。

细节:
如果注解中有且只有一个属性要赋值时,
且名称是 value,value 在赋值是可以不写。
  1. @Autowired
相当于:
<property name="" ref=""> 
<property name="" value="">

自动按照 类型 注入。
当使用注解注入属性时,set 方法可以省略。
它只能注入其他 bean 类型。

当有多个类型匹配时,
使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,
找到了也可以注入成功。
找不到就报错。

@Qualifier

在以上自动按照类型注入的基础之上,
再按照 Bean 的 id 注入。

它在给字段注入时不能独立使用,
必须和@Autowire 一起使用;
但是给方法参数注入时,可以独立使用。

属性:
	value:指定 bean 的 id。

@Resource

作用:
	直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。
属性:
	name:指定 bean 的 id。

@Value

作用:
	注入 基本数据类型 和 String 类型数据
属性:
	value:用于指定值
  1. 指定作用范围
    @Scope
相当于:<bean id="" class="" scope="">

作用:
	指定 bean 的作用范围。
属性:
	value:指定范围的值。
	【取值:singleton prototype request session globalsession】

开始写吧:
注意:
当我们使用注解注入时,等待注入的类中 set 方法不用写
1.工程结构图:
在这里插入图片描述也可以这样:
在这里插入图片描述
此时的xml文件:
我们发现,之所以我们现在离不开 xml 配置文件,是因为我们有一句很关键的配置:

注意:
基于注解整合时,导入约束时需要多导入一个 context 名称空间下的约束。
由于我们使用了注解配置,此时不能在继承 JdbcDaoSupport,需要自己配置一个 JdbcTemplate
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
 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
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd">
 
 因为之前是xml文件,现在注解是在类中,
 所以要在xml中写清楚 扫描包,扫出注解
<!-- 告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中 --> 
<context:component-scan base-package="com.itheima"></context:component-scan>

如果他要也能用注解配置,那么我们就离脱离 xml 文件又进了一步。
另外,数据源和 JdbcTemplate 的配置也需要靠注解来实现。

<!-- 配置 dbAssit -->
 <bean id="dbAssit" class="com.itheima.dbassit.DBAssit">
 	 <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置数据源 -->
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 		
 	<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
 	<property name="jdbcUrl" value="jdbc:mysql:///spring_day02"></property> 
 	<property name="user" value="root"></property>
 	<property name="password" value="1234"></property>
</bean>

2.尝试用 类 代替以上xml文件:【详见day02代码最后1个】
2.1

package config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

import javax.sql.DataSource;

/**
 * 和spring连接数据库相关的配置类
 */
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;

    /**
     * 用于创建一个QueryRunner对象
     * @param dataSource
     * @return
     */
    @Bean(name="runner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(@Qualifier("ds2") DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    /**
     * 创建数据源对象
     * @return
     */
    @Bean(name="ds2")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    @Bean(name="ds1")
    public DataSource createDataSource1(){
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy02");
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

2.2

package config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

/**
 * 该类是一个配置类,它的作用和bean.xml是一样的
 * spring中的新注解
 * Configuration
 *     作用:指定当前类是一个配置类
 *     细节:当配置类作为AnnotationConfig ApplicationContext对象创建的参数时,该注解可以不写。
 * ComponentScan
 *      作用:用于通过注解指定spring在创建容器时要扫描的包
 *      属性:
 *          value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
 *                 我们使用此注解就等同于在xml中配置了:
 *                      <context:component-scan base-package="com.itheima"></context:component-scan>
 *  Bean
 *      作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
 *      属性:
 *          name:用于指定bean的id。当不写时,默认值是当前方法的名称
 *      细节:
 *          当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
 *          查找的方式和Autowired注解的作用是一样的
 *  Import
 *      作用:用于导入其他的配置类
 *      属性:
 *          value:用于指定其他配置类的字节码。
 *                  当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类
 *  PropertySource
 *      作用:用于指定properties文件的位置
 *      属性:
 *          value:指定文件的名称和路径。
 *                  关键字:classpath,表示类路径下
 */
//@Configuration
@ComponentScan("com.itheima")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {


}

2.3

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

2.4 测试类:

package com.itheima.test;

import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import config.SpringConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
 * 使用Junit单元测试:测试我们的配置
 * Spring整合junit的配置
 *      1、导入spring整合junit的jar(坐标)
 *      2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的
 * 			用@RunWith 注解替换原有运行器
 * 			详解见下方  *1
 *      3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
 *          @ContextConfiguration
 *                  locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
 *                  classes:指定注解类所在地位置
 *
 *   当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {

    @Autowired
    private IAccountService as = null;


    @Test
    public void testFindAll() {
        //3.执行方法
        List<Account> accounts = as.findAllAccount();
        for(Account account : accounts){
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne() {
        //3.执行方法
        Account account = as.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("test anno");
        account.setMoney(12345f);
        //3.执行方法
        as.saveAccount(account);

    }

    @Test
    public void testUpdate() {
        //3.执行方法
        Account account = as.findAccountById(4);
        account.setMoney(23456f);
        as.updateAccount(account);
    }

    @Test
    public void testDelete() {
        //3.执行方法
        as.deleteAccount(4);
    }
}

2.5

package com.itheima.test;

import config.SpringConfiguration;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 测试queryrunner是否单例
 */
public class QueryRunnerTest {

    @Test
    public  void  testQueryRunner(){
        //1.获取容易
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.获取queryRunner对象
        QueryRunner runner = ac.getBean("runner",QueryRunner.class);
        QueryRunner runner1 = ac.getBean("runner",QueryRunner.class);
        System.out.println(runner == runner1);
        //false
    }
}

详解:
*1 为什么需要
在这里插入图片描述在这里插入图片描述上面代码,由于junit的 主方法只执行了单元测试,
ioc容器没有搞起,导致as没有注入,导致空指针异常。

我们要替换Junit的主方法——使之 会创建IOC容器 :
1, pom.xml 中spring-test 依赖(是spring对junit的整合)
2.测试类上@RunWith(SpringJUnit4ClassRunner.class) 来替换成 此字节码的运行器
3.告知新的运行器我们是 xml 还是 注解 搞的。 位置在哪。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值