框架学习-2.Spring-2.基于注解的IOC配置及Spring整合Junit

2.基于注解的IOC配置及Spring整合Junit 总览

参考狂神说MyBatis课程笔记

参考黑马程序员Spring课程笔记

Spring笔记思维导图:

思维导图笔记链接

问题扩展与汇总:

1. Bean的自动装配

1.1 自动装配的理解

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

  1. 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean
  2. 自动装配(autowiring):spring自动满足bean之间的依赖,减少配置文件中的注入,也就是我们说的IoC/DI;

组件扫描和自动装配组合发挥巨大威力,使的显示的配置降低到最少,工厂模式效果更加明显

1.2 基于xml的自动装配

1)环境搭建(基于xml手动装配)
导入依赖包
<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring.version>5.2.9.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
  • spring 需要导入commons-logging进行日志记录 .我们利用maven , 他会自动下载对应的依赖项
    • 导入spring-webmvc ,会自动导入Spring几个需要用的包
新建三个entity实体类
public class Cat {
    public void shout() {
        System.out.println("miao~");
    }
}
public class Dog {
    public void shout() {
        System.out.println("wang~");
    }
}
public class User {
    private Cat cat;
    private Dog dog;
    private String name;
}
配置文件导入支持自动注解的约束以及注入三个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">
    
    <bean id="dog" class="com.sheng.entity.Dog"/>
    <bean id="cat" class="com.sheng.entity.Cat"/>
    <bean id="user" class="com.sheng.entity.User">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
        <property name="name" value="user1"/>
    </bean>
</beans>
测试
public class MyTest {
    @Test
    public void testMethodAutowire() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
        user.getCat().shout();
        user.getDog().shout();
    }
}
miao~
wang~
2)byName

autowire byName (按名称自动装配)

  • 由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。

  • 采用自动装配将避免这些错误,并且使配置简单化

修改bean配置,增加一个属性 autowire=”byName”

  • 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
  • 去spring容器中寻找是否有此字符串名称id的对象,即查看对象是否已经装配到容器中
  • 如果有,就取出注入;如果没有,就报空指针异常
<?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="dog" class="com.sheng.entity.Dog"/>
    <bean id="cat" class="com.sheng.entity.Cat"/>
    
<!--    容器自动通过set方法名查找对应字符串名称的id,并自动注入该对象中-->
    <bean id="user" class="com.sheng.entity.User" autowire="byName">
        <property name="name" value="user1"/>
    </bean>
</beans>
3)byType

autowire byType (按类型自动装配)

  • 使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一
  • 如果不唯一,会报不唯一的异常。
<!--    容器自动通过set方法名查找对应字符串名称的id,并自动注入该对象中-->
    <bean id="user" class="com.sheng.entity.User" autowire="byType">
        <property name="name" value="user1"/>
    </bean>
</beans>

再注册一个cat 的bean对象!

<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>

<bean id="user" class="com.kuang.pojo.User" autowire="byType">
    <property name="name" value="user1"/>
</bean>
  1. 测试,报错:NoUniqueBeanDefinitionException
  2. 删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。
    • 甚至将id属性去掉,也不影响结果。
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>

1.3 基于注解的自动装配

1)环境搭建:导入约束和注解支持
  • 在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
  • 开启属性注解支持!
<context:annotation-config/>
2)@Autowired
  • @Autowired按类型自动转配的,不支持id匹配。
    • 即,与byType功能类似,不是byName
    • 默认name是类的名字
  • 需要导入 spring-aop的包!

测试:

  • 将User类中的set方法去掉,使用@Autowired注解,
    • 自动将bean装入目标对象User的bean中,无需set注入
public class User {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
    public Cat getCat() {
        return cat;
    }
    public Dog getDog() {
        return dog;
    }
    public String getName() {
        return name;
    }
}
  • 配置文件,只需将类注入到IOC容器中即可,bean之间的依赖,IOC容器会根据注解信息自动装配管理
<bean id="dog" class="com.sheng.entity.Dog"/>
    <bean id="cat2" class="com.sheng.entity.Cat"/>
    <bean id="user" class="com.sheng.entity.User">
        <property name="name" value="user1"/>
    </bean>
