4.Spring配置进阶

本章目标

  1. 自动装配(了解)
  2. Spring注解分类(理解)
  3. 使用注解完成自动装配(熟练)
  4. 基于注解声明组件(熟练)
  5. JavaConfig(掌握)
  6. MapperScannerConfigurer(理解)

具体内容

Spring 容器提供配置元数据有三种方式

  • XML配置文件。
  • 基于注解的配置。
  • 基于java的配置。

一、自动装配

本节目标:掌握自动装配思想,作为过渡,后期我们频繁使用的是注解形式

之前的例子中我们都是手动装配

自动装配就是让应用程序上下文为你找出依赖项的过程。说的通俗一点,就是Spring会在上下文中自动查找,并自动给bean装配与其关联的属性!

Spring中实现自动装配的方式有两种,一种是通过xml文件、另一种是通过注解。

1、通过xml文件实现自动装配

要使用自动装配,需要在xml配置文件中的<bean>要配置元素的autowire属性。autowire常用属性具体说明如下:

模式 描述
no 这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。
byName 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。
byType 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出
constructor 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。

这里我们讲byName,byType两种方式

2、byName

设置autowire属性为byName,那么Spring会根据class属性找到实体类,然后查询实体类中所有setter方法的名字,根据setter方法后面的名字(例如SetDog,则setter方法后面的名字为dog)再到配置文件中寻找一个与该名字相同id的Bean,注入进来。

  • 相关dao,service接口代码

    public interface UserDao {
      void save();
     }
     public class UserDaoImpl implements UserDao {
      @Override
      public void save(){
      System.out.println(“save User”);
      }
     }
     public interface UserService {
     
      void test();
     
     }
     public class UserServiceImpl implements UserService {
     
      private UserDao  userDao;
      public void setUserDao(UserDao userDao) {
      this.userDao = userDao;
      }
      public void test(){
        userDao.save();
    
      }
    }
    

注意UserServiceImpl中的setUserDao方法一定要加入,采用设置注入方式

  • 配置文件:

    <bean id="userDao" class="com.woniuxy.dao.impl.UserDaoImpl"></bean>
    <bean id="userService" class="com.woniuxy.service.impl.UserServiceImpl" autowire=“byName”></bean>
    

如果把userDao改为userDao1,这时就会出现空指针了

3、byType

设置autowire属性为byType,那么Spring会自动寻找一个与该属性类型相同的Bean,注入进来。

类同上,配置文件如下

<bean id="userDao1" class="com.woniuxy.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.woniuxy.service.impl.UserServiceImpl" autowire="byType"></bean>

这时发现只要类型相同就会注入成功

二、Spring注解分类

在之前已经XML配置了,使用大量的XML配置,使得整个项目变得非常的臃肿,Spring主要的目标是简化企业开发,提高开发效率,学习框架以后,没减轻工作量,反而工作量变大,有着大量的XML配置,XML配置的年代过度到注解配置的时代,XML的配置依然存在,主要是以注解为主,XML为辅。

默认情况下,<context:component-scan/>查找使用构造型(stereotype)注解所标注的类,如@Component(组件),@Service(服务),@Controller(控制器),@Repository(/rɪˈpɑːzətɔːri/数据仓库)。

Spring中有非常多的注解,这里我们只列举了目前我们常用到的几个注解

1、Spring中类级别的注解

@Component、@Repository、@Controller、@Service以及JavaEE6的@ManagedBean和@Named注解,都是添加在类上面的类级别注解

Spring容器根据注解的过滤规则扫描读取注解Bean定义类,并将其注册到Spring IoC容器中。

1、使用asm技术扫描.class文件,并将包含@Component、@Controller、@Service、@Repository或者其他自定义的的bean注册到beanFactory中,

2、然后spring在注册处理器

3、实例化处理器,然后将其放到beanPostFactory中,然后我们就可以在类中进行使用了。

4、创建bean时,会自动调用相应的处理器进行处理。

2、类内部的注解

@Autowire、@Value、@Resource注解等,都是添加在类内部的字段或者方法上的类内部注解。

SpringIoC容器通过Bean后置注解处理器解析Bean内部的注解,比如Spring实现@Autowire解析和注入的核心的类是通过AutowiredAnnotationBeanPostProcessor来实现的

三、注解实现自动装配

类内部注解

前面我们已经实现了自动装配,除了在配置文件中通过autowire属性来实现该功能,我们也可以通过注解的形式来实现。

1、使用注解前的准备:

在Spring使用注解之前首先要开户注解的自动扫描功能

