Spring组件注册-springboot实战电商项目m

springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)

java商城系统源码

1. bean注册

我们有个Person类

public class Person {

	private Integer age;
	
	private String name;

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Person [age=" + age + ", name=" + name + "]";
	}
}
复制代码

1.1 传统bean注册

新建beans.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="person" class="com.opgame.spring.Person">
		<property name="age" value="18"/>
		<property name="name" value="张三"/>
	</bean>

</beans>
复制代码

使用person对象进行测试

@Test
public void testBean() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
	Person person = (Person)context.getBean("person");
	System.out.println(person);
}
复制代码

1.2 使用annotation代替xml注册

创建一个带有@Configuration的java类,使用 @Bean进行注册

@Configuration // 告诉spring这个是一个config类
public class BeansConfig {

	// 给容器注册一个bean,类型为返回值类型,默认id为方法名,在bean注解中设置id
	@Bean("person")
	public Person person() {
		Person person = new Person();
		person.setAge(20);
		person.setName("李四");
		return person;
	}
}
复制代码

使用person对象进行测试

@Test
public void testBean() {
	ApplicationContext context = new AnnotationConfigApplicationContext(BeansConfig.class);
	Person person = (Person)context.getBean("person");
	System.out.println(person);
}
复制代码

2 exclude、include filter

在传统的spring mvc项目中为了使事务起效,我们通常在context.xml 进行exclude @Controller类,在mvc.xml 进行include @Controller,当我们使用一些第三方类库的时候,有时也需要对某些类进行排除扫码,那该怎么办呢?

2.1 传统xml排除与导入扫描类

context.xml(需要context命名空间)

<context:component-scan base-package="com.opgame">
	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
复制代码

mvx.xml

<!-- use-default-filters默认ture,默认扫描@Component @Repository @Service @Controller -->
	<context:component-scan base-package="com.opgame" use-default-filters="false">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
复制代码

2.2使用注解的形式排除与导入扫描类

@ComponentScan 注解使用

  • value:指定要扫描的包
  • excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
  • includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
  • useDefaultFilters:是否使用默认的Filters

Filter

  • FilterType.ANNOTATION:按照注解
  • FilterType.ASSIGNABLE_TYPE:按照给定的类型
  • FilterType.ASPECTJ:使用ASPECTJ表达式
  • FilterType.REGEX:使用正则指定
  • FilterType.CUSTOM:使用自定义规则
  • classes:ANNOTATION、ASSIGNABLE_TYPE、CUSTOM 给定的类
  • pattern:ASPECTJ、REGEX 给定的表达式
@Configuration // 告诉spring这个是一个config类
@ComponentScan(value="com.opgame",useDefaultFilters=false,
includeFilters= {
		@Filter(type=FilterType.ANNOTATION,classes= {Controller.class})
},
excludeFilters= {
		@Filter(type=FilterType.ASSIGNABLE_TYPE,classes= {PersonController.class})
})
public class BeansConfig{
    
} 
复制代码

自定义规则

// 实现org.springframework.core.type.filter.TypeFilter 接口
public class BeanTypeFilter implements TypeFilter{

	/**
	 * @param metadataReader 获取被spring扫描到的当前类的元数据
	 * @param metadataReaderFactory 获取元数据的工厂,用于获取其他类的信息
	 */
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {
		// 获取当前类注解元信息
		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
		// 获取当前类的类信息
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		// 获取当前类的资源(类路径)
		Resource resource = metadataReader.getResource();
		
		// 如果当前类名含有person则配成功
		if(classMetadata.getClassName().contains("person")) {
			return true;
		}
		return false;
	}
}
复制代码

3. 作用域与懒加载

3.1 xml

<!-- 默认scope="singleton" 单例-->
<!-- lazy-init="true" 当需要用到的时候才创建对象,默认为spring初始化时创建-->
<bean id="person" class="com.opgame.spring.Person" scope="singleton" lazy-init="true">
		<property name="age" value="18"/>
		<property name="name" value="张三"/>
</bean>
复制代码

3.2 注解

@Lazy
@Scope("singleton")
@Bean("person")
public Person person() {
	Person person = new Person();
	person.setAge(20);
	person.setName("李四");
	return person;
}

复制代码

4 按条件注册

在有的时候,我们编写框架,又或者编写代码,在不同环境时需要切换到不同的实现方法。那么在何时加载那种类的实现,spring为我们提供了这个注解

在spring中使用@Conditional 注解实现按需加载类。

@Conditional({
	LinuxCondition.class
})
public Person person1() {
	Person person = new Person();
	person.setAge(18);
	person.setName("李小四");
	return person;
}
复制代码
// 实现 org.springframework.context.annotation.Condition 接口
public class LinuxCondition  implements Condition{
	
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		// 获取ioc使用的beanfactory
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		// 获取类加载器
		ClassLoader classLoader = context.getClassLoader();
		// 获取当前环境
		Environment environment = context.getEnvironment();
		// 获取bean注册类
		BeanDefinitionRegistry registry = context.getRegistry();
        
