有没有做视频的网站网站如何做市场推广
文章目录
- 一、目标:自动扫描Bean对象注册
 - 二、设计:自动扫描Bean对象注册
 - 三、实现:自动扫描Bean对象注册
 - 3.0 引入依赖
 - 3.1 工程结构
 - 3.2 Bean生命周期中自动加载包扫描注册Bean对象和设置占位符属性类图
 - 3.3 主力占位符配置
 - 3.4 定义拦截注解
 - 3.4.1 定义拦截注解
 - 3.4.2 定义注册注解
 
- 3.5 处理对象扫描装配
 - 3.5.1 类路径扫描装配提供者类
 - 3.5.2 类路径扫描装配实现类
 
- 3.6 解析xml中调用扫描
 
- 四、测试:自动扫描Bean对象注册
 - 4.1 添加测试配置
 - 4.1.1 用户服务层实现类
 - 4.1.2 属性配置文件
 - 4.1.3 Spring属性配置文件
 - 4.1.4 Spring扫描配置文件
 
- 4.2 单元测试
 - 4.2.1 占位符测试
 - 4.2.2 包扫描测试
 
- 五、总结:自动扫描Bean对象注册
 
一、目标:自动扫描Bean对象注册
💡 怎么完成自动化扫描Bean对象,自动注册填充?
- 早期的 Spring 版本,需要一个一个在 spring.xml 中进行配置。
 - 现在的 Spring 版本,在核心功能逻辑上建设的更少的配置下,做到更简化的使用。包括:包的扫描注册、注解配置的使用、占位符属性的填充等。
 - 目标:在目前的核心逻辑上填充一些自动化的功能。
 
二、设计:自动扫描Bean对象注册
💡 怎么简化 Bean 对象的配置?怎么让整个 Bean 对象的注册都是自动扫描的?
- 需要的元素包括:扫描路径入口、XML 解析扫描信息、给需要扫描的 Bean 对象做注解标记、扫描 Class 对象摘取 Bean 注册的基本信息、组装注册信息、注册成 Bean 对象。
 - 在这些条件元素的支撑下,就可以实现出自定义注解和配置扫描路径的情况下,完成 Bean 对象的注册。
 - 除此之外再可以解决一个配置中占位符属性的知识点, 
- 比如:
${token}给 Bean 对象注入进去属性信息,那么就需要用到BeanFactoryPostProcessor。 - 因为它可以处理 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制。
 - 而实现这部分内容是为了后续把此类内容结合到自动化配置处理中。
 
 - 比如:
 

- 结合 Bean 的生命周期,包扫描只不过是扫描特定注解的类,提取类的相关信息组装成 
BeanDefinition注册到容器中。 - 在 
XmlBeanDefinitionReader中解析<context component-scan />标签,扫描类组装BeanDefinition然后注册到容器中的操作在ClassPathBeanDefinitionScanner#doScan中实现。- 自动扫描注册:主要是扫描添加了自定义注解的类,在 xml 加载过程中提取类的信息,组装 BeanDefinition 注册到 Spring 容器中。
 - 需要用到 
<context component-scan />:配置包路径并在XmlBeanDefinitionReader解析并做相应的处理(对类的扫描、获取注解信息等)。 - 最后包括了 
BeanFactoryPostProcessor的使用。因为我们需要完成对占位符配置信息的加载,所以需要使用到BeanFactoryPostProcessor在所有的BeanDefinition加载完成后,实例化 Bean 对象之前,修改BeanDefinition的属性信息。 
 
三、实现:自动扫描Bean对象注册
3.0 引入依赖
pom.xml
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version>
</dependency>
 
