7.Spring系列之Bean的配置4

一、配置Bean的两种方式之使用注解配置Bean


1.使用注解配置Bean和使用xml配置Bean的对比

如果我们要将一个Bean上交给IOC容器管理,要么使用xml配置方式,要么使用注解,以下是注解配置以及xml配置的对比:

创建一个Bean:

public class Annotation {

    public String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Annotation [name=" + name + "]";
    }
    
}

将其使用xml的配置方式交给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-4.0.xsd">
    
    <bean id="annotation" class="com.spring.model.Annotation"></bean>
</beans>

测试代码:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Annotation an = ctx.getBean(Annotation.class);
        System.out.println(an);
        //运行后输出:Annotation [name=null]
    }
}

接着我们使用注解的方式,将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-4.0.xsd">
    <!-- 开启注解扫描包 -->
    <context:component-scan base-package="com.spring.model"></context:component-scan>
</beans>

其次,在Bean上添加注解:

@Component
public class Annotation {

    public String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Annotation [name=" + name + "]";
    }
    
}

测试程序:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Annotation an = ctx.getBean(Annotation.class);
        System.out.println(an);
        //运行后输出:Annotation [name=null]
    }
}

总结:

使用注解的方式,相当于Bean被配置到IOC容器中,不同的是使用注解的方式需要开启注解扫描的包。

2.组件扫描

①.组件扫描(component scanning):  Spring 能够从 classpath 下自动扫描, 侦测和实例化具有特定注解的组件,特定组件包括:
  • @Component: 基本注解, 标识了一个受 Spring 管理的组件
  • @Respository: 标识持久层组件
  • @Service: 标识服务层(业务层)组件
  • @Controller: 标识表现层组件
②.对于扫描到的组件, Spring 有默认的命名策略: 
  • 使用非限定类名, 第一个字母小写,例如Annotation这个Bean添加上注解,那么默认生成的Bean id为annotation,我们就可以通过getBean("annotation")来获取这个Bean了;
  • 也可以在注解中通过 value 属性值标识组件的名称,例如Annotation这个Bean上添加注解并指定名称,如@Component("anno"),,我们就可以通过getBean("anno")来获取这个Bean了;
在一个bean上添加注解后,该bean还不会自动配置在IOC容器中,我们还需要对标注注解的bean组件进行扫描,被扫描到的bean,才会被自动配置到ioc容器中,在 Spring 的配置文件中声明<context:component-scan base-package="com.xxx.xxx"/>属性指定一个需要扫描的基类包,Spring 容器将会扫描这个基类包里及其子包中的所有类;
注意:
  • 当需要扫描多个包时, 可以使用逗号分隔
  • 如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern 属性过滤特定的类
③.我们对resource-pattern进行测试:
步骤1.使用上方的Annotation这个自定义的Bean;
步骤2.在Annotation这个Bean上方添加Component注解;
步骤3.该Bean所在的包名为:com.spring.model
步骤4.配置开启扫描包
<?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-4.0.xsd">
    <!-- 开启注解扫描包 -->
    <context:component-scan base-package="com.spring" resource-pattern="model/Annotation.class"></context:component-scan>
</beans>

这里,我们基包为com.spring,resource-pattern配置为model包下的Annotation.class这个类,说明目前只需要扫描Annotation这个自定义Bean,以下是测试程序:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Annotation an = ctx.getBean(Annotation.class);
        System.out.println(an);
        //运行后输出:Annotation [name=null]
    }
}

相反,如果配置不包含这个Bean,

<context:component-scan base-package="com.spring" resource-pattern="model/Hello.class"></context:component-scan>

那么会报错:No qualifying bean of type [com.spring.model.Annotation] is defined

同时,它支持*通配符,即某个包下的所有类:

<context:component-scan base-package="com.spring" resource-pattern="model/*.class"></context:component-scan>

