Spring-基于注解的配置[01定义Bean+扫描Bean]

简介: Spring-基于注解的配置[01定义Bean+扫描Bean]

概述


前几篇博文中主要讲述了基于XML的配置。


不管是XML还是注解,他们都是在表达Bean定义的载体,其实质都是为Spring容器提供Bean定义的信息,在表现形式上都是将XML定义的内容通过类注解进行描述。


基于注解的配置方式,在Spring2.0引入,Spring2.5完善,Spring4.0得到了进一步的增强。


我们知道,Spring容器成功启动的三大要件分别是:


bean的定义信息

bean的实现类

Spring本身


如果采用XML的配置,则Bean的定义信息和Bean的实现类本身是分离的。

而如果采用基于注解的配置方式,则Bean的定义信息通过Bean实现类上标注的注解实现。


使用注解定义Bean

在另外一篇博客中:Spring-Spring MVC + Spring JDBC + Spring Transaction + Maven 构建web登录模块

以案例来说明:

// 通过Spring注解定义一个DAO(这里略微改动了下,使用@Component ,更合适的是@Repository,仅仅是为了演示)
@Component 
public class UserDao {
    .......


使用@Component注解在UserDao类声明处对类进行标注,它可以被Spring容器识别,Spring容器自动将POJO转换为容器管理的Bean。


它和下面的XML配置是等效的

<bean id="userDao" class="com.xgj.dao.UserDao">


使用注解的方式,默认生成的beanId为类名的首字母小写,也可指定其id,如:@Service("xgjService")


除了@Component注解外,Spring还提供了3个功能基本和@Component等效的注解,分别用于对DAO、Service和Web层的Controller进行注解


@Repository用于对DAO实现类进行标注

@Service用于对Service实现类进行标注

@Controller用于对Controller实现类进行注解


之所以要在@Component之外提供3个特殊的注解,是为了让标注类本身的用途清晰化,当然了完全可以用@Component替代这3个特殊的注解, 但是我们推荐使用特定的注解标注特定的Bean,这样可以明确的了解Bean的真是身份


扫描注解定义的Bean


Spring提供了一个context命名空间,它提供了通过扫描类包以应用注解定义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" 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">
    <!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
    <context:component-scan base-package="com.xgj.ioc.configuration"/>
</beans>


使用步骤


  1. 首先声明context命名空间和schema文件
  2. 然后通过context命名空间的component-scan的base-package属性指定一个需要扫描的基类包,Spring容器会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。


扫描特定的类 resource-pattern


假设我们希望扫描特定的类,而非基包下所有的类,怎么办呢?

Spring context命名空间提供了resouce-pattern属性过滤出特定的类