        // 判断是否包含person对象
        boolean hasPerson = registry.containsBeanDefinition("person");
		if (!hasPerson) {
			return false;
		}
        
        // 判断是否属于linux 环境
		if(environment.getProperty("os.name").contains("linux")) {
			return true;
		}
		return false;
	}
}
复制代码

在启动程序时设置 vm arguments 也可以查看Environment相关的笔记

-Dos.name=linux
复制代码

也可以在

在spring boot中,使用@ConditionalOnMissingBean(name="xxx") @ConditionalOnMissingBean(xxx.class) 也可以判断当没有某些类或者方法的时候进行注册。

5. 使用import进行注册

现有多个类

class Blue {}

class Color {}

class Red {}
复制代码

5.1 进行简单的注册

当我们需要无参注册的时候,可以使用@import 进行注册如:

@Configuration
@Import({Blue.class,Red.class})
public class BeansConfig {
    
}
复制代码

其中bean id为类全名称 如com.opgame.spring.bean.Blue com.opgame.spring.bean.Red

相当于

@Configuration
public class BeansConfig {
    @Bean("com.opgame.spring.bean.Blue")
	public Blue blue() {
		return new Blue();
	}
	
	@Bean("com.opgame.spring.bean.Red")
	public Red red() {
		return new Red();
	}
}
复制代码

5.2 使用ImportSelector 进行类名注册

// 自定义逻辑返回需要导入的组件
// 实现org.springframework.context.annotation.ImportSelector 接口
public class BeanImportSelector implements ImportSelector{

    /**
	 * importingClassMetadata: 可以获取当前被标注@Import注解的所有注解元信息
	 * 	即当前例子中 BeansConfig.class的所有注解的元信息
	 */
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
	    // 不允许返回null
		return new String[] {"com.opgame.spring.bean.Blue","com.opgame.spring.bean.Red"};
	}
}
复制代码
@Configuration
@Import({BeanImportSelector.class})
public class BeansConfig {
    
}
复制代码

5.3 ImportBeanDefinitionRegistrar 注册一个bean到容器中

此时注册的是BeanDefinition,也就是bean的定义信息,而spring 会根据bean的定义信息,进行bean的注册!也就是此时并没有注册bean!!!

// 实现org.springframework.context.annotation.ImportBeanDefinitionRegistrar接口
public class BeansImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{

	/**
	 * importingClassMetadata:当前config类的注解信息(beansConfig)
	 * registry:Bean注册类,可以在此把所有需要添加到容器中的bean调用 BeanDefinitionRegistry.registerBeanDefinition()方法进行注册
	 */
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean hasBlue = registry.containsBeanDefinition("com.opgame.spring.bean.Blue");
		boolean hasRed = registry.containsBeanDefinition("com.opgame.spring.bean.Red");
		// 如果有红色和蓝色,则注册一个id为color的 Color对象
		if (hasBlue && hasRed) {
			RootBeanDefinition beanDefinition = new RootBeanDefinition(Color.class);
			registry.registerBeanDefinition("color", beanDefinition);
		}
	}

}
复制代码
@Configuration
@Import({Blue.class,Red.class,BeansImportBeanDefinitionRegistrar.class})
public class BeansConfig {
    
}
复制代码

6 实现FactoryBean<?>接口注册bean

// 实现org.springframework.beans.factory.FactoryBean接口
public class PersonFactoryBean implements FactoryBean<Person>{

	// 构造person对象
	public Person getObject() throws Exception {
		return new Person();
	}

	// 获取当前对象的类型
	public Class<?> getObjectType() {
		return Person.class;
	}

	// 是否为单例
	public boolean isSingleton() {
		return true;
	}
}
复制代码
@Configuration
public class BeansConfig {
    @Bean
	public PersonFactoryBean personFactoryBean() {
		return new PersonFactoryBean();
	}
}
复制代码

使用person对象进行测试

@Test
public void testBean() {
    ApplicationContext context = new AnnotationConfigApplicationContext(BeansConfig.class);
	context.getBean("personFactoryBean").getClass(); // com.opgame.spring.Person
	
	// 获取该工厂构造除的bean对象需要&符号
	Person person = (Person)context.getBean("&personFactoryBean"); 
	
	// 不使用&则获取工厂本身
	PersonFactoryBean personFactoryBean = (PersonFactoryBean)context.getBean("personFactoryBean"); 
	
	System.out.println(person);
}
复制代码

7 通过BeanDefinitionBuilder创建

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
builder.addPropertyValue("id", 1);
builder.addPropertyValue("name", "李四");
registry.registerBeanDefinition("user", builder.getBeanDefinition());

复制代码

springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)

java商城系统源码