修改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
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         https://www.springframework.org/schema/context/spring-context.xsd">
 
     <!--设置扫描位置,自动扫描该包下的注解-->
         <context:component-scan base-package="com.woniuxy"></context:component-scan>
 
 </beans>

2、 @Autowired注解

@Autowired为Spring提供的注解,只按照类型(byType)注入,导入org.springframework.beans.factory.annotation.Autowired;

在默认情况下使用 @Autowired 注解进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的

Bean 时,Spring 容器将抛出 BeanCreationException 异常,并指出必须至少拥有一个匹配的 Bean。

当不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用@Autowired(required= false)。这等于告诉 Spring:在找不到匹配 Bean 时也不报错。

@Autowired可以放在字段setter方法上。

2.1 注解放在属性上

在实体类的对应属性上添加@Autowired注解,实现自动装配。当在字段上,那么就不需要再写setter方法

修改SysUserMapperImpl如下:

 public class SysUserMapperImpl implements SysUserMapper {
     @Autowired
     private SqlSessionFactory sqlSessionFactory;
     @Override
     public List<SysUser> queryAll() {
         List<SysUser> list;
         try(SqlSession sqlSession = sqlSessionFactory.openSession()){
            SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
            list = sysUserMapper.queryAll();
         }
         return list;
     }
 }

2.2 注解放在setter方法上

修改SysUserServiceImpl类如下:

 public class SysUserServiceImpl implements SysUserService {

     private SysUserMapper sysUserMapper;
     @Autowired
     public void setSysUserMapper(SysUserMapper sysUserMapper) {
         this.sysUserMapper = sysUserMapper;
     }
     @Override
     public List<SysUser> queryAll() {
         return sysUserMapper.queryAll();
     }
 
     @Override
     public Integer updateById(SysUser sysUser) {
         return sysUserMapper.updateById(sysUser);
     }
 }
 

2.3 spring-config.xml文件结构

<bean id="sysUserMapper" class="com.woniuxy.ssm.mapper.impl.SysUserMapperImpl"></bean>
<bean id="sysUserService" class="com.woniuxy.ssm.service.impl.SysUserServiceImpl"></bean>

3、@Resource注解

默认按名称进行装配

@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。

@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。

@Resource可以写在字段和setter方法上。当在字段上,那么就不需要再写setter方法

如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配

3.1 默认按名字

 public class SysUserMapperImpl implements SysUserMapper {
     @Resource
     private SqlSessionFactory sqlSessionFactory;
 
     public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
         this.sqlSessionFactory = sqlSessionFactory;
     }
 
     @Override
     public List<SysUser> queryAll() {
         List<SysUser> list;
         try(SqlSession sqlSession = sqlSessionFactory.openSession()){
            SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
            list = sysUserMapper.queryAll();
         }
         return list;
     }
 }

3.2 按类型

效果和@Autowired一样

public class SysUserMapperImpl implements SysUserMapper {
    @Resource(type = SqlSessionFactory.class)
    private SqlSessionFactory sqlSessionFactory;

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Override
    public List<SysUser> queryAll() {
        List<SysUser> list;
        try(SqlSession sqlSession = sqlSessionFactory.openSession()){
           SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
           list = sysUserMapper.queryAll();
        }
        return list;
    }
}

@Resource装配顺序:

①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。

②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。

③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。

④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。

4、@Qualifier注解(了解)

什么情况会使用到@Qualifier(/kwɒlɪfaɪə(r)/)注解?

当ioc容器根据属性类型去容器中找找到多个相同类型的组件,再将属性的名称作为组件(bean)的id去容器中查找找不到时就是用这两个注解搭配,指定需要装配的bean的id。

@Qualifier注解可以和使用Autowired搭配使用

@Qualifier指定需要装配的组件的id,而不是使用属性名。例如下边例子,spring就会优先在容器中查找id为“cat”的组件。

public class Peopel {
    @Autowired
    @Qualifier(value = "cat")
    private Cat cat;
}

思考?

至此我们实现了通过注解的形式实现自动装配,那能不能通过注解的形式声明组件,这样就不需要在spring-config.xml文件中声明了

四、基于注解声明组件

类注解

目前常用的组件有:@Component(组件),@Service(服务),@Controller(控制器),@Repository(数据仓库)。后期随着知识点的增加,还会学很多新的注解,最终我们会在Spring中使用大量的注解来实现我们的功能。

1、使用@Component注解

