Bean管理的注解实现
Classpath扫描与组件管理
- 从Spring3.0开始,Spring JavaConfig项目提供了很多特性,包括使用Java而不是XML定义bean,比如:@Configuration、@Bean、@Import、@DependsOn
- @Component是一个通用注解,可以用于任何bean
@Repository、@Service、@Controller是更有针对性的注解
- @Repository通常用户注解DAO类,即持久层
- @Service通常用于注解Service类,即服务层
- @Controller通常用于注解Controller类,即控制层
元注解(Meta-annotations)
- 许多Spring提供的注解可以作为自己的代码,即”元数据注解”,元注解是一个简单的注解,可以应用到另一个注解。
- 除了value(),元注解还可以有其他的属性,允许定制
类的自动检测及Bean的注册
- Spring可以自动检测类并注册Bean到ApplicationContext中
- 通过在基于XML的Spring配置如下标签(注意包含上下文命名空间)
- 仅会查找在同一个applicationContext中的bean注解
- 为了能够检测这些类并注册相应的Bean,需要如下内容
<context:component-scan base-package="org.example"/>
- 包含,通常使用前者后,不用在使用后者
- AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor也会被包含进来
使用过滤器进行自定义扫描
- 默认情况下,类被自动发现并注册bean的条件是:使用@Component、@Repository、@Service、@Controller注解或使用@Component的自定义注解
- 可以通过过滤器修改上面的行为,如:下方XML配置忽略所有的@Repository注解,并用”Stub”代替
<beans>
<context:component-scan>
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
- 还可以使用use-default-filters=”false”禁用自动发现与注册
上面例子中的type分为以下几种
- annotation
- assignable
- aspectj
- regex
- custom
定义Bean
- 扫描过程中组件被自动检测,那么Bean名称是由BeanNameGenerator生成的(@Component、@Repository、@Service、@Controller都会有个name属性用于显示设置Bean Name)
//bean名字为myMovieLister,不写默认生成的是simpleMovieLister
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
//自动生成,类名第一个字母小写,即movieFinderImpl
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
- 可自定义bean命名策略,实现BeanNameGenerator接口,并且一定要包含一个无参构造器
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator"/>
</beans>
作用域(Scope)
- 通常情况下自动查找的Spring组件,其scope是singleton,Spring2.5提供了一个标识scope的注解@Scope自定义作用域
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
- 也可以自定义scope策略,实现ScopeMetaMetadataResolver接口,并且提供一个无参构造器
<beans>
<context:component-scan base-package="org.example"
scope-resolver="org.example.MyScopeResolver"/>
</beans>
代理方式
- 可以使用scope-proxy属性指定代理,有三个值可选:no、interfaces、targetClass
<beans>
<context:component-scan base-package="org.example"
scope-proxy="interfaces"/>
</beans>
Bean的定义和作用域
常用注解
- @Required
- @Required注解适用于bean属性的setter方法,必须有值
- 这个注解仅仅表示,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值。
2.@Autowired(按类型注入)
- 可以将@Autowired注解在传统的setter方法上
- 可用于构造器或成员变量
- 默认情况下,如果找不到合适的bean将会导致autowiring失败抛出异常,可以通过如下方式避免:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
- 每个类只能有一个构造器被标记为required=true
- @Autowired的必要属性,建议使用@Required注解
- 可以使用@Autowired注解那些众所周知的解析依赖性接口,比如:BeanFactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,MessageSource
- 可以通过添加注解给需要该类型的数组的字段或方法,以提供ApplicationContext中的所有特定类型的bean
- 可以用于装配key为String的Map
- 如果希望数组有序,可以让bean实现org.springframework.core.Order接口或使用@Order注解(对list注入有效,对map注入无效)
- @Autowired是由Spring BeanPostProcessor处理,所以不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型应用这些注解,这些类型必须通过XML或Spring的@Bean注解加载
数组和Map的注入
- @Qualifier
- 按类型自动装配可能有多个bean实例的情况时,可以使用Spring的@ualifier注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法参数
- 可用于注解集合类型变量
xml中Qualifier的配置
<bean class="org.exampe.Demo">
<qualifier value="main"/>
</bean>
- 如果通过名字进行注解注入,主要使用的不是@Autowired(即使在技术上能够通过@Qualifier指定bean的名字),替代方式是使用JSR-250@Resource(按名字注入),它是通过其独特的名称来识别特定的目标(这是一个与所声明的类型是无关的匹配过程)
- 因语义差异,集合或Map类型的bean无法通过@Autowired来注入,因为没有类型匹配到这样的bean,为这些bean使用@Resource注解,通过唯一名称引用集合或Map的bean
- @Autowired适用于fields、constructors、multi-argument methods这些允许在参数级别使用@Qualifier缩小范围的情况
- @Resource适用于成员变量、只有一个参数的setter方法,所以在目标是构造器或一个多参数的方法时,最好的方式是使用Qualifiers
基于Java的容器注解
- @Bean(默认是单例)
- @Bean标识一个用于配置和初始化一个由SpringIoC容器管理的新对象的方法,类似于XML配置文件的
- 可以在Spring的@Component注解的类中使用@Bean注解任何方法(仅仅是可以)
- 上一点中,通常使用的是@Configuration
2.@ImportResource和@Value注解
属性文件config.properties
jdbc.password=root
jdbc.url=jdbc\:mysql\://127.0.0.1.8\:3306/database
jdbc.username=root
在配置文件中读取配置文件config.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:property-placeholder location="classpath:/config.properties"/>
<!-- 读取配置文件 方式二-->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!--在配置文件中使用${jdbc.url}访问-->
<value>classpath*:jdbc.properties</value>
<value>classpath*:hibernate.properties</value>
</list>
</property>
</bean>
</beans>
在代码中使用注解实现:
MyDriverManager类
package com.imooc.beanannotation.javabased;
public class MyDriverManager {
public MyDriverManager(String url, String userName, String password) {
System.out.println("url : " + url);
System.out.println("userName: " + userName);
System.out.println("password: " + password);
}
}
package com.imooc.beanannotation.javabased;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@Configuration
@ImportResource("classpath:config.xml")
public class StoreConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public MyDriverManager myDriverManager() {
return new MyDriverManager(url, username, password);
}
}
测试类:
package com.imooc.test.beanannotation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import com.imooc.beanannotation.javabased.MyDriverManager;
import com.imooc.test.base.UnitTestBase;
@RunWith(BlockJUnit4ClassRunner.class)
public class TestJavabased extends UnitTestBase {
@Test
public void testMyDriverManager() {
MyDriverManager manager = super.getBean("myDriverManager");
System.out.println(manager.getClass().getName());
}
}
UnitTestBase类如下:
package com.imooc.test.base;
import org.junit.After;
import org.junit.Before;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StringUtils;
public class UnitTestBase {
private ClassPathXmlApplicationContext context;
private String springXmlpath;
public UnitTestBase() {}
public UnitTestBase(String springXmlpath) {
this.springXmlpath = springXmlpath;
}
@Before
public void before() {
if (StringUtils.isEmpty(springXmlpath)) {
springXmlpath = "classpath*:spring-*.xml";
}
try {
context = new ClassPathXmlApplicationContext(springXmlpath.split("[,\\s]+"));
context.start();
} catch (BeansException e) {
e.printStackTrace();
}
}
@After
public void after() {
context.destroy();
}
@SuppressWarnings("unchecked")
protected <T extends Object> T getBean(String beanId) {
try {
return (T)context.getBean(beanId);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
protected <T extends Object> T getBean(Class<T> clazz) {
try {
return context.getBean(clazz);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
}
@Bean和@Scope
- 默认@Bean是单例的
- 铜鼓@Scope指定作用域
基于泛型的自动装配
spring4的新特性
CustomAutowireConfigurer
- CustomAutowireConfigurer是BeanFactoryPostProcessor的子类,通过它可以注册自己的qualifier注解类型(即使没有使用Spring的@Qualifier注解)
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="cystomQqalifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
该AutowireCandidateResolver决定自动装配的候选者:
- 每个Bean定义的autowire-candidate值
- 任何中的default-autowire-candidates
- @Qualifier注解及使用CustomAutowireConfigurer的自定义类型
Spring对JSR的支持
@Resource
- Spring支持使用JSR-250@Resource注解的变量或setter方法,这是一种在Java EE5和6的通用模式,Spring管理的对象也支持这种模式
- @Resource有一个name属性,并且默认Spring解释改值作为被注入bean的名称
- 如果没有显示地指定@Resource的name,默认的名称是从属性名或setter方法得出
- 注解提供的名字被解析为一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的
@PostConstruct和@PreDestory
- CommonAnnotationBeanPostPressor不仅能识别JSR-250中的生命周期注解@Resource,在Spring2.5中引入支持初始化回调和销毁回调,前提是CommonAnnotationBeanPostPressor是Spring的ApplicationContext中注册的
使用JSR330标准注解
- 从Spring3.0开始支持JSR330标准注解(依赖注入注解), 其扫描方式与Spring注解一致
- 使用JSR330需要依赖javax.inject包
- 使用Maven引入方式
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
- @Inject
- @Inject等效于@Autowired,可以使用类、属性、方法、构造器
- @Named
- 如果想使用特定名称进行依赖注入,使用@Named
- @Named与@Component是等效的