Spring底层架构核心概念


文章目录

  • ExcludeFilter和IncludeFilter
    • 示例
  • @Conditional注解
    • 示例
  • @Lookup
    • 示例
  • BeanWrapper
    • 主要用途
    • BeanWrapper的数据绑定


ExcludeFilter和IncludeFilter

这两个Filter是Spring扫描过程中用来过滤的。ExcludeFilter表示排除过滤器,IncludeFilter表示包
含过滤器。
比如以下配置,表示扫描com.example.service这个包下面的所有类,但是排除UserServiceOrderService类,也就是就算
它上面有@Component注解也不会成为Bean。

示例

@ComponentScan(value = "com.example.service",
		excludeFilters = {@ComponentScan.Filter(
				type = FilterType.ASSIGNABLE_TYPE,
				classes = {UserService.class,OrderService.class})})
public class AppConfig {
}

再比如以下配置,就算UserServiceOrderService类上没有@Component注解,它也会被扫描成为一个Bean。

@ComponentScan(value = "com.example.service",
		includeFilters = {@ComponentScan.Filter(
				type = FilterType.ASSIGNABLE_TYPE,
				classes = {UserService.class,OrderService.class})})
public class AppConfig {
}

FilterType分为:

  1. ANNOTATION:表示是否包含某个注解
  2. ASSIGNABLE_TYPE:表示是否是某个类
  3. ASPECTJ:表示否是符合某个Aspectj表达式
  4. REGEX:表示是否符合某个正则表达式
  5. CUSTOM:自定义

在Spring的扫描逻辑中,默认会添加一个AnnotationTypeFilter给includeFilters,表示默认情况下Spring扫描过程中会认为类上有@Component注解的就是Bean。

@Conditional注解

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。
@Conditional的定义:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	/**
	 * All {@link Condition} classes that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();

}

需要传入一个Class数组,并且需要继承Condition接口

@FunctionalInterface
public interface Condition {

	/**
	 * Determine if the condition matches.
	 * @param context the condition context
	 * @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
	 * or {@link org.springframework.core.type.MethodMetadata method} being checked
	 * @return {@code true} if the condition matches and the component can be registered,
	 * or {@code false} to veto the annotated component's registration
	 */
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

Condition是一个接口,需要实现matches方法,返回true则注入bean,false则不注入。

示例

MyCondition实现了Condition接口,如果类上有@scope注解则会注入,没有则不注入

public class MyCondition implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		if(metadata.isAnnotated(Scope.class.getName())){
			return true;
		}
		return false;
	}
}

Spring底层架构核心概念_第1张图片
MyCondition中实现了UserService.class上有@scope注解则会注入进容器

@Lookup

假设一个单例Bean A 需要依赖注入一个原型模式的Bean B,每次拿单例Bean A的时候都想取到一个新的Bean B,这个时候应该怎么做?因为单例Bean A只会创建一次,也就是只会依赖注入一次Bean B,所以每次取到的Bean B都是相同的。

OrderService.class

@Component
@Scope("prototype")
public class OrderService {

}

UserService.class

@Component
public class UserService {

	@Autowired
	private OrderService orderService;


	public void test(){
		System.out.println(orderService);
	}

}

Test.class

	public static void main(String[] args) {

		// 创建一个Spring容器
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		//ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
		((UserService) applicationContext.getBean("userService")).test();
		((UserService) applicationContext.getBean("userService")).test();
		((UserService) applicationContext.getBean("userService")).test();
		((UserService) applicationContext.getBean("userService")).test();

/**
 * 
 * com.zhouyu.service.OrderService@ba4d54
 * com.zhouyu.service.OrderService@ba4d54
 * com.zhouyu.service.OrderService@ba4d54
 * com.zhouyu.service.OrderService@ba4d54
 */

这时候可以见到获取orderService对象是同一个。
这时候可以用@Lookup注解解决,先看一下@Lookup注解的定义

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lookup {

	/**
	 * This annotation attribute may suggest a target bean name to look up.
	 * If not specified, the target bean will be resolved based on the
	 * annotated method's return type declaration.
	 */
	String value() default "";

}

可见@Lookup只能注解在方法上,这时候可以注解在抽象方法上,让一个抽象类也称为一个Bean

示例

用@Lookup注解修改后的UserService.class

@Component
public abstract class UserService {

	@Lookup
	public abstract OrderService getOrderService();

	public void test(){
		System.out.println(getOrderService());
	}


}

测试结果

com.zhouyu.service.OrderService@3f2a3a5
com.zhouyu.service.OrderService@4cb2c100
com.zhouyu.service.OrderService@6fb554cc
com.zhouyu.service.OrderService@614c5515

BeanWrapper

  BeanWrapper 是 Spring 框架中的一个核心接口,它定义了设置和获取属性值、获取属性描述符以及查询属性的读写状态等一系列操作。BeanWrapper 提供了一种方便的方式来操作 JavaBean 的属性,它通过标准的 JavaBean 属性描述符和属性编辑器来实现这些操作。

主要用途

BeanWrapper 的主要用途是为 Spring 的依赖注入和数据绑定提供支持。在依赖注入过程中,Spring 容器需要读取和写入 Bean 的属性。BeanWrapper 提供了一种统一的方式来操作属性,无论属性是简单类型的,还是复杂类型的,或者是嵌套的属性。

对于简单的属性,你可以直接使用getPropertyValuesetPropertyValue方法来获取和设置属性值。例如:

BeanWrapper bw = new BeanWrapperImpl(user);
bw.setPropertyValue("name", "John");
String name = (String) bw.getPropertyValue("name");

但如果是复杂或者嵌套的呢?也可以使用同样的方法。例如,假设User类有一个Company属性,Company类有一个Address属性

public class User {
    private String name;
    private Company company;
    // getters and setters
}

public class Company{
    private Address address;
    // getters and setters
}

public class Address {
    private String street;
    // getters and setters
}

你可以这样设置CompanyAddressstreet属性:

BeanWrapper bw = new BeanWrapperImpl(user);
bw.setPropertyValue("company.address.street", "Main Street");

BeanWrapper的数据绑定

  在数据绑定过程中,Spring 需要将用户提交的表单数据绑定到后端的 JavaBean 上。BeanWrapper 提供了一种方便的方式来完成这个操作,它可以自动地将字符串类型的表单数据转换为相应的属性类型。

  在Spring MVC中,数据绑定通常发生在处理HTTP请求时,将请求参数绑定到后端的JavaBean上。这个过程通常由Spring的@ModelAttribute注解和数据绑定API完成。

假设我们有一个简单的JavaBean,表示一个用户:

public class User {
    private String name;
    private int age;

    // getters and setters
}

然后我们有一个处理HTTP请求的控制器方法,这个方法需要一个User对象作为参数:

@RequestMapping("/user")
public String handleUser(@ModelAttribute User user) {
    // handle the user
}

  当一个HTTP请求到达/user路径时,Spring MVC会自动创建一个User对象,并尝试将请求参数绑定到这个对象上。例如,如果请求的URL是/user?name=John&age=30,那么Spring MVC会创建一个User对象,设置它的name属性为"John"age属性为30

  这个过程是通过Spring的数据绑定API完成的,具体来说,是通过WebDataBinder类和BeanWrapper接口。WebDataBinder是数据绑定的主要类,它使用BeanWrapper来读取和写入属性值。


你可能感兴趣的:(Spring,spring,架构,java)