这时,Bean又是可以被扫描到的:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Annotation an = ctx.getBean(Annotation.class);
        System.out.println(an);
        //运行后输出:Annotation [name=null]
    }
}

④.<context:include-filter和 <context:exclude-filter节点支持多种类型的过滤表达式

顾名思义,include是包含,exclude是不包含的意思,我们可以在<context:component-scan></context:component-scan>这个标签中配置有哪些是包含在扫描之内,有哪些是不被扫描;

另外可以指定要包含扫描的特定的注解和不被扫描的特定的注解以及包含要扫描的类和不被扫描的类,常用的两个属性为:annotation,表示注解,assinable表示特定的类

以下是案例:

===>针对特定的注解进行配置

首先,Bean还是使用上方的那个Annotation Bean,并且标注@Component注解,我们针对特定的注解来配置:

<!-- 开启注解扫描包 -->
<context:component-scan base-package="com.spring" resource-pattern="model/*.class">
  <!-- 表示不扫描包含Component的注解 -->
   <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>

测试程序:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Annotation an = (Annotation) ctx.getBean(Annotation.class);
        System.out.println(an);
        //运行后输出:No qualifying bean of type [com.spring.model.Annotation] is defined
    }
}
<!-- 开启注解扫描包 -->
    <context:component-scan base-package="com.spring" resource-pattern="model/*.class">
        <!-- 表示不扫描包含Component的注解 -->
        <!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/> -->
        <!-- 表示扫描包含Component的注解 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>

测试程序:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Annotation an = (Annotation) ctx.getBean(Annotation.class);
        System.out.println(an);
        //运行后输出:Annotation [name=null]
    }
}

===>针对特定的类进行配置:

<!-- 开启注解扫描包 -->
<context:component-scan base-package="com.spring" resource-pattern="model/*.class">
  <!-- 不扫描Annotation这个类 -->
  <context:exclude-filter type="assignable" expression="com.spring.model.Annotation"/>
</context:component-scan>

测试程序:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Annotation an = (Annotation) ctx.getBean(Annotation.class);
        System.out.println(an);
        //运行后输出:No qualifying bean of type [com.spring.model.Annotation] is defined
    }
}
<!-- 开启注解扫描包 -->
    <context:component-scan base-package="com.spring" resource-pattern="model/*.class">
        <!-- 扫描Annotation这个类 -->
        <context:include-filter type="assignable" expression="com.spring.model.Annotation"/>
    </context:component-scan>

测试程序:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Annotation an = (Annotation) ctx.getBean(Annotation.class);
        System.out.println(an);
        //运行后输出:Annotation [name=null]
    }
}

注意:如果type="assignable",并且 expression配置的是接口或者类,那么该接口下的所有实现类或者该类下的所有子类都被/不被扫描。

3.使用@autowire自动装配Bean

说明:

构造器, 普通字段(即使是非 public),一切具有参数的方法都可以应用@Authwired 注解;

默认情况下, 所有使用 @Autowired 注解的属性都需要被设置(被IOC管理),当 Spring 找不到匹配的 Bean 装配属性时,会抛出异常,若某一属性允许不被设置, 可以设置 @Autowired 注解的 required 属性为 false;

默认情况下, 当 IOC 容器里存在多个类型兼容的 Bean 时(例如一个接口有多个实现类), 通过类型的自动装配将无法工作,此时可以在 @Qualifier 注解里提供 Bean 的名称, Spring 允许对方法的入参标注 @Qualifiter 已指定注入 Bean 的名称;

@Autowired 注解也可以应用在数组类型的属性上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配;
@Autowired 注解也可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean;
@Autowired 注解用在 java.util.Map 上时, 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值;
在开发过程中,我们都是使用分层架构,而这也体现着Bean与Bean之间的依赖引用,所以我们使用@Autowired配置常用的注解来模拟(为了省略代码量,dao层省略):
首先,创建几个Bean,分别是UserController、UserService及实现类UserServiceImpl
@Controller
public class UserController {

    @Autowired
    private UserService userService;
    
