spring-03


一、基于注解方式实现bean管理

1、针对 Bean 管理中创建对象提供注解

 1. @Component
 2. @Service
 3. @Controller
 4. @Repository

四个注解的功能都是一样的,但是为了区分,一般要对这四个注解进行区分。
@Component:主要时作用于实体类(Entity或POJO),把普通pojo实例化到spring容器中。
@Service:用于标注服务层(Service层),主要用来进行业务的逻辑处理 @Repository:用于标注数据访问层(Dao层)
@Controller:用于标注控制层(Controller层)

在此之前要先进行组件扫描,比如在com.scan包下的所有注解。
需要在xml配置文件中添加下面的标签,进行组件扫描

<context:component-scan base-package="com.scan"></context:component-scan>

比如在UserService添加@Service注解,在xml配置文件中开启了Service包下的组件扫描后,只需要在测试代码中getBean获取UserService的实例就可以了。

UserService:

/**
 * 在注解里默认value属性值可以忽略不写
 * 如果默认不写的话,那就取默认值,即类名称的首字母小写
 * UserService --> userService
 */
@Service(value = "userService")  // <bean id = "userServcie" class=""/>
public class UserService {
    public void add(){
        System.out.println("service add ...");
    }
}

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

    <!--
        开启组件扫描:
            1、如果扫描多个包,多个包使用逗号隔开(下面扫描dao和entity包)
                <context:component-scan base-package="com.scan.entity,com.scan.dao"></context:component-scan>

            2、如果扫描多个包,可以包的上层目录
                <context:component-scan base-package="com.scan"></context:component-scan>

    -->
    <context:component-scan base-package="com.scan"></context:component-scan>


    <!--
        过滤默认扫描文件夹下面的部分子文件夹:
            use-default-filters="false":不适用默认扫描的情况
            context:include-filter:带扫描的内容
            expression="org.springframework.stereotype.Service":扫描包下面的Service注解
            org.springframework.stereotype.Service:为import导入@Servcie的包,不是本地的包
    -->

    <!--

        <context:component-scan base-package="com.scan" use-default-filters="false">
             下面为只扫描com.scan包下面的Service和Controller注解
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    -->

    <!--
        过滤默认扫描文件夹下面的部分子文件夹:
            context:exclude-filter:不扫描该注解,其他都扫描(下面的例子即不扫描Service注解)

    <context:component-scan base-package="com.scan">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>

    -->
</beans>

测试:

/**
     * 用于包下的注解全部扫描
     */
    @Test
    public void testUserService1(){
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }

结果:

service add ...

接下来我们可以想一下,上面那种方式并不能完全注解开发的功能,上面的例子时实现包下面的所有注解扫描,但是有时候我们只想扫描部分注解或者只扫描几个类,又或者时指定某个类不进行扫描,只扫描这个类包下面的其他同注解的类。

比如我们想扫描com.scan包下的@Repository@Controller,这时候我们就需要引入context:include-filter
对应的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">


    <!--
        过滤默认扫描文件夹下面的部分子文件夹:
            use-default-filters="false":不适用默认扫描的情况
            context:include-filter:带扫描的内容
            expression="org.springframework.stereotype.Service":扫描包下面的Service注解
            org.springframework.stereotype.Service:为import导入@Servcie的包,不是本地的包
    -->
        <context:component-scan base-package="com.scan" use-default-filters="false">
             <!--下面为只扫描com.scan包下面的Service和Controller注解 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

</beans>

此时还是对上UserService(@Service)进行getBean获取时,就会报一下错误:
No bean named 'userService' available

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

	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:814)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1282)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:297)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1114)
	at com.scan.test.AnnotationTest.testUserService1(AnnotationTest.java:18)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

下面介绍在包下哪些注解不扫描的情况,这里要用到context:exclude-filter
比如我们在扫描com.scan包下的组件时,不扫描带有@Servcie的注解

<?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:exclude-filter:不扫描该注解,其他都扫描(下面的例子即不扫描Service注解)
    -->
    <context:component-scan base-package="com.scan">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
</beans>

还是要上面的代码进行测试会发现报一样错。

如果我们要扫描多个包,可以用逗号隔开<context:component-scan base-package="com.scan.entity,com.scan.dao"></context:component-scan>
也可以使用扫描包共同的上层目录。比如我们扫描com.scan.entity和com.scan.dao包,那么可以直接扫描com.scan目录。

如果我们要特定的指定哪些类不扫描或者扫描的话,只需要将typeannotation(指注解的意思)改为assignable

