Spring入门(Bean的定义及作用域的注解实现)

Classpath扫描与组件管理

从Spring3.0开始,Spring JavaConfig项目提供了很多特性,包括使用java而不是xml定义bean,比如@Configuration、@Bean、@Import、@DependsOn
@Component是一个通用注解,可用于任何bean
@Repository、@Service、@Controller是更有针对性的注解
-@Repository通常用于注解DAO类,及持久层
-@Service通常用于注解Service类,即服务层
-@Controller通常用于Controller类,即控制层(MVC)

类的自动检测及Bean的注册

Spring可以自动检测所有的类并将相应的类注册Bean到ApplicationContext中。
那么能够被Spring自动检测的注解是什么样的?
首先这个注解要注册到类上,即基于类的注解,比如@Service、@Repository。还有一些注解注册在方法或者成员变量上的,如@Autowired。这样的注解是可以被Spring自动检测到的。
然后如果是注册在类上的,它可以作为Spring的Bean注册到ApplicationContext中去。

然后看一下context的配置文件
这个<context:annotation-config/>实在基于xml的Spring配置中配置的一个标签。
注意看一下头文件中的内容,以前我们使用Spring来配置文件去配置一个bean的时候,我们的头文件是这样的:

<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" >

当我们用到<context:annotation-config/>这种以context开头的标签的时候,要引入另外的东西,头文件如下:

<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能检测到这些类并注册相应的Bean,需要下面内容

<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="org.example"/>

 </beans>

component是组件,我们知道Spring有一个@Component注解在类上,component-scan从字面上理解就是组件-扫描,它会完成组件的扫描,也就是基于类注解的扫描。扫描到@Component、@Service、@Repository等,把这些类注册到IOC容器中去。
里边有一个属性base-package,它的意思就是扫描这个包下面的所有类。
这里<context:component-scan>包含<context:annotation-config>,通常在使用前者后,不用在使用后者。component-scan可以扫描基于类的注解,annotation-config只能在完成了bean的注册之后去处理bean中的方法或者成员变量的注解。使用前者就包含了后者的全部功能。开发过程中通常使用前者。

如何使用过滤器进行自定义扫描

1.默认情况下,类被自动发现并注册bean的条件是:使用@Component、@Repository、@Service、@Controller注解或者使用@Component的自定义注解
2.可以通过过滤器修改上面自动发现的行为,如:下面例子的XML配置忽略所有的@Repository注解并用“Stub”代替

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex" 
                expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation" 
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

include包含,包含regex,通配符形式
exclude排除,排出的类型是注解,排除所有用Repository去注解的类
这里边的type有以下几种
这里写图片描述
3.还可以使用use-default-filters=”false”禁用自动发现与注册

定义Bean

如何使用注解去定义一个Bean,这个Bean在我的系统应用中能够为我所用?

扫描过程中组件被自动检测,那么Bean名称是由BeanNameGenerator生成的(@Component、@Repository、@Service、@Controller都会有个name属性用于显式设值Bean Name)。
显式设值Bean Name

@Service("myMovieLister")
public class SimpleMovieLister{
    //...
}

这里定义了一个类,用@Service来注解它,@Service会有一个属性叫做name,我们用Service去注解这个类的时候,可以显式地指定这个类在注册到Bean容器中(或者叫IOC容器中)它所对应的名称id,相当于xml配置文件中的id。
如果我们显式指定了就会用我们指定的名称,这里为myMovieLister。
如果我们没有指定,即只写了@Service,没有显式指定name,就会根据BeanNameGenerator生成,通常的生成规则是以类名为基础,并把类名的第一个字母小写。

可自定义bean命名策略,实现BeanNameGenerator接口,并一定要包含一个无参数构造器。
如何使用自己定义的命名策略的构造器?

<beans>
    <context:component-scan base-package="org.example" 
            name-generator="org.example.MyNameGenerator"/>
</beans>

在component-scan中的name-generator里边自己去指定命名策略的实现。