3.1 工程结构
spring-step-12
|-src|-main|	|-java|		|-com.lino.springframework|			|-aop|			|	|-aspectj|			|	|	|-AspectJExpressionPointcut.java|			|	|	|-AspectJExpressionPointcutAdvisor.java|			|	|-framework|			|	|	|-adapter|			|	|	|	|-MethodBeforeAdviceInterceptor.java|			|	|	|-autoproxy|			|	|	|	|-DefaultAdvisorAutoProxyCreator.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|			|	|	|-config|			|	|	|	|-AutowireCapableBeanFactory.java|			|	|	|	|-BeanDefinition.java|			|	|	|	|-BeanFactoryPostProcessor.java|			|	|	|	|-BeanPostProcessor.java|			|	|	|	|-BeanReference.java|			|	|	|	|-ConfigurableBeanFactory.java|			|	|	|	|-InstantiationAwareBeanPostProcessor.java|			|	|	|	|-SingletonBeanRegistry.java|			|	|	|-support|			|	|	|	|-AbstractAutowireCapableBeanFactory.java|			|	|	|	|-AbstractBeabDefinitionReader.java|			|	|	|	|-AbstractBeabFactory.java|			|	|	|	|-BeabDefinitionReader.java|			|	|	|	|-BeanDefinitionRegistry.java|			|	|	|	|-CglibSubclassingInstantiationStrategy.java|			|	|	|	|-DefaultListableBeanFactory.java|			|	|	|	|-DefaultSingletonBeanRegistry.java|			|	|	|	|-DisposableBeanAdapter.java|			|	|	|	|-FactoryBeanRegistrySupport.java|			|	|	|	|-InstantiationStrategy.java|			|	|	|	|-SimpleInstantiationStrategy.java|			|	|	|-xml|			|	|	|	|-XMLBeanDefinitionReader.java|			|	|	|-Aware.java|			|	|	|-BeanClassLoaderAware.java|			|	|	|-BeanFactory.java|			|	|	|-BeanFactoryAware.java|			|	|	|-BeanNameAware.java|			|	|	|-ConfigurableListableBeanFactory.java|			|	|	|-DisposableBean.java|			|	|	|-FactoryBean.java|			|	|	|-HierarcgicalBeanFactory.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|			|-util|			|	|-ClassUtils.java|-test|-java|-com.lino.springframework.test|-bean|	|-IUserService.java|	|-UserService.java|-ApiTest.java|-resources|-spring-property.xml|-spring-scan.xml|-token.properties
 
3.2 Bean生命周期中自动加载包扫描注册Bean对象和设置占位符属性类图

- 整个类图看,主要包括的就是 xml 解析类 
XmlBeanDefinitionReader对ClassPathBeanDefinitionScanner#doScan的使用。 - 在 
doScan方法中处理所有指定路径下添加了注解的类,拆解出类的信息:名称、作用范围等,进行创建BeanDefinition好用于 Bean 对象的注册操作。 PropertyPlaceholderConfigurer后续会与自动加载 Bean 对象进行整合,也就是可以在注解上使用占位符配置一些在配置文件里的属性信息。
3.3 主力占位符配置
PropertyPlaceholderConfigurer.java
package com.lino.springframework.beans.factory;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.lino.springframework.core.io.DefaultResourceLoader;
import com.lino.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Properties;/*** @description: 处理占位符配置类*/
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {/*** 占位符前缀*/public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";/*** 占位符后缀*/public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";private String location;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 加载属性文件try {DefaultResourceLoader resourceLoader = new DefaultResourceLoader();Resource resource = resourceLoader.getResource(location);Properties properties = new Properties();properties.load(resource.getInputStream());String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();for (String beanName : beanDefinitionNames) {BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);PropertyValues propertyValues = beanDefinition.getPropertyValues();for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {Object value = propertyValue.getValue();if (!(value instanceof String)) {continue;}String strVal = (String) value;StringBuilder buffer = new StringBuilder(strVal);int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) {String propKey = strVal.substring(startIdx + 2, stopIdx);String propVal = properties.getProperty(propKey);buffer.replace(startIdx, stopIdx + 1, propVal);propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), buffer.toString()));}}}} catch (IOException e) {throw new BeansException("Could not load properties", e);}}public void setLocation(String location) {this.location = location;}
}
 
- 依赖于 
BeanFactoryPostProcessor在 Bean 生命周期的属性,可以在 Bean 对象实例化之前,改变属性信息。- 所以这里通过实现 
BeanFactoryPostProcessor接口,完成对配置文件的加载以及摘取占位符在属性文件中的配置。 
 - 所以这里通过实现 
 - 这样可以把提取到的配置信息放置到属性信息中: 
buffer.replace(startIdx, stopIdx + 1, propVal)、propertyValues.addPropertyValue
 