required属性
  • 表明要传入的参数或注入的对象是否一定要有,默认是true,必须得有,不能为null,否则报错

  • @Autowired(required=false) 说明: false,对象可以为null;true,对象必须存对象,不能为null。

//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
3)@Qualifier

测试:

  • 配置文件修改内容,保证类型存在对象。且id设置的name属性存在多个
	<bean id="dog1" class="com.sheng.entity.Dog"/>
    <bean id="dog2" class="com.sheng.entity.Dog"/>
    <bean id="cat1" class="com.sheng.entity.Cat"/>
    <bean id="cat2" class="com.sheng.entity.Cat"/>
  • 没有加Qualifier测试,直接报错
  • 在属性上添加Qualifier注解
@Autowired
@Qualifier(value = "cat2")
private Cat cat;

@Autowired
@Qualifier(value = "dog2")
private Dog dog;
4)@Resource
  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配
  • 如果以上都不成功,则按byType的方式自动装配
  • 都不成功,则报异常。
public class User {
    //如果允许对象为null,设置required = false,默认为true
    @Resource(name = "cat2")
    private Cat cat;
    @Resource
    private Dog dog;
    private String str;
}
    <bean id="dog" class="com.sheng.entity.Dog"/>
    <bean id="dog2" class="com.sheng.entity.Dog"/>
    <bean id="cat1" class="com.sheng.entity.Cat"/>
    <bean id="cat2" class="com.sheng.entity.Cat"/>
  • 测试ok,即先按照默认byName,类名的dog查找id=“dog”

配置文件2:beans.xml , 删掉cat2,修改dog的id名

<bean id="dog2" class="com.sheng.entity.Dog"/>
<bean id="cat1" class="com.sheng.entity.Cat"/>
@Resource
private Cat cat;
@Resource
private Dog dog;

结论:先进行byName查找,失败;再进行byType查找,成功。

@Autowired@Resource异同:
  1. @Autowired@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
  2. @Autowired默认按类型装配(属于spring规范),
    • 默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,
    • 如果我们想使用名称装配可以结合@Qualifier注解进行使用
  3. @Resource(属于J2EE复返),默认按照名称进行装配
    • 名称可以通过name属性进行指定。
    • 如果没有指定name属性,**当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 **
    • 当找不到与名称匹配的bean时才按照类型进行装配
    • 但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

2. 使用注解开发

1)使用注解需要的环境搭建

  • 在spring4之后,想要使用注解形式,必须得要引入aop的包
img
  • 在配置文件进行初始配置
    • 还得要引入一个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">
    
<!--开启注解支持-->
    <context:annotation-config/>
<!--指定注解扫描包-->
    <context:component-scan base-package="com.sheng"/>
</beans>

2)使用注解生成Bean

@Component
  • 配置扫描哪些包下的注解
<!--指定注解扫描包-->
    <context:component-scan base-package="com.sheng"/>
  • 在指定包下编写类,增加注解,自动将类生成bean,装配到IOC容器中
@Component
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    ...
}
@Component
public class Cat {
    public void shout() {
        System.out.println("miao~");
    }
}
@Component
public class Dog {
    public void shout() {
        System.out.println("wang~");
    }
}

测试

public class MyTest {
    @Test
    public void testMethodAutowire() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
        user.getCat().shout();
        user.getDog().shout();
    }
miao~
wang~
@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样

  • @Controller:web视图层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

3)属性数据注入

@Autowired
  • @Autowired按类型自动转配的,不支持id匹配。
    • 即,与byType功能类似,不是byName
    • 默认name是类的名字
@Qualifier
@Resource
  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配
  • 如果以上都不成功,则按byType的方式自动装配
  • 都不成功,则报异常
@value(“值”)
  • 可以不用提供set方法,直接在直接名上添加@value(“值”)
@Component
public class User {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;

    @Value("user1")
     // 相当于配置文件中 <property name="name" value="user1"/>
    private String name;
  • 如果提供了set方法,在set方法上添加@value(“值”);
@Value("user1")
    public void setName(String name) {
        this.name = name;
    }

4)用于改变作用范围

