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属性设置
