spring的java配置方式以及注入时的注意点

Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置。

通过两个注解@Configuration和@Bean来实现。

1、@Configuration作用于类上,可以理解为一个xml配置文件。

2、@Bean作用于方法上,可以理解为xml配置中的<bean>。

@Configuration
@ComponentScan("com.xw.test")
public class SpringConfig {
	@Bean
	public UserDao getUserDao() {
		return new UserDaoImpl();
	}
}

@Bean注解会自动把方法的名字设置为bean的名字,如上面的就会设置为getUserDao

public static void main(String[] args) {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
	UserDao userDao1 = (UserDao) context.getBean("getUserDao");
	System.out.println(userDao1);
	context.destroy();
}

@ComponentScan("com.xw.test")的作用:扫描包


在使用注解的方式时,必须配置

<context:annotation-config/>

作用:向 Spring 容器注册这 4 个BeanPostProcessor

AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor

使用@Autowired注解,就必须事先在Spring容器中声明AutowiredAnnotationBeanPostProcessor。
使用@Resource 、@PostConstruct、@PreDestroy等注解就必须声明CommonAnnotationBeanPostProcessor。
使用@PersistenceContext注解,就必须声明PersistenceAnnotationBeanPostProcessor。

使用@Required的注解,就必须声明RequiredAnnotationBeanPostProcessor。

但是:<context:component-scan base-package=XX.XX/>包含了自动注入上述processor的功能。所以就不用写了。


@Autowired和@Resource的区别

使用@Autowired时,先在容器中查询类型的bean(byType)
1、如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据。

2、如果查询的结果不止一个,那么@Autowired会根据名称来查找。

名称:是指的如下userDao这个名称,或者自己通过注解@Qualifier("getUserDao")来指定名称查找。

@Autowired

private UserDao userDao;

3、如果根据名称查询的结果为空,那么会抛出异常。解决方法时,使用required=false。

所以在使用的时候我们一般是在实现类中加@Service、@Repository,名字是默认的,如

@Repository

public class UserDaoImpl implements UserDao 
如上面这种,名称就是类首字母小写userDaoImpl。

注意:像上面的例子,如果有多个实现类UserDaoImpl UserDaoImpl2,根据上面@Autowired的规则,根据类型查找的结果不止一个,且根据名称userDao来查找肯定也不匹配(一个是userDaoImpl,一个是userDaoImpl2),此时就像上面第三步一样,抛出异常,注入失败。此时就可以用@Qualifier("userDaoImpl")来区分。

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.xw.test.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl2

使用@Resource时,是根据名字来查找,如下:

@Resource
private UserDao userDao;

 1、先查找是否有名称为userDao的bean。

 2、如果没有找到,则看是否有name属性(@Resource  name=“”),有则根据name查找。

 3、如果还没有找到,则根据类型来查找,如果没有找到或者找到多个则抛出异常。


疑惑:

第一种情况:一个在类上@Repository,一个在标有@Configuration类的方法上。

@Repository("userDaoImpl")
public class UserDaoImpl implements UserDao {
@Bean("userDaoImpl")
public UserDao getUserDao2() {
	return new UserDaoImpl2();
}
信息: Overriding bean definition for bean 'userDaoImpl' with a different definition: replacing [Generic bean: class [com.xw.test.UserDaoImpl];

这样是可以共存的,会有个如上Overriding的提示而已。

第二种情况:两个都在类上@Repository

@Repository("userDaoImpl")
public class UserDaoImpl implements UserDao {
@Repository("userDaoImpl")
public class UserDaoImpl2 implements UserDao {
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.xw.test.SpringConfig]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'userDaoImpl' for bean class [com.xw.test.UserDaoImpl2] conflicts with existing, non-compatible bean definition of same name and class [com.xw.test.UserDaoImpl]
此时初始化报错。

两种情况都是注入两个名字一样的不同属于不同类的实例对象,但结果不一样。猜测:和@Configuration有关,有时间再看下源码。

=====================2018-06-26更新=====================

最近一直在看Spring的源码,了解了启动大概的流程,想起这个问题,debug跟了一下:

1、ioc容器启动会刷新容器,refresh();

2、refresh();中调用invokeBeanFactoryPostProcessors(beanFactory);执行BeanFactory后置处理器

3、Spring有一个内部的BeanFactory:

    internalConfigurationAnnotationProcessor,类型为ConfigurationClassPostProcessor。

4、而且是实现了PriorityOrdered,优先级很高的后置处理器

5、排序好之后执行 invokeBeanDefinitionRegistryPostProcessors。

6、执行ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法

7、执行方法 processConfigBeanDefinitions(registry);

8、解析每一个标有@Configuration的类

9、调用 parser.parse(candidates);

10、parse中一直会调到 doProcessConfigurationClass

11、会去@ComponentScan("")中的路径下扫描包

12、最终会调到ComponentScanAnnotationParser的doScan(String... basePackages)方法

13、AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);

    会处理一些其他逻辑,primary,Lazy等

14、之后会检查候选的 checkCandidate(beanName, candidate)

15、这里就是会出现上面那个报错的地方了,如果现有的bean定义中没有才会返回true,最后会throw new ConflictingBeanDefinitionException,就是上面出现的错误了。

以上是都在扫描包的时候冲突的处理逻辑,还有一个是在@Bean中定义一个名字一样的,不会报错,只会提示,因为@Bean不在扫描包中处理,而是在扫描包之后再处理的,所以@Bean的会覆盖扫描中的bean定义信息。如下:

1、会读取配置类中的bean定义信息,然后load其中的bean定义信息。


2、会解析配置类中的beanMethod,然后调用loadBeanDefinitionsForBeanMethod

3、先判断现有定义的信息中是否有这个定义信息,有就在判断是否允许定义信息覆盖,不允许就会报错,允许就判断角色,也只会提示,再之后就是我们出现覆盖信息的地方了。


所以最终会出现两种不同的情况是因为扫描包和类中定义bean是分开解析的,扫描包不允许重复,@Bean的形式允许重复(也可设置不允许)。

=====================2018-06-26更新=====================


读取外部参数:通过注解@PropertySource("classpath:test.properties")读取指定的配置。

使用参数:

常用的方式一:通过@Value

@Value("${name}")
private String name;
@Value("${age}")
private String age;

常用的方式二:通过Environment

@Autowired
private Environment enviroment;
enviroment.getProperty("name")即可获取。

借此温习记录下spring的注解。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值