基于注解的 IOC 配置

 

基于注解的 IoC 配置,和XML要实现的功能都是一样的,都是要降低程序之间的耦合,只是配置的形式不一样。
1、环境搭建: 建立maven工程,导入所需的maven坐标:
<?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.mingqi</groupId>
    <artifactId>springAnnoioc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>

        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>sqljdbc4</artifactId>
            <version>4.0</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.10</version>
        </dependency>
    </dependencies>

</project>

2、使用@Component 注解配置管理的资源

/**
* 账户的业务层实现类
*/
@Component("accountService")
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao; } }
/**
* 账户的持久层实现类
*/
@Component("accountDao")
public class AccountDaoImpl implements IAccountDao {
private DBAssit dbAssit; }
注意:
 当我们使用注解注入时,set 方法不用写
3、创建 spring 的 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.mingqi"></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.microsoft.sqlserver.jdbc.SQLServerDriver"/>
        <property name="jdbcUrl" value="jdbc:sqlserver://localhost:1433;DatabaseName=test"/>
        <property name="user" value="sa"/>
        <property name="password" value="123sa"/>
    </bean>
    <bean id="usersDao" class="com.mingqi.dao.impl.UsersDaoimpl">
        <property name="runner" ref="runner"></property>
    </bean>
    <bean id="usersService" class="com.mingqi.service.impl.UsersServiceImpl">
        <property name="usersDao" ref="usersDao"></property>
    </bean>
</beans>

4、常用注解

(1)用于创建对象的  相当于:<bean id="" class="">

       ① @Component   

            作用:把资源让 spring 来管理。相当于在 xml 中配置一个 bean。

            属性:value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。

        ② @Controller @Service @Repository 他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。他们只不过是提供了更加明确的语义化。

            @Controller:一般用于表现层的注解。
            @Service:一般用于业务层的注解。
            @Repository:一般用于持久层的注解。
            细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值是可以不写
(2)用于注入数据的 相当于:<property name="" ref=""> <property name="" value="">
      ① @Autowired 自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到 了也可以注入成功。找不到就报错。
      ②@Qualifier
         作用:在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和@Autowire 一起使用;但是给方法参数注入时,可以独立使用。
         属性:value:指定 bean 的 id
      ③@Resource
       作用:直接按照Bean的Id注入,他也只能注入其bean类型
       属性: name:指定bean的id
      ④@Value
       作用:注入基本数据类型和String数据类型
       属性:value:用于指定值
(3)用于改变作用范围的 相当于:<bean id="" class="" scop="">
   @Scope 作用:用于指定bean的作用范围   
    属性: value:指定范围的值:  取值:singleton(单例) prototype(多例)  request session  globalsession
 (4)和声明周期相关的
      ①@PostConstruct  作用:用于指定初始化方法
      ②@PreDestroy      作用:用于指定销毁方法
(5)关于 Spring 注解和 XML 的选择问题
注解的优势:
配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
XML 的优势:
修改时,不用改源码。不涉及重新编译和部署。
Spring 管理 Bean 方式的比较:

基于注解的 spring IoC 配置中,bean 对象的特点和基于 XML 配置是一模一样的,但是还是需要spring的xml配置文件,name能不能不写这个bean.xml,所有的配置都用注解来实现呢?

(6)新注解说明

      ①@Configuration 作用:用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(有@Configuration 注解的类.class)。

       属性:value:用于指定配置类的字节码
       
/*
 spring 的配置类,相当于 bean.xml 文件
 */
@Configuration
public class SpringConfig {
}
我们已经把配置文件用类来代替了,但是如何配置创建容器时要扫描的包呢?
请看下一个注解。
      ②@ComponentScan 作用:用于指定 spring 在初始化容器时要扫描的包。作用和在 spring 的 xml 配置文件中的:<context:component-scan base-package="com.itheima"/>是一样的。
        属性:basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。 

 

    ③@Bean  作用:该注解只能写在方法上,表明使用此方法创建一个对象,并放入spring容器中

                   属性:name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)

       
/*
 spring 的配置类,相当于 bean.xml 文件
 */
@Configuration
@ComponentScan("com.mingqi")
public class SpringConfig {

    private DataSource dataSource;
    private QueryRunner runner;

