Spring提供了三种方法进行Bean的配置:
在XML中进行配置
在java接口和类中用注解进行配置
在现实工作中,这三种方式都会被用到,并常常混合使用。基于“约定优于配置”的原则,最优先的是隐式Bean的发现机制和自动装配原则。这样的好处是减少开发者的决定权,简单而灵活。其次是通过注解的方式进行配置。它的好处是避免XML配置的泛滥,且更为简单方便。而如果你配置的类不是自己工程中开发的建议使用XML方式。本文主要介绍注解方式装配Bean。
在Spring中,提供两种方式来让Spring IoC容器发现Bean:
1、使用@Component装配Bean
首先定义POJO类:
package com.yozzs.ioc.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "role")//Value不写默认简单类名首字母小写
public class Role {
@Value("1")//值的注入,Spring会将1转化为long型
private Long id;
@Value("张三")
private String name;
@Value("北京")
private String location;
/*****setter and getter*****/
}
再定义一个Service接口:
package com.yozzs.ioc.service;
import com.yozzs.ioc.pojo.Role;
public interface IRoleService {
public void printRoleInfo(Role role);
}
以及Service实现类:
package com.yozzs.ioc.service.impl;
import org.springframework.stereotype.Component;
import com.yozzs.ioc.pojo.Role;
import com.yozzs.ioc.service.IRoleService;
@Component
public class RoleServiceImpl implements IRoleService{
@Override
public void printRoleInfo(Role role) {
System.out.println("id="+role.getId());
System.out.println("name="+role.getName());
System.out.println("location="+role.getLocation());
}
}
此时Spring IoC还不知道去哪里扫描对象,需要定义一个java config来告诉它,它是一个没有逻辑的配置类,代码如下:
package com.yozzs.ioc.config;
import org.springframework.context.annotation.ComponentScan;
import com.yozzs.ioc.pojo.Role;
import com.yozzs.ioc.service.impl.RoleServiceImpl;
@ComponentScan(basePackages = {"com.yozzs.ioc.pojo","com.yozzs.ioc.service"})
/*@ComponentScan(basePackages = {"com.yozzs.ioc.pojo","com.yozzs.ioc.service"},
basePackageClasses = {Role.class,RoleServiceImpl.class})*/
/*@ComponentScan(basePackageClasses = {Role.class,RoleServiceImpl.class})*/
public class ApplicationConfig {
}
注意:两处注释是两种另外的写法,同时定义basePackages和basePackageClasses不会对同一个Bean生成多个对象。
测试类:
package com.yozzs.ioc.test;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.yozzs.ioc.config.ApplicationConfig;
import com.yozzs.ioc.pojo.Role;
import com.yozzs.ioc.service.IRoleService;
public class MyTest {
@Test
public void test01() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class);
Role role = (Role) ctx.getBean("role");
IRoleService roleService = ctx.getBean(IRoleService.class);
roleService.printRoleInfo(role);
ctx.close();
}
}
测试结果:
id=1
name=张三
location=北京
2、自动装配——@Autowired
前面的方法是一些简单的值的注入,对于注入对象我们采用自动装配。所谓自动装配技术是一种Spring发现对应的Bean,自动完成装配工作的方式,这是应用到一个十分常用的注解@Autowired。下面改写以上代码中接口及其实现来举例:
Service类:
package com.yozzs.ioc.service;
public interface IRoleService2 {
public void printRoleInfo();
}
Service实现类:
package com.yozzs.ioc.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.yozzs.ioc.pojo.Role;
import com.yozzs.ioc.service.IRoleService2;
@Component
public class RoleServiceImpl2 implements IRoleService2{
@Autowired
private Role role ;
/****setter and getter****/
@Override
public void printRoleInfo() {
System.out.println("id="+role.getId());
System.out.println("name="+role.getName());
System.out.println("location="+role.getLocation());
}
}
当IoC容器里找不到注入需要的Bean时,默认情况下会抛出异常。此时可以通过@Autowired的配置项required来改变它,如@Autowired(required=false),required配置成false表示找不到Bean时不抛出异常,允许字段为空。required默认为true。@Autowired还可应用于方法和参数。
3、自动装配的歧义性(@Primary和@Qualifier)
@Autowired是按类型装配,当需要自动注入的是一个接口类型,并且IoC容器中存在多个该接口的不同实现类Bean时,IoC容器无法判断把哪个对象注入进来,于是就会抛出异常。这就是自动装配的歧义性。为了消除歧义性,Spring提供了两个注解,@Primary和@Qualifier。
package com.yozzs.ioc.service.impl;
import org.springframework.context.annotation.Primary;
@Component("roleService3")
@Primary
public class RoleServiceImpl3 implements IRoleService2{
/*......*/
}
package com.yozzs.ioc.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.yozzs.ioc.service.IRoleService;
public class RoleController {
@Autowired
@Qualifier("roleService3")
private IRoleService roleService ;
/****setter and getter****/
public void printRole() {
roleService.printRoleInfo();
}
}
4、使用@Bean装配Bean
@Component装配Bean只能注解在类上,@Bean可以注解在方法上,并且将返回的对象作为Spring Bean,存放在IoC容器中。例:
@Bean(name = "dataSource")
public DataSource getDataSource() {
DataSource dataSource = new DataSource();
return dataSource;
}
@Bean包含四个配置项:
Spring中的Profile配置可以定义不同的环境,比如配置两个数据库连接池,一个用于开发(dev),一个用于测试(test)。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<beans profile="test">
<bean id="devDataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driver" value="com.mysql.jdbc.Driver">property>
<property name="url" value="jdbc:mysql://localhost:3306/test01">property>
<property name="username" value="root">property>
<property name="password" value="123456">property>
bean>
beans>
<beans profile="dev">
<bean id="devDataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driver" value="com.mysql.jdbc.Driver">property>
<property name="url" value="jdbc:mysql://localhost:3306/test02">property>
<property name="username" value="root">property>
<property name="password" value="123456">property>
bean>
beans>
beans>
启动Profile的方法有多种:
下面对几种方式进行介绍:
在eclipse的IDE中开发,可以给运行的类加入虚拟机参数,如图:
在web.xml中使用环境参数:
param>
<param-name>spring.profiles.activeparam-name>
<param-value>testparam-value>
param>
使用Spring MVC的DispatcherServlet环境参数:
<servlet>
<servlet-name>dispatcherservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>spring.profiles.activeparam-name>
<param-value>testparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
在测试类上使用注解@ActiveProfiles:
/*......*/
@ActiveRrofiles("dev")
public class ProfileTest {
@Autowired
private DataSource dataSource;
@Test
public void test01() {
/*......*/
}
}
1、使用注解方式加载属性文件
Spring提供了注解@PropertySource来加载属性文件,其配置项如下:
用法代码示例:
database-config.properties:
mysqlDriver=org.gjt.mm.mysql.Driver
mysqlURL=jdbc:mysql://localhost:3306/testjdbc
mysqlUser=root
mysqlPwd=123456
配置类:
package com.yozzs.ioc.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@Configuration
@ComponentScan(basePackages= {"com.yozzs.ioc"})
@PropertySource(value= {"classpath:database-config.properties"},ignoreResourceNotFound=true)
public class ApplicationConfig {
//用于解析占位符
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
使用引入的配置文件:
package com.yozzs.ioc.pojo;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class DataSourceBean {
@Value("${mysqlDriver}")
private String driver = null;
@Value("${mysqlURL}")
private String url = null;
@Value("${mysqlUser}")
private String username = null;
@Value("${mysqlPwd}")
private String password = null;
@Bean(name = "dataSource")
public DataSource getDataSource() {
Properties props = new Properties();
props.setProperty("driver", driver);
props.setProperty("url", url);
props.setProperty("username", username);
props.setProperty("password", password);
DataSource dataSource = null;
try {
dataSource = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
e.printStackTrace();
}
return dataSource;
}
}
2、使用XML方式加载属性配置文件
使用XML方式加载属性配置文件,只需使用< context:property-placeholder>元素加载一些配置项即可。
<context:property-placeholder ignore-resource-not-found="true" location="classpath:database-config.properties" />
location是一个配置文件路径的选项,它可以配置单个或者多个文件,多个文件之间用逗号分隔。
默认情况下,Spring IoC容器创建的Bean是单例的,有时我们需要多个实例在不同的线程运行,这些由Spring的作用域决定。Spring提供了4中作用域:
用注解方式声明作用域用@Scope(ConfigurableBeanFactory.SCOPE_xxx)注解Bean类。
Spring EL一种非常灵活的注入方式,它有许多功能:
用@Value给Bean的属性注入值,在属性文件中读取是用“$”,而在Spring EL中是使用“#”。下面一角色为例用代码演示:
初始角色类:
package com.yozzs.ioc.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("role")
public class Role {
@Value("#{1}")
private Long id;
@Value("#{'张三'}")
private String name;
@Value("#{'北京'}")
private String location;
/****setter and getter****/
}
通过Spring EL引用role的属性,调用其方法
package com.yozzs.ioc.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("elBean")
public class ElBean {
//通过beanName获取Bean,然后注入
@Value("#{role}")
private Role role;
//获取Bean的属性id
@Value("#{role.id}")
private Long id;
//调用bean的getName方法,获取角色名称
@Value("#{role.getName().toString()}")
private String name;
/****setter and getter****/
}
注意表达式”#{role.getName().toString()}”的注入,因为getName()可能返回null,这样toString()方法就会抛出异常了。为了解决这个问题,可以这样写:”#{role.getName()?.toString()}”,问号的含义是先判断返回是否为非null,如果不是则不再调用toString方法。
3、使用类的静态常量和方法
使用静态方法和常量,比如圆周率π:
@Value("#{T(Math).PI}")
private double pi;
对于不是java.lang.*包下的类要写类的全限定名,类似:
@Value("#{T(java.lang.Math).PI}")
private double pi;
还可以使用静态方法:
@Value("#{T(Math).random()}")
private double random;
4、Spring EL运算
角色编号加1注入:
@Value("#{role.id+1}")
private int number;
连接字符串:
@Value("#{role.name+role.location}")
private String str;
判断:
@Value("#{role.id==1}")
private boolean equalNum;
@Value("#{role.name eq '张三'}")
private boolean equalString;
@Value("#{role.id<2}")
private boolean less;
三目运算:
@Value("#{role.id>1 ? 5 : 1}")
private int munber;
@Value("#{role.location?:'北京'}")
private String defaultStr;