作用域(Scope)

之前接触过Spring的作用域,有singleton,prototype,session,request等,之前是通过xml配置文件的方式来实现的。那么在注解里边是通过什么方式来达到控制作用域的目的呢?

1.通常情况下自动查找的Spring组件,其scope是singleton(这个singleton是指在某一个IOC容器中是单例的),Spring2.5提供了一个标识scope的注解@Scope。

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder{
    //...
}

这里指明为prototype,也可以指定为singleton,不过这个时候可以不写。

2.也可以自定义scope策略,实现ScopeMetadataResolver接口并提供一个无参构造器。

<beans>
    <context:component-scan base-package="org.example" 
            scope-resolver="org.example.MyScopeResolver"/>
</beans>

在component-scan里边有一个scope-resolver,用来指定自己的实现类。基于这种方式,或者说在这一段去扫描的所有类,他们的scope都是由我们自定义的scope策略去实现的。什么时候需要我们自定义Scope策略?假如我们希望在每一个线程中使用一个bean的实例,那就可以实现ScopeMetadataResolver接口并定义一种这样的作用域,只针对线程有效。

代理方式
可以使用scoped-proxy属性指定代理,有三个值可选:no(默认)、interfaces、targetClass

<beans>
    <context:component-scan base-package="org.example" scope-resolver="interfaces"/>
</beans>

Bean的定义及作用域的例子

定义一个类BeanAnnotation,写一个单元测试类,还需要一个配置文件。
配置文件如下:

<?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.imooc.beanannotation"></context:component-scan>

 </beans>

指定base-package,只处理那个包下的相关的注解。

在BeanAnnotation类中使用通用的注解@Component

@Component
public class BeanAnnotation {

    public void say(String arg) {
        System.out.println("BeanAnnotation : " + arg);
    }
}

单元测试类中

@RunWith(BlockJUnit4ClassRunner.class)
public class TestBeanAnnotation extends UnitTestBase {

    public TestBeanAnnotation() {
        super("classpath*:spring-beanannotation.xml");
    }

    @Test
    public void testSay() {
        BeanAnnotation bean = super.getBean("beanAnnotation");
        bean.say("This is test.");
    }
}

从IOC容器中取到这个bean:beanAnnotation,前边有讲到如何命名。BeanAnnotation这个类被扫描到,自动注册了一个bean,id为beanAnnotation。然后要做的就是调用这个bean的set方法。

这个时候也可以把@Component写为@Component(“bean”),那么这个bean就被命名为bean,而不是beanAnnotation,在getBean()是2就要写为getBean(“bean”)。

所以我们自定义bean的名称是ok的。

接下来我们来看一下作用域。
配置文件不需要修改。
BeanAnnotation中用上@Scope注解,这里指的为prototype。要判断是否是同一个对象,可以使用hashCode()。

@Scope("prototype")
@Component
public class BeanAnnotation {

    public void say(String arg) {
        System.out.println("BeanAnnotation : " + arg);
    }

    public void myHashCode() {
        System.out.println("BeanAnnotation : " + this.hashCode());
    }
}

然后是单元测试类

@RunWith(BlockJUnit4ClassRunner.class)
public class TestBeanAnnotation extends UnitTestBase {

    public TestBeanAnnotation() {
        super("classpath*:spring-beanannotation.xml");
    }

    @Test
    public void testSay() {
        BeanAnnotation bean = super.getBean("beanAnnotation");
        bean.say("This is test.");

        bean = super.getBean("bean");
        bean.say("This is test.");
    }

    @Test
    public void testScpoe() {
        BeanAnnotation bean = super.getBean("beanAnnotation");
        bean.myHashCode();

        bean = super.getBean("beanAnnotation");
        bean.myHashCode();
    }
}

两次getBean时hashCode不一样,所以不是同一个对象,符合prototype。

把prototype去掉,只写@Scope,即默认值singleton,得到的是相同的hashCode

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值