@Scope
  • 相当于:

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。

  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

@Component
@Scope("prototype")
public class User {
    @Autowired
//    @Resource
    private Cat cat;
    @Autowired
//    @Resource
    private Dog dog;
}

5)和生命周期相关的: (了解)

  • 相当于:
@PostConstruct
  • 作用:
    用于指定初始化方法。
@PreDestroy
  • 作用:
    用于指定销毁方法。

    • 在初始化方法上注解**@PostConstruct**,表明在构造对象后进行初始化

    • 在销毁方法前加注解**@PreDestroy**, 表明在销毁对象前,进行销毁方面逻辑的处理

    • @Service
      public class AlphaService {
          public AlphaService() {
              System.out.println("实例化构造AlphaService");
          }
      
          @PostConstruct
          public void init() {
              System.out.println("初始化AlphaService");
          }
      
          @PreDestroy
          public void destroy() {
              System.out.println("销毁AlphaService");
          }
      }
      
在这里插入图片描述

3. 关于 Spring 注解和 XML 的选择问题

  • 注解的优势

    • 配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
  • XML 的优势

    • 修改时,不用改源码。不涉及重新编译和部署。
  • Spring 管理 Bean 方式的比较:

在这里插入图片描述
  • xml与注解整合开发 :推荐最佳实践
    • xml管理Bean的定义
    • 注解完成属性注入
    • 使用过程中, 可以不用扫描,扫描是为了类上的注解

4. spring 的纯注解配置

@Configuration

  • JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息
  • 在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能
  • spring 的配置类,相当于 bean.xml 文件

作用:

  • 用于指定当前类是一个 spring 配置类, 当创建容器时会从该类上加载注解。 获取容器时需要使用
  • AnnotationApplicationContext(有@Configuration 注解的类.class)。
    • 当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。

属性:

  • value:用于指定配置类的字节码class

示例:

@Configuration
public class MyConfig {
}

@ComponentScan

作用:

  • 用于指定 spring 在初始化容器时要扫描的包。
  • 作用和在 spring 的 xml 配置文件中的:
    <context:component-scan base-package=“com.sheng”/>是一样的。

属性:

  • basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。

示例:

@Configuration
@ComponentScan("com.sheng")
public class MyConfig {
}

@Bean

作用:

  • 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器
  • 用此方法==可以将其他jar包中的类注入到IOC容器中==
  • 相当于注册的bean,

属性:

  • name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。
  • 如果不指定,这里的返回值就Bean的类型,方法名就是默认的bean的id!

细节:

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

    • @Configuration
      public class JdbcConfig {    
          @Bean(name="runner")
          @Scope("prototype")
          public QueryRunner createQueryRunner(@Qualifier("ds2") DataSource dataSource){
              return new QueryRunner(dataSource);
          }
      
          /**
           * 创建数据源对象
           * @return
           */
          @Bean(name="ds2")
          public DataSource createDataSource(){
              
          }
      }
      

综合测试示例

@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
@Configuration
@ComponentScan("com.sheng")
public class MyConfig {
    @Bean("user")
    public User getUser() {
        return new User();
    }
}
public class MyTest {
    @Test
    public void testMethodAutowire() {
        // 1.获取容器
        ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
        // 2.获取对象,并处理对象
        User user = ac.getBean("user", User.class);
        user.setName("user2");
        System.out.println(user.getName());
    }
user2

@Import

作用:

  • 用于导入其他配置类
    • 在引入其他配置类时,可以不用再写@Configuration 注解。 当然,写上也没问
      题。

属性:

  • value[]:用于指定其他配置类的字节码。

综合示例:

@Configuration
@ComponentScan("com.sheng")
@Import({AlphaConfig.class, JdbcConfig.class})
public class MyConfig {
//    @Bean("user")
//    public User getUser() {
//        return new User();
//    }
}
@Configuration
public class JdbcConfig {
    @Bean("jdbcUser")
    public User getUser() {
        return new User();
    }
}
@Configuration
public class AlphaConfig {
    @Bean("data")
    public SimpleDateFormat simpleDateFormat() {
        return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    }
}
public class MyTest {
    @Test
    public void testMethodAutowire() {
        // 1.获取容器
        ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
        // 2.获取对象,并处理对象
        User user = ac.getBean("jdbcUser", User.class);
        user.setName("user2");
        System.out.println(user.getName());

        SimpleDateFormat simpleDateFormat = ac.getBean("data", SimpleDateFormat.class);
        System.out.println(simpleDateFormat.format(new Date()));
    }
}
user2
2022-02-13 10:59:57

@PropertySource

作用:

