依赖注入,spring基于注解的IoC,spring整合junit

依赖注入,spring基于注解的IoC,spring整合junit

环境:
Idea:2019.3.1
系统:windows10 家庭版
Jdk: 8
spring:5.0.3 release
spring文档
项目代码

依赖注入

spring中的依赖注入,依赖关系的管理,以后都交给spring来维护,在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明,依赖关系的维护,就称为依赖注入,但如果是经常变化的数据,并不适用于注入的方式

一.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></bean>
</beans>

二.数据类型

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

三.注入方式

1.构造函数注入

标签:<constructor-arg/>

位置:<bean></bean>内部
属性:
    type:    用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
    index:   用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始的
    name:    用于指定给构造函数中指定名称的参数赋值(最常用)
    value:   用于提供基本类型和String类型的数据
    ref:     用于指定其他的bean类型数据,它指的就是在spring的IoC核心容器中出现的bean对象
优势:    在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功,即当我们需要某个类的实例对象一定拥有值的时候,就可以用这种方式注入
弊端:    改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供

例子:

    <bean id="accountService" class="com.cbw.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="test"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        //birthday是date对象,spring无法用字符串转换,所以要创建一个date类型的bean对象, 再用ref属性引用
        <constructor-arg name="birthday" ref="birthday"></constructor-arg>
    </bean>
    //创建一个date类型bean供birthday使用
    <bean id="birthday" class="java.util.Date"></bean>
2.set方法注入(常用)

标签:<property/>

位置:<bean></bean>内部
属性:
    name:     用于指定注入时所调用的set方法名称
    value:    用于提供基本类型和String类型的数据
    ref:      用于指定其他的bean类型数据,它指的就是在spring的IoC核心容器中出现的bean对象
优势:    创建对象时没有明确的限制,可以直接使用默认构造函数,即使用无参构造函数都行
弊端:    如果有某个成员必须有值,则获取对象是有可能set方法没有执行,即可能调用的默认构造函数创建对象,然后用完了都没有调用set方法,也就没有值

例子:

    <bean id="accountServic" class="com.cbw.service.impl.AccountServiceImpl2">
        <property name="name" value="cbw"></property>
        <property name="age" value="23"></property>
        <property name="birthday" ref="birthday"></property>
    </bean>
    <bean id="birthday" class="java.util.Date"></bean>
3.注解注入

见下文

四.复杂类型的注入/集合类型的注入(能够使用构造方法和set方法注入,这里我们使用set方法)

1.用于给List结构集合注入的标签:
       <list></list> 
       <array></array>
       <set></set>
2.用于给Map结构集合注入的标签:
       <map></map> 
       <props></props>
注:结构相同,标签可以互换,也就是能共用标签,当然只能使用一种,这些标签的位置都是在<bean></bean>标签里面
字符串数组 <array></array>
                        <property name="myStrs">
                             <array>
                                 <value>sss</value>
                                 <value>fff</value>
                                 <value>ddd</value>
                             </array>
                        </property>
List<list></list>
                        <property name="myList">
                            <list>
                                <value>123</value>
                                <value>234</value>
                                <value>345</value>
                            </list>
                        </property>
Map <map></map>
                        <property name="myMap">
                            //想使用自定义bean,用key-ref="***" value-ref="***"属性连接外部bean
                            <map key-type="java.lang.String" value-type="java.lang.String">
                                //用value属性传值
                                <entry key="墨菲特" value="势不可挡" ></entry>
                                <entry key="亚托克斯" value="大灭"></entry>
                                //用value标签传值
                                <entry key="亚索">
                                    <value>狂风绝息斩</value>
                                </entry>
                            </map>
                        </property>
Set<set></set>
                        <property name="mySet">
                            <set>
                                <value>set1</value>
                                <value>set2</value>
                                <value>set3</value>
                            </set>
                        </property>
Properties <props></props>
                        <property name="myProperties">
                            <props>
                                <prop key="accountService1">唉唉唉</prop>
                                <prop key="accountService2">哈哈哈</prop>
                                <prop key="accountService3">嘻嘻嘻</prop>
                            </props>
                        </property>
用 key-ref=”“ value-ref=”“属性连接外部bean
                <bean id="now" class="java.util.Date"></bean>//自定义date对象
                <bean id="accountService" class="com.cbw.service.impl.AccountServiceImpl4">
                    <property name="myMap">
                        <map>
                            <entry key-ref="now" value-ref="now"></entry>
                        </map>
                    </property>
                </bean>

基于注解的IoC

一.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"
       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-3.0.xsd">