@Component:通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。

  1. 在applictionContext.xml文件中添加扫描

    <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:component-scan base-package="com.woniuxy"></context:component-scan>
    </beans>
    
  2. 修改SysUserMapperImpl中使用@Component进行声明

    @Component
    public class SysUserMapperImpl implements SysUserMapper {
        @Resource
        private SqlSessionFactory sqlSessionFactory;
    
        public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
            this.sqlSessionFactory = sqlSessionFactory;
        }
    
        @Override
        public List<SysUser> queryAll() {
            List<SysUser> list;
            try(SqlSession sqlSession = sqlSessionFactory.openSession()){
               SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
               list = sysUserMapper.queryAll();
            }
            return list;
        }
    }
    

    说明:如果一个类中有@Component,没有指定名称,那么这个组件名称就是类名首字母小写

  3. 修改SysUserServiceImpl类结构

    把普通pojo实例化到spring容器中,相当于配置文件中的

    @Component("sysUserService")
    public class SysUserServiceImpl implements SysUserService {
        @Autowired
        private SysUserMapper sysUserMapper;
    
        public void setSysUserMapper(SysUserMapper sysUserMapper) {
            this.sysUserMapper = sysUserMapper;
        }
        @Override
        public List<SysUser> queryAll() {
            return sysUserMapper.queryAll();
        }
    }
    

    注意@Component(“sysUserService”),如果不指定名称时默认为“组件名称就是类名首字母小写”,这样在测试类中将出现找不到的情况,所以此处指定了名称

  4. 修改spring-config.xml文件

    删除掉声明的dao层的bean信息,以及service信息,只保留基础配置信息

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
        <context:property-placeholder location="classpath:jdbc.properties"/>
        <context:component-scan base-package="com.woniuxy.ssm"/>
    
        <bean id="dataSource" destroy-method="close"
              class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="typeAliasesPackage" value="com.woniuxy.ssm.entity"/>
            <property name="mapperLocations" value="classpath:mapper/*"/>
            <property name="configuration">
                <bean class="org.apache.ibatis.session.Configuration">
                    <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
                    <property name="cacheEnabled" value="true"/>
                    <property name="lazyLoadingEnabled" value="true"/>
                </bean>
            </property>
        </bean>
    
    </beans>
    

2、优化注解

如果上所有的组件都是用的@Component,但是在实际开发过程中使用@Component,虽然也能完成Bean实例化声明,但是不能见名知意,Spring提供了如下注解:

  • @Repository:用于声明DAO层的
  • @Service:用于业务层
  • @Controller:用于控制器 (暂时先不用,后面接触Spring MVC再使用)

修改对应代码如下:

@Repository
public class SysUserMapperImpl implements SysUserMapper {
    @Resource
    private SqlSessionFactory sqlSessionFactory;

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }
    ......
}

@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
    @Autowired
    private SysUserMapper sysUserMapper;

    public void setSysUserMapper(SysUserMapper sysUserMapper) {
        this.sysUserMapper = sysUserMapper;
    }
    .......
}

测试……

五、JavaConfig

1、什么是JavaConfig

JavaConfig是指基于java配置的spring。传统的Spring一般都是基本xml配置的,后来spring3.0新增了许多JavaConfig的注解。

JavaConfig相关的配置注解

注解 说明
@Configuration 在类上打上这一标签,表示这个类是配置类
@ComponentScan 相当于xml的<context:componentscan basepakage=>
@Bean bean的定义,相当于xml的<bean id="objectMapper" class="org.it.map.ObjectMapper" />
@EnableWebMvc 相当于xml的<mvc:annotation-driven>,springMVC阶段涉及
@ImportResource 相当于xml的 <import resource="applicationContext-cache.xml">
@PropertySource spring 3.1开始引入,它是基于java config的注解,用于读取properties文件
@Profile spring3.1开始引入,一般用于多环境配置,激活时可用@ActiveProfiles注解,@ActiveProfiles(“dev”)
@Value 读取外部配置文件对应的property

2、 @Configuration

被该注解标识的类,表明该类是一个JavaConfig形式的Spring Ioc容器的配置类

下面我们看一下XML配置方式跟config配置方式的区别:

2.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-3.0.xsd”> </beans>
    

一个xml文件代表一个配置

  • 而基于JavaConfig的配置方式是这样:

    @Configuration
    public class MockConfiguration{
    //bean定义
    }
    

任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。