    <!-- 通过resource-pattern使Spring加载特定的Bean -->
    <context:component-scan base-package="com.xgj.ioc.configuration.resourcePattern"
        resource-pattern="scan/*.class"/>


这里设置的基类包为com.xgj.ioc.configuration.resourcePattern,默认情况下resource-pattern属性的值为 **./*.class,即基类包里所有的类。


如果有设置为scan/*.class,则Spring仅会扫描基类包里scan子包中的类。


实例


通过配置文件指定Spring只加载 scan目录下的类的注解,测试resource-pattern属性。


20170723131416080.jpg


配置文件:

<?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">
    <!-- 通过resource-pattern使Spring加载特定的Bean -->
    <context:component-scan base-package="com.xgj.ioc.configuration.resourcePattern"
        resource-pattern="scan/*.class"/>
</beans>

scan目录下的POJO

package com.xgj.ioc.configuration.resourcePattern.scan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Pilot {
    @Autowired
    private Plane plane;
    public void drivePlane() {
        plane.fly();
    }
}
package com.xgj.ioc.configuration.resourcePattern.scan;
import org.springframework.stereotype.Component;
@Component
public class Plane {
    private String brand;
    private String color;
    private int speed;
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public int getSpeed() {
        return speed;
    }
    public void setSpeed(int speed) {
        this.speed = speed;
    }
    public void introduce() {
        System.out.println("Plane information【 brand:" + brand + ",color:"
                + color + ",speed:" + speed);
    }
    public void fly() {
        System.out.println("Plane begins to  fly");
    }
}

测试类:

package com.xgj.ioc.configuration.resourcePattern.scan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ConfigBeanTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/configuration/resourcePattern/resourcePatternBeans.xml");
        Pilot pilot = ctx.getBean("pilot", Pilot.class);
        pilot.drivePlane();
        Plane plane = ctx.getBean("plane", Plane.class);
        plane.setBrand("A380");
        plane.setColor("White");
        plane.setSpeed(800);
        plane.introduce();
    }
}


NoScan包下的POJO (PilotNoScan、PlaneNoScan)仅仅类名不同(同名的话,默认ID相同,Spring加载会报错。)

测试类

package com.xgj.ioc.configuration.resourcePattern.noScan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ConfigBeanTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/configuration/resourcePattern/resourcePatternBeans.xml");
        PilotNoScan pilot = ctx.getBean("pilotNoScan", PilotNoScan.class);
        pilot.drivePlane();
        PlaneNoScan plane = ctx.getBean("planeNoScan", PlaneNoScan.class);
        plane.setBrand("A380-NoScan");
        plane.setColor("White-NoScan");
        plane.setSpeed(800);
        plane.introduce();
    }
}


运行scan报下的测试类

20170723132037267.jpg

运行noScan报下的测试类,报错

No bean named 'pilotNoScan' available

20170723131913707.jpg

说明,Spring容器并没有实例化这个类。resource-pattern起了作用。

当我们去掉配置文件中的 resource-pattern="scan/*.class"后,再此运行


20170723132137944.jpg


可见,Spring容器可以正确加载并实例化Bean


include-filter exclude-filter过滤子元素的使用


通过resource-pattern属性可以按照资源名称对基类包中的类进行过滤,如果我们有个需求:仅需过滤基类包中实现了XxxService接口的类或者标注了某个特定注解的类等 ,该怎么办呢?


Spring的<context:componet-scan>为我们提供了过滤子元素,我们可以轻松的通过其实现上面的需求。

<context:component-scan base-package="com.xgj.ioc.configuration.resourcePattern"
        resource-pattern="scan/*.class">
    <context:include-filter type="regex" expression="com\.xgj\.ioc\.configuration\.resourcePattern\.scan.*"/>   
    <context:exclude-filter type="aspectj" expression="com.xgj.ioc.configuration.resourcePattern..*Controller+"/>
</context:component-scan>

<context:include-filter>表示要包含的目标类

<context:exclude-filter>表示要排除的目标类

一个<context:component-scan>下可以有多个<context:include-filter>和<context:exclude-filter>元素


支持多种类型的过滤表达式


image.png


在所有的过滤类型中,除了custom类型外,aspectj的过滤表达能力是最强的,可以轻易实现其他类型所能表达的过滤规则。

实例

我们先将测试类改下名,保证不重名,否则会抛出如下异常

Annotation-specified bean name 'configBeanTest' for bean class [com.xgj.ioc.configuration.resourcePattern.scan.ConfigBeanTest] conflicts with existing, non-compatible bean definition of same name and class [com.xgj.ioc.configuration.resourcePattern.noScan.ConfigBeanTest]

20170724015228541.jpg


配置文件:

<?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">
    <!-- 通过resource-pattern使Spring加载特定的Bean 
    <context:component-scan base-package="com.xgj.ioc.configuration.resourcePattern" 
        resource-pattern="scan/*.class"/> -->
    <!-- context:include-filter context:exclude-filter -->
    <context:component-scan base-package="com.xgj.ioc.configuration.resourcePattern">
        <context:include-filter type="regex"
            expression="com\.xgj\.ioc\.configuration\.resourcePattern.*" />
        <context:exclude-filter type="aspectj" expression="com.xgj.ioc.configuration.resourcePattern..*NoScan+"/>
    </context:component-scan>
</beans>


配置文件解析:

<context:include-filter type="regex"
    expression="com\.xgj\.ioc\.configuration\.resourcePattern.*" />


使用<context:include-filter> ,通过regex正则表达式过滤方式,包含com.xgj.configuration.resourcePattern目录下的所有类,按照项目结果包括scan和noScan子包中的注解类。

<context:exclude-filter type="aspectj"    expression="com.xgj.ioc.configuration.resourcePattern..*NoScan+"/>

使用<context:exclude-filter>,通过aspectj的过滤方式,排除掉com.xgj.ioc.configuration.resourcePattern包及子包下所有以NoScan+结尾的类,按照项目结构即排除掉noScan子包下的注解类。


其余POJO和测试类同上。


测试scan包下的测试类


20170724015525695.jpg

测试noScan包下的测试类

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'pilotNoScan' available


20170724015608275.jpg


可见 <context:include-filter> 和 <context:exclude-filter>标签起了作用。


use-default-filters属性


use-default-filters属性默认值为true,表示会对标注@Component、@Controller、@Service、@Reposity的Bean进行扫描。


<context:component-scan>首先根据 exclude-filter列出需要排除的黑名单,然后再根据include-filter流出需要包含的白名单。 但是由于use-default-filters属性默认值的作用,看下下面的配置片段,不但会对@Controller的bean进行扫描,还会会对@Component、@Service、@Reposity的Bean进行扫描。

    <context:component-scan base-package="com.xgj.ioc.configuration.resourcePattern">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

20170724023957068.jpg


换句话讲,上面的配置和不加<context:include-filter>的效果是一样的,如果仅仅想扫描@Controller的bean,必须将use-default-filters属性设置为false.

如下所示

<context:component-scan base-package="com.xgj.ioc.configuration.resourcePattern" use-default-filters="false">
        <context:include-filter type="annotation"
    expression="org.springframework.stereotype.Controller" />   
</context:component-scan>

实例

配置文件

<?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:component-scan base-package="com.xgj.ioc.configuration.resourcePattern" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

运行测试类 com.xgj.ioc.configuration.resourcePattern.scan.ConfigBeanTest

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'pilot' available

20170724034346796.jpg

如果我们去掉use-default-filters=”false”试下呢?


20170724034452730.jpg

由此可见 use-default-filters=”false” 起到了只加载或者排除特定注解的功能。

相关文章
|
6天前
|
Java uml Spring
手写spring第四章-完善bean实例化,自动填充成员属性
手写spring第四章-完善bean实例化,自动填充成员属性
15 0
|
2天前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
11 5
|
3天前
|
XML Java 数据格式
如何在Spring AOP中定义和应用通知?
【4月更文挑战第30天】如何在Spring AOP中定义和应用通知?
9 0
|
3天前
|
消息中间件 安全 Java
在Spring Bean中,如何通过Java配置类定义Bean?
【4月更文挑战第30天】在Spring Bean中,如何通过Java配置类定义Bean?
11 1
|
4天前
|
Java 开发者 Spring
Spring Boot中的资源文件属性配置
【4月更文挑战第28天】在Spring Boot应用程序中,配置文件是管理应用程序行为的重要组成部分。资源文件属性配置允许开发者在不重新编译代码的情况下,对应用程序进行灵活地配置和调整。本篇博客将介绍Spring Boot中资源文件属性配置的基本概念,并通过实际示例展示如何利用这一功能。
14 1
|
5天前
|
前端开发 Java 数据格式
【Spring系列笔记】定义Bean的方式
在Spring Boot应用程序中,定义Bean是非常常见的操作,它是构建应用程序的基础。Spring Boot提供了多种方式来定义Bean,每种方式都有其适用的场景和优势。
20 2
|
6天前
|
XML Java 数据格式
手写spring第八章-定义标记类型Aware接口,实现感知容器对象
手写spring第八章-定义标记类型Aware接口,实现感知容器对象
4 0
|
6天前
|
XML Java 数据格式
手写spring第七章-完成便捷实现bean对象初始化和销毁方法
手写spring第七章-完成便捷实现bean对象初始化和销毁方法
6 0
|
6天前
|
XML Java 数据格式
手写spring第六章-实现应用上下文,完成bean的扩展机制
手写spring第六章-实现应用上下文,完成bean的扩展机制
12 0
|
6天前
|
设计模式 搜索推荐 Java
手写spring第三章-重构,使用依赖关系完善实例化bean操作
手写spring第三章-重构,使用依赖关系完善实例化bean操作
13 0