Spring系列(一)装配Bean

Spring容器最常见的三种方法。

1.    隐式的bean发现机制和自动装配。

2.    在Java中进行显式配置。

3.在XML中进行显式配置。

总结 Spring3中推荐用xml方法进行配置 Spring4,推荐使用类型安全并且比XML更加强大的JavaConfig


1自动装配

1)组件扫描(componentscanning):Spring会自动发现应用上下文中所创建的bean。

2)自动装配(autowiring):Spring自动满足bean之间的依赖。

 

@ComponentScan和@Component和@Configuration

1)@ComponentScan和作用和在xml中配置的<context:component-scanbase-package="org.springframework.test"/>作用是相同的

不过@ComponentScan的默认扫描的是**/*.class路径所以最好设置basePackage属性指定扫描路径,减少加载时间

 

个人理解:自动扫描作用自动扫描路径下面所有的类(实现方式就是递归base-package下面所有的文件,把最后的文件以.class命名保存,之后可以利用反射机制创建该类)

Spring会把Bean的数据结构存放在beanDefinition中 主要包括BeanName和BeanClass 其中BeanName默认为类名小写,并保存在一个beanDefinitionMap(ConcurrentHashMap)中

2)@Component告知Spring要为这个类创建bean。

需要创建Bean的标签有

1、@controller 控制器(注入服务)

用于标注控制层,相当于struts中的action层

2、@service 服务(注入dao)

用于标注服务层,主要用来进行业务的逻辑处理

3、@repository(实现dao访问)

用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件.

4、@component (把普通pojo实例化到spring容器中,相当于配置文件中的

<bean id=""class=""/>)

注意:只有在ComponentScan扫描过的包才会创建这些bean

 

个人理解:在SpringIOC容器中,调用getBean()方法或(lazy-init= false)(默认为ture)才会创建Bean,根据beanDefinitionMap来创建的Bean的会保存在在一个BeanCacheMap(注册式单利容器)中,之后再把BeanCacheMap包装成BeanWrapper,我们造作的Bean都是BeanWrapper,这样的好处1、保留原来的OOP关系2、我需要对它进行扩展,增强(为了以后AOP打基础)最后进行注解的扫描,存在上面注解的进行自动注入,不存在的不进行注入。

 

3)@Configuration 表示这个类是一个spring 配置类,一般这里面会定义Bean,会把这个类中bean加载到spring容器中

因为Spring默认的BeanName为类名小写可以用@Configuration给bean设置不同的ID

案例

 

@Configuration(”mypay”)

Public class Pay{}

或者用

@Named(”mypay”)

Public class Pay{}

 


2在Java中进行显式配置JavaConfig

JavaConfig在java中显示配置主要依赖的标签为@Configuration

上面解释过加有@Configuration表示这个类是一个Spring的配置类

这种配置方式主要通过配置文件进行注入Bean

案例

public class LogDao {
   
public void print(){
       
System.out.println("hello+我是LogDao");
   
}
}

 


public class UserDao {
   
public void print(){
       
System.out.println("Hello+我是UserDao");
   
}
}

 


public class LogonService {

   
public UserDao userDao;

   
public LogDao logDao;
   
//注入Bean
   
public void setLogDao(LogDao logDao) {
       
this.logDao = logDao;
   
}
    //注入Bean
   
public void setUserDao(UserDao userDao) {
       
this.userDao = userDao;
   
}

    public void print() {
       
userDao.print();
       
logDao.print();
   
}
}

 

 


//当通过手动注册配置类的时候,这个可以不写,如果想通过应用程序上下文获得这个bean,这个必须写
@Configuration
public class AppConf {

   
// 以下两个方法定义了两个Bean,并提供了Bean的实例化逻辑
   
@Bean
   
public UserDao userDao() {
       
return new UserDao();
   
}
    @Bean
   
public LogDao logDao() {
       
return new LogDao();
   
}
    // 定义了LogonService的Bean,名字是logonService1
   
@Bean(name="logonService1")
   
public LogonService logonService() {
       
LogonService logonService = new LogonService();
       
// 将上面定义的Bean注入到logonService Bean中

       
logonService.setLogDao(logDao());
       
logonService.setUserDao(userDao());
        return logonService;
   
}
}

public class ConfigTest {
@Test
   
public void testConf1(){
       
//手动注册配置类
       
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConf.class);
       
//需找类型为LogonService,名字为logonService1的bean,如果没有指定名字,默认寻找匹配的类型.
       
LogonService logonService = ac.getBean("logonService1",LogonService.class);
       
logonService.print();
    }

}

 

输出:

 

3.   在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"
>
   
<!-- 在Spring参数类型'123456' 能够转换成int,所以Spring只是转换它,并采用第二个构造来执行,
    即使你认为它应该是一个字符串。这样就造成了属性注入问题。为了解决这个问题,
    应该为构造函数指定确切的数据类型,Spring bean的配置文件应该修改为如下形式-->

    <!-- 第二个构造函数-->
     <!--   <bean id="StudentBean" class="Student">
            <constructor-arg><value>jack</value></constructor-arg>
            <constructor-arg><value>123456</value></constructor-arg>
            <constructor-arg><value>12</value></constructor-arg>
        </bean>-->
    <!-- 第一个构造函数-->
   
<bean id="StudentBean" class="springinaction3.wiringbean.xmlwiring.constructorwiring.Student">
       
<constructor-arg type="java.lang.String"><value>jack</value></constructor-arg>
       
<constructor-arg type="java.lang.String"><value>123456</value></constructor-arg>
       
<constructor-arg type="int"><value>12</value></constructor-arg>

   
</bean>

</
beans>

 

 

<constructor-arg>表示构造器注入,注入方式会根据参数类型,进行注入。

 

4处理自动装配的歧义性

当一个接口有多个实现类的时候进行Bean的装配就会出现歧义,Spring会傻傻分不清,你到底要装配那个Bean,此时Spring会报错,会抛NoUniqueBeanDefinitionException

案例 有一个支付接口,有两种支付方式一种为微信支付,一种为支付宝支付

@Component
public interface Pay {
   
public void Pay();
}

 

 

@Component
public class WeiXin implements Pay{
   
public void Pay() {
       
System.out.println("微信支付");
   
}
}

 

 

 

@Component
public class ZhiFuBao implements Pay {
   
public void Pay() {
       
System.out.println("支付宝支付");
   
}
}

 

 

@Component
public class Shopping {

@Autowired
   
public Pay pay;

   
public void buy(){
       
pay.Pay();
   
}
}

 

 

public class test {

   
public static void main(String[] args) {
       
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("paybean.xml");

       
Shopping shopping =(Shopping)applicationContext.getBean("shopping");
       
shopping.buy();
    }
}

 

 

 

<?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="springinaction3.wiringbean.qiyi"></context:component-scan>

</
beans>

 

报错信息

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'shopping': Unsatisfied dependency expressed through field 'pay'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'springinaction3.wiringbean.qiyi.Pay' available: expected single matching bean but found 2: weiXin,zhiFuBao

 

错误显示 pay有两个实现类weiXin,zhiFuBao  Spring不知道注入那个Bean


这就是产生歧义的原因

解决方式

第一种(注入具体类)

依赖出入的时候可以注入具体类,而不是接口

@Component
public class Shopping {

@Autowired
   
public WeiXin pay;

   
public void buy(){
       
pay.Pay();
   
}
}

 


第二种(@Primary)

用(@Primary)标示首选的bean

@Component
@Primary
public class ZhiFuBao implements Pay {
   
public void Pay() {
       
System.out.println("支付宝支付");
   
}
}

 

 

第三种(@Qualifier)

设置首选bean的局限性在于@Primary无法将可选方案的范围限定到唯一一个无歧义性的选项中。它只能标示一个优先的可选方案。当首选 bean的数量超过一个时,我们并没有其他的方法进一步缩小可选范围。

@Qualifier注解是使用限定符的主要方式。它可以与@Autowired和@Inject协同使用,在注入的时候指定想要注入进去的是哪个bean。

@Component
public class Shopping {
@Autowired
@Qualifier
("weiXin")
   
public Pay pay;

   
public void buy(){
       
pay.Pay();
   
}
}

 

5.bean的作用域

 

Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。也就是说,不管给定的一个bean被注入到其他bean 多少次,每次所注入的都是同一个实例。

 

Spring定义了多种作用域,可以基于这些作用域创建bean,包括:

单例(Singleton):在整个应用中,只创建bean的一个实例。

原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。

会话(Session):在Web应用中,为每个会话创建一个bean实例。

请求(Rquest):在Web应用中,为每个请求创建一个bean实例。

 

1.通过@Scope的ConfigurableBeanFactory属性设置单例和原型作用域

2. .通过@Scope的WebApplicationContext属性设置会话和请求作用域

3.通过Xml设置作用域

或在Xml中通过scope属性设置

 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值