IoC容器通过定义bean的元数据来感知bean,本节主要讲述Bean的元数据配置模式。Bean的元数据配置模式由三种:xml模式、java代码模式和注解模式。xml模式是SF初始就采用的模式,是个完整的体系;实际上,java代码模式和注解模式是否被采用也需要通过xml模式进行定义。Java代码模式主要是通过专门的类来声明Bean和Bean之间的关系,注解模式是在Java代码中通过注解进行Bean关系定义。三种模式不是截然分开的,在实际使用过程中可以混用。
xml模式是在xml文件中定义元数据来完成Bean配置,是最早也是最经典的元数据配置模式。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
bean>
beans>
样例1:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
bean>
beans>
样例2:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
#属性类引用关系
<property name="accountDao" ref="accountDao"/>
bean>
beans>
可在多个配置文件中进行各自配置,然后组合在一起。
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
beans>
注意要点:import文件的位置路径是相对于执行导入的定义文件目录或类路径位置
配置文件一般存放在ClassPath可寻址的位置,支持其它方式如:
file:配置文件绝对路径
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
1、配置文件必须位于ClassPath可寻址的位置。
2、可多个文件导入,类似配置的组合格式。
ApplicationContext context = new FileSystemXmlApplicationContext("/resource/services.xml");
1、位置必须相对于应用根目录位置。
2、路径即使以"/"开始,也是相对于应用根目录。
用GenericApplicationContext 结合XmlBeanDefinitionReader阅读器使用导入,该种方式非常灵活。
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
Java代码模式配置Bean,就是通过特定注解(核心是@Bean和@Configuration)实现Bean的相关定义。
1、@Bean注解用于指示一个方法实例化、配置和初始化一个新的对象。相当于xml模式下的
2、@Configuration注解一个类,表明该类的主要目就是作为 bean 定义的来源。 此外,@Configuration注解的类中,允许通过调用同一类中的其他@Bean方法来定义 bean 间依赖关系。
@Configuration
public class AppConfig {
@Bean
public MyServiceImpl myService() {
return new MyServiceImpl();
}
}
AppConfig 相当于xml模式下的xml定义文件。
@Bean注解的MyServiceImpl相当于xml的如下定义:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
beans>
注意:@Bean方法名对应示例出来Bean的id。
虽然@Bean可在未使用@Configuration 注释的类中声明(此时不能声明 bean间的依赖关系),但在常见的场景中,@Bean 方法将在@Configuration 类中声明,保证Bean定义及相互间引用关系的完整。
@Configuration
public class AppConfig {
@Bean
public ClientService clientService1() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientService clientService2() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
}
1、实例化时bean-clientService1和clientService2都引用了实例化Bean-clientDao。
2、代码中clientDao()创建了一个新的 ClientDaoImpl 实例并返回它,在 clientService1() 中被调用一次,在 clientService2() 中被调用一次,正常理解clientDao()是有两个实例(每个服务一个)。但实际上是只有一个,因为Spring 中实例化的 bean默认是单实例的。
用AnnotationConfigApplicationContext进行java代码模式的初始化,代码示例如下:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
下面代码演示多个Bean定义类如何组合:
@Configuration
public class ServiceConfig {
@Autowired
private AccountRepository accountRepository;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
private final DataSource dataSource;
public RepositoryConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
1、SystemTestConfig 通过@Import注解组合了ServiceConfig、RepositoryConfig
2、ServiceConfig通过@Autowired自动注入RepositoryConfig中的Bean实例accountRepository。
spring针对主流的分层结构,spring提供了几个通用Bean注解。当注解作用在类上时,表明这些类是由spring容器自动管理,不需要在xml或@Configuration类中定义。
@Controller:表示当前类是控制层类。
@Service:表示当前类是注服务层类。
@Repository:表示当前类是注数据访问层类。
@Component:当不好归类到上述三种类时,同该注解标注,表示一个通用的由Spring容器管理的单例bean。
关于bean的名称,默认为类名且第一个字母小写。如果需要特别名称,采用@Component(“thisIsBeanName”)模式配置。
注解模式是通过在相关类、方法或字段声明上使用注解将配置移到组件类本身来完成元数据配置。spring同时支持JSR-250和JSR-330注解规范。
注解模式使配置更简单,但同时配置也更分散。
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.learnsf.main,com.learnsf.service"/>
beans>
context:annotation-config/ 元素隐式注册了以下后处理器:
ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor、EventListenerMethodProcessor
由这些后处理器完成注解配置解析
1、@Autowired表示自动装配,相当于JSR-330中的@Inject。
2、@Autowired位置:
在类构造函数前:表示类自动装配。注解模式下,类只有一个默认构造函数,不需要加@Autowired也表示自动装配;如果没有默认构造函数,则至少一个构造函数前需加@Autowired,才能自动装配。
在属性变量前:表示自动装配依赖类。
在方法前:表示自动装配依赖类。
在参数前:表示自动装配依赖参数类。
在特殊接口前:将 @Autowired 用于如下接口:BeanFactory、ApplicationContext、Environment、ResourceLoader、ApplicationEventPublisher 和 MessageSource。容器会自动装配合适Bean。
3、@Autowired、@Inject、@Value 和@Resource 注解均在 Spring BeanPostProcessor 实现处理。 这意味着不能在自己的 BeanPostProcessor 或 BeanFactoryPostProcessor中使用这些注解。
当@Autowired注入的类有多个Bean实现时,Bean定义时用@Primary表示优先使用该Bean
当@Autowired注入的类有多个Bean实现时,Bean定义时用@Qualifier的限定词表示使用该Bean。
@Resource在JSR-250中定义。
@Resource(name=xx):表示按指定name的bean来注入。如果未明确指定名称(仅@Resource),则默认名称派生自属性名称或 setter 方法。 在属性前注解,它采用属性名称。 在 setter 方法前注解,它采用要设置的 bean 属性名称。
@Autowired默认按byType自动装配,而@Resource默认byName自动装配。
@Autowired只包含一个参数:required,表示是否开启自动准入,默认是true。而@Resource包含七个参数,其中最重要的两个参数是:name 和 type。
@Autowired如果要使用byName,需要使用@Qualifier一起配合。而@Resource如果指定了name,则用byName自动装配,如果指定了type,则用byType自动装配。
@Autowired能够用在:构造器、方法、参数、成员变量和注解上,而@Resource能用在:类、成员变量和方法上。
@Autowired是spring定义的注解,而@Resource是JSR-250定义的注解。
@Value 通常用于注入外部化属性。@ Value属性注入功能可以分为两种:通过配置文件进行属性注入和通过非配置文件进行属性注入法:
(1)@Value(“${}”):可以获取对应属性文件(默认加载的application.yml)中定义的属性值。
(2)@Value(“#{}”):表示 通常用SpEL来获取 bean 的属性,或者调用 bean 的某个方法。