学习链接地址:http://www.iteye.com/blogs/subjects/spring3
注解实现Bean配置主要用来进行如依赖注入、生命周期回调方法定义等,不能消除XML文件中的Bean元数据定义,且基于XML配置中的依赖注入的数据将覆盖基于注解配置中的依赖注入的数据。
Spring3的基于注解实现Bean依赖注入支持如下三种注解:
基于@Required的依赖检查表示注解的setter方法必须,即必须通过在XML配置中配置setter注入,如果没有配置在容器启动时会抛出异常从而保证在运行时不会遇到空指针异常,@Required只能放置在setter方法上,且通过XML配置的setter注入,可以使用如下方式来指定:
@Requried
setter方法
public class TestBean {
private String message;
@Required
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
id="testBean" class="cn.javass.spring.chapter12.TestBean">
<property name="message" ref="message"/>
id="message" class="java.lang.String">
"0" value="hello"/>
public class DependencyInjectWithAnnotationTest {
private static String configLocation = "classpath:chapter12/dependecyInjectWithAnnotation.xml";
private static ApplicationContext ctx = new ClassPathXmlApplicationContext("configLocation");
//1、Spring自带依赖注入注解
@Test
public void testRequiredForXmlSetterInject() {
TestBean testBean = ctx.getBean("testBean", TestBean.class);
Assert.assertEquals("hello", testBean.getMessage());
}
}
基于@Autowired的自动装配,默认是根据类型注入,可以用于构造器、字段、方法注入,使用方式如下:
@Autowired(required=true)
构造器、字段、方法
@Autowired默认是根据参数类型进行自动装配,且必须有一个Bean候选者注入,如果允许出现0个Bean候选者需要设置属性“required=false”,“required”属性含义和@Required一样,只是@Required只适用于基于XML配置的setter注入方式。
public class TestBean11 {
private String message;
@Autowired //构造器注入
private TestBean11(String message) {
this.message = message;
}
//省略message的getter和setter
}
"testBean11" class="cn.javass.spring.chapter12.TestBean11"/>
@Test
public void testAutowiredForConstructor() {
TestBean11 testBean11 = ctx.getBean("testBean11", TestBean11.class);
Assert.assertEquals("hello", testBean11.getMessage());
}
public class TestBean12 {
@Autowired //字段注入
private String message;
//省略getter和setter
}
"testBean12" class="cn.javass.spring.chapter12.TestBean12"/>
@Test
public void testAutowiredForField() {
TestBean12 testBean12 = ctx.getBean("testBean12", TestBean12.class);
Assert.assertEquals("hello", testBean12.getMessage());
}
public class TestBean13 {
private String message;
@Autowired //setter方法注入
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
public class TestBean14 {
private String message;
private List list;
@Autowired(required = true) //任意一个或多个参数方法注入
private void initMessage(String message, ArrayList list) {
this.message = message;
this.list = list;
}
//省略getter和setter
}
/**
* xml配置
*/
"testBean13" class="cn.javass.spring.chapter12.TestBean13"/>
"testBean14" class="cn.javass.spring.chapter12.TestBean14"/>
"list" class="java.util.ArrayList">
"0">
/**
* 测试类
*/
@Test
public void testAutowiredForMethod() {
TestBean13 testBean13 = ctx.getBean("testBean13", TestBean13.class);
Assert.assertEquals("hello", testBean13.getMessage());
TestBean14 testBean14 = ctx.getBean("testBean14", TestBean14.class);
Assert.assertEquals("hello", testBean14.getMessage());
Assert.assertEquals(ctx.getBean("list", List.class), testBean14.getList());
}
用于注入SpEL表达式,可以放置在字段方法或参数上,使用方式如下
@Value(value = “SpEL表达式”)
字段、方法、参数
//可以在类字段上使用该注解
@Value(value = "#{message}")
private String message;
//可以放置在带@Autowired注解的方法的参数上
@Autowired
public void initMessage(@Value(value = "#{message}#{message}") String message) {
this.message = message;
}
//还可以放置在带@Autowired注解的构造器的参数上
@Autowired
private TestBean43(@Value(value = "#{message}#{message}") String message) {
this.message = message;
}
@Qualifier限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者,具体使用方式如下:
@Qualifier(value = “限定标识符”)
字段、方法、参数
type="org.springframework.beans.factory.annotation.Qualifier"
value="限定标识符"/>
public class TestBean31 {
private DataSource dataSource;
@Autowired
//根据标签指定Bean限定标识符
public void initDataSource(@Qualifier("mysqlDataSource") DataSource dataSource) {
this.dataSource = dataSource;
}
public DataSource getDataSource() {
return dataSource;
}
}
//配置
"testBean31" class="cn.javass.spring.chapter12.TestBean31"/>
"mysqlDataSourceBean" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
value="mysqlDataSource"/>
//测试方法
@Test
public void testQualifierInject1() {
TestBean31 testBean31 = ctx.getBean("testBean31", TestBean31.class);
try {
//使用指定的标识符只能被@Qualifier使用
ctx.getBean("mysqlDataSource");
Assert.fail();
} catch (Exception e) {
//找不到该Bean
Assert.assertTrue(e instanceof NoSuchBeanDefinitionException);
}
Assert.assertEquals(ctx.getBean("mysqlDataSourceBean"), testBean31.getDataSource());
}
public class TestBean32 {
private DataSource dataSource;
@Autowired
@Qualifier(value = "mysqlDataSource2") //指定Bean限定标识符
//@Qualifier(value = "mysqlDataSourceBean")
//是错误的注入,不会发生回退容错,因为你指定了
public void initDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public DataSource getDataSource() {
return dataSource;
}
}
"testBean32" class="cn.javass.spring.chapter12.TestBean32"/>
"oracleDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
@Test
public void testQualifierInject2() {
TestBean32 testBean32 = ctx.getBean("testBean32", TestBean32.class);
Assert.assertEquals(ctx.getBean("oracleDataSource"), testBean32.getDataSource());
}
/** 表示注入Mysql相关 */
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Mysql {
}
/** 表示注入Oracle相关 */
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Oracle {
}
public class TestBean33 {
private DataSource mysqlDataSource;
private DataSource oracleDataSource;
@Autowired
public void initDataSource(@Mysql DataSource mysqlDataSource, @Oracle DataSource oracleDataSource) {
this.mysqlDataSource = mysqlDataSource;
this.oracleDataSource = oracleDataSource;
}
public DataSource getMysqlDataSource() {
return mysqlDataSource;
}
public DataSource getOracleDataSource() {
return oracleDataSource;
}
}
<bean id="testBean33" class="cn.javass.spring.chapter12.TestBean33"/>
<bean id="mysqlDataSourceBean" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<qualifier value="mysqlDataSource"/>
<qualifier type="cn.javass.spring.chapter12.qualifier.Mysql"/>
bean>
<bean id="oracleDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<qualifier type="cn.javass.spring.chapter12.qualifier.Oracle"/>
bean>
/**
* 测试方法
*/
@Test
public void testQualifierInject3() {
TestBean33 testBean33 = ctx.getBean("testBean33", TestBean33.class);
Assert.assertEquals(ctx.getBean("mysqlDataSourceBean"),
testBean33.getMysqlDataSoruce());
Assert.assertEquals(ctx.getBean("oracleDataSource"),
testBean33.getOracleDataSoruce());
}
带参数的注解
public enum DataBase {
ORACLE, MYSQL;
}
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface DataSourceType {
String ip(); //指定ip,用于多数据源情况
DataBase database();//指定数据库类型
}
public class TestBean34 {
private DataSource mysqlDataSource;
private DataSource oracleDataSource;
@Autowired
public void initDataSource(
@DataSourceType(ip="localhost", database=DataBase.MYSQL)
DataSource mysqlDataSource,
@DataSourceType(ip="localhost", database=DataBase.ORACLE)
DataSource oracleDataSource) {
this.mysqlDataSource = mysqlDataSource;
this.oracleDataSource = oracleDataSource;
}
//省略getter方法
}
<bean id="testBean34" class="cn.javass.spring.chapter12.TestBean34"/>
<bean id="mysqlDataSourceBean" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<qualifier value="mysqlDataSource"/>
<qualifier type="cn.javass.spring.chapter12.qualifier.Mysql"/>
<qualifier type="cn.javass.spring.chapter12.qualifier.DataSourceType">
<attribute key="ip" value="localhost"/>
<attribute key="database" value="MYSQL"/>
qualifier>
bean>
<bean id="oracleDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<qualifier type="cn.javass.spring.chapter12.qualifier.Oracle"/>
<qualifier type="cn.javass.spring.chapter12.qualifier.DataSourceType">
<attribute key="ip" value="localhost"/>
<attribute key="database" value="ORACLE"/>
qualifier>
bean>
@Test
public void testQualifierInject3() {
TestBean34 testBean34 = ctx.getBean("testBean34", TestBean34.class);
Assert.assertEquals(ctx.getBean("mysqlDataSourceBean"),
testBean34.getMysqlDataSource());
Assert.assertEquals(ctx.getBean("oracleDataSource"),
testBean34.getOracleDataSoruce());
}
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomQualifier {
String value();
}
public class TestBean35 {
private DataSource dataSoruce;
@Autowired
public TestBean35(@CustomQualifier("oracleDataSource") DataSource dataSource) {
this.dataSoruce = dataSource;
}
public DataSource getDataSoruce() {
return dataSoruce;
}
}
<bean id="testBean35" class="cn.javass.spring.chapter12.TestBean35"/>
<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>cn.javass.spring.chapter12.qualifier.CustomQualifiervalue>
set>
property>
bean>
@Test
public void testQualifierInject5() {
TestBean35 testBean35 = ctx.getBean("testBean35", TestBean35.class);
Assert.assertEquals(ctx.getBean("oracleDataSource"), testBean35.getDataSource());
}
默认根据类型装配,如果指定name属性将根据名字装配,可以使用如下方式来指定:
@Resource(name = “标识符”)
字段或setter方法
public class TestBean41 {
@Resource(name = "message")
private String message;
//省略getter和setter
}
"testBean41" class="cn.javass.spring.chapter12.TestBean41"/>
@Test
public void testResourceInject1() {
TestBean41 testBean41 = ctx.getBean("testBean41", TestBean41.class);
Assert.assertEquals("hello", testBean41.getMessage());
}
使用@Resource需要注意以下几点:
通过注解指定初始化和销毁方法定义;
@PostConstruct
public void init() {
System.out.println("==========init");
}
@PreDestroy
public void destroy() {
System.out.println("==========destroy");
}
@Test
public void resourceInjectTest1() {
((ClassPathXmlApplicationContext) ctx).registerShutdownHook();
TestBean41 testBean41 = ctx.getBean("testBean41", TestBean41.class);
Assert.assertEquals("hello", testBean41.getMessage());
}
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface JSR330Mysql {
}
public class TestBean51 {
private DataSource mysqlDataSource;
private DataSource oracleDataSource;
@Inject
public void initDataSoruce(
@JSR330Mysql DataSource mysqlDataSource,
@Named("oracleDataSource") DataSource oracleDataSource) {
this.mysqlDataSource = mysqlDataSource;
this.oracleDataSource = oracleDataSource;
}
//省略getter
}
"testBean51" class="cn.javass.spring.chapter12.TestBean51"/>
"mysqlDataSourceBean" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
value="mysqlDataSource"/>
type="cn.javass.spring.chapter12.qualifier.Mysql"/>
type="cn.javass.spring.chapter12.qualifier.DataSourceType">
"ip" value="localhost"/>
"database" value="MYSQL"/>
type="cn.javass.spring.chapter12.qualifier.JSR330Mysql"/>
@Test
public void testInject() {
TestBean51 testBean51 = ctx.getBean("testBean51", TestBean51.class);
Assert.assertEquals(ctx.getBean("mysqlDataSourceBean"),
testBean51.getMysqlDataSource());
Assert.assertEquals(ctx.getBean("oracleDataSource"),
testBean51.getOracleDataSource());
}
用于注入EntityManagerFactory和EntityManager。
public class TestBean61 {
@PersistenceContext(unitName = "entityManagerFactory")
private EntityManager entityManager;
@PersistenceUnit(unitName = "entityManagerFactory")
private EntityManagerFactory entityManagerFactory;
public EntityManager getEntityManager() {
return entityManager;
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
}
<import resource="classpath:chapter7/applicationContext-resources.xml"/>
<import resource="classpath:chapter8/applicationContext-jpa.xml"/>
<bean id="testBean61" class="cn.javass.spring.chapter12.TestBean61"/>
@Test
public void testJpaInject() {
TestBean61 testBean61 = ctx.getBean("testBean61", TestBean61.class);
Assert.assertNotNull(testBean61.getEntityManager());
Assert.assertNotNull(testBean61.getEntityManagerFactory());
}
Spring提供通过扫描类路径中的特殊注解类来自动注册Bean定义,同注解驱动事务一样需要开启自动扫描并注册Bean定义支持
"http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
"cn.javass.spring.chapter12"/>
使用< context:component-scan>标签来表示需要要自动注册Bean定义,而通过base-package属性指定扫描的类路径位置。< context:component-scan>标签将自动开启“注解实现Bean依赖注入”支持。此处我们还通过< aop:aspectj-autoproxy/>用于开启Spring对@AspectJ风格切面的支持。
Spring基于注解实现Bean定义支持如下三种注解:
定义Spring管理Bean
@Component(“标识符”)
POJO类
@Component("component")
public class TestCompoment {
@Autowired
private ApplicationContext ctx;
public ApplicationContext getCtx() {
return ctx;
}
}
@AspectJ风格的切面可以通过@Compenent注解标识其为Spring管理Bean,而@Aspect注解不能被Spring自动识别并注册为Bean,必须通过@Component注解来完成,示例如下
@Component
@Aspect
public class TestAspect {
@Pointcut(value="execution(* *(..))")
private void pointcut() {}
@Before(value="pointcut()")
public void before() {
System.out.println("=======before");
}
}
@Component扩展,被@Repository注解的POJO类表示DAO层实现,从而见到该注解就想到DAO层实现,使用方式和@Component相同;
@Repository("testHibernateDao")
public class TestHibernateDaoImpl {
}
@Component扩展,被@Service注解的POJO类表示Service层实现,从而见到该注解就想到Service层实现,使用方式和@Component相同;
@Service("testService")
public class TestServiceImpl {
@Autowired
@Qualifier("testHibernateDao")
private TestHibernateDaoImpl dao;
public TestHibernateDaoImpl getDao() {
return dao;
}
}
@Component扩展,被@Controller注解的类表示Web层实现,从而见到该注解就想到Web层实现,使用方式和@Component相同;
@Controller
public class TestAction {
@Autowired
private TestServiceImpl testService;
public void list() {
//调用业务逻辑层方法
}
}
Spring内置了三种通用的扩展注解@Repository、@Service、@Controller ,大多数情况下没必要定义自己的扩展,在此我们演示下如何扩展@Component注解来满足某些特殊规约的需要;
在此我们可能需要一个缓存层用于定义缓存Bean,因此我们需要自定义一个@Cache的注解来表示缓存类。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Cache{
String value() default "";
}
@Cache("cache")
public class TestCache {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ManagedBean {
String value() default "";
}
@javax.annotation.ManagedBean("managedBean")
public class TestManagedBean {
@Resource
private ApplicationContext ctx;
public ApplicationContext getCtx() {
return ctx;
}
}
@Test
public void testManagedBean() {
TestManagedBean testManagedBean = ctx.getBean("managedBean", TestManagedBean.class);
Assert.assertNotNull(testManagedBean.getCtx());
}
@Named不仅可以用于依赖注入来指定注入的Bean的标识符,还可以用于定义Bean。即注解在类型上表示定义Bean,注解在非类型上(如字段)表示指定依赖注入的Bean标识符。
@Named("namedBean")
public class TestNamedBean {
@Inject
private ApplicationContext ctx;
public ApplicationContext getCtx() {
return ctx;
}
}
@Test
public void testNamedBean() {
TestNamedBean testNamedBean = ctx.getBean("namedBean", TestNamedBean.class);
Assert.assertNotNull(testNamedBean.getCtx());
}
<context:component-scan
base-package=""
resource-pattern="**/*.class"
name-generator="org.springframework.context.annotation.AnnotationBeanNameGenerator"
use-default-filters="true"
annotation-config="true">
<context:include-filter type="aspectj" expression=""/>
<context:exclude-filter type="regex" expression=""/>
context:component-scan>
定义Bean将延迟初始化
@Component("component")
@Lazy(true)
public class TestCompoment {
……
}
定义Bean初始化及销毁时的顺序
@Component("component")
@DependsOn({"managedBean"})
public class TestCompoment {
……
}
定义Bean作用域,默认单例
@Component("component")
@Scope("singleton")
public class TestCompoment {
……
}
指定限定描述符,对应于基于XML配置中的< qualifier>标签
@Component("component")
@Qualifier("component")
public class TestCompoment {
……
}
自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@Component("component")
@Primary
public class TestCompoment {
……
}
基于Java类定义Bean配置元数据中的@Configuration注解的类等价于XML配置文件,@Bean注解的方法等价于XML配置文件中的Bean定义。
通过@Configuration注解的类将被作为配置类使用,表示在该类中将定义Bean配置元数据,且使用@Configuration注解的类本身也是一个Bean
import org.springframework.context.annotation.Configuration;
@Configuration("ctxConfig")
public class ApplicationContextConfig {
//定义Bean配置元数据
}
通过@Bean注解配置类中的相应方法,则该方法名默认就是Bean名,该方法返回值就是Bean对象,并定义了Spring IoC容器如何实例化、自动装配、初始化Bean逻辑
@Bean(name={},
autowire=Autowire.NO,
initMethod="",
destroyMethod="")
@Bean
public String message() {
return new String("hello");
}