spring注入方式

转载:
https://blog.csdn.net/a745233700/article/details/89307518
https://blog.csdn.net/m0_37556444/article/details/83108929
https://www.jianshu.com/p/0c3f7e00eba2

注入

平常的Java开发中,程序员在某个类中需要依赖其它类的方法。 通常是new一个依赖类的实例再调用该实例的方法,这种开发存在的问题是new的类实例不好统一管理。

Spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。

依赖注入的另一种说法是”控制反转”。通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员。而控制反转是指new实例工作不由我们程序员来做而是交给Spring容器来做。

Spring有多种依赖注入的形式,本篇文章仅介绍Spring通过xml进行IOC配置的方式。

Set()注入:

这是最简单的注入方式,假设有一个SpringAction,类中需要实例化一个SpringDao对象,那么就可以定义一个private的SpringDao成员变量,然后创建SpringDao的set方法(这是ioc的注入入口):

package com.bless.springdemo.action; 
public class SpringAction { 
    //注入对象springDao 
    private SpringDao springDao; 
    //一定要写被注入对象的set方法 
    public void setSpringDao(SpringDao springDao) { 
    this.springDao = springDao; 
} 
 
public void ok(){ 
    springDao.ok(); 
} 
}

随后编写spring的xml文件中,name属性是class属性的一个别名,class属性指类的全名,因为在SpringAction中有一个公共属性Springdao,所以要在标签中创建一个标签指定SpringDao。标签中的name就是SpringAction类中的SpringDao属性名,ref指下面,这样其实是spring将SpringDaoImpl对象实例化并且调用SpringAction的setSpringDao方法将SpringDao注入:

<!--配置bean,配置后该类由spring管理--> 
<bean name="springAction" class="com.bless.springdemo.action.SpringAction"> 
<!--(1)依赖注入,配置当前类中相应的属性--> 
<property name="springDao" ref="springDao"></property> 
</bean> 
<bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>

构造器注入:

这种方式的注入是指带有参数的构造函数注入,看下面的例子,我创建了两个成员变量SpringDao和User,但是并未设置对象的set方法,所以就不能支持第一种注入方式,这里的注入方式是在SpringAction的构造函数中注入,也就是说在创建SpringAction对象时要将SpringDao和User两个参数值传进来:

public class SpringAction { 
    //注入对象springDao 
    private SpringDao springDao; 
    private User user; 
 
    public SpringAction(SpringDao springDao,User user){ 
    this.springDao = springDao; 
    this.user = user; 
    System.out.println("构造方法调用springDao和user"); 
} 
 
public void save(){ 
    user.setName("卡卡"); 
    springDao.save(user); 
} 
} 

在XML文件中同样不用的形式,而是使用标签,ref属性同样指向其它标签的name属性:

<!--配置bean,配置后该类由spring管理--> 
<bean name="springAction" class="com.bless.springdemo.action.SpringAction"> 
<!--(2)创建构造器注入,如果主类有带参的构造方法则需添加此配置--> 
<constructor-arg ref="springDao"></constructor-arg> 
<constructor-arg ref="user"></constructor-arg> 
</bean> 
<bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean> 
<bean name="user" class="com.bless.springdemo.vo.User"></bean> 

解决构造方法参数的不确定性:你可能会遇到构造方法传入的两参数都是同类型的,为了分清哪个该赋对应值,则需要进行一些小处理:下面是设置index,就是参数位置:

<bean name="springAction" class="com.bless.springdemo.action.SpringAction"> 
<constructor-arg index="0" ref="springDao"></constructor-arg> 
<constructor-arg index="1" ref="user"></constructor-arg> 
</bean> 

另一种是设置参数类型:

<constructor-arg type="java.lang.String" ref=""/>

静态工厂/实例工厂的方法注入:

<!-- 使用静态工厂进行创建-->
<!-- class的值不是写User对象的全路径,而是写静态工厂的全路径-->
<!-- factory-method的值写要调用的方法-->
<bean id="user2" class="com.imooc.entity.factory.StaticFactory" factory-method="getUser" scope="singleton" />
<!-- 使用实例工厂进行创建-->
<!-- 需要先创建factoryBean对象,再通过factoryBean对象进行调用-->
<bean id="userFactory" class="com.imooc.entity.factory.UserFactory"/>
<bean id="user3" factory-bean="userFactory" factory-method="getUser" scope="singleton" />

自动装配

XML中使用的自动装配

  • byName方式:把该Bean的id改成了与引用它的Bean属性相同的名字(id=”account” 可忽略属性首字每大小写),然后使用byName的方式来自动装配,对user bean来说省略配置一个元素。
<!--以下是使用自动装配,假设这里定义的id为account-->
<bean id="account" class="twm.demo.Account"/>

<bean id="user" class="twm.demo.User" autowire="byName">
  <property name="username" value="Yanglan"/>