例如我们在com.scan包的组件的时候,不扫描UserService类,并且不扫描含有@Controller注解的类,其他都扫描:

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">
    <!--
        过滤默认扫描文件夹下面的部分子文件夹:
            type="assignable":按照类形式进行扫描
            context:exclude-filter:设置哪些类不扫描
    -->
    <context:component-scan base-package="com.scan">
        <!-- 指定com.scan.service.UserService类不进行扫描 -->
        <context:exclude-filter type="assignable" expression="com.scan.service.UserService"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

</beans>

此时还是用前面的UserServcie和测试代码的话,会发现还是会报上面的错误
No bean named 'userService' available

如果看到这里我们发现还是不能实现完全注解开发,因为我们还需要注入属性。
这个时候就需要额外添加注解,下面介绍几个注解:
@Autowired:根据类型(类的)注入
@Qualifier:根据名称注入,要和Autowired一起使用
@Resource:既可以根据类型,也可以根据名称注入
@Value:给基本数据类型的属性赋值

注意:@Autowired,@Qualifier和@Resource都作用在对象上面,@Value作用在基本数据类型上面

下面我来一个一个解释:
首先@Autowired是根据类型注入,这个有点像之前xml配置时讲的自动注入autowire,在xml配置中如果使用了autowire属性中的byType,那么系统就会在所在的xml配置文件中进行查找对应类型的bean实例,如果有多个就会报错,提示多个。

其次@Qualifier也类似于之前xml配置时讲的自动注入autowire中的byName,这里要和@Autowired一起使用。

@Autowired
@Qualifier(value = "card")

再次就是@Resource,这个注解可以根据类型也可以根据名称注入。
根据类型注入只需要加注解@Resource,根据名称注入就要使用这种形式@Resource(name = "")@Value的话就是对基本数据类型的注入(->_->)没啥花样。

为了解释这些注解,我添加了几个类和接口:UserDao接口,UserDaoImpl实现类,EyesIdCard实体类,可能不是很好的设计,我当时也时为了一下解释所有的注解,就东拼西凑随便写了几个实体类(->_->)见谅。

UserDao接口:

public interface UserDao {
    void add();
}

UserDaoImpl实现类:

@Repository
public class UserDaoImpl implements UserDao{

    /**
     * Autowired:根据类型(类的)注入
     * Qualifier:根据名称注入,要和Autowired一起使用
     * 这里我把下面的Autowired删除之后运行结果发现输出的为null
     *
     * Resource:既可以根据类型,也可以根据名称注入
     *
     * @Resource(name = "card") 等价于 @Autowired + @Qualifier(value = "card")
     *
     */
    @Autowired
    @Qualifier(value = "card")
    private IdCard idCard;

    /**
     * 这里Resource根据类型诸如
     *
     * 若根据名称注入@Resource(name = "...")
     */
    @Resource
    private Eyes eyes;

    @Override
    public void add() {
        System.out.println("userDao add ...");
        System.out.println(idCard);
        System.out.println(eyes);
    }
}

实体类(EyesIdCard):

@Component(value = "eye")
public class Eyes {

    @Value("蓝眼睛")
    private String eyesType;

    @Override
    public String toString() {
        return "Eyes{" +
                "eyesType='" + eyesType + '\'' +
                '}';
    }
}

@Component(value = "card")
public class IdCard {

    @Value("26181")
    private String idCardNo;

    @Override
    public String toString() {
        return "IdCard{" +
                "idCardNo='" + idCardNo + '\'' +
                '}';
    }
}

测试:

/**
     * 测试Autowired,Autowired,Value,Resource的使用
     */
    @Test
    public void testUserService5(){
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.addExtension();
    }

结果展示:

userDao add ...
IdCard{idCardNo='26181'}
Eyes{eyesType='蓝眼睛'}
service add ...

到了这里我们还是没有实现完全注解开发,因为我们前面的操作都是基于在xml配置文件中添加组件扫描。所以要实现完全注解开发,我们还要有一个用于组件扫描的配置类用来代替xml配置文件中实现的组件扫描。

下面是一个配置类:

// Configuration:配置类
@Configuration
@ComponentScan(basePackages = "com.scan")
public class SpringConfig {
}

关于配置类也有很多知识点,所以待更新。。。

声明:写这个文章主要是用于自己对知识点的梳理,当然也有想让各位跟我一样的小白参考,作者学识浅薄,有可能有错误,所以欢迎纠错,一起学习讨论,一起进步。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值