<!--告知spring在创建容器的时候要扫描的包,配置所需要的标签不是在beans的约束中,
    而是一个名称为context名称空间和约束中-->
<context:component-scan base-package="com.cbw">

</context:component-scan>

</beans>

二.创建对象相关

他们的作用就和在xml配置文件中编写一个<bean></bean>标签实现的功能是一样的

1.@Component
    作用:    用于把当前类对象存入spring容器
    位置:    类
    属性:
         value:用于指定bean的id,当我们不写时,默认值是当前类名,且首字母变小写
2.@Controller ———一般用在表现层
3.@Service ———–一般用在业务层
4.@Repository ——–一般用在持久层
      以上三个注解他们的作用和属性和Component是一模一样,他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层更加清晰

三.用于注入数据
他们的作用就和在xml配置文件中的bean标签中写一个property标签的作用是一样的

1.@Autowired
    作用:    自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
    位置:    方法,变量
    细节:    在使用注解注入时,set方法就不是必须的
    例子:
                                   @Repository("accountDaoImpl")
                                   public class AccountDaoImpl implements IAccountDao {
                                   @Autowired
                                   private IAccountDao iAccountDao;

                                   省略众多方法

                                   }
    注意:    IoC容器是一个Map类型,注解时的id是key,bean的类名是value,这里就是id=accountDaoImpl,value=AccountDaoImpl(implement IAccountDao也会记录下来)
    自动注入:
                         1.根据数据类型寻找value是一样的bean
                         2.如果IoC容器中,一个匹配的bean类型都没有,注入就会失败报错(如果是接口,那么他的实现类也能匹配)
                         3.如果IoC容器中,有bean匹配的时候:
                                1.有一个匹配的时候
                                       直接注入
                                2.有多个匹配的时候
                                       根据变量类型,圈定出所有匹配的bean
                                       根据变量名称,逐个比对圈定的bean的id值,如果有相同的,那么就匹配这个
                                       如果几个都一样,或者一个都没有,就会报错
2.@Qalifier
    作用:在按照类中注入的基础上再按照名称注入。它在给类成员注入时不能单独使用,必须和@Autowired同时使用。但是在给方法参数注入时可以单独使用
    属性:
         value:用于指定注入bean的id
    例子:
        @Autowired
        @Qualifier("accountDaoImpl")
        private IAccountDao accountDaoImpl;
3.@Resource
    作用:直接按照bean的id注入,可以单独使用
    属性:
        name:用于指定bean的id
    例子:
        @Resource(name = "accountDaoImpl")
        private IAccountDao accountDaoImpl;
**注意:** 以上三个注解,只能注入其他bean类型的数据,集合类型的注入只能通过xml来实现
4.@Value
    作用:用于注入基本类型和String类型的数据
    属性:
        value:    用于指定数据的值。它可以使用spring中的SpEL(也就是spring中的el表达式)
        SpEl写法: ${表达式}
        注意:     el表达式在哪里,就是谁的el表达式,如果出现在jsp里面,就是jsp的el表达式,如果写在spring的配置文件中就是spring的el表达式,如果写在mybatis的配置文件中,那么就是mybatis的配置文件中

四.作用范围相关

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

1.@Scope
     作用:用于指定bean的作用范围
     位置:类
     属性:
         value:指定范围的取值。取值和xml中bean标签中的scope属性一样,常用取值为singleton prototype,默认值是singleton

五.生命周期相关

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

1.@PreDestroy
作用:用于指定销毁方法
2.@PostConstruct
作用:用于指定初始化方法

用法:在bean类中,定义init()方法,用PostConstruct注解,定义destory()方法,用ProDestroy注解,bean对象在创建时候会执行init()方法,在销毁时会执行destroy()方法

xml和注解相结合的IoC

就是在applicationContext.xml文件中<context:component-scan/><bean/>一起使用
自定义类可以用注解也就是扫描包的形式加入IoC,而外部的包中的类可以用<bean/>标签来加入IoC

<?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-3.0.xsd">

<!--告知spring在创建容器的时候要扫描的包,配置所需要的标签不是在beans的约束中,
    而是一个名称为context名称空间和约束中-->
<context:component-scan base-package="com.cbw">
</context:component-scan>

<bean id="" class=""></bean>
<bean id="" class=""></bean>

</beans>

纯注解的IoC

根据xml配置文件获取容器

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

//这个同理,也可以传xml配置文件数组
根据配置类获取容器

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

//该构造方法可以传字节码数组,也就意味着,如果你有多个配置类,你都不想加@Configuration注解,那么就一起传进构造方法,用,隔开

