springNote3 - juedaiyuer/researchNote GitHub Wiki

#spring笔记[三]---基于注解的Bean装配#

##ClassPath扫描与组件管理##

从Spring3.0开始,Spring JavaConfig项目提供了很多特性,包括使用java而不是XML定义bean

@Component 是一个通用注解,可用于任何bean
@Repository 通常用于注解DAO类,即持久层
@Service 注解Service类,即服务层
@Controller 控制层,MVC

@Configuration
@Bean
@Import
@DependsOn

##类的自动检测及Bean的注册##

spring可以自动检测类并注册Bean到ApplicationContext

<context:component-scan>


<context:annotation-config/>

使用过滤器进行自定义扫描

默认情况下,类被自动发现并注册bean的条件是:使用@component,@Repository,@Controller注解或者使用@Component的自定义注解

可以通过过滤器修改上面的行为

use-default-filters="false" 禁用自动发现与注册

using filters to customize scanning

##定义Bean##

扫描过程中组件被自动检测,那么Bean名称是由BeanNameGenerator生成的

@Component 
@Repository 
@Service 
@Controller 

上面的注解都会有name属性用于显式设置Bean Name,相当于XML定义的id

@Service("myMovieListener")
public class SimpleMovieListener
{
	//TODO
}

可自定义bean命名策略,实现BeanNameGenerator接口,并一定要包含一个无参数构造

<beans>

	<context:component-scan base-package="org.example"
		name-generator="org.example.MyNameGenerator"/>

</beans>

##作用域##

通常情况下自动查找的Spring组件,其scope是singleton,Spring2.5提供了一个标识scope的注解@Scope

@Scope("prototype")

也可以自定义scope策略,实现ScopeMetadataResolver接口并提供一个无参构造器

<beans>

	<context:component-scan base-package="org.example"
		scope-resolver="org.example.MyScopeResolver" />

</beans>

##代理方式##

可以使用scoped-proxy属性指定代理,有三个值可选:no,interfaces,targetClass

<beans>

	<context:component-scan base-package="org.example"
		scoped-proxy="interfaces"/>

</beans>

##代码示例##

package juedaiyuer.annotation;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

//@Component("bean")
@Scope
@Component
public class BeanAnnotation {

	public void say(String arg) {
		System.out.println("BeanAnnotation : " + arg);
	}

	public void myHashCode() {
		System.out.println("BeanAnnotation : " + this.hashCode());
	}

}


<?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="juedaiyuer.annotation"></context:component-scan>
	    
</beans>



package test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

import juedaiyuer.annotation.BeanAnnotation;



@RunWith(BlockJUnit4ClassRunner.class)
public class TestBeanAnnotation extends UnitTestBase {

	public TestBeanAnnotation() {
		super("classpath*:spring-beanannotation.xml");
	}

	@Test
	public void testSay() {
		BeanAnnotation bean = super.getBean("beanAnnotation");
		bean.say("This is test.");
	
		bean = super.getBean("bean");
		bean.say("This is test.");
	}

	@Test
	public void testScpoe() {
		BeanAnnotation bean = super.getBean("beanAnnotation");
		bean.myHashCode();
	
		bean = super.getBean("beanAnnotation");
		bean.myHashCode();
	}

}

##Required注解##

适用于bean属性的setter方法

这个注解仅仅表示,受影响的bean属性必须在配置时被填充,通过在bean定义或者通过自动装配一个明确的属性值

实际开发中,并不常用

public class SimpleMovieListener{
	private MovieFinder movieFinder;

	@Required
	public void setMovieFinder(MovieFinder movieFinder)
	{
		this.movieFinder = movieFinder;
	}

	//...
}

##Autowired注解##

public class SimpleMovieListener{
	private MovieFinder movieFinder;

	@Autowired
	public void setMovieFinder(MovieFinder movieFinder)
	{
		this.movieFinder = movieFinder;
	}

	//...
}

可以注解到传统的setter方法
可用于构造器或成员变量

默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常,可以通过下面的方式避免

	public class SimpleMovieListener{
	private MovieFinder movieFinder;

	@Autowired(required=false)
	public void setMovieFinder(MovieFinder movieFinder)
	{
		this.movieFinder = movieFinder;
	}

	//...
}

每一个类只能有一个构造器被标记为required=ture
@autowired的必要属性,建议使用@Required注解

##代码示例##

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" >
	    
	    <context:component-scan base-package="juedaiyuer.annotation"></context:component-scan>
	    
</beans>

DAO的一个接口

package juedaiyuer.annotation.injection.dao;

public interface InjectionDAO {

	public void save(String arg);

}

DAO接口实现类

package juedaiyuer.annotation.injection.dao;

import org.springframework.stereotype.Repository;

@Repository
public class InjectionDAOImpl implements InjectionDAO {

