Spring接口
FactoryBean接口
Spring中有两种类型的Bean:一种是普通的JavaBean;另一种就是工厂Bean(FactoryBean),这两种Bean都受Spring的IoC容器管理。
FactoryBean 是一个特殊的bean,要想得到FactoryBean本身,必须通过&FactoryBeanName,可以在BeanFactory中通过getBean(&FactoryBeanName)来得到 FactoryBean。
一般来说我们自己写的Bean在只继承自己的接口时创建Bean的过程是交给IOC容器来实现的。但是某些特殊情况如果单纯的交给Spring的IOC容器来实现会配置非常复杂有时甚至没办法实现。所以这种情况下Spring为我们提供了FactoryBean这个接口来实现这个创建的过程。
public interface FactoryBean<T> {
//获取FactoryBean初始化的Bean实例
T getObject() throws Exception;
//获取Bean实例的类型
Class<?> getObjectType();
//判断是否是单例模式
boolean isSingleton();
}
示例:
@Component
public class MyBean implements FactoryBean {
private String message;
public MyBean() {
this.message = "通过构造方法初始化实例";
}
@Override
public Object getObject() throws Exception {
MyBean myBean = new MyBean();
// 这里可以做一些复杂的操作,比如解析资源等IOC没办法实现的情况
// 这里并不一定要返回MyBean自身的实例,可以是其他任何对象的实例如:SqlSessionFactory
return myBean;
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
BeanFactory接口
BeanFactory有三个是子类:AutowireCapableBeanFactory,HierarchicalBeanFactory,ListableBeanFactory。最终的默认实现类是 DefaultListableBeanFactory。
BeanFactory接口是Spring bean容器(也就是IoC容器)的核心根接口,主要是用于创建bean的工厂接口,其定义的接口方法分为以下几个部分:获取bean实例、判断容器中是否包含某个bean、判断实例是否为单列模式或则原型模式、类型匹配、实例类型、获取别名。
// 用于区分FactoryBean实例,列如:如果myJndiObject是一个FactoryBean,通过 &myJndiObject获取返回的是工厂,而不是工厂返回的实例。
String FACTORY_BEAN_PREFIX = "&"; // 获取bean
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException; // 获取bean的提供者(对象工厂)
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType); boolean containsBean(String name); // 是否包含指定名字的bean
boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // 是否为单例
boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // 是否为原型 // 指定名字的bean是否和指定的类型匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch);
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 获取指定名字的bean的类型
String[] getAliases(String name); // 获取指定名字的bean的所有别名
BeanDefinition
管理每个bean的定义信息,容器启动的时候,spring会将配置文件中定以的信息或则使用注解(如@Componet @Service @Controller等)定义的bean封装成一个个 BeanDefinition 对象,每个bean对应一个BeanDefinition对象,在对象实例化期间,使用bean对应的BeanDenifition对象通过反射生成实例对象。BeanDefinition接口方法用来管理bean的属性信息。
BeanDefinition接口中的属性信息跟xml配置文件中的标签中的属性一一对应,也就是说一个BeanDefinition对应一个标签信息。在容器启动的时候,就会先将标签信息解析封装成BeanDefinition接口实现类对象。
BeanDefinition有一个子接口 AnnotatedBeanDefinition;三个间接实现类,分别是RootBeanFactory、ChildBeanFactory和GenericBeanDefinition。其中RootBeanFactory是用来封装父类定义信息(没有显示继承其他类的类)、ChildBeanDefinition是用来封装子类定义信息。
BeanDefinitionRegistry接口
定义了关于 BeanDefinition 的注册、移除、查询等一系列的操作。BeanDefinition 注册的容器为 DefaultListableBeanFactory下 beanDefinitionMap
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); /** Map from bean name to merged BeanDefinitionHolder. */
private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256); /** Map of singleton and non-singleton bean names, keyed by dependency type. */
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64); /** Map of singleton-only bean names, keyed by dependency type. */
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64); /** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
BeanDefinitionRegistryPostProcessor接口
BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口,用于动态注册、修改、移除beanDefinitionMap的BeanDefinition
//修改应用程序上下文的内部bean定义注册表,在所有Bean定义将要被加载,Bean实例还未创建的时候运行,它优先于postProcessBeanFactory方法执行。
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry);
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Appconfig.class);
}
}
//自定义BeanDefinitionRegistryPostProcessor实现注册、移除、修改BeanDefinition
class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("---->postProcessBeanDefinitionRegistry容器中BeanDefinition的数量为:" + registry.getBeanDefinitionCount());
RootBeanDefinition beanDefinition = new RootBeanDefinition(Person.class);
// 还可以这样给容器中注册
//Bean AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Dog.class).getBeanDefinition();
registry.registerBeanDefinition("person", beanDefinition);
}
//这是继承父类BeanFactoryPostProcessor的方法
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String str : beanDefinitionNames) {
System.out.println("----->" + str);
}
System.out.println("====>postProcessBeanFactory容器中BeanDefinition的数量为:" + beanFactory.getBeanDefinitionCount());
}
} @Component
class Appconfig {
@Bean
public CustomBeanDefinitionRegistryPostProcessor customBeanDefinitionRegistryPostProcessor() {
return new CustomBeanDefinitionRegistryPostProcessor();
}
}
BeanFactoryPostProcessor接口
BeanFactoryPostProcessor允许修改容器中的BeanDefinitions ,但不允许触发Bean的实例化;这样做可能会导致bean实例化过早,从而违反了容器并造成了意外的副作用。如果需要bean实例交互,请使用BeanPostProcessor。
副作用1——使用注解进行依赖注入失败;
副作用2——可能会将ApplicationContext容器启动过程暴露在多线程之下;
可以配置多个BeanFactoryPostProcessor的实现类,通过”order”控制执行次序,值越大优先级越低(要实现Ordered接口)
//BeanFactoryPostProcessor是在 Bean定义加载完成之后,Bean实例初始化之前会调用postProcessBeanFactory方法
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Appconfig.class);
Person person = (Person)context.getBean("person");
System.out.println(person.toString());
}
}
@Component
public class Appconfig {
@Bean
public CustomBeanDefinitionRegistryPostProcessor customBeanDefinitionRegistryPostProcessor() {
return new CustomBeanDefinitionRegistryPostProcessor();
}
@Bean
public CustomBeanFactoryPostProcessor customBeanFactoryPostProcessor(){
return new CustomBeanFactoryPostProcessor();
}
@Bean
public Person person(){
Person person = new Person();
person.setName("张三");
return person;
}
}
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
int count = beanFactory.getBeanDefinitionCount();
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames) {
if ("person".equals(beanName)) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
MutablePropertyValues m = beanDefinition.getPropertyValues();
m.addPropertyValue("name", "赵四");
System.out.println("--修改了name属性初始值了");
}
}
}
}
@Bean
public Person person(){
Person person = new Person();
person.setName("张三");
return person;
}
BeanPostProcessor接口
Spring
的AOP
代理就是作为BeanPostProcessor
实现.
BeanPostProcessor主要是在类初始化的前后处理相应的事,必须在普通对象创建之前被创建。
可以配置多个BeanPostProcessor的实现类,通过”order”控制执行次序,值越大优先级越低(要实现Ordered接口)
先执行的是postProcessBeforeInitialization,然后是afterPropertiesSet,然后是init-method,然后是postProcessAfterInitialization。
//在任何bean初始化回调(如InitializingBean 或自定义init-method)之前
Object postProcessBeforeInitialization(Object bean, String beanName) //在任何bean初始化回调(如InitializingBean 或自定义init-method)之后
Object postProcessAfterInitialization(Object bean, String beanName)
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Appconfig.class);
Person person = (Person)context.getBean("person");
System.out.println(person.toString());
}
}
@Configuration
public class Appconfig {
// 提示信息: is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
// 对于返回Spring BeanFactoryPostProcessor(BFPP)类型或者BeanPostProcessor(BPP)类型的@Bean方法,必须特别注意。
// 因为BFPP对象必须在容器生命周期的早期实例化,所以它们会干扰@Configuration类中的@Autowired,@ Value和@PostConstruct之类的注释的处理。
// 为了避免这些生命周期问题,请将BFPP或BPP返回的@Bean方法标记为static
@Bean
public static CustomBeanPostProcessor customBeanPostProcessor(){
return new CustomBeanPostProcessor();
} @Bean
public Person person(){
Person person = new Person();
person.setName("张三");
return person;
}
}
public class CustomBeanPostProcessor implements BeanPostProcessor { public CustomBeanPostProcessor() {
System.out.println("CustomBeanPostProcessor 实例化......");
} @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("spring中bean实例:" + beanName + " 初始化之前处理......");
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("spring中bean实例:" + beanName + " 初始化之后处理......");
if ("person".equals(beanName)){
Field[] declaredFields = bean.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
if ("name".equals(declaredField.getName())){
try {
//修改对象的属性
declaredField.setAccessible(true);
declaredField.set(bean,"哈哈哈");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
return bean;
}
}
注意点:
(一)BeanPostProcessor依赖的bean,不会执行BeanPostProcessor的方法,因为所依赖的Bean需要在BeanPostProcessor之前创建完成!
(二)BeanPostProcessor以及依赖的bean无法使用AOP!
(三)当使用ConfigurableBeanFactory接口的addBeanPostProcessor方法手动添加BeanPostProcessor时,只能作用于那些延迟加载的Bean或非单例
bean
!
(四)当使用addBeanPostProcessor方式添加的BeanPostProcessor,Ordered接口的作用将失效,而是以注册的顺序执行!
(五)在使用@Bean声明工厂方法返回BeanPostProcessor实现类对象时,返回值必须是BeanPostProcessor类型,或者更低级的类型!
InstantiationAwareBeanPostProcessor接口
InstantiationAwareBeanPostProcessor接口继承了BeanPostProcess接口,区别如下:
BeanPostProcess接口只在bean的初始化阶段进行扩展(注入spring上下文前后),而InstantiationAwareBeanPostProcessor接口在此基础上增加了3个方法,把可扩展的范围增加了实例化阶段和属性注入阶段。
//实例化bean之前,如果该方法返回null,后面的方法都正常执行,如果不为null,则直接执行postProcessAfterInitialization方法,(实例化之后 和 初始化之前都不执行)
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
//实例化bean之后,如果该方法返回false,并且不需要check,那么postProcessProperties就会被忽略不执行;如果返回true,postProcessProperties就会被执行
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
//bean已经实例化完成,可以对属性值进行修改(这个时候属性值还未被设置,但是我们可以修改原本该设置进去的属性值)
PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Appconfig.class);
Object person = context.getBean("person");
System.out.println(person.toString());
}
}
@Configuration
class Appconfig {
@Bean
public CustomInstantiationAwareBeanPostProcessor customInstantiationAwareBeanPostProcessor(){
return new CustomInstantiationAwareBeanPostProcessor();
}
@Bean
public Person person(){
Person person = new Person();
person.setName("张三");
return person;
}
}
public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("--->postProcessBeforeInstantiation");//顺序1
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("--->postProcessAfterInstantiation");//顺序2
return true;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out.println("--->postProcessProperties");//顺序3
return pvs;
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
System.out.println("--->postProcessPropertyValues");
return pvs;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("--->postProcessBeforeInitialization");//顺序4
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("--->postProcessAfterInitialization");//顺序5
return bean;
}
}
BeanFactoryAware接口
实现BeanFactoryAware接口可以直接访问 Spring 容器(BeanFactory)
//可以获取到Spring容器
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Appconfig.class);
Object person = CustomBeanFactoryAware.getBean("person");
System.out.println(person.toString());
}
}
@Configuration
class Appconfig {
@Bean
public CustomBeanFactoryAware customBeanFactoryAware(){
return new CustomBeanFactoryAware();
}
@Bean
public Person person(){
Person person = new Person();
person.setName("张三");
return person;
}
}
public class CustomBeanFactoryAware implements BeanFactoryAware {
private static BeanFactory bff;
public static void setContextBean(BeanFactory bf){
bff=bf;
}
public static Object getContextBean(String beanName){
return bff.getBean(beanName);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
CustomBeanFactoryAware.setContextBean(beanFactory);
}
public static Object getBean(String beanName){
return CustomBeanFactoryAware.getContextBean(beanName);
}
}
ApplicationContextAware接口
实现ApplicationContextAware接口可以直接获得Spring上下文管理器(ApplicationContext),使用方法参考BeanFactoryAware接口
EnvironmentAware接口
用于获取EnviromentAware的一个扩展类,可以获得系统内的所有参数,使用方法参考BeanFactoryAware接口
ResourceLoaderAware接口
用于获取ResourceLoader的一个扩展类,获取classpath内所有的资源对象,使用方法参考BeanFactoryAware接口
EmbeddedValueResolverAware接口
用于获取StringValueResolver的一个扩展类,StringValueResolver用于获取基于String类型的properties的变量,使用方法参考BeanFactoryAware接口
MessageSourceAware接口
用于获取MessageSource的一个扩展类,MessageSource主要用来做国际化,使用方法参考BeanFactoryAware接口
ApplicationEventPublisherAware接口
用于获取ApplicationEventPublisher的一个扩展类,ApplicationEventPublisher可以用来发布事件,结合ApplicationListener来共同使用。
ApplicationContextInitializer接口
用于在spring容器refresh()刷新之前初始化Spring ConfigurableApplicationContext的回调接口,允许我们对ConfigurableApplicationContext的实例做进一步的设置和处理。例如,根据上下文环境注册属性源或激活配置文件等。
- ApplicationContextInitializer支持Order注解,表示执行顺序,越小越早执行;
//初始化给定的应用程序上下文
void initialize(C applicationContext);
@Order(1)
class CustomApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("-----MyApplicationContextInitializer initialize-----");
// 打印人所有 beanName
System.out.println(applicationContext.getBeanDefinitionCount() + "个Bean的名字如下:");
Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
//从容器中获取环境变量组件
ConfigurableEnvironment environment = applicationContext.getEnvironment();
//自己自定义一些属性放在map集合中
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1");
//包装为MapPropertySource组件
MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map);
//存到环境变量中去
environment.getPropertySources().addLast(mapPropertySource);
System.out.println("FirstInitializer属性注册成功...");
//注册后置处理器
applicationContext.addBeanFactoryPostProcessor(new CustomBeanDefinitionRegistryPostProcessor());
}
}
三种实现方式:
①SpringBoot方式
@SpringBootApplication
public class SpringBootStudyApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(SpringBootStudyApplication.class);
//手动注册初始化器
springApplication.addInitializers(new CustomApplicationContextInitializer());
springApplication.run(args);
}
}
②resources目录下新建application.properties方式
#initializer
context.initializer.classes = com.yue.config.CustomApplicationContextInitializer
③resources目录下新建META-INF/spring.factories方式
org.springframework.context.ApplicationContextInitializer = com.yue.config.CustomApplicationContextInitializer
SmartInitializingSingleton接口
可以对所有单例对象(非懒加载对象)初始化完毕后,做一些后置的业务处理。
CommandLineRunner / ApplicationRunner接口
整个项目启动完毕后,自动执行。如果有多个CommandLineRunner,可以利用@Order来进行排序。
Spring接口的更多相关文章
- Spring学习4-面向切面(AOP)之Spring接口方式
一.初识AOP 关于AOP的学习可以参看帮助文档:spring-3.2.0.M2\docs\reference\html目录下index.html的相关章节 1.AOP:Aspect ...
- spring接口文档注解:@ApiOperation(转)
spring接口文档注解:@ApiOperation @ApiOperation不是spring自带的注解是swagger里的 com.wordnik.swagger.annotations.ApiO ...
- java 调用Spring接口上传文件及其他参数填充
第一步:在Spring配置中添加以下内容 <!-- 配置MultipartResolver 用于文件上传 使用spring的CommosMultipartResolver --> < ...
- 基于Spring接口,集成Caffeine+Redis两级缓存
原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 在上一篇文章Redis+Caffeine两级缓存,让访问速度纵享丝滑中,我们介绍了3种整合Caffeine和Redis作为两级缓存使用的方法,虽 ...
- Spring接口编程_设值注入和构造注入
说明: UserManagerImp是设值注入,UserManagerImp2是构造注入 接口不注入,也就是在Spring配置文件中没有接口的<bean>,但是定义的时候是用接口 priv ...
- Java Listener中Spring接口注入的使用
在项目中使用Spring通常使用他的依赖注入可以很好的处理,接口与实现类之间的耦合性,但是通常的应用场景中都是Service层和DAO层,或者web层的话, 也是与Strust2来整合,那么如何在Li ...
- spring接口文档注解:@ApiOperation
@ApiOperation不是spring自带的注解是swagger里的 com.wordnik.swagger.annotations.ApiOperation; @ApiOperation和@Ap ...
- Spring 接口日志 AOP
接口日志记录AOP实现-LogAspect - 91博客it技术开发者 - 博客园https://www.cnblogs.com/007sx/p/5810818.html Spring AOP(一) ...
- spring 接口校验参数(自定义注解)
1. 注解类 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.l ...
随机推荐
- swoft 切面AOP尝试
官网文档 https://www.swoft.org/documents/v2/basic-components/aop/ 视频教程 https://www.bilibili.com/video/BV ...
- 后羿:我射箭了快上—用MotionLayout实现王者荣耀团战
前言 昨晚跟往常一样,饭后开了一局王者荣耀,前中期基本焦灼,到了后期一波决定胜负的时候,我果断射箭,射中对面,配合队友直接秒杀,打赢团战一波推完基地.那叫一个精彩,队友都发出了666666的称赞,我酷 ...
- css和js实现硬件加速渲染自定义滚动条
听别人说用CSS的变换来实现渲染有硬件加速的效果,看到很多大网站都开始陆续使用上了,我也来说说怎么做,我这边实现的滚动条有自然滚动效果,看起来比较自然,说的再多不如直接写,让我们开始吧! 我们需要自己 ...
- v s
关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且只能修改变量,而synchronized可以修饰方法,以及代码块. ...
- zookeeper Cli的常用命令
zookeeper Cli的常用命令 服务管理 启动ZK服务: zkServer.sh start 查看ZK状态: zkServer.sh status 停止ZK服务: zkServer.sh sto ...
- js 小数点失精度
解决方法思路:将小数化成整数后再作运算.具体代码如下: /*** 加法运算,避免数据相加小数点后产生多位数和计算精度损失.** @param num1加数1 | num2加数2*/function ...
- 云计算管理平台之OpenStack镜像服务glance
一.glance简介 openstack中的glance服务是用来存储在openstack上启动虚拟机所需镜像:它主要用于发现.注册及检索虚拟机镜像:它通过提供RESTful风格的api对外提供服务: ...
- 联考day7 C. 树和森林 树形DP
题目描述 样例 样例输入 8 5 BBWWWBBW 1 2 2 3 4 5 6 7 7 8 样例输出 84 2 1 4 样例解释 分析 首先,我们要预处理出一个点到该联通块内所有点的距离之和 \(f\ ...
- JAVA类库之——Character类(持续更新)
Character 类 目录 Character 类 判断该字符是不是一个数字的方法:isDigit(ch) 判断该字符是不是一个字母的方法:isLetter(ch) 判断该字符是不是一个数字或字母的 ...
- 监控制图OxyPlot组件的下载与安装
1.在工具(T)-NuGet包管理器(N)-管理解决方案的NuGet程序包(N),打开组件管理界面 2.切换到浏览窗口,安装以下三个窗口组件即可 3.OxyPlot文档手册 https://oxypl ...