为什么要提供配置的方式呢,之前的内容中我们测试的时候都是通过代码来进行的:
下面看下平时使用的时候,通过配置是什么样的: 可以看出,提供配置的方式的优点:需要定义什么样的XML标记和注解呢?通过之前的内容知道,配置的内容就是Bean定义信息,那么Bean定义的内容就是需要配置的内容XML配置的方式,首先需要定义一个DTD或者XSD文档,来定义一套标记信息,去指定Bean定义
可以看出,bean的配置指定的内容就是Bean定义接口中的信息Bean配置的解析
-
注解方式的实现
那么可以使用外观模式,让用户只需要知道ApplicationContext和其子类就行了,ApplicationContext可以继承BeanFactory,继而把两个接口合在一起:
/**
* @className: AbstractApplicationContext
* @description: ApplicationContext的抽象类实现
* @author: TR
*/
public abstract class AbstractApplicationContext implements ApplicationContext {
/** 用组合的方式来持有BeanFactory,完成BeanFactory接口的方法 */
protected BeanFactory beanFactory;
public AbstractApplicationContext() {
super();
this.beanFactory = new PreBuildBeanFactory();
}
public AbstractApplicationContext(BeanFactory beanFactory) {
super();
this.beanFactory = beanFactory;
}
@Override
public Object getBean(String beanName) throws Exception {
return this.beanFactory.getBean(beanName);
}
@Override
public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
this.beanFactory.registerBeanPostProcessor(beanPostProcessor);
}
}
xml配置方式的ApplicationContext实现类
XML方式
XML文件来源的处理
不同来源的XML文件,它的加载方式是不一样的,但是在解析的过程中,最后都希望获取到InputStream
/**
* @className: Resource
* @description: 输入流的资源扩展接口
* @author: TR
*/
public interface Resource extends InputStreamSource {
//classpath形式的xml配置文件
String CLASS_PATH_PREFIX = "classpath:";
//系统文件形式的xml配置文件
String File_SYSTEM_PREFIX = "file:";
/**
* 判断资源是否存在
* @return: boolean
**/
boolean exists();
/**
* 是否可读
* @return: boolean
**/
boolean isReadable();
/**
* 是否打开
* @return: boolean
**/
boolean isOpen();
/**
* 获取资源文件
* @return: java.io.File
**/
File getFile();
}
InputStreamSource接口的实现类
* @className: FileSystemResource
* @description: 系统文件类型的资源实现类
* @author: TR
*/
public class FileSystemResource implements Resource {
/** 文件资源对象 */
private File file;
public FileSystemResource(String fileName) {
super();
this.file = new File(fileName);
}
public FileSystemResource(File file) {
super();
this.file = file;
}
@Override
public boolean exists() {
return this.file == null ? false : this.file.exists();
}
@Override
public boolean isReadable() {
return this.file == null ? false : this.file.canRead();
}
@Override
public boolean isOpen() {
return false;
}
@Override
public File getFile() {
return file;
}
@Override
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
}
/**
* @className: ClassPathResource
* @description: classpath形式的资源实现类
* @author: TR
*/
public class ClassPathResource implements Resource {
//classpath所需要的信息
private String path;
private Class<?> clazz;
private ClassLoader classLoader;
public ClassPathResource(String path) {
this(path, null );
}
public ClassPathResource(String path, Class<?> clazz) {
this(path, clazz, null);
}
public ClassPathResource(String path, Class<?> clazz, ClassLoader classLoader) {
super();
this.path = path;
this.clazz = clazz;
this.classLoader = classLoader;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Class<?> getClazz() {
return clazz;
}
public void setClazz(Class<?> clazz) {
this.clazz = clazz;
}
public ClassLoader getClassLoader() {
return classLoader;
}
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public boolean exists() {
if (StringUtils.isNotBlank(path)) {
if (this.clazz != null) {
return this.clazz.getResource(path) != null;
}
if (this.classLoader != null) {
return this.classLoader.getResource(path.startsWith("/") ? path.substring(1) : path) != null;
}
return this.getClass().getResource(path) != null;
}
return false;
}
@Override
public boolean isReadable() {
return exists();
}
@Override
public boolean isOpen() {
return false;
}
@Override
public File getFile() {
return null;
}
@Override
public InputStream getInputStream() throws IOException {
if (StringUtils.isNotBlank(path)) {
if (this.clazz != null) {
return this.clazz.getResourceAsStream(path);
}
if (this.classLoader != null) {
return this.classLoader.getResourceAsStream(path.startsWith("/") ? path.substring(1) : path);
}
return this.getClass().getResourceAsStream(path);
}
return null;
}
}
/**
* @className: UrlResource
* @description: URL形式的资源实现类
* @author: TR
*/
public class UrlResource implements Resource {
/** url的资源对象 */
private URL url;
public UrlResource(String url) throws IOException {
this.url = new URL(url);
}
public UrlResource(URL url) {
super();
this.url = url;
}
public URL getUrl() {
return url;
}
public void setUrl(URL url) {
this.url = url;
}
@Override
public boolean exists() {
return this.url != null;
}
@Override
public boolean isReadable() {
return exists();
}
@Override
public boolean isOpen() {
return false;
}
@Override
public File getFile() {
return null;
}
@Override
public InputStream getInputStream() throws IOException {
return null;
}
}
XML资源加载器
ResourceLoader接口:
在这里,还需要区分用户给的字符串代表的是哪种资源,所以需要定义字符串的规则:扫描的包有哪些呢?
需要定义一个资源路径的匹配行为
扫描的类ClassPathBeanDefinitionScanner
解析成Bean定义/**
* @className: BeanDefinitionReader
* @description: 将Resource资源解析成Bean定义的接口
* @author: TR
*/
public interface BeanDefinitionReader {
/**
* 解析单个资源
* @param resource:
* @return: void
**/
void loadBeanDefintions(Resource resource) throws Throwable;
/**
* 解析多个资源
* @param resource:
* @return: void
**/
void loadBeanDefintions(Resource... resource) throws Throwable;
}
/**
* @className: AbstractBeanDefinitionReader
* @description: TODO
* @date: 2021/6/10 15:58
* @author: jinpeng.sun
*/
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
/** 持有BeanDefinitionRegistry接口,以便完成注册到BeanFactory中 */
protected BeanDefinitionRegistry beanDefinitionRegistry;
public AbstractBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
super();
this.beanDefinitionRegistry = beanDefinitionRegistry;
}
}
/**
* @className: XmlBeanDefinitionReader
* @description: xml配置方式的bean定义解析器
* @author: TR
*/
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
super(beanDefinitionRegistry);
}
@Override
public void loadBeanDefintions(Resource resource) throws Throwable {
this.loadBeanDefintions(new Resource[] {resource});
}
@Override
public void loadBeanDefintions(Resource... resource) throws Throwable {
if (resource != null && resource.length > 0) {
for (Resource r : resource) {
this.parseXml(r);
}
}
}
private void parseXml(Resource r) {
//TODO 解析xml文档,获取bean定义,创建bean定义对象,注册到BeanDefinitionRegistry中
}
}
* @className: AnnotationBeanDefinitionReader
* @description: 注解配置方式的bean定义解析器:
* @author: TR
*/
public class AnnotationBeanDefinitionReader extends AbstractBeanDefinitionReader {
public AnnotationBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
super(beanDefinitionRegistry);
}
@Override
public void loadBeanDefintions(Resource resource) throws Throwable {
this.loadBeanDefintions(new Resource[] {resource});
}
@Override
public void loadBeanDefintions(Resource... resource) throws Throwable {
if (resource != null && resource.length > 0) {
for (Resource r : resource) {
this.retriveAndRegistBeanDefinition(r);
}
}
}
private void retriveAndRegistBeanDefinition(Resource resource) {
if(resource != null && resource.getFile() != null) {
String className = getClassNameFormFile(resource.getFile());
try {
Class<?> clazz = Class.forName(className);
Component component = clazz.getAnnotation(Component.class);
if (component != null) {
GeneralBeanDefinition beanDefinition = new GeneralBeanDefinition();
beanDefinition.setBeanClass(clazz);
beanDefinition.setScope(component.scope());
beanDefinition.setFactoryMethodName(component.factoryMethodName());
beanDefinition.setFactoryBeanName(component.factoryBeanName());
beanDefinition.setInitMethodName(component.initMethodName());
beanDefinition.setDestroyMethodName(component.destroyMethodName());
//获取所有的构造方法,在构造方法上找Autowired注解,如果有的话,将这个构造方法set到bd
this.handleConstructor(clazz, beanDefinition);
//处理工厂方法参数依赖
if(StringUtils.isNotBlank(beanDefinition.getFactoryMethodName())) {
this.handleFactoryMethodArgs(clazz, beanDefinition);
}
//处理属性依赖
this.handlePropertyDi(clazz, beanDefinition);
String beanName = "".equals(component.value()) ? component.name() : null;
if (StringUtils.isBlank(beanName)) {
// TODO 应用名称生成规则生成beanName;
// 默认驼峰命名法
beanName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, clazz.getSimpleName());
}
// 注册bean定义
this.beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
}
} catch (ClassNotFoundException | BeanDefinitionException e) {
e.printStackTrace();
}
}
}
private void handlePropertyDi(Class<?> clazz, GeneralBeanDefinition bd) {
// TODO Auto-generated method stub
}
private void handleFactoryMethodArgs(Class<?> clazz, GeneralBeanDefinition bd) {
// TODO Auto-generated method stub
}
private void handleConstructor(Class<?> clazz, GeneralBeanDefinition bd) {
//获取所有的构造方法,在构造方法上找Autowired注解,如果有的话,将这个构造方法set到bd
Constructor<?>[] constructors = clazz.getConstructors();
if (constructors != null && constructors.length > 0) {
for (Constructor c : constructors) {
if (c.getAnnotation(Autowired.class) != null) {
bd.setConstructor(c);
Parameter[] ps = c.getParameters();
//遍历获取参数上的注解,及创建参数依赖
break;
}
}
}
}
private int classPathAbsLength = AnnotationBeanDefinitionReader.class.getResource("/").toString().length();
private String getClassNameFormFile(File file) {
//返回绝对路径名字符串
String absPath = file.getAbsolutePath();
String name = absPath.substring(classPathAbsLength+1, absPath.indexOf("."));
return StringUtils.replace(name, File.separator, ".");
}
}
public class XmlApplicationContext extends AbstractApplicationContext {
private List<Resource> resources;
private BeanDefinitionReader definitionReader;
public XmlApplicationContext(String... locations) throws Throwable {
super();
load(locations);
//资源解析成BeanDefinition,外派给BeanDefinitionReader接口来实现
this.definitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) this.beanFactory);
Resource[] resourceArray = new Resource[resources.size()];
resources.toArray(resourceArray);
//将解析后的BeanDefinition装载到BeanFactory中
definitionReader.loadBeanDefintions(resourceArray);
}
/**
* 根据用户指定的配置文件位置,加载资源信息
* @param locations:
* @return: void
**/
private void load(String[] locations) throws IOException {
if (resources == null) {
resources = new ArrayList<Resource>();
}
//完成加载,创建好Resource
if (locations != null && locations.length > 0) {
for (String lo : locations) {
Resource resource = getResource(lo);
if (resource != null) {
this.resources.add(resource);
}
}
}
}
@Override
public Resource getResource(String location) throws IOException {
if (StringUtils.isNotBlank(location)) {
//根据字符串的前缀判断区分,class、系统文件、url三种资源的加载
if (location.startsWith(Resource.CLASS_PATH_PREFIX)) {
return new ClassPathResource(location.substring(Resource.CLASS_PATH_PREFIX.length()));
} else if (location.startsWith(Resource.File_SYSTEM_PREFIX)) {
return new FileSystemResource(location.substring(Resource.File_SYSTEM_PREFIX.length()));
} else {
return new UrlResource(location);
}
}
return null;
}
}