	public void save(String arg) {
		//模拟数据库保存操作
		System.out.println("保存数据:" + arg);
	}

}

Service的一个接口

package juedaiyuer.annotation.injection.service;

public interface InjectionService {

	public void save(String arg);

}

Service接口实现类

package juedaiyuer.annotation.injection.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import juedaiyuer.annotation.injection.dao.InjectionDAO;

@Service
public class InjectionServiceImpl implements InjectionService {

//	@Autowired
	private InjectionDAO injectionDAO;

	@Autowired
	public InjectionServiceImpl(InjectionDAO injectionDAO) {
		this.injectionDAO = injectionDAO;
	}

//	@Autowired
	public void setInjectionDAO(InjectionDAO injectionDAO) {
		this.injectionDAO = injectionDAO;
	}



	public void save(String arg) {
		//模拟业务操作
		System.out.println("Service接收参数:" + arg);
		arg = arg + ":" + this.hashCode();
		injectionDAO.save(arg);
	}

}

单元测试子类

package test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

import juedaiyuer.annotation.injection.service.InjectionService;



@RunWith(BlockJUnit4ClassRunner.class)
public class TestInjection extends UnitTestBase {

	public TestInjection() {
		super("classpath:spring-beanannotation.xml");
	}

	@Test
	public void testAutowired() {
		InjectionService service = super.getBean("injectionServiceImpl");
		service.save("This is autowired.");
	}

//	@Test
//	public void testMultiBean() {
//		BeanInvoker invoker = super.getBean("beanInvoker");
//		invoker.say();
//	}

}

##Autowired注解##

  1. 解析依赖性接口(beanfactory,applicationContext,environment,resourceloader,applicationEventPublisher,messagesource...)
  2. 可以用于装配key为string的Map
  3. 如果希望数组有序,可以让bean实现org.springframework.core.Ordered接口或使用@Order注解
  4. Spring BeanPostProcessor处理,不能在自己的BeanPostProcessor或BeanfactoryBeanPostProcessor类型应用该注解,这些类型必须通过XML或Spring的@Bean注解
  5. order注解只对数组有效

###数组及map的自动注入###

package juedaiyuer.annotation.multibean;

public interface BeanInterface {

}


package juedaiyuer.annotation.multibean;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

//@Order(2)
@Component
public class BeanImplOne implements BeanInterface {

}


package juedaiyuer.annotation.multibean;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

//@Order(1)
@Component
public class BeanImplTwo implements BeanInterface {

}

package juedaiyuer.annotation.multibean;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class BeanInvoker {

	@Autowired
	private List<BeanInterface> list;

	@Autowired
	private Map<String, BeanInterface> map;

	@Autowired
	@Qualifier("beanImplTwo")
	private BeanInterface beanInterface;

	public void say() {
		if (null != list && 0 != list.size()) {
			System.out.println("list...");
			for (BeanInterface bean : list) {
				System.out.println(bean.getClass().getName());
			}
		} else {
			System.out.println("List<BeanInterface> list is null !!!!!!!!!!");
		}
	
		System.out.println();
	
		if (null != map && 0 != map.size()) {
			System.out.println("map...");
			for (Map.Entry<String, BeanInterface> entry : map.entrySet()) {
				System.out.println(entry.getKey() + "      " + entry.getValue().getClass().getName());
			}
		} else {
			System.out.println("Map<String, BeanInterface> map is null !!!!!!!!!!");
		}
	
		System.out.println();
		if (null != beanInterface) {
			System.out.println(beanInterface.getClass().getName());
		} else {
			System.out.println("beanInterface is null...");
		}
	
	
	}

}



package test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

import juedaiyuer.annotation.injection.service.InjectionService;
import juedaiyuer.annotation.multibean.BeanInvoker;



@RunWith(BlockJUnit4ClassRunner.class)
public class TestInjection extends UnitTestBase {

	public TestInjection() {
		super("classpath:spring-beanannotation.xml");
	}

	@Test
	public void testMultiBean() {
		BeanInvoker invoker = super.getBean("beanInvoker");
		invoker.say();
	}

}

##Qualifier注解##

按照类型自动装配可能多个bean实例的情况,可以使用spring的qualifier注解缩小范围(或指定唯一),也可以用于制定单独的构造器参数或方法参数

可用于注解集合类型变量

如果通过名字进行注解注入,主要使用的不是@autowired(即使在技术上能够通过@qualifier指定bean的名字),替代方法是使用JSR-250的@Resource注解,它通过其独特的名称来定义识别特定的目标.

因语义差异,集合或Map类型的bean无法通过@autowired来注入,因为没有类型匹配这样的bean,@resource注解通过唯一名称引用集合或map的bean

适用于fields,constructors,multi-argument mehods这些允许在参数级别使用qualifier注解缩小范围

@resource适用于成员变量,只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方式是使用qualifiers

⚠️ **GitHub.com Fallback** ⚠️