3.4 定义拦截注解
3.4.1 定义拦截注解
Scope.java
package com.lino.springframework.context.annotation;import java.lang.annotation.*;/*** @description: 拦截注解*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {String value() default "singleton";
}
 
- 用于配置作用域的自定义注解,方便通过配置 Bean 对象注解的时候,拿到 Bean 对象的作用域。
 
3.4.2 定义注册注解
Component.java
package com.lino.springframework.stereotype;import java.lang.annotation.*;/*** @description: 注册注解*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {String value() default "";
}
 
- Component 自定义注解,用于配置到 Class 类上。
 
3.5 处理对象扫描装配
3.5.1 类路径扫描装配提供者类
ClassPathScanningCandidateComponentProvider.java
package com.lino.springframework.context.annotation;import cn.hutool.core.util.ClassUtil;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.stereotype.Component;
import java.util.LinkedHashSet;
import java.util.Set;/*** @description: 类路径扫描装配提供者*/
public class ClassPathScanningCandidateComponentProvider {public Set<BeanDefinition> findCandidateComponents(String basePackages) {Set<BeanDefinition> candidates = new LinkedHashSet<>();Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackages, Component.class);for (Class<?> clazz : classes) {candidates.add(new BeanDefinition(clazz));}return candidates;}
}
 
- 这里先要提供一个可以通过配置路径 
beanPackage=com.lino.springframework.test.bean,解析出classes信息的工具方法findCandidateComponents,通过这个方法就可以扫描到所有@Component注解的 Bean 对象。 
3.5.2 类路径扫描装配实现类
ClassPathBeanDefinitionScanner.java
package com.lino.springframework.context.annotation;import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.lino.springframework.stereotype.Component;
import java.util.Set;/*** @description: 类路径扫描装配实现类*/
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {private BeanDefinitionRegistry registry;public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {this.registry = registry;}public void doScan(String... basePackages) {for (String basePackage : basePackages) {Set<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition beanDefinition : candidates) {// 解析 bean 的作用域 singleton、prototypeString beanScope = resolveBeanScope(beanDefinition);if (StrUtil.isNotEmpty(beanScope)) {beanDefinition.setScope(beanScope);}registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);}}}private String resolveBeanScope(BeanDefinition beanDefinition) {Class<?> beanClass = beanDefinition.getBeanClass();Scope scope = beanClass.getAnnotation(Scope.class);if (null != scope) {return scope.value();}return StrUtil.EMPTY;}private String determineBeanName(BeanDefinition beanDefinition) {Class<?> beanClass = beanDefinition.getBeanClass();Component component = beanClass.getAnnotation(Component.class);String value = component.value();if (StrUtil.isEmpty(value)) {value = StrUtil.lowerFirst(beanClass.getSimpleName());}return value;}
}
 
ClassPathBeanDefinitionScanner是继承自ClassPathScanningCandidateComponentProvider的具体扫描包处理的类,在doScan中获取到扫描的类信息后,还需要获取 Bean 的作用域和类名,如果不配置类名基本都是把首字母缩写。
3.6 解析xml中调用扫描
XmlBeanDefinitionReader.java
package com.lino.springframework.beans.factory.xml;import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanReference;
import com.lino.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.lino.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.lino.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import com.lino.springframework.core.io.Resource;
import com.lino.springframework.core.io.ResourceLoader;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;/*** @description: XML处理Bean注册*/
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {...@Overridepublic void loadBeanDefinitions(Resource resource) throws BeansException {try {try (InputStream inputStream = resource.getInputStream()) {doLoadBeanDefinitions(inputStream);}} catch (IOException | ClassNotFoundException | DocumentException e) {throw new BeansException("IOException parsing XML document from " + resource, e);}}...protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException, DocumentException {SAXReader reader = new SAXReader();Document document = reader.read(inputStream);Element root = document.getRootElement();// 解析 context:component-scan标签,扫描包中的类并提取相关信息,用于组装 BeanDefinitionElement componentScan = root.element("component-scan");if (null != componentScan) {String scanPath = componentScan.attributeValue("base-package");if (StrUtil.isEmpty(scanPath)) {throw new BeansException("The value of base-package attribute can not be empty or null");}scanPackage(scanPath);}List<Element> beanList = root.elements("bean");for (Element bean : beanList) {String id = bean.attributeValue("id");String name = bean.attributeValue("name");String className = bean.attributeValue("class");String initMethod = bean.attributeValue("init-method");String destroyMethodName = bean.attributeValue("destroy-method");String beanScope = bean.attributeValue("scope");// 获取 Class, 方便获取类中的名称Class<?> clazz = Class.forName(className);// 优先级 id > nameString beanName = StrUtil.isNotEmpty(id) ? id : name;if (StrUtil.isEmpty(beanName)) {beanName = StrUtil.lowerFirst(clazz.getSimpleName());}// 定义beanBeanDefinition beanDefinition = new BeanDefinition(clazz);beanDefinition.setInitMethodName(initMethod);beanDefinition.setDestroyMethodName(destroyMethodName);if (StrUtil.isNotEmpty(beanScope)) {beanDefinition.setScope(beanScope);}List<Element> propertyList = bean.elements("property");// 读取属性并填充for (Element property : propertyList) {// 解析标签:propertyString attrName = property.attributeValue("name");String attrValue = property.attributeValue("value");String attrRef = property.attributeValue("ref");// 获取属性值:引入对象、值对象Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;// 创建属性信息PropertyValue propertyValue = new PropertyValue(attrName, value);beanDefinition.getPropertyValues().addPropertyValue(propertyValue);}if (getRegistry().containsBeanDefinition(beanName)) {throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");}// 注册 BeanDefinitiongetRegistry().registerBeanDefinition(beanName, beanDefinition);}}private void scanPackage(String scanPath) {String[] basePackages = StrUtil.splitToArray(scanPath, ',');ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());scanner.doScan(basePackages);}
}
 
XmlBeanDefinitionReader主要是在加载配置文件后,处理新增的自定义配置属性component-scan,解析后调用scanPackage方法。- 其实就是 
ClassPathBeanDefinitionScanner#doScan方法。 
- 其实就是 
 - 为了方便加载和解析 xml,
XmlBeanDefinitionReader替换为dom4j进行解析处理。 
四、测试:自动扫描Bean对象注册
4.1 添加测试配置
4.1.1 用户服务层实现类
UserService.java
package com.lino.springframework.test.bean;import com.lino.springframework.stereotype.Component;import java.util.Random;/*** @description: 用户接口实现类*/
@Component("userService")
public class UserService implements IUserService {private String token;@Overridepublic String queryUserInfo() {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "张三,100001,杭州";}@Overridepublic String register(String userName) {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "注册用户:" + userName + " success!";}@Overridepublic String toString() {return "UserService#token = {" + token + "}";}public String getToken() {return token;}public void setToken(String token) {this.token = token;}
}
 
- 给 
UserService添加了一个自定义注解@Component("userService")和一个属性信息String token。 
4.1.2 属性配置文件
token.properties
token=RejDlI78hu223Opo983Ds
 
- 这里配置一个 
token属性信息,通过占位符的方式进行获取。 
4.1.3 Spring属性配置文件
spring-property.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="com.lino.springframework.beans.factory.PropertyPlaceholderConfigurer"><property name="location" value="classpath:token.properties"/></bean><bean id="userService" class="com.lino.springframework.test.bean.UserService"><property name="token" value="${token}"/></bean></beans>
 
- 加载 
classpath:token.properties设置占位符属性值${token} 
4.1.4 Spring扫描配置文件
spring-scan.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context"><context:component-scan base-package="com.lino.springframework.test.bean"/></beans>
 
- 添加 
component-scan属性,设置包扫描根路径。 
4.2 单元测试
4.2.1 占位符测试
ApiTest.java
@Test
public void test_property() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-property.xml");IUserService userService = applicationContext.getBean("userService", IUserService.class);System.out.println("测试结果:" + userService);
}
 
测试结果
测试结果:UserService#token = {RejDlI78hu223Opo983Ds}
 
- 通过测试结果看,
UserService的token属性已经通过占位符的方式设置进去配置文件里的token.properties的属性值了。 
4.2.2 包扫描测试
ApiTest.java
@Test
public void test_scan() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-scan.xml");IUserService userService = applicationContext.getBean("userService", IUserService.class);System.out.println("测试结果:" + userService.queryUserInfo());
}
 
测试结果
测试结果:张三,100001,杭州
 
- 测试结果看,现在使用注解的方式就可以让 Class 注册完成 Bean 对象了。
 
五、总结:自动扫描Bean对象注册
- 通过整篇的内容实现来看,目前的功能添加其实不是很复杂,都是在 
IOC、AOP核心的基础上来补全功能。这些补全的功能也是在完善 Bean 的生命周期,让整个功能使用越来越容易。 
