Spring学习一、组件注册
Spring 组件注册的方式
1.XML-based metadata
1.1 XML Configuration
services.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
The id
attribute is a string that identifies the individual bean definition.
The class
attribute defines the type of the bean and uses the fully qualified classname.
daos.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"
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">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
1.2 Using the Container
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
2.Annotation-based Configuration
Spring 2.5 introduced support for annotaion-based configruation metadata.
XML-based configuration metadata configures these beans as
2.1 Use Configuration Annotated
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
// instantiate, configure and return bean ...
}
}
2.2 Bootstrapping @Configuration classes
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MyBean myBean = ctx.getBean(MyBean.class);
2.3 使用CompentScan 扫描
/**
* Alias for {@link #basePackages}.
* <p>Allows for more concise annotation declarations if no other attributes
* are needed — for example, {@code @ComponentScan("org.my.pkg")}
* instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
*/
@AliasFor("basePackages")
String[] value() default {};
上面是@CompentScan源码中的value说明,如果给默认值,就相当于扫描basePackages.
@ComponentScan(
// 配置扫描包的根路径
value = {"com.zuoyan"}
)
2.4 @compentScan 配置过滤扫描
/**
* Specifies which types are not eligible for component scanning.
指定那些类型不适合进行组件扫描
需要指定的是一个Filter数组
* @see #resourcePattern
*/
Filter[] excludeFilters() default {};
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
/**
* The type of filter to use.
* 指定FilterType 使用的类型
*/
FilterType type() default FilterType.ANNOTATION;
/**
* Alias for {@link #classes}.
* @see #classes
*/
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
FilterType 类型
public enum FilterType {
/**
* Filter candidates marked with a given annotation.
* @see org.springframework.core.type.filter.AnnotationTypeFilter
*/
ANNOTATION,
/**
* Filter candidates assignable to a given type.
* @see org.springframework.core.type.filter.AssignableTypeFilter
*/
ASSIGNABLE_TYPE,
/**
* Filter candidates matching a given AspectJ type pattern expression.
* @see org.springframework.core.type.filter.AspectJTypeFilter
*/
ASPECTJ,
/**
* Filter candidates matching a given regex pattern.
* @see org.springframework.core.type.filter.RegexPatternTypeFilter
*/
REGEX,
/** Filter candidates using a given custom
* {@link org.springframework.core.type.filter.TypeFilter} implementation.
*/
CUSTOM
}
使用过滤
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.ComponentScans;
import com.atguigu.bean.Person;
//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
@ComponentScans(
value = {
@ComponentScan(value="com.atguigu",includeFilters =
{
/*
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
*/
@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
},
useDefaultFilters = false)
}
)
//@ComponentScan value:指定要扫描的包
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
}
2.5 @Scope 配置Bean的作用域
@Configuration
public class StudentBeanConfiguration {
@Scope("prototype")
@Bean
Student student(){
return new Student(10001L, "zuoyan");
}
}
@Test
public void testBeanScope(){
Student stu1 = (Student) applicationContext.getBean("student");
Student stu2 = (Student) applicationContext.getBean("student");
System.out.println(stu1 == stu2);
}
输出结果
Spring Contation Initializer Ok ...
Student Construct is exec ...
Student Construct is exec ...
false
可以看到创建了两次对象,而且两个对象不相等,因为这个是使用的多例的Bean作用域
更改bean的作用域
@Scope("singleton")
还是上面的代码查看控制台输出结果
Student Construct is exec ...
Spring Contation Initializer Ok ...
true
2.6 @Lazy 配置懒加载
@Configuration
public class StudentBeanConfiguration {
@Scope("singleton")
@Lazy
@Bean
Student student(){
return new Student(10001L, "zuoyan");
}
}
控制台输出内容
Spring Contation Initializer Ok ...
Student Construct is exec ...
true
可以通过和上面的控制台输出内容进行对比,发现Student的构造函数打印在 Spring Container 初始化完成之后调用的,这说明 Bean是在我们需要使用的时候才执行从初始化的。
2.7 @Conditional 条件配置
It is often useful to conditionally enable or disable a complete
@Configuration
class or even individual@Bean
methods, based on some arbitrary system state. One common example of this is to use the@Profile
annotation to activate beans only when a specific profile has been enabled in the SpringEnvironment
(see Bean Definition Profiles for details).
大意就是条件注解对一个类或者独立的Bean注册方法的启用或者禁用非常有帮助,一个最常见的例子就是使用@Profile注解指定一个特定的配置文件来激活Bean
在需要添加的配置类上添加条件注解,控制粒度可以具体到某个方法上
@Conditional({SystemOSCondition.class})
@Configuration
public class StudentBeanConfiguration {
@Scope("singleton")
@Lazy
@Bean
Student student(){
return new Student(10001L, "zuoyan");
}
@Bean
Student windows(){
return new Student(00001L, "Windows");
}
@Bean
Student linux(){
return new Student(00002L, "Windows");
}
}
需要自己实现Spring-Framework 中的Condition接口
/**
* @Author: ZuoYanCoder
* @Description: 实现自定义的条件
* @Date: 2020/4/17 10:50
* @Version: 1.0
*/
public class SystemOSCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String systemOsNanme = environment.getProperty("os.name");
System.out.println(systemOsNanme);
return systemOsNanme.contains("Windows");
}
}
测试方法代码类
@Test
public void testConditionalAnnotated(){
String[] beanNamesForType = applicationContext.getBeanNamesForType(Student.class);
for (String name:beanNamesForType) {
System.out.println(name);
}
Map<String, Student> studentMap = applicationContext.getBeansOfType(Student.class);
System.out.println(studentMap);
}
执行测试方法打印结果
Windows 10
Windows 10
Windows 10
Student Construct is exec ...
Student Construct is exec ...
Spring Contation Initializer Ok ...
student
windows
linux
Student Construct is exec ...
{student=Student{stuId=10001, name='zuoyan'}, windows=Student{stuId=1, name='Windows'}, linux=Student{stuId=2, name='Windows'}}
配置IDEA 运行时环境变量
-Dos.name=Linux
然后在运行打印结果
Linux
Spring Contation Initializer Ok ...
{}
2.8 @Import导入组件使用
2.8.1@Import 导入所需组件
首先通过代码打印输出Spring容器中有的组件
@Test
public void testImportAnnotation(){
String[] names = applicationContext.getBeanDefinitionNames();
for (String name: names) {
System.out.println(name);
}
}
控制台输出
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studentBeanConfiguration
student
windows
linux
通过使用@Import导入需要的组件
@Conditional({SystemOSCondition.class})
@Configuration
@Import({Animal.class})
public class StudentBeanConfiguration {
}
控制台输出
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studentBeanConfiguration
com.zuoyan.bean.Animal
student
windows
linux
@Import可以导入多个类,默认的id是类的全路径
2.8.2 ImportSelector 导入组件
@Conditional({SystemOSCondition.class})
@Configuration
@Import({Animal.class, MyImportSelector.class})
public class StudentBeanConfiguration {}
MyImportSelector类的内容
public class MyImportSelector implements ImportSelector {
/**
* 方法返回值就是类的全路径名的数组
*
* @param importingClassMetadata 可以获取标有Import注解类的的所有注解
*
* @return
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 方法可以返回空字符串数组,但是不能返回Null
return new String[]{"com.zuoyan.bean.Cat","com.zuoyan.bean.Dog"};
}
}
控制台打印输出
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studentBeanConfiguration
com.zuoyan.bean.Animal
com.zuoyan.bean.Cat
com.zuoyan.bean.Dog
student
windows
linux
可以看到需要导入的组件已经导入到容器之中了。
2.8.3ImportBeanDefinitationRegistrar
在配置类上添加 ImportBeanDefinitationRegistrar
@Conditional({SystemOSCondition.class})
@Configuration
@Import({Animal.class, MyImportSelector.class, MyImportBeanDefinitationRegister.class})
public class StudentBeanConfiguration {}
MyImportBeanDefinitationRegister实现
public class MyImportBeanDefinitationRegister implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata 当前类的所有注解信息
* @param registry BeanDefinition注册类,把所有需要添加的组件注册到容器中
* 调用BeanDefinitionRegistry.registerBeanDefinition手工注册来
*
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
boolean catFlag = registry.containsBeanDefinition("com.zuoyan.bean.Cat");
boolean dogFlag = registry.containsBeanDefinition("com.zuoyan.bean.Dog");
if(catFlag && dogFlag)
{
// 指定Bean的定义信息
RootBeanDefinition beanDefinition = new RootBeanDefinition(Elephant.class);
// 指定注册Bean的名称
registry.registerBeanDefinition("Elephant",beanDefinition);
}
}
}
测试类
@Test
public void testImportAnnotation(){
String[] names = applicationContext.getBeanDefinitionNames();
for (String name: names) {
System.out.println(name);
}
}
控制台打印输出
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studentBeanConfiguration
com.zuoyan.bean.Animal
com.zuoyan.bean.Cat
com.zuoyan.bean.Dog
student
windows
linux
Elephant
2.9 使用FactoryBean注册组件
首先通过自己定义一个类然后实现BeanFactory的接口
import org.springframework.beans.factory.FactoryBean;
import org.springframework.lang.Nullable;
/**
* @Author: ZuoYanCoder
* @Description:
* @Date: 2020/4/23 14:17
* @Version: 1.0
*/
public class CarFactoryBean implements FactoryBean<JeepCar> {
/**
* 需要管理的对象
* @return
* @throws Exception
*/
@Nullable
public JeepCar getObject() throws Exception {
return new JeepCar();
}
/**
* 返回类的类型
* @return
*/
@Nullable
public Class<?> getObjectType() {
return JeepCar.class;
}
/**
* 是否为单例模式
* @return
*/
public boolean isSingleton() {
return false;
}
}
然后在配置中将注入进去
@Bean
CarFactoryBean carFactoryBean(){
return new CarFactoryBean();
}
通过测试
/**
* 通过对象工程类管理对象
*/
@Test
public void testBeanFactory(){
Object carFactoryBean = applicationContext.getBean("carFactoryBean");
System.out.println("获取Bean的类型是:" + carFactoryBean.getClass());
}
查看获取到的对象是
获取Bean的类型是:class com.zuoyan.bean.JeepCar
如果需要获取工厂类对象需要在前面加上 & 符号
获取Bean的类型是:class com.zuoyan.bean.CarFactoryBean
通过isSingleton() 方法可以设置放进容器的对象是单例还是多例。