    public void controller() {
        System.out.println("这是controller,准备调用service...");
        userService.service();
    }
}
public interface UserService {

    public void service();
}
@Service
public class UserServiceImpl implements UserService{

    @Override
    public void service() {
        System.out.println("这是service");
    }

}

注意:被引用的Bean上都添加了@Autowire注解,表示在当前Bean中引入这个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-4.0.xsd">
    
    <!-- 开启注解扫描包 -->
    <context:component-scan base-package="com.spring"/>
</beans>

最后,是测试方法:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserController userController = (UserController) ctx.getBean("userController");
        userController.controller();
        /**
         * 四月 07, 2018 8:31:51 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
         *信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c4437c4: startup date [Sat Apr 07 08:31:51 GMT+08:00 2018]; root of context hierarchy
         *四月 07, 2018 8:31:51 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
         *信息: Loading XML bean definitions from class path resource [applicationContext.xml]
         *这是controller,准备调用service...
         *这是service
         */
    }
}

我们在创建一个UserService实现类,即UserLoggingServiceImpl:

@Service
public class UserLoggingServiceImpl implements UserService{

    @Override
    public void service() {
        System.out.println("这是UserLoggingServiceImpl...");
    }

}

再次运行测试程序:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserController userController = (UserController) ctx.getBean("userController");
        userController.controller();
        /**
         * Error creating bean with name 'userController': Injection of autowired dependencies failed; 
         * nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: 
         * private com.spring.service.UserService com.spring.controller.UserController.userService; 
         * nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
         * No qualifying bean of type [com.spring.service.UserService] is defined: 
         * expected single matching bean but found 2: userLoggingServiceImpl,userServiceImpl
         */
    }
}

意思是UserController引入UserService后,由于UserService有两个实现类,且这两个实现类都被IOC管理,不知道该注入哪个Bean!

我们尝试着在引入UserService时,使用@Qualifiter来跟UserController说明该注入哪个Bean:

@Controller
public class UserController {

    @Autowired
    @Qualifier("userServiceImpl")
    private UserService userService;
    
    public void controller() {
        System.out.println("这是controller,准备调用service...");
        userService.service();
    }
}

再次运行测试程序:

public class Main {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws SQLException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserController userController = (UserController) ctx.getBean("userController");
        userController.controller();
        /**
         * 这是controller,准备调用service...
         * 这是service
         */
    }
}
 
使用 @Resource 或 @Inject 自动装配 Bean:
  • Spring 还支持 @Resource 和 @Inject 注解,这两个注解和 @Autowired 注解的功用类似
  • @Resource 注解要求提供一个 Bean 名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为 Bean 的名称
  • @Inject 和 @Autowired 注解是按类型匹配注入的 Bean, 但没有 reqired 属性
  • 建议使用 @Autowired 注解

总结:

1.对于组件扫描,有以下组件:

  • @Component: 基本注解, 标识了一个受 Spring 管理的组件
  • @Respository: 标识持久层组件
  • @Service: 标识服务层(业务层)组件
  • @Controller: 标识表现层组件

虽然这里都说明了标识的位置,比如Controller标识在web层,但是这并不是强制要求,而是建议最好这么做,开发中达成一致不至于导致每个人有自己的一套标注而导致混乱;

2.组件扫描注解,例如@Service可以指定参数,例如@Service("userService"),那么可以通过getBean("userService")来获得Bean;

3.当某个Bean有多个实现类时,spring不知道该注入哪个实现类时,可以使用@qualifier指定注入的Bean;

4.开发中,注解扫描被大量运用,必须掌握;

5.配置注解扫描包,在整合web框架的时候经常用到include和exclude,后续整合springmvc时会详细说明

6.Bean的配置有很多内容,这里只是列出开发中最常用的几点,有时间或有兴趣的可以多多去研究和运用,相信会有更多的收获。

原文地址:https://www.cnblogs.com/Json1208/p/8727984.html