Spring注解注入

Spring基于Annotation配置Bean

简介

这种注入方式也比较常见,并且在Spring企业级开发中用途最广的DI模式之一,有时候相对于XML配置模式,Autowire注入是很方便的。
首先需要导入支持AOP的包,因为需要在XML配置文件中通知Spring容器扫描含有注解文件的包。

分类

1)@Component
可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
2)@Repository
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
3)@Service
通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
4)@Controller
通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
5)@Autowired
用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。
6)@Resource
其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。

@Resource 中有两个重要属性:name 和 type。

Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。

如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。
7)@Qualifier
与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。

案例

1)Dao层实现类

@Repository("userDao")  // 持久层组件注解
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("Dao层的add()方法。。。");
    }
}

2)Service层实现类

@Service("userService")
public class UserServiceImpl implements UserService {
    @Resource(name = "userDao")
    private UserDao userDao;

    // public void setUserDao(UserDao userDao) {
    //     this.userDao = userDao;
    // }
    
    @Override
    public void add() {
        userDao.add();
        System.out.println("Service层的add()方法。。。");
    }
}

3)Annotation层

@Controller("userAction")
public class UserAction {
    @Resource(name = "userService")
    private UserService userService;

    // public void setUserService(UserService userService) {
    //     this.userService = userService;
    // }
    public void add(){
        userService.add();
        System.out.println("Action层的add()方法。。。");
    }
}

4)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-2.5.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 通知Spring扫描指定包下含有组件注解的Java文件 -->
    <context:component-scan base-package="com.lyl.*"/>
</beans>

5)测试类

public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserAction userAction = (UserAction) context.getBean("userAction");
        userAction.add();
    }

打印结果:

Dao层的add()方法。。。
Service层的add()方法。。。
Action层的add()方法。。。

上面案例使用的就是通过注解来实现注入bean。其中可以发现Service层、Annotation层的实现类中由属性,但是setter方法注释了也能注入成功,说明注解方法实现的注入,可以不用写setter方法,Spring容器会自动的帮我们实现注入。

将Service层的@Resource(name = "userService")改为@Resource(name = "userService1"),运行会报错,由此发现,使用@Resoure默认按照byName模式进行注入。改为@Autowired,并更改Service的属性名:private UserDao userDao;改为private UserDao userDao1;,运行通过并有正确结果,说明@Autowired注解默认按照byType模式进行注入。

@Qualifier注解的作用:
简单来说就是使用了@Autowired注解,但是此时出现了使用同一接口的多个实现类,此时由于默认使用的是byType模式,Spring并不知道我们需要注入的是什么,使用我们可以用@Qualifier调用byName模式告诉Spring我们需要注入那个bean。


注解注入之构造函数

在注解注入这块除了Setter注解注入,成员变量注解注入,还有一个比较重要的——“构造函数注入”。

其实也很简单理解,就是在构造方法上面写@Autowired即可,但是在学习的时候,碰到一个问题,就是一个有参构造函数中有多个同一类型形参,这个时候Spring就无法分辨我们实参的传递和赋值。
这个问题也想了好久,找了也蛮久的,因为这个问题是我临时想的,所以无从下手,一开始尝试使用@Qualifier解决问题,因为可以根据byName模式注入嘛,但是放在构造方法上面又报错,用@Resource也报错,就开始漫长的百度之旅,终于让我找到了一个很小很小的例子,@Qualifier注解放入构造函数的形参前面,运行,发现能够正确的Run出结果,后面仔细一想,发现我们的问题本质是Spring无法识别我们实参的传递和赋值,使用@Qualifier是正确的思路,主要是放置位置是在形参前面,来指明该形参使用的是byName模式并指明使用的BeanName,这样一来就能够理解了这一个特殊情况的解决问题方法。

代码如下:

@Controller("userAction")
public class UserAction {
    private UserService userService1;
    private UserService userService2;

    @Autowired
    public UserAction(@Qualifier("userService") UserService userService1, @Qualifier("userService1")UserService userService2) {
        this.userService1 = userService1;
        this.userService2 = userService2;
    }

    public void add(){
        userService1.add();
        userService2.add();
        System.out.println("Action层的add()方法。。。");
    }
}

在初学者应该会碰到这个问题,这个问题也是我在学习注解注入的构造方法的时候临时想到的!如果没有出现同类型的多参构造函数,一般情况下不需要使用@Qualifier

小结

实际上,我们使用注解来帮助我们注入时,是Spring帮助我们节省了代码,实现了操作,原理依旧是咱们写在XML配置文件中的一样,需要注意的就是@Autowired@Repository注解默认使用的Autowire注入模式不同。还有就是在构造方法中@Autowired@Qualifier的搭配使用

1.强制性的依赖性或者当目标不可变时,使用构造函数注入(应该说尽量都使用构造器来注入)
2.可选或多变的依赖使用setter注入(建议可以使用构造器结合setter的方式来注入)
3.在大多数的情况下避免field域注入(感觉大多数同学可能会有异议,毕竟这个方式写起来非常简便,但是它的弊端确实远大于这些优点)

会加油学习Java的,目前水平还不够,基础不扎实。

posted @ 2021-05-10 10:58  CN_DADA  阅读(815)  评论(1编辑  收藏  举报