1.创建一个Configuration类
作用:配置类,相当于applicationContext.xml文件
2.@Configuration
作用:指定当前类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注释可以不写,如果有多个配置类,不作为参数的配置类必须加上这个注解,且要在@ComponentScan注解中加入这个配置类,否则,扫描包的时候,spring不会去扫描该类下的其他注解
3.@ComponentScan
    作用:用于通过注释指定spring在创建容器时要扫描的包,相当于applicationContext.xml文件中<context:component-scan/>标签
    属性:
        basePackage:用于指定创建容器时要扫描的包
        value:它和basePackages的作用一样
    注意:
        1.这两个都是数组类型的,一个类的时候,直接写,多个类要用{}隔开
            @ComponentScan(basePackages = {"com.cbw","com.cbw.domain"})
        2.basePackage和value也可以不写,直接写字符串
            @ComponentScan("com.cbw")
4.@Bean
作用:用于把当前方法的返回值作为bean对象存入spring的核心容器中
属性:
    name:用于指定bean的id,当不写时,默认值是当前方法的名称
细节:当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象,查找方式和@AutoWired注解是一样的
5.@Import
作用:用来导入其他的配置类
位置:主配置类
属性:
    value:用于指定其他配置类的字节码,是个数组
细节:
    当我们使用import注解之后,有import注解的类就是父配置类,而导入的都是子配置类,而我们如果通过ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class)构造方法传入多个配置类,那他们就是并列关系,推荐使用第一种
6.@PropertySource与@PropertySources
作用:用于指定properties文件的位置
位置:主配置类
属性:
    value:指定文件的名称和路径
    关键字:classpath,表示类路径下,如果properties文件在包中,可以再加上包路径
格式:@PropertySource("classpath:jdbc.properties"),@PropertySource("classpath:包/包/jdbc.properties")
细节:前者只能传一个properties文件,后者可以传properties文件数组
7.例子
    @Configuration
    @ComponentScan("com.cbw")
    public class SpringConfiguration {
     /**
     * 用于创建一个QueryRuanner对象,并且存入springIoC容器中
     * @param dataSource
     * @return
     */
    @Bean("runner")
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    /**
     * 用于创建一个数据源对象,并且存入springIoC容器中
     * @return
     */
    @Bean("dataSource")
    public DataSource createDataSource(){
        ComboPooledDataSource ds = new ComboPooledDataSource();
        try {
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/myspringspace?useUnicode=true&characterEncoding=utf8");
            ds.setUser("root");
            ds.setPassword("123456");
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return ds;
    }
    }

spring整合Junit

问题分析

实际开发中,开发和测试是分开的,测试类中只能有测试方法,就像这样

而创建容器或者初始化对象都是没有的,这就引出来一串问题

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

解决

1.导入spring整合Junit的jar包(坐标)
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
2.使用Junit提供的一个注解替换原来Junit集成的main()方法,替换成spring提供的@Runwith注解

意思就是,Junit提供了一个注解替换自己的main方法,将它替换成spring提供的@Runwith注解
@RunWith标签

@RunWith(SpringJUnit4ClassRunner.class)
public class accountTest2 {
    private IAccountService accountService;
    ~
    ~
    一堆测试方法
    ~
    ~
}
3.告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置

@ContextConfiguration标签

属性:
    locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
    classes:指定注解类(配置类)所在地位置
注意:spring版本为5.x版本时,要求Junit的版本必须是4.12及以上
例子:

使用注解:

/**
 * 使用junit单元测试测试我们的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class accountTest2 {
    private IAccountService accountService;

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

        //3.执行方法
        List<Account> accountList = accountService.findAllAccount();
        for (Account aclist:accountList
             ) {
            System.out.println(aclist);
        }
    }
}

使用xml文件:

/**
 * 使用junit单元测试测试我们的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class accountTest2 {
    @Autowired
    private IAccountService accountService = null;

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

        //3.执行方法
        List<Account> accountList = accountService.findAllAccount();
        for (Account aclist:accountList
             ) {
            System.out.println(aclist);
        }
    }
}    

 上一篇
spring中的JdbcTemplate spring中的JdbcTemplate
spring中的JdbcTemplate 环境:Idea:2019.3.1系统:windows10 家庭版Jdk: 8spring:5.0.3 releasespring文档项目代码 1.概述它是spring框架中提供的一个对象,是对原始
2020-04-10
下一篇 
java对象的内存 java对象的内存
一个对象的内存首先要了解java中的内存分配机制 栈(stack):存放的都是方法中的局部变量,方法的运行在栈当中 局部变量:方法的参数,或者视方法体内部的变量 作用域:一旦超出作用域,立即从栈内存中消失
2020-02-11
  目录