</bean>
  • byType方式:把autowire属性值改为byType后,在注入account属性时,并不关心bean id了,而是查找容器中是否有类型为twm.demo.Account的bean。但是如果有多个bean的类型都匹配的情况,那么就会出错,因为byType方式只允许匹配一个类型相同的Bean。如果在容器中存在多个类型相同的bean怎么办呢?见后面的多个bean的问题。 spring提供了另外两种选择,可以设置一个首选bean,或者排除一些bean。元素的primary属性代表是否是首选bean,如果标注为true,那么该bean将成为首选bean。但是spring默认每个bean的primary属性都是true,所以如果需要设置首选bean需要将那些非首选bean的primary属性标注为false。
<bean id="ac_anyname" class="twm.demo.Account"/>
<bean id="user" class="twm.demo.User" autowire="byType">
  <property name="username" value="Yanglan"/>
</bean>
<!--。。。。。。。分割线。。。。。。。。。-->
<bean id="account" class="twm.demo.Account"/>
<bean id="account_ent" class="twm.demo.Account" primary="false" />
  • constructor构造方式:构造函数的参数通过byType进行装配
<bean id="yanglan" class="twm.demo.User" autowire="constructor">
</bean>
  • autodetect最佳自动装配:首先使用constructor方式进行装配,如果不行,就使用byType方式装配。使用方法跟以上介绍的都是一样的 ,这里不多说了
<bean id="yanglan" class="twm.demo.User" autowire="autodetect">
</bean>
  • 默认自动装配:在元素中添加一个default-autowire属性,该配置文件当中的所有bean将会进行自动装配,如果有特定的bean需要使用其他的方式,在该bean上直接设置autowire属性就可以了,会覆盖掉默认自动装配的配置,代码如下。
<beans ... default-autowire="byType">
</beans>
  • 自动装配侯选者:XML配置中默认所有的bean都是自动装配的侯选者。如果设置元素的autowire-candidate属性为false,该bean将不用于自动装配。autowire-candidate默认值为true。元素的default-autowire-candidates属性的值允许使用通配符,例如我们制定default-autowire-candidates=“*abc”,则所有以“abc”结尾的Bean都将被包含到自动装配的待选类中。该属性可以指定多个匹配字符串,匹配任一字符串的Bean都将作为侯选者。

使用注解自动装配

如果不想在xml文件中使用autowire属性来启用自动装配,还可以直接在类定义中使用@Autowired或@Resource来装配bean。

使用@Autowired注解:

@Autowired注解默认使用的是byType的方式向Bean里面注入相应的Bean。使用@Autowired自动装配时,容器中只能有一个适合的Bean待选,否则的话,spring会抛出异常。@Autowired注解可以用在任何方法上,不一定非得是setter方法,只要方法有需要自动装配的参数都可以,但是一般都是使用在setter方法和构造器上的。

  • 用于setter方法:看如下代码:把@Autowired注解在setter方法上,在spring创建该类的bean的时候,就会自动寻找匹配的参数注入到该bean当中。
@Autowired
public void setNotifyservice(NotifyService notifyservice) {
    this.notifyservice = notifyservice;
}
  • 用于构造函数:@Autowired另外一个用法就是注解构造函数
public class Order {
@Autowired
public Order(NotifyService notifyservice) {
    this.notifyservice = notifyservice;
}
//......省略部分代码

  • 直接注解在属性(最常用):
@Autowired
private NotifyService notifyservice;

@Resource

可以用@Resource代替@Qualifier和@Autowired,但是 @Resource 是 jdk 的注解,而 @Qualifier 和 @Primary 是spring 的注解,这是它们的区别。 @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

@Service("service")
public class DemoServiceImpl {
    @Resource(name="d1")
    private DemoDao demoDao;

    public void service(){
        demoDao.test();
    }
}

自动装配时有多个bean的问题

当一个接口有多个不同的实现类时,使用@Autowired注解时会报org.springframework.beans.factory.NoUniqueBeanDefinitionException异常信息。

@Primary

在众多相同的bean中,优先使用用@Primary注解的bean。

public interface DemoDao {
    void test();
}
@Repository("d1")
public class DemoDaoImpl1 implements DemoDao{
    public void test() {
        System.out.println("11111111");
    }
}
@Repository("d2")
@Primary
public class DemoDaoImpl2 implements DemoDao{
    public void test() {
        System.out.println("22222222");
    }
}
@Service("service")
public class DemoServiceImpl {
    @Autowired
    private DemoDao demoDao;

    public void service(){
        demoDao.test();
    }
}
@Configuration
@ComponentScan("com.msj.demo02")
public class AppConfig {
}
public class TestDemo {
    @Autowired
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        DemoServiceImpl s = (DemoServiceImpl)context.getBean("service");
        s.service();
    }
}

@Qualifier

Qualifier的意思是合格者,通过这个标示,指定某个bean有没有资格进行注入。
在上面的例子进行修改,修改DemoServiceImpl.java

@Service("service")
public class DemoServiceImpl {
    @Autowired
    @Qualifier("d1")
    private DemoDao demoDao;

    public void service(){
        demoDao.test();
    }
}

两个方案进行对比,当一个接口有多个实现类的时候,使用 @Qualifier 比较好,可以通过 @Qualifier(“xxx”) ,就可以看出使用哪个实现类。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值