    /**
     * 注入dataSource
     * @return
     */
    @Bean(name="dataSource")
    public ComboPooledDataSource createDataSource() {
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setUser("sa");
            ds.setPassword("123sa");
            ds.setDriverClass("com.microsoft.sqlserver.jdbc.SQLServerDriver");
            ds.setJdbcUrl("jdbc:sqlserver://localhost:1433;DatabaseName=test");
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } }

    /**
     * 创建一个QueryRunner 并放入bean容器中
     * @param dataSource
     * @return
     */
    @Bean(name="runner")
    public QueryRunner runner(DataSource dataSource) {
        //this.dataSource = dataSource;
        return new QueryRunner(dataSource);
    }
}
我们已经把数据源和 QueryRunner从配置文件中移除了,此时可以删除 bean.xml 了。
但是由于没有了配置文件,创建数据源的配置又都写死在类中了。下面我们将它们从类中拿出来
 
④ @PropertySource  作用:加载.properties文件中的配置,例如我们配置数据源时,可以把链接数据库的信息写到properties配置文件中,就可以使用此注解指定properties配置文件的位置
   属性:value[] 指定properties文件位置,如果是在类路径下,需要写上classpath
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;
    @Bean(name="dataSource")
    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);
        } }
}

properties 文件内容:

jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc.url=jdbc:sqlserver://localhost:1433;DatabaseName=test
jdbc.username=sa
jdbc.password=123sa
此时我们已经有了两个配置类,但是他们还没有关系。如何建立他们的关系呢?
⑤ @Import 作用:用于导入其他配置类,在引入其他配置类时,可以不再写@Configuration注解,当然写上也没有什么问题
              属性:value[] 用于指定其他配置类的字节码
@Configuration
@PropertySource("classpath:com/mingqi/config/jdbc.properties")
public class JdbcConfig {
}

@Configuration
@ComponentScan("com.mingqi")
@Import({ JdbcConfig.class})
public class SpringConfig {
}

 

 (6) 通过注解获取容器:

        ApplicationContext ac =new AnnotationConfigApplicationContext(SpringConfiguration.class); 

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

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

IAccountService as = ac.getBean("accountService",IAccountService.class);

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

针对上述问题,我们需要的是程序能自动帮我们创建容器。一旦程序能自动为我们创建 spring 容器,我们就无须手动创建了,问题也就解决了。

junit 是无法实现的,因为它自己都无法知晓我们是否使用了 spring 框架,更不用说帮我们创建 spring 容器了。不过好在,junit 给我们暴露了一个注解,可以让我们替换掉它的运行器。这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器。我们只需要告诉它配置文件在哪就行了。
   ①第一步:拷贝整合 junit 的必备 jar 包到 lib 目录  此处需要注意的是,导入 jar 包时,需要导入一个 spring 中 aop 的 jar 包。
   ②使用@RunWith 注解替换原有运行器 
     @RunWith(SpringJUnit4ClassRunner.class)
public class AccountServiceTest {
} 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:bean.xml"})
public class AccountServiceTest {
}
/*
@ContextConfiguration 注解:
locations 属性:用于指定配置文件的位置。如果是类路径下,需要用 classpath:表明
classes 属性:用于指定注解的类。当不使用 xml 配置时,需要用此属性指定注解类的位置。
*/

  ③ 使用@Autowired 给测试类中的变量注入数据

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:bean.xml"})
public class AccountServiceTest {
@Autowired
private IAccountService as ; }
为什么不把测试类配到 xml 中 ?
在解释这个问题之前,先解除大家的疑虑,配到 XML 中能不能用呢?
答案是肯定的,没问题,可以使用。
那么为什么不采用配置到 xml 中的方式呢?
这个原因是这样的:
第一:当我们在 xml 中配置了一个 bean,spring 加载配置文件创建容器时,就会创建对象。
第二:测试类只是我们在测试功能时使用,而在项目中它并不参与程序逻辑,也不会解决需求上的问
题,所以创建完了,并没有使用。那么存在容器中就会造成资源的浪费。
所以,基于以上两点,我们不应该把测试配置到 xml 文件中。
posted @ 2020-06-06 18:12  心有玲曦遇奇缘  阅读(156)  评论(0编辑  收藏  举报