Author: Xiao Fu Ge
Blog: https://bugstack.cn
Precipitate, share, and grow, so that you and others can gain something! 😄
Chapter Table of Contents (Handwritten Spring to let you know more)
- [x]
Chapter 01: Opening introduction, I want to show you Spring!
- [x]
Chapter 02: A small test to realize a simple Bean container
- [x]
Chapter 03: First show your skills, use design patterns to realize the definition, registration, and acquisition of
- [x]
Chapter 04: Making a splash, implement class instantiation strategy with constructor based on Cglib
- [x]
Chapter 05: A blockbuster, injecting attributes and dependent Bean functions into Bean objects to achieve
- [x]
rivers, designing and implementing resource loader, parsing and registering Bean objects from
- [x]
Chapter 07: Invincible, realize application context, automatic identification, resource loading, extension mechanism
- [x]
Chapter 08: Windy Dragon, register hooks to the virtual machine, implement the initialization and destruction methods of Bean objects
- [x]
Chapter 09: There is rain in the tiger line, define the mark type Aware interface, realize the perception container object
- [x]
Chapter 10: Prancing Horse, About Bean Object Scope and Implementation and Use of
- [x]
the next level, based on observer implementation, container events and event listeners
- [x]
Chapter 12: Thorough knowledge, based on JDK and Cglib dynamic proxy, to achieve AOP core functions
- [x]
Flowing Water, integrating AOP dynamic proxy into the life cycle of Bean
- [x]
Chapter 14: Swordsman, complete registration of Bean objects through annotation configuration and automatic package scanning
- [x]
People, inject configuration and Bean objects into attributes through annotations
- [] Chapter 16: To be archived...
I. Introduction
writing code is to constantly toss from being able to use to being easy to use!
Have you heard of perturbation functions? Have you written about Fibonacci hashes? Have you implemented the Mason rotation algorithm? Why have never heard of these can not write code! No, even if you haven’t heard of it, you can write the same code. For example, the database routing data you implement always falls in 1 database and 1 table, it is not hashed and distributed, and the lottery system you implement is always the largest red envelope of operational configuration. Sending it out increases the operating cost, and the spike system you develop always hangs up 1 second after the start, and the goods cannot be delivered at all.
In addition to some programmers who only treat coding as a bricks-and-mortar to deal with their work, there are also some programmers who are always pursuing the ultimate. can still make money by writing code, so happy! this code farmers will always consider 🤔 there is no better implement the logic to make the code not only can, but also easy to use it? In fact, the pursuit of this point to completion requires a lot of extensible learning and deep mining, so that the system you design can be more comprehensive and can deal with various complex scenarios.
2. Goal
With the support of the current two core functional modules of IOC and AOP, the registration and acquisition of Bean objects can be fully managed, but this way of use always feels like slash and burn is a bit difficult to use. Therefore, in the previous chapter, we solved the need to manually configure the Bean
object to the spring.xml
file. Instead, it can automatically scan @Component
to complete the automatic assembly and registration to the Spring
container.
Then after the automatic scanning package registers the Bean object, you need to change the original configuration file through property name="token"
configure the attributes and Bean operations to be automatically injected. This is just like we use the @Autowired
and @Value
annotations in the Spring framework to complete our injection operations on attributes and objects.
Three, the plan
In fact, after we have completed the basic functions of the Bean object, the subsequent functions added are all around the life cycle of the Bean, such as modifying the definition of Bean BeanFactoryPostProcessor, processing the attributes of the Bean must use BeanPostProcessor, and completing individual attribute operations Specially inherit BeanPostProcessor to provide a new interface, because in this way, the marked interface can be judged by instanceof. Therefore, operations on Beans, etc., as well as monitoring Aware and obtaining BeanFactory, all need to be completed in the life cycle of the Bean. So when we design attributes and Bean object injection, we will also use BeanPostProcessor to allow BeanPostProcessor to modify attribute values before setting Bean attributes. The overall design structure is as follows:
- To handle automatic scan injection, including property injection and object injection, you need to write the property information into the PropertyValues collection before filling the
applyPropertyValues
This step is equivalent to solving the previous process of configuring properties in spring.xml. - In the reading of attributes, it is necessary to rely on the scanning of the annotations configured for the attributes in the class of the Bean object.
field.getAnnotation(Value.class);
takes out the corresponding attributes in turn and fills in the corresponding configuration information. There is one point here. The configuration information of the attribute needs to depend on the implementation class PropertyPlaceholderConfigurer of BeanFactoryPostProcessor, and write the value to the embeddedValueResolvers collection of AbstractBeanFactory, so that the beanFactory can be used in the attribute filling to obtain the corresponding attribute value - Another is about @Autowired's injection of objects. In fact, the only difference between this one and property injection is the acquisition of objects
beanFactory.getBean(fieldType)
, and the others are no different. - When all the properties are set to PropertyValues, the next step is to create the object, property filling, and at this time, the configuration and objects we obtained one by one will be filled into the properties, and automatic injection is realized. Function.
Fourth, realize
1. Engineering structure
small-spring-step-14
└── src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── aop
│ │ ├── aspectj
│ │ │ └── AspectJExpressionPointcut.java
│ │ │ └── AspectJExpressionPointcutAdvisor.java
│ │ ├── framework
│ │ │ ├── adapter
│ │ │ │ └── MethodBeforeAdviceInterceptor.java
│ │ │ ├── autoproxy
│ │ │ │ └── MethodBeforeAdviceInterceptor.java
│ │ │ ├── AopProxy.java
│ │ │ ├── Cglib2AopProxy.java
│ │ │ ├── JdkDynamicAopProxy.java
│ │ │ ├── ProxyFactory.java
│ │ │ └── ReflectiveMethodInvocation.java
│ │ ├── AdvisedSupport.java
│ │ ├── Advisor.java
│ │ ├── BeforeAdvice.java
│ │ ├── ClassFilter.java
│ │ ├── MethodBeforeAdvice.java
│ │ ├── MethodMatcher.java
│ │ ├── Pointcut.java
│ │ ├── PointcutAdvisor.java
│ │ └── TargetSource.java
│ ├── beans
│ │ ├── factory
│ │ │ ├── annotation
│ │ │ │ ├── Autowired.java
│ │ │ │ ├── AutowiredAnnotationBeanPostProcessor.java
│ │ │ │ ├── Qualifier.java
│ │ │ │ └── Value.java
│ │ │ ├── config
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java
│ │ │ │ ├── InstantiationAwareBeanPostProcessor.java
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ │ ├── DisposableBeanAdapter.java
│ │ │ │ ├── FactoryBeanRegistrySupport.java
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ ├── support
│ │ │ │ └── XmlBeanDefinitionReader.java
│ │ │ ├── Aware.java
│ │ │ ├── BeanClassLoaderAware.java
│ │ │ ├── BeanFactory.java
│ │ │ ├── BeanFactoryAware.java
│ │ │ ├── BeanNameAware.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── DisposableBean.java
│ │ │ ├── FactoryBean.java
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ ├── InitializingBean.java
│ │ │ ├── ListableBeanFactory.java
│ │ │ └── PropertyPlaceholderConfigurer.java
│ │ ├── BeansException.java
│ │ ├── PropertyValue.java
│ │ └── PropertyValues.java
│ ├── context
│ │ ├── annotation
│ │ │ ├── ClassPathBeanDefinitionScanner.java
│ │ │ ├── ClassPathScanningCandidateComponentProvider.java
│ │ │ └── Scope.java
│ │ ├── event
│ │ │ ├── AbstractApplicationEventMulticaster.java
│ │ │ ├── ApplicationContextEvent.java
│ │ │ ├── ApplicationEventMulticaster.java
│ │ │ ├── ContextClosedEvent.java
│ │ │ ├── ContextRefreshedEvent.java
│ │ │ └── SimpleApplicationEventMulticaster.java
│ │ ├── support
│ │ │ ├── AbstractApplicationContext.java
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ ├── ApplicationContextAwareProcessor.java
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── ApplicationContext.java
│ │ ├── ApplicationContextAware.java
│ │ ├── ApplicationEvent.java
│ │ ├── ApplicationEventPublisher.java
│ │ ├── ApplicationListener.java
│ │ └── ConfigurableApplicationContext.java
│ ├── core.io
│ │ ├── ClassPathResource.java
│ │ ├── DefaultResourceLoader.java
│ │ ├── FileSystemResource.java
│ │ ├── Resource.java
│ │ ├── ResourceLoader.java
│ │ └── UrlResource.java
│ ├── stereotype
│ │ └── Component.java
│ └── utils
│ ├── ClassUtils.java
│ └── StringValueResolver.java
└── test
└── java
└── cn.bugstack.springframework.test
├── bean
│ ├── IUserService.java
│ └── UserService.java
└── ApiTest.java
project source code : public account "bugstack wormhole stack", reply: Spring column, get the complete source code
Automatically scan the injected placeholder configuration and the class relationship of the object, as shown in Figure 15-2
- In the entire class diagram, the class AutowiredAnnotationBeanPostProcessor that implements the interface InstantiationAwareBeanPostProcessor is used as the entry point, which is called by AbstractAutowireCapableBeanFactory during the creation of the Bean object. The property configuration of the entire class contains custom annotations
Value
,Autowired
,Qualifier
, and the property values. - The slight change here is about the acquisition of attribute value information. When the attribute field of the annotation configuration scans to the information injection, the placeholder is included. The information obtained from the configuration file also includes the Bean object. The Bean object can be obtained directly, but the configuration information needs Add a new set of properties embeddedValueResolvers in AbstractBeanFactory, which is filled into the property set by PropertyPlaceholderConfigurer#postProcessBeanFactory.
2. Fill the read attribute to the container
defines the interface for parsing strings
cn.bugstack.springframework.util.StringValueResolver
public interface StringValueResolver {
String resolveStringValue(String strVal);
}
- The interface StringValueResolver is an interface for parsing string operations
Fill string
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
// 加载属性文件
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource(location);
// ... 占位符替换属性值、设置属性值
// 向容器中添加字符串解析器,供解析@Value注解使用
StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties);
beanFactory.addEmbeddedValueResolver(valueResolver);
} catch (IOException e) {
throw new BeansException("Could not load properties", e);
}
}
private class PlaceholderResolvingStringValueResolver implements StringValueResolver {
private final Properties properties;
public PlaceholderResolvingStringValueResolver(Properties properties) {
this.properties = properties;
}
@Override
public String resolveStringValue(String strVal) {
return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties);
}
}
}
- In the class PropertyPlaceholderConfigurer that parses the property configuration, the most important thing is actually the operation of this line of code
beanFactory.addEmbeddedValueResolver(valueResolver)
which writes the property value into the embeddedValueResolvers of AbstractBeanFactory. - Here is a description, embeddedValueResolvers is a newly added collection of AbstractBeanFactory class
List<StringValueResolver> embeddedValueResolvers
String resolvers to apply eg to annotation attribute values
3. Custom attribute injection annotation
custom annotations, Autowired, Qualifier, Value
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
public @interface Autowired {
}
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
/**
* The actual value expression: e.g. "#{systemProperties.myProp}".
*/
String value();
}
- Three annotations are also very common in our daily use of Spring, injecting objects and injecting properties, and Qualifier is generally used in conjunction with Autowired.
4. Scan custom annotations
cn.bugstack.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
// 1. 处理注解 @Value
Class<?> clazz = bean.getClass();
clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
Value valueAnnotation = field.getAnnotation(Value.class);
if (null != valueAnnotation) {
String value = valueAnnotation.value();
value = beanFactory.resolveEmbeddedValue(value);
BeanUtil.setFieldValue(bean, field.getName(), value);
}
}
// 2. 处理注解 @Autowired
for (Field field : declaredFields) {
Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
if (null != autowiredAnnotation) {
Class<?> fieldType = field.getType();
String dependentBeanName = null;
Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
Object dependentBean = null;
if (null != qualifierAnnotation) {
dependentBeanName = qualifierAnnotation.value();
dependentBean = beanFactory.getBean(dependentBeanName, fieldType);
} else {
dependentBean = beanFactory.getBean(fieldType);
}
BeanUtil.setFieldValue(bean, field.getName(), dependentBean);
}
}
return pvs;
}
}
- AutowiredAnnotationBeanPostProcessor is a class and operation method that implements the interface InstantiationAwareBeanPostProcessor and is used to process the attribute information before setting the attribute operation after the Bean object is instantiated. Only when the BeanPostProcessor interface is implemented can you have the opportunity to process the initialization information in the life cycle of the Bean
- The core method postProcessPropertyValues is mainly used to process properties annotated with @Value and @Autowired in the class, and to extract and set property information.
- One thing to note here is that because we use CglibSubclassingInstantiationStrategy in the AbstractAutowireCapableBeanFactory class to create the class, we need to determine whether to create an object for CGlib in AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues, otherwise the class information cannot be obtained correctly.
ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;
5. Call attribute injection in the life cycle of Bean
cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
// 判断是否返回代理 Bean 对象
bean = resolveBeforeInstantiation(beanName, beanDefinition);
if (null != bean) {
return bean;
}
// 实例化 Bean
bean = createBeanInstance(beanDefinition, beanName, args);
// 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值
applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);
// 给 Bean 填充属性
applyPropertyValues(beanName, bean, beanDefinition);
// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
// 注册实现了 DisposableBean 接口的 Bean 对象
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
// 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPE
if (beanDefinition.isSingleton()) {
registerSingleton(beanName, bean);
}
return bean;
}
/**
* 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值
*
* @param beanName
* @param bean
* @param beanDefinition
*/
protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor){
PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName);
if (null != pvs) {
for (PropertyValue propertyValue : pvs.getPropertyValues()) {
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
}
}
}
}
// ...
}
- The AbstractAutowireCapableBeanFactory#createBean method has this newly added method call, which is to allow the BeanPostProcessor to modify the attribute value before setting the Bean attribute
applyBeanPostProcessorsBeforeApplyingPropertyValues
- Then in the applyBeanPostProcessorsBeforeApplyingPropertyValues method, the first step is to obtain the injected BeanPostProcessor collection and filter out the implementation classes that inherit the interface InstantiationAwareBeanPostProcessor.
- The last is to call the corresponding postProcessPropertyValues method and set the property value information circularly,
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
Five, test
1. Prepare in advance
Configure Dao
@Component
public class UserDao {
private static Map<String, String> hashMap = new HashMap<>();
static {
hashMap.put("10001", "小傅哥,北京,亦庄");
hashMap.put("10002", "八杯水,上海,尖沙咀");
hashMap.put("10003", "阿毛,香港,铜锣湾");
}
public String queryUserName(String uId) {
return hashMap.get(uId);
}
}
- Configure the class with an annotation
@Component
that automatically scans registered Bean objects, and then inject this class into UserService.
annotation is injected into UserService
@Component("userService")
public class UserService implements IUserService {
@Value("${token}")
private String token;
@Autowired
private UserDao userDao;
public String queryUserInfo() {
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return userDao.queryUserName("10001") + "," + token;
}
// ...
}
- There are two types of injections, one is placeholder injection attribute information
@Value("${token}")
, and the other is injection object information@Autowired
2. Property Configuration File
token.properties
token=RejDlI78hu223Opo983Ds
spring.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context">
<bean class="cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:token.properties"/>
</bean>
<context:component-scan base-package="cn.bugstack.springframework.test.bean"/>
</beans>
- The scan attribute information and the automatic scan package path range are configured in spring.xml.
3. Unit Testing
@Test
public void test_scan() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
IUserService userService = applicationContext.getBean("userService", IUserService.class);
System.out.println("测试结果:" + userService.queryUserInfo());
}
- During unit testing, a class can be completely tested and injected into the Spring container, and this attribute information can also be automatically scanned and filled.
test result
测试结果:小傅哥,北京,亦庄,RejDlI78hu223Opo983Ds
Process finished with exit code 0
- From the test results, we can see that our usage method has been passed, there are automatic scanning classes, and annotation injection properties. This is more and more like using the Spring framework.
Six, summary
- From the perspective of the implementation content of the entire annotation information scanning injection, we have been focusing on processing in the life cycle of the Bean, just like BeanPostProcessor is used to modify the extension point of the newly instantiated Bean object, and the interface methods provided can be used to process the Bean Processing operations are performed before and after the object is instantiated. Sometimes you need to do some differentiated control, so you need to inherit the BeanPostProcessor interface and define a new interface InstantiationAwareBeanPostProcessor so that you can distinguish the operations of different extension points.
- For example, the interface is judged by instanceof, and the annotation is obtained by Field.getAnnotation(Value.class);, which are equivalent to some identifying information on the class, so that these function points can be found in some ways for processing. Therefore, these features can also be used in our daily development and design components.
- When you think about integrating your implementation into the life cycle of a Bean that has been subdivided, you will find that its design is so good that it allows you to be you at any point in time and on any aspect of initialization. Need to expand or change, this is also the flexibility we pursue when doing program design.
Seven, series recommendation
- Investigate bytecode instrumentation technology for system monitoring design and implementation
- working for two or three years, and I don't understand what the architecture diagram is drawn?
- Thread.start(), how does it start the thread? -%E5%AE%83%E6%98%AF%E6%80%8E%E4%B9%88%E8%AE%A9%E7%BA%BF%E7%A8%8B%E5%90%AF %E5%8A%A8%E7%9A%84%E5%91%A2.html)
- A demo version of Mybatis based on jdbc is implemented
- a bug that made me discover the .AJ (cone) in the Java world! .html)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。