  • 用于加载.properties 文件中的配置
    • 例如我们配置数据源时,可以把连接数据库的信息写到
      properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。

属性:

  • value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:

示例:

jdbc.properties 文件:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis2?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=@
jdbc.password=@

连接数据库的配置类:

import com.mchange.v2.c3p0.ComboPooledDataSource; // 导入的c3p0连接池坐标
import org.apache.commons.dbutils.QueryRunner; // 导入的dbutils数据库简单封装管理工具
/**
 * 和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")
//import org.apache.commons.dbutils.QueryRunner; // 导入的dbutils数据库简单封装管理工具
    public QueryRunner createQueryRunner(@Qualifier("ds2") DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    /**
     * 创建数据源对象
     * @return
     */
    @Bean(name="ds2")
    public DataSource createDataSource(){
        try {
            //import com.mchange.v2.c3p0.ComboPooledDataSource; // 导入的c3p0连接池坐标
            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);
        }
    }
}

在配置类中读取配置文件jdbcConfig.properties

@Configuration
@ComponentScan("com.sheng")
@Import({AlphaConfig.class, JdbcConfig.class})
// 读取配置文件
@PropertySource("classpath:jdbcConfig.properties")
public class MyConfig {
//    @Bean("user")
//    public User getUser() {
//        return new User();
//    }
}

扩展:SpringBoot自动注解方案

  • @SpringBootApplication注解的类就是配置类
在这里插入图片描述
  • @SpringBootApplication源码查看:
在这里插入图片描述
  • @SpringBootConfiguration结构
在这里插入图片描述
  • @EnableAutoConfiguration结构
在这里插入图片描述

5. Spring 整合 Junit

解决的问题:
  • 在测试类中,每个测试方法都有以下两行代码:

    // 1.获取容器
    ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
    // 2.获取对象,并处理对象
    User user = ac.getBean("jdbcUser", User.class);
    
  • 这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉

  • 程序能自动帮我们创建容器。一旦程序能自动为我们创建 spring 容器,我们就

    • 无须手动创建了,问题也就解决了。
  • junit 给我们暴露了一个注解,可以让我们替换掉它的运行器

    • 这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器。我们只需要告诉它配置文件在哪就行了。

在这里插入图片描述

配置的步骤:
  • 1、导入spring整合junit的jar(坐标)

    • 当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上

    • <!--导入junit测试坐标-->
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.12</version>
              </dependency>
      <!-- 导入spring整合junit的jar(坐标)      -->
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-test</artifactId>
                  <version>5.1.7.RELEASE</version>
              </dependency>
          </dependencies>
      
  • 2、使用Junit提供的一个注解的运行器把原有的main方法替换了,替换成spring提供的

    •  ==@Runwith()==
      
  • 3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置

    • @ContextConfiguration

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

      • @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration(locations= {"classpath:bean.xml"})
        public class AccountServiceTest {
        }
        
    • classes:指定注解类所在地位置

      • @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration(classes = MyConfig.class)
        public class MyTest {
        }
        

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfig.class)
public class MyTest {
    @Autowired
    private User user;

    @Autowired
    private SimpleDateFormat simpleDateFormat;

    @Test
    public void testMethodAutowire() {

        user.setName("user2");
        System.out.println(user.getName());

        System.out.println(simpleDateFormat.format(new Date()));
    }
}
user2
2022-02-14 12:23:00
扩展:SpringBoot整合测试类的源码
@RunWith(SpringRunner.class) // 运行器
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class) // 主配置类
public class CommunityApplicationTests{
}
  • SpringRunner源码:
在这里插入图片描述
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值