2.2 注册bean定义层面

  • 基于XML的配置形式是这样:

    <bean id=“mockService” class=“..MockServiceImpl”>
    …
    </bean>
    
  • 而基于JavaConfig的配置形式是这样的:

    @Configuration
    public class MockConfiguration{
    @Bean
    public MockService mockService(){
      return new MockServiceImpl();
      }
    }
    

任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。

@Bean一般用在使用第三方类来生成对象注入到spring容器的情况下使用

2.3 表达依赖注入关系层面

  • 为了表达bean与bean之间的依赖关系,在XML形式中一般是这样:

    <bean id=“dependencyService” class=“DependencyServiceImpl”></bean>
    <bean id=“mockService” class=“..MockServiceImpl”>
    <propery name =“dependencyService” ref=“dependencyService” />
    </bean>
    
  • 而基于JavaConfig的配置形式是这样的:

    @Configuration
    public class MockConfiguration{
    @Bean
    public DependencyService dependencyService(){
        return new DependencyServiceImpl();
    }
    @Bean
    public MockService mockService(){
        return new MockServiceImpl(dependencyService());
        }
    }
    

如果一个bean的定义依赖其他bean,则直接调用对应的JavaConfig类中依赖bean的创建方法就可以了。

3、@Value

@Value的值有两类:

  1. ${ property : default_value }——读取外部配置文件对应的property (常用)
  2. #{ obj.property? :default_value }——SpEL表达式对应的内容 (了解)

default_value是前面的值为空时的默认值。注意二者的不同,#{}里面那个obj代表对象。

3.1、优化自定义数据源

主要是测试${ property : default_value }的使用

创建MyConfig类来读取jdbc.properties配置文件中信息

package com.woniuxy.ssm.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

/**
 * @author :fengSir
 * @date :Created By 2022-05-28 17:42
 * @description :TODO
 */
@Configuration
@PropertySource("classpath:jdbc.properties")//解析指定文件
public class MyConfig {
    //@Value("${jdbc.url}")通过springEL表达式(读取括号里面表达式的值)并且赋给下面的变量
    @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
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        return dataSource;
    }
}

3.2、 修改spring-config.xml

注释掉xml文件中的dataSource进行测试

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <context:component-scan base-package="com.woniuxy.sm" />

<!--    <bean id="dataSource" destroy-method="close"-->
<!--          class="com.alibaba.druid.pool.DruidDataSource">-->
<!--        <property name="driverClassName" value="${jdbc.driver}"/>-->
<!--        <property name="url" value="${jdbc.url}"/>-->
<!--        <property name="username" value="${jdbc.username}"/>-->
<!--        <property name="password" value="${jdbc.password}"/>-->
<!--    </bean>-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.woniuxy.ssm.entity"/>
        <property name="mapperLocations" value="classpath:mapper/*"/>
        <property name="configuration">
            <bean class="org.apache.ibatis.session.Configuration">
                <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
                <property name="cacheEnabled" value="true"/>
                <property name="lazyLoadingEnabled" value="true"/>
            </bean>
        </property>
    </bean>

</beans>

测试程序,发现程序依然可以正常访问数据库,说明我们自定义的数据源生效了。

六、MapperScannerConfigurer

在上面采用自动装配的基础上,操作当前配置,

添加以下配置

MyBatis中可以使用SqlSession的getMapper(Class type)方法,根据指定的映射器和映射文件直接生成实现类,这样不必自行编写映射器的实现类,就可以调用映射器的方法进行功能实现。

MyBatis-Spring中提供了MapperScannerConfigurer,它可以扫描指定包中的接口并将它直接注册为MapperFactoryBean

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    private Class<T> mapperInterface;
    private boolean addToConfig = true;
    ......

    public T getObject() throws Exception {
        return this.getSqlSession().getMapper(this.mapperInterface);
    }
    ......
}

MapperScannerConfigurer的配置方法如下示例所示:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    <property name="basePackage" value="com.woniuxy.mapper"/>
</bean>
  1. basePackage属性中可以包含多个包名,多个包含之间使用逗号或分号隔开。
  2. MapperScannerConfigurer会为所有又它创建的映射器实现开启自动装配,也就是MapperScannerConfigurer创建的所有映射器实现都会被自动注入SqlSessionFactory例,因此在如上示例中配置DAO组件时无需显示注入SqlSessionFactory实例
  3. 映射器被注册到Spring容器时,Spring会根据其接口名称为其命名,默认规则是首字母小写的非完全限定类名。例如EmpMapper类型的组件会被默认命名为empMapper

思维导图

image

posted @ 2025-04-21 17:45  icui4cu  阅读(19)  评论(0)    收藏  举报