(38)Spring框架(2)Spring的依赖注入、常用的IOC注解、用spring框架创建一个案例实现CRUD、基于注解的IOC配置(依赖注入)

一、Spring的依赖注入

依赖注入:Dependency Injection
IOC的作用:降低程序间耦合(依赖关系)。
依赖关系的管理,以后都交给spring来维护,在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明。
依赖关系的维护就称之为依赖注入。

1.依赖注入:能注入的数据有三种

基本类型和String
其他bean类型(在配置文件中或者注解配置过的bean)
复杂类型/集合类型

2.依赖注入方式:有三种

1)第一种:使用构造函数提供

构造函数注入:
使用的标签:consteuctor-arg
标签出现的位置:bean标签的内部
(1)标签中的属性:
type:指定参数在构造函数中的数据类型
index:指定参数在构造函数参数列表的索引位置
name:指定参数在构造函数中的名称
=以上三个用于指定给构造函数中哪个参数赋值========
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件(spring的IOC核心配置)中配置过的 bean。
(2)优势:在获取bean对象时,注入数据是必须操作,否则对象无法创建成功。
(3)缺点:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。

    <bean id="accountService" class="com.anyi.service.impl.AccountServiceImpl" scope="singleton">
        <constructor-arg name="name" value="test"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="brithday" ref="now"></constructor-arg>
    </bean>
    <!--配置一个日期对象-->
    <bean id="now" class="java.util.Date"></bean>

2)第二种:使用set方法提供

set方法注入(更常用)
涉及的标签:propety
出现的位置:bean标签内部、
标签的属性:
name:指定注入时所调用的set方法名称
=以上三个用于指定给构造函数中哪个参数赋值========
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件(spring的IOC核心配置)中配置过的 bean对象
优点:创建对象时没有明确的限制,可以直接使用默认构造函数。
缺点:如果有某个成员必须有值,则获取对象时有可能set方法没有执行。

    <bean id="accountService2" class="com.anyi.service.impl.AccountServiceImpl2">
        <property name="name" value="TEST"></property>
        <property name="age" value="21"></property>
        <property name="brithday" ref="now"></property>
    </bean>

复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签:list、array、set
用于给Map集合注入的标签:map、props
结构相同,标签可以互换。

 <bean id="accountService3" class="com.anyi.service.impl.AccountServiceImpl3">
        <!-- 给数组注入数据 -->
        <property name="myStrs">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
        <!-- 注入 list 集合数据 -->
        <property name="myList">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>

        <!-- 注入 Map 数据 -->
        <property name="myMap">
            <props>
                <prop key="testA">aaa</prop>
                <prop key="testB">bbb</prop>
            </props>
         </property>

        <!-- 注入 properties 数据 -->
        <property name="myProps">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB"> <value>bbb</value></entry>
            </map>
        </property>
    </bean>

3)第三种:使用注解提供

二、常用的IOC注解(按照作用分类)、

曾经的xml配置:

<bean id="accountService" class="com.anyi.service.impl.AccountServiceImpl"
scope ="" inint-method="" destory-method="">
<property name="" value=""/ref=""></property>
</bean>

1.用于创建对象的:

他们的作用就和在xml配置文件中编写一个标签实现的功能是一样的
1)Component:
作用:用于把当前类对象存入spring容器中
属性:
value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
2)Controller:一般用于表现层
3)Service:一般用在业务层
4)Repository:一般用在持久层
以上三个注解他们的作用和属性于Component是一模一样的。他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰。

@Component(value = "accountService")//只有一个value可以不写
public class AccountServiceImpl implements IAccountService {

   private IAccountDao accountDao = new AccountDaoImpl();

   public AccountServiceImpl(){
       System.out.println("对象创建了");
   }

    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}

2.用于注入数据的:

他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的。
1)Autowried:

  • 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
  • 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
  • 如果ioc容器中有多个类型匹配时:
  • 出现位置:可以是变量上,也可以是方法上。
  • 细节:使用注解注入,set方法就不是必须的。

2) Qualifier

  • 作用:在按照类中注入的基础之上再按照名称注入,它在给类成员注入时不能单独使用。但是在给方法参数注入时可以单独使用
  • 属性: value:用于指定bean的id。

3)Resource

  • 作用:直接按照bean的id注入。它可以独立使用。
  • 属性: value:用于指定bean的id。
    以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。另外,集合类型的注入只能通过xml来实现。

3.用于改变作用范围的:

他们的作用就和在bean标签中使用scope属性实现的功能是一样的。

4.和生命周期相关的:

他们的作用就和在bean标签中使用init-method和destory-method的作用是一样的。

三、用spring框架创建一个案例实现CRUD

1.创建一个maven项目,在pom配置文件中配置需要用到的依赖jar包。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.anyi</groupId>
    <artifactId>0910_spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

2.创建数据库表。

在这里插入图片描述

3.创建三层架构的类、接口以及实现类。

在这里插入图片描述

4.创建并配置spring框架的核心文件

<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.anyi.service.impl.AccountServiceImpl">
        <!--注入dao对象(set方法注入)-->
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!--配置dao对象-->
    <bean id="accountDao" class="com.anyi.dao.impl.AccountDaoImpl">
    <!--注入QueryRunner(默认单例对象)-->
    <property name="runner" ref="runner"></property>
    </bean>
    <!--配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源(构造函数注入)-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </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://localhost:3306/user?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"></property>
        <property name="user" value="root"></property>
        <property name="password" value=""></property>
    </bean>
</beans>

5.测试类:

/**
 * @description:使用junit单元测试:测试我们的配置成功与否
 **/
public class AccountServiceTest {
    @Test
    public void testFindAll(){
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        List<Account> accounts = as.findAllAccount();
        for (Account account:accounts) {
            System.out.println(account);
        }
    }

#1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key。
这个错误1653的意思是:#1075-表定义不正确;只能有一个自动列,必须将其定义为主键。
如果出现此错误需要检查建表语句,如果设置了自增,需要把自增字段设置成主键。

四、基于注解的IOC配置(依赖注入)

业务层实现类和持久层实现类的改动:

/**
 * @description:业务层实现类
 **/
@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;
}
/**
 * @description:持久层实现类
 **/
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
    @Autowired
    private QueryRunner runner;
}

bean.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"
       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">
    <!--告知spring在创建容器时要扫描的包-->
    <context:component-scan base-package="com.anyi"></context:component-scan>
    <!--配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源(构造函数注入)-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </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://localhost:3306/user?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"></property>
        <property name="user" value="root"></property>
        <property name="password" value=""></property>
    </bean>
</beans>

1.创建一个配置类,它的作用和bean.xml是一样的

spring中的新注解
(1) Configuration
作用:指定当前类是一个配置类
(2) ComponentScan

  • 作用:用于通过注解指定spring在创建容器时要扫描的包
  • 属性:
  • value:它和backPackages的作用是一样的,都是用于指定创建容器时要扫描的包

我们使用此注解就等同于在xml文件中配置了:<context:component-scan base-package=“com.anyi”></context:component-scan>

(3)Bean

  • 作用:用于把当前方法返回值作为bean对象存入spring的ioc容器中
  • 属性:
  • name:用于指定bean的id(有默认值——当前方法的名称)

细节:当我们用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。 查找的方式和Autowired注解的作用是一样的

(4)import

  • 作用:用于导入其他的配置类
  • 属性:
  • value:用于指定其他配置类的字节码。
  • 当我们使用import的注解之后,有import注解的类就是父配置类,而导入的都是子配置类。
    (5)PropertySource
    作用:用于指定properties文件的位置
    属性:
    value:指定文件的名称和路径。
    关键字:classpath,表示类路径下
@Configuration
@ComponentScan(basePackages = "com.anyi")//<!--告知spring在创建容器时要扫描的包-->
public class SpringConfiguration {

    /***
    * @Description: 用于创建一个QueryRunner对象
    */
    @Bean(name = "runner")
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }
    /***
    * @Description: 创建数据源对象
    */
    @Bean(name = "dataSource")
    public DataSource createDataSource(){
       try {
           ComboPooledDataSource ds = new ComboPooledDataSource();
           ds.setDriverClass("com.mysql.jdbc.Driver");
           ds.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT");
           ds.setUser("root");
           ds.setPassword("");
           return ds;
       }catch (Exception e){
           throw new RuntimeException(e);
       }
    }
}

有了上面的配置类就可以取代bean.xml。

总结:在实际开发中可以选择纯注解配置核心容器,也可以使用xml文件配置,或者两种方法相结合。
选择的原则:首先考虑公司所使用的技术,如果能够自己选择,类存在于jar包中,选择xml文件配置更加方便;如果类是自己写的,用注解更方便(直接在类的前面写注解即可 )。

五、spring整合junit的配置

1.junit单元测试

1.应用程序的入口:main方法
2.junit单元测试中,没有main方法也能执行。
junit集成了一个main方法。
该方法就会判断当前测试类中哪些方法有@Test注解
junit就让有Test注解的方法执行。
3.junit不会管我们是否采用spring框架,在执行测试方法时,junit根本不知道我们是不是使用了spring,所有也就不会为我们读取配置文件和配置类创建spring核心容器。
4.由以上三点可知,当测试方法执行时,没有Ioc容器,就算写了Autowired注解,也无法实现。

2.导入整合需要的jar包坐标

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

3.使用junit提供的一个注解把原有的main方法替换了,替换成spring提供的

@RunWith(SpringJUnit4ClassRunner.class)

4.告知spring的运行器,spring和ioc创建是基于xml还是注解,并且说明位置。

@ContextConfiguration(classes = SpringConfiguration.class)

locations:指定xml文件的位置,加上classpath关键字,表示在类路径下。
classes:指定注解类所在位置。

最后记录一个坑了我一下午的错误:
jar包明明在项目里面,编译也不报错,但是运行的时候就是找不到。
(例如:Error:(4, 52) java: 程序包org.springframework.beans.factory.annotation不存在),ctrl+鼠标左键却能定位到jar包(说明jar成功导入)
在这里插入图片描述
报错原因:
IDEA启动程序按钮和maven的build使用的jar包环境不一样
解决方案:
设置idea构建/运行操作委托给maven就行了。具体设置:
Settings搜索Runner,勾选delegate ide build/run actions to maven
在这里插入图片描述
进行以上修改后可能会出现中文乱码:
原因:maven自带的字符集要与JDK提供的字符集保持一致。
我们可以在 File -->setting --> maven --> runner 里面的 VM Options 添加 -Dfile.encoding=GB2312 或者-Dfile.encoding=UTF-8
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值