这两个Filter是Spring扫描过程中用来过滤的。ExcludeFilter表示排除过滤器,IncludeFilter表示包
含过滤器。
比如以下配置,表示扫描com.example.service
这个包下面的所有类,但是排除UserService
和OrderService
类,也就是就算
它上面有@Component
注解也不会成为Bean。
@ComponentScan(value = "com.example.service",
excludeFilters = {@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = {UserService.class,OrderService.class})})
public class AppConfig {
}
再比如以下配置,就算UserService
和OrderService
类上没有@Component
注解,它也会被扫描成为一个Bean。
@ComponentScan(value = "com.example.service",
includeFilters = {@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = {UserService.class,OrderService.class})})
public class AppConfig {
}
FilterType分为:
在Spring的扫描逻辑中,默认会添加一个AnnotationTypeFilter给includeFilters,表示默认情况下Spring扫描过程中会认为类上有@Component注解的就是Bean。
@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;
}
}
在MyCondition
中实现了UserService.class上有@scope
注解则会注入进容器
假设一个单例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
是 Spring 框架中的一个核心接口,它定义了设置和获取属性值、获取属性描述符以及查询属性的读写状态等一系列操作。BeanWrapper
提供了一种方便的方式来操作 JavaBean 的属性,它通过标准的 JavaBean 属性描述符和属性编辑器来实现这些操作。
BeanWrapper
的主要用途是为 Spring 的依赖注入和数据绑定提供支持。在依赖注入过程中,Spring 容器需要读取和写入 Bean 的属性。BeanWrapper
提供了一种统一的方式来操作属性,无论属性是简单类型的,还是复杂类型的,或者是嵌套的属性。
对于简单的属性,你可以直接使用getPropertyValue
和setPropertyValue
方法来获取和设置属性值。例如:
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
}
你可以这样设置Company
的Address
的street
属性:
BeanWrapper bw = new BeanWrapperImpl(user);
bw.setPropertyValue("company.address.street", "Main Street");
在数据绑定过程中,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
来读取和写入属性值。