BeanDefinition
BeanDefinition represents the definition of a Bean. There are many attributes in BeanDefinition to describe the characteristics of a Bean. For example:
- class, indicating the type of bean
- Scope, indicating the scope of the bean, singleton or prototype
- lazyInit, indicating whether the bean is lazy to load
- initMethodName, indicating the method to be executed for bean initialization
- Destrorymethodname, which indicates the method to execute the bean when the bean is destroyed
- and more ...
In Spring, we can define bean 1 and xml in the following ways, define a bean 2 through tags, and define bean 2 and annotation in @ Bean, @ Component (@ Service, @ Controller, @ Repository). We call it * * declarative definition Bean * *
We can also define beans programmatically, that is, directly through BeanDefinition, such as:
// Define BeanDefinition AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); // Set scope beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON); // Set bean type beanDefinition.setBeanClass(BeanTest.class); // Set lazy load beanDefinition.setLazyInit(true); // Register bean applicationContext.registerBeanDefinition("test", beanDefinition); Copy code
Similar to declarative transactions and programming transactions, beans defined through, @ Bean, @ Component and other declarative methods will eventually be resolved into the corresponding BeanDefinition object by Spring and put into the Spring container.
BeanDefinitionReader
Beandefinitionreaders are BeanDefinition readers provided in the Spring container. These beandefinitionreaders are rarely used when we use Spring, but they are used more in the Spring source code, which is equivalent to the internal infrastructure of Spring.
AnnotationBeanDefinitionReader
The class can be directly converted to BeanDefinition, and the annotations on the class will be parsed, for example:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotatedBeanDefinitionReaderTest.class); AnnotatedBeanDefinitionReader annotationBeanDefinitionReader = new AnnotatedBeanDefinitionReader(applicationContext); annotationBeanDefinitionReader.register(UserService.class); System.out.println(applicationContext.getBean(UserService.class)); Copy code
AnnotationBeanDefinitionReader
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(XmlBeanDefinitionReaderTest.class); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(applicationContext); xmlBeanDefinitionReader.loadBeanDefinitions("classpath:spring-context.xml"); System.out.println(applicationContext.getBean(UserService.class)); Copy code
ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner is a scanner, but its function is similar to BeanDefinitionReader. It can scan, scan a package path, and parse the scanned class. For example, if there is @ Component annotation on the scanned class, the class will be parsed into a BeanDefinition, such as:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ClassPathBeanDefinitionScannerTest.class); ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(applicationContext); classPathBeanDefinitionScanner.scan("com.summer.test.service"); System.out.println(applicationContext.getBean(UserService.class)); Copy code
BeanFactory
BeanFactory represents a Bean factory, so it is obvious that BeanFactory is responsible for creating beans and providing API s for obtaining beans. SpringApplicationContext is a subclass of BeanFactory, which is defined in the Spring source code as follows:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver { // ... } Copy code
First of all, in Java, interfaces can be * * multi inherited, * * ApplicationContext inherits ListableBeanFactory and hierarchalbeanfactory, while ListableBeanFactory and hierarchalbeanfactory both inherit from BeanFactory, so we can think that ApplicationContext inherits BeanFactory, which is equivalent to Apple inheriting fruit, BMW inherits from the automobile. ApplicationContext is also a kind of BeanFactory. UFIDA has all the functions supported by BeanFactory. However, ApplicationContext is more powerful than BeanFactory, and ApplicationContext also implements other basic interfaces. For example, MessageSource means internationalization, ApplicationEventPublisher means event publishing, EnvironmentCapable means obtaining environment variables, etc. we will discuss in detail about ApplicationContext later.
In the implementation of Spring source code, when we create a new ApplicationContext, the bottom layer will create a new BeanFactory, which is equivalent to using some methods of ApplicationContext, such as getBean(), the getBean of BeanFactory called by the bottom layer.
In the Spring source code, there is a very important implementation class for the BeanFactory interface: DefaultLIsttableBeanFactory, which is also very core.
Therefore, we can directly use DefaultLIsttableBeanFactory instead of an implementation class of ApplicationContext:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(UserService.class); beanFactory.registerBeanDefinition("userService", beanDefinition); System.out.println(beanFactory.getBean("userService")); Copy code
DefaultLIsttableBeanFactory is very powerful. We can monitor its inheritance relationship:
From the perspective of inheritance relationship, it implements many interfaces and has the following functions: 1. Alias registry: supports alias function. A name can correspond to multiple aliases. 2. BeanDefinitionRegistry: you can register, save, remove, and obtain a BeanDefinition. 3. SingletonBeanFactory: you can register directly to obtain a singleton bean. 4. Simpleliasregistry: it is a mine that implements all the definitions in the alias registry interface and supports the alias function. 5. ListableBeanFactory: Based on BeanFactory, Other functions are added. You can get the definition information of all beandefinitions.
ApplicationContext
AnnotationConfigApplicationContext
ClassPathXmlApplicationContext
internationalization
Define a MessageSource first:
@Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("messages"); return messageSource; } Copy code
After creating this Bean, we can use the Bean object of the MessageSource wherever internationalization is needed. At the same time, because ApplicationContext also has internationalization function, it can be used directly as follows:
context.getMessage("test", null, new Locale("en")); Copy code
Resource loading
ApplicationContext also has the function of loading resources. For example, you can directly use applicationcoext to obtain the contents of a file:
AnnotationConfigApplicationContext context = new AnnotaionConfigApplicaitonContext(AppConfig.class); Resource resource = context.getResource("file://C:\\a.txt") System.out.println(resource.contextLength()) Copy code
Implementing this function through ApplicationConext can improve our development efficiency. For example, we can also use:
AnnotationConfigApplicationContext context = new AnnotaionConfigApplicaitonContext(AppConfig.class); Resource resource = context.getResource("classpath:spring-context.xml") System.out.println(resource.contextLength()) Resource resource2 = context.getResource("https://baidu.com"); System.out.println(resource2.getURL()); Copy code
We can also access multiple resources
AnnotationConfigApplicationContext context = new AnnotaionConfigApplicaitonContext(AppConfig.class); Resource resource = context.getResource("classpath:spring-context.xml") System.out.println(resource.contextLength()) Copy code
Get runtime environment variables
Map<String, Object> systemEnvironment = context.getEnvironment().getSystemEnvironment(); System.out.println(systemEnvironment); Map<String, Object> systemProperties = context.getEnvironment().getSystemProperties(); System.out.println(systemProperties); MutablePropertySources propertySources = context.getEnvironment().getPropertySources(); System.out.println(propertySources); Copy code
You can also parse files
@PropertySource("classpath:spring.properties") Copy code
You can add a property file to our environment variable, which can be obtained through the following code:
String abc = context.getEnvironment().getProperty("abc", "1"); System.out.println(abc); Copy code
Event release
Define events
public class TestEvent extends ApplicationEvent { public TestEvent(Object source) { super(source); } } Copy code
Define event listeners
public class TestListener implements ApplicationListener<TestEvent> { @Override public void onApplicationEvent(TestEvent event) { System.out.println("Received an event,,,,,"); } } Copy code
Calling program
@Configuration public class ApplicationEventTest { @Bean public ApplicationListener applicationListener() { return new TestListener(); } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationEventTest.class); context.publishEvent(new TestEvent(new Object())); } } Copy code
Type conversion
In Spring, there are many places where you may need to convert String to other types. Today, let's learn about the use of three types of conversion: PropertyEditor, ConversionService and TypeConverter.
PropertyEditor
PropertyEditor is a type converter provided by JDK. First, create bean s:
@Service public class OrderService { @Value("orderVal") private Order order; public void test() { System.out.println("test order : " + order); } } Copy code
Create a type converter to convert a string to an Order instance object.
public class String2ObjectPropertyEditor extends PropertyEditorSupport implements PropertyEditor { @Override public void setAsText(String text) throws IllegalArgumentException { Order order = new Order(); order.setName("haha"); order.setAge(12); this.setValue(order); } } Copy code
Register converter and test code:
@Import({OrderService.class}) @Configuration public class PropertyEditorTest { @Bean public CustomEditorConfigurer customEditorConfigurer() { Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>(); customEditors.put(Order.class, String2ObjectPropertyEditor.class); CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer(); customEditorConfigurer.setCustomEditors(customEditors); return customEditorConfigurer; } public static void main(String[] args) { // Usage 1 String2ObjectPropertyEditor propertyEditor = new String2ObjectPropertyEditor(); propertyEditor.setAsText("1"); Object value = propertyEditor.getValue(); System.out.println(value); // Usage 2 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PropertyEditorTest.class); OrderService orderItemService = applicationContext.getBean(OrderService.class); orderItemService.test(); } } Copy code
ConversionService
ConversionService is a type converter provided in Sprign, which is more powerful than PrppertyEditor. Define converter:
public class String2ObjectConversionService implements ConditionalGenericConverter { @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { return Objects.equals(sourceType.getType(), String.class) && Objects.equals(targetType.getType(), Order.class); } @Override public Set<ConvertiblePair> getConvertibleTypes() { return Collections.singleton(new ConvertiblePair(String.class, Order.class)); } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { return new Order("haha", 32); } } Copy code
Use alone
DefaultConversionService conversionService = new DefaultConversionService(); conversionService.addConverter(new String2ObjectConversionService()); Order order = conversionService.convert("1", Order.class); System.out.println(order); Copy code
Use in Spring:
@Bean public ConversionServiceFactoryBean conversionService() { ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean(); conversionServiceFactoryBean.setConverters(Collections.singleton(new String2ObjectConversionService())); return conversionServiceFactoryBean; } Copy code
Bean injection and calling code:
// Testing and injection @Service public class OrderService { //Inject via @ Value @Value("orderVal") private Order order; public void test() { System.out.println("test order : " + order); } } // Calling code ApplicationContext appliciton = new AnnotationConfigApplicationContext(ConvertTest.class); OrderItemService orderItemService = appliciton.getBean(OrderItemService.class); orderItemService.test(); Copy code
TypeConverter
TypeConverter integrates PropertyEditor and ConversionService and is used inside Spring:
SimpleTypeConverter typeConverter = new SimpleTypeConverter(); typeConverter.registerCustomEditor(Order.class, new String2ObjectPropertyEditor()); Order order = typeConverter.convertIfNecessary("orderVal", Order.class); System.out.println(order); Copy code
For example, in #abstractbeanfactory#adaptbeaninstance #
// AbstractBeanFacotry.java <T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType) { // Check if required type matches the type of the actual bean instance. // If the conversion type is not empty and the bean type does not match the target type if (requiredType != null && !requiredType.isInstance(bean)) { try { // Try conversion Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return (T) convertedBean; } catch (TypeMismatchException ex) { if (logger.isTraceEnabled()) { logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; } Copy code
OrderComparator
OrderComparator is a comparator provided by Spring. It can compare according to the @ Order annotation or implement the Ordered interface to sort. For example:
public class A implements Ordered { @Override public int getOrder() { return 1; } } Copy code
public class B implements Ordered { @Override public int getOrder() { return 2; } } Copy code
Sort using:
public class OrderComparatorTest { public static void main(String[] args) { A a = new A(); B b = new B(); OrderComparator orderComparator = new OrderComparator(); System.out.println(orderComparator.compare(a, b)); // -1 List list = new ArrayList(); list.add(a); list.add(b); list.sort(orderComparator); System.out.println(list); // a,b } } Copy code
In addition, Spring also provides a subclass of OrderComparator: * * annotationawarerodecomparator, * * which supports @ order to specify the value of order, such as:
@Order(1) public class A1 { } Copy code
@Order(1) public class B1 { } Copy code
public class OrderComparatorTest1 { public static void main(String[] args) { A1 a = new A1(); B1 b = new B1(); AnnotationAwareOrderComparator orderComparator = new AnnotationAwareOrderComparator(); System.out.println(orderComparator.compare(a, b)); // -1 List list = new ArrayList(); list.add(a); list.add(b); list.sort(orderComparator); System.out.println(list); // a,b } } Copy code
BeanPostProessor
BeanPostProcessor represents the post processor of a bean. We can define one or more beanpostprocessors, for example, through the following code:
public class TestBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ("userService".equals(beanName)) { System.out.println("postProcessBeforeInitialization userService"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if ("userService".equals(beanName)) { System.out.println("postProcessAfterInitialization userService"); } return bean; } } Copy code
A BeanPostProcessor can do some additional user-defined logic before and after the initialization of any Bean.
BeanFactoryPostProcessor
BeanFactoryProcessor refers to the post processor of a Bean factory. In fact, it is similar to BeanPostProcessor. BeanPostProcessor interferes with the creation process of a Bean and BeanFactoryProcessor interferes with the creation process of a BeanFactory. We can define a BeanFactoryProcessor as follows:
@Component public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("bean factory post processor"); } } Copy code
We can extend beanfactory in the postProcessBeanFactory() method.
FactoryBean
As mentioned above, we can interfere with the process of Spring creating beans through BeanPostProcessor, but if we want a Bean to be completely created by us, it is also possible, for example, through FactoryBean.
@Component public class TestFactoryBean implements FactoryBean { @Override public Object getObject() throws Exception { UserService userService = new UserService(); return userService; } @Override public Class<?> getObjectType() { return UserService.class; } } Copy code
Through the above code, we can create a UserService object and define it as a Bean, but the Bean defined in this way * * will only go through initialization * *, and other Spring life cycles will not go through, such as dependency injection.
An object can also be generated as a Bean through @ Bean. What is the difference between @ Bean and FactoryBean? In fact, in many scenarios, the two cities can be replaced, but in terms of principle, the difference is obvious. The Bean of @ Bean will go through the complete Bean life cycle.
ExcludeFilter and IncluderFilter
These two filters are used for filtering in Spring scanning. ExcludeFilter means exclude Filter and IncluderFilter means include Filter. For example, the following configuration indicates that the scan is on www.com.com All classes returned under this package, but excluding the UserService class, that is, all @ Component annotations above it will not become beans.
@ComponentScan(value = "com.summer.test.service", excludeFilters = { @ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, classes = UserService.class) }) public class AppConfig { } Copy code
For example, in the following configuration, even if there is no @ Component annotation on the UserService class, it will be scanned into a Bean
@ComponentScan(value = "com.summer.test.service", includeFilters = { @ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, classes = UserService.class) }) public class AppConfig { } Copy code
FilterType is divided into:
- ANNOTATION,
- ASSIGNABLE_TYPE,
- ASPECTJ,
- REGEX,
- CUSTOM
MetadataReader/ClassMetaData/AnnotationMetadata
- Metadata reader metadata reading
- Metadata information of ClassMetaData class
- Metadata information of annotation metadata
Test code
SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(); // Construct MetadataReader MetadataReader metadataReader = metadataReaderFactory.getMetadataReader("com.summer.test.service.UserService"); // Get a ClassMetadata and get the class name ClassMetadata classMetadata = metadataReader.getClassMetadata(); System.out.println(classMetadata.getClassName()); // Get an AnnotationMetadata and get the annotation information on the class AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); System.out.println(annotationMetadata.hasMetaAnnotation(Component.class.getName())); //true System.out.println(annotationMetadata.hasAnnotation(Component.class.getName())); //false for (String annotationType : annotationMetadata.getAnnotationTypes()) { // org.springframework.stereotype.Service System.out.println(annotationType); }