目录

1.spring运行流程

2.Bean定义,注册,获取

ApplicationContext

1.bean对象的创建流程

2.bean的扩展接口

3.回调接口

4.监听与事件

5.FactoryBean

SpringAop的功能创建

Spring注解功能

Spring依赖注入

占位符处理


1.spring运行流程

2.Bean定义,注册,获取

BeanFactory代表了bean工厂

该接口里面有getBean(String name)方法,用来返回bean对象

BeanDefinition,用于定义 Bean 实例化信信息
public class BeanDefinition{//bean对象名称
private String beanName;//bean对象类型
private Class<?> beanClass;public BeanDefinition(String beanName,Class beanClass){this.beanName = beanName;this.beanClass = beanClass;    }public String getBeanName(){return this.beanName;}
}
首先非常重要的一点是在 Bean 注册的时候只注册一个类信息,而不会直接把实例化信息 注册到 Spring 容器中。那么就需要修改 BeanDefinition 中的属性Class,接下来在需要做的就是在获取 Bean 对象时需要处理 Bean 对象的 实例化操作以及判断当前单例对象在容器中是否已经缓存起来了。
单例注册接口定义和实现

单例注册接口 SingletonBeanRegistry

public interface SingletonBeanRegistry {//返回一个注册好的bean单例对象
Object getSingleton(String beanName); }
这个类比较简单主要是定义了一个获取单例对象的接口。

默认实现类DefaultSingletonBeanRegistry

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {//创建一个容器存放单例对象
private Map<String, Object> singletonObjects = new HashMap<>();@Override
//通过名字查找一个单例对象返回
public Object getSingleton(String beanName) {
return singletonObjects.get(beanName);
}//注册一个单例对象
protected void addSingleton(String beanName, Object singletonObject) {singletonObjects.put(beanName, singletonObject);} }
在 DefaultSingletonBeanRegistry 中主要实现 getSingleton 方法,同时实现了一个 受保护的 addSingleton 方法,这个方法可以被继承此类的其他类调用。包括:
AbstractBeanFactory 以及继承的 DefaultListableBeanFactory 调用。

抽象类定义模板方法AbstractBeanFactory
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {@Override
//只负责返回一个bean对象
public Object getBean(String name) throws BeansException {//去单例容器中按照name寻找bean对象
Object bean = getSingleton(name);//如果获取bean对象不为空则直接返回
if (bean != null) {return bean;}//没拿到则定义一个bean的信息类,传入名称
BeanDefinition beanDefinition = getBeanDefinition(name);//返回创建好的对象
return createBean(name, beanDefinition);
}//返回bean对象的定义信息
protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;//创建bean对象
protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException; }
AbstractBeanFactory 首先继承了 DefaultSingletonBeanRegistry,也就具备了使用单例注册类方法。
接下来很重要的一点是关于接口 BeanFactory 的实现,在方法 getBean 的实现过程中可以看到,主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean的定义做相应 Bean 实例化操作。那么 getBean 并没有自身的去实现这些方法,而是只定义了调用过程以及提供了抽象方法,由实现此抽象类的其他类做相应实
现。
后续继承抽象类 AbstractBeanFactory 的类有两个,包括:
AbstractAutowireCapableBeanFactory、DefaultListableBeanFactory
AbstrcatAutowireCapableBeanFactory 只负责实例化bean对象及其实例化策略
DefaultListableBeanFactory则是默认实现spring ioc的核心,用来存储BeanDefinition对象
但是创建的bean对象有的会带有一个或者多个构造方法,所以要创建一个实例化策略来应对,定义一个InitiationStrategy接口
该接口中定义了一个instantiationStrategy方法,用来对bean对象进行实例化策略
public interface InstantiationStrategy {Object instantiationStrategy(String beanName, BeanDefinition beanDefinition, Constructor ctor,Object[] args);
}

这块要求我们传入一个构造方法,在AutowireCapableBeanFactory中定义一个createInstantiation方法,通过反射拿到bean对象的构造方法集合,然后放入到实例化策略中进行实例化

 protected Object createInstantiation(String beanName, BeanDefinition beanDefinition, Object[] args) {Constructor<?> constructorToUse = null;Class<?> beanClass = beanDefinition.getBeanClass();//拿到所有构造器Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();for (Constructor ctor : declaredConstructors) {if (null != args && ctor.getParameterTypes().length == args.length) {constructorToUse = ctor;break;}}return getInstantiationStrategy.instantiationStrategy(beanName, beanDefinition, constructorToUse, args);}

在spring中有两种方式可以实现实例化策略一种是SimpleInstantiationStrategy另一种是CGlib实现的,下面列出简单类型的实现
通过判断构造方法来定义实例化的方法
(这里简单的使用判断构造方法参数数量来进行判断,实际上spring中更加复杂)
public class SimpleInstantiationStrategy implements InstantiationStrategy {@Overridepublic Object instantiationStrategy(String beanName, BeanDefinition beanDefinition, Constructor ctor, Object[] args) {Class<?> beanClass = beanDefinition.getBeanClass();try {if (null != ctor) {return beanClass.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);} else {return beanClass.getDeclaredConstructor().newInstance();}} catch (Exception e) {throw new RuntimeException();}}}

而这样bean就拥有了构造方法,但是其属性值却都还是默认值,所以还要对bean的属性值进行初始化

初始化就要将属性名和值放入到BeanDefinition中,先创建一个PropertyValue来存放单个属性的属性名和属性值,这个PropertyValue代表bean对象中的一个属性

public class PropertyValue {private String name;private Object value;public PropertyValue(String name, Object value) {this.name = name;this.value = value;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Object getValue() {return value;}public void setValue(Object value) {this.value = value;}
}

然后创建一个PropertyValues类,该类里定义了一个Map集合用来存放一个bean对象中的所有的PropertyValue,并且可以返回一个bean中的所有属性值

public class PropertyValues {private final List<PropertyValue> propertyValueList = new ArrayList();public void addPropertyValue(PropertyValue pv) {this.propertyValueList.add(pv);}//返回多个属性值public PropertyValue[] getPropertyValues() {return propertyValueList.toArray(new PropertyValue[0]);}//用属性名称进行对比,找到就返回public PropertyValue getPropertyValue(String propertyName) {for (PropertyValue pv : propertyValueList) {if (pv.getName().equals(propertyName)) {return pv;}}return null;}
}

但是在spring中也会存在对其他bean对象的依赖,所以还要将bean属性值和其他属性区分开来

需要创建一个BeanReference来存放bean属性的bean名称和类型,方便到时赋值时去查找或者递归创建

public class BeanReference {private String beanName;private Class beanClass;public BeanReference(String beanName) {this.beanName = beanName;}public BeanReference(String beanName, Class beanClass) {this.beanName = beanName;this.beanClass = beanClass;}public String getBeanName() {return beanName;}public Class getBeanClass() {return beanClass;}
}

这样,bean的属性基本上是定义好了,现在就要通过读取资源配置文件去创建bean了

如果要读取资源文件来创建bean对象,则首先要定义一个Resource接口
public interface Resource{InputStream getInputStream();
}

这个接口定义了一个返回读取到资源流的方法

然后分别用ClassPathResource,FileSystemResource,UrlResource来实现该接口,分别对类路径下文件,指定资源文件,云文件进行对应的读取和包装.

ClassPathResource:

public class ClassPathResource implements Resource {private final String path;private ClassLoader classLoader;public ClassPathResource(String path) {this(path, (ClassLoader) null);}public ClassPathResource(String path, ClassLoader classLoader) {Assert.assertNotNull("path must not be null", path);this.path = path;this.classLoader = classLoader != null ? classLoader : new ClassLoader() {@Overridepublic String getName() {return super.getName();}};}@Overridepublic InputStream getInputStream() throws IOException {InputStream in = classLoader.getResourceAsStream(path);if (in == null) {throw new FileNotFoundException(this.path + "cannot be opened because it does not exist");}return in;}
}

FileSystemResource:

public class FileSystemResource implements Resource {private final String path;private final File file;public FileSystemResource(String path) {this.path = path;this.file = new File(path);}public FileSystemResource(File file) {this.file = file;this.path = file.getPath();}@Overridepublic InputStream getInputStream() throws IOException {return new FileInputStream(file);}public String getPath() {return path;}
}

UrlResource:

public class UrlResource implements Resource {private final URL url;public UrlResource(URL url) {Assert.assertNotNull("Url must not be null", url);this.url = url;}@Overridepublic InputStream getInputStream() throws IOException {URLConnection connection = this.url.openConnection();try {return connection.getInputStream();} catch (IOException e) {if (connection instanceof HttpURLConnection) {((HttpURLConnection) connection).disconnect();}throw e;}}
}

有了这三个包装类就可以对指定的资源进行读取,但是我们一般只会放入资源地址比如classpath:spring.xml这样的路径来加载资源,所以我们需要一个ResourceLoader(资源加载器)来对我们传入的路径进行判断,决定用哪个类来进行包装和读取资源.

定义一个ResourceLoader接口,里面定义了一个类路径文件前缀,定义了一个返回资源包装的方法,然后用一个默认实现类来按照给到的资源路径来选择用哪个包装类。

ResourceLoader:

public interface ResourceLoader {String CLASSPATH_URL_PROFIX = "classpath:";Resource getResource(String location);
}

DefaultResourceLoader:

该默认实现通过判断是否是类路径,url路径,文件路径来对该资源路径进行资源包装,然后返回该包装进行资源的读取

public class DefaultResourceLoader implements ResourceLoader {@Overridepublic Resource getResource(String location) {Assert.assertNotNull("location must not be null", location);if (location.startsWith(CLASSPATH_URL_PROFIX)) {return new ClassPathResource(location.substring(CLASSPATH_URL_PROFIX.length()));} else {try {URL url = new URL(location);return new UrlResource(url);} catch (MalformedURLException e) {return new FileSystemResource(location);}}}
}

当我们做完上面两部分后,我们就可以放入资源路径然后读取文件了,但是只是读取到了文件,现在就要将读到的流转换成bean对象

定义一个BeanDefinitionReader接口,该接口用来读取资源文件并且将其转换为BeanDefinition注入到容器中,供ioc创建bean对象使用

接口中定义了五个方法,其中loadeBeanDefinitions用来传入资源或者资源路径进行加载。

public interface BeanDefinitionReader {BeanDefinitionRegistry getRegistry();ResourceLoader getResourceLoader();void loadBeanDefinitions(String location) throws IOException, ClassNotFoundException;void loadBeanDefinitions(Resource resource) throws IOException, ClassNotFoundException;void loadBeanDefinitions(Resource... resource) throws IOException, ClassNotFoundException;void loadBeanDefinitions(String[] locations) throws IOException, ClassNotFoundException;
}

首先先创建一个AbstractBeanDefinitonReader方法,获得加载所需要的ioc核心类和资源加载器

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {private final BeanDefinitionRegistry registry;private ResourceLoader resourceLoader;protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {this(registry, new DefaultResourceLoader());}protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {this.registry = registry;this.resourceLoader = resourceLoader;}@Overridepublic BeanDefinitionRegistry getRegistry() {return this.registry;}@Overridepublic ResourceLoader getResourceLoader() {return this.resourceLoader;}
}

有了BeanDefinitionRegistry就可以将读取到的bean定义存入容器中,而ResourceLoader可以对资源路径进行包装.

接下来就是实现读取xml配置的类了,创建读取xml文件的实现类XmlBeanDefinitionReader

这个类实现了接口的三个加载资源文件的方法,通过doLoadBeanDefinition来读取出xml文件中的bean信息然后转换成BeanDefinition,并将创建好的BeanDefinition放入ioc容器。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {super(registry);}public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {super(registry, resourceLoader);}@Overridepublic void loadBeanDefinitions(String location) throws IOException, ClassNotFoundException {InputStream inputStream = getResourceLoader().getResource(location).getInputStream();doLoadBeanDefinition(inputStream);}@Overridepublic void loadBeanDefinitions(Resource resource) throws IOException, ClassNotFoundException {InputStream inputStream = resource.getInputStream();doLoadBeanDefinition(inputStream);}@Overridepublic void loadBeanDefinitions(Resource... resources) throws IOException, ClassNotFoundException {for (Resource rc : resources) {InputStream inputStream = rc.getInputStream();doLoadBeanDefinition(inputStream);}}@Overridepublic void loadBeanDefinitions(String[] locations) throws IOException, ClassNotFoundException {for (String location : locations) {loadBeanDefinitions(location);}}protected void doLoadBeanDefinition(InputStream in) throws ClassNotFoundException {Document doc = XmlUtil.readXML(in);Element element = doc.getDocumentElement();NodeList nodeList = element.getChildNodes();for (int i = 0; i < nodeList.getLength(); i++) {if (!(nodeList.item(i) instanceof Element))continue;if (!("bean".equals(nodeList.item(i).getNodeName())))continue;Element bean = (Element) nodeList.item(i);String id = bean.getAttribute("id");String name = bean.getAttribute("name");String className = bean.getAttribute("class");String initMethodName = bean.getAttribute("init-method");String destroyMethodName = bean.getAttribute("destroy-method");Class<?> clazz = Class.forName(className);String beanName = id != null ? id : name;if (!StrUtil.isNotEmpty(beanName)) {beanName = StrUtil.lowerFirst(clazz.getSimpleName());}BeanDefinition beanDefinition = new BeanDefinition(clazz, initMethodName, destroyMethodName);NodeList childNodes = bean.getChildNodes();for (int j = 0; j < childNodes.getLength(); j++) {if (!(childNodes.item(j) instanceof Element))continue;if (!("property".equals(childNodes.item(j).getNodeName())))continue;Element property = (Element) childNodes.item(j);String attrName = property.getAttribute("name");String attrValue = property.getAttribute("value");String attrRef = property.getAttribute("ref");Object value = StrUtil.isNotEmpty(attrValue) ? attrValue : new BeanReference(attrRef);PropertyValue propertyValue = new PropertyValue(attrName, value);beanDefinition.getPropertyValues().addPropertyValue(propertyValue);}if (getRegistry().containsBeanDefinition(beanName)) {throw new BeanException();}getRegistry().registerBeanDefinition(beanName, beanDefinition);}}}

读取文件并且将BeanDefinition加载到ioc后,我们就可以创建bean对象并且使用了,但是这还是太复杂了,如果要使用还要进行这些操作

手工的创建bean工厂对象

手工读取xml文件

public class Tt {public static void main(String[] args) throws IOException, ClassNotFoundException {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions("classpath:spring.xml");UserService userService = (UserService) beanFactory.getBean("userService", UserService.class);userService.queryUserInfo();}
}

这样并不是spring想要的,所以要想办法将创建工厂和读取xml文件合并,这样只要传入路径就简单多了

ApplicationContext

在实现了基本的文件配置bean对象后,我们需要考虑的是如何让用起来更简单,如果只是我们开发使用我们知道这个类,这个方法是做什么的,该放什么值,但是如果提供给未参加开发的人使用则是灾难,这样它就要去看底层究竟是干嘛的,如果是简化版的spring还好,那如果正真spring呢?这无疑直接令人想放弃这个框架的使用,所以我们要使用上下文来定义spring的运行.

ApplicationContext接口,继承了三个BeanFactory的子接口,同时令它拥有更多功能.

ListableBeanFactory是负责beanfactory的bean查寻功能

HierarchicalBeanFactory负责bean的父类使用

ApplicationEventPublisher是负责推送事件(在spring监听器中会讲到)

public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, ApplicationEventPublisher {
}

这表示是一个上下文功能,其实真正的方法定义在后面继承该接口的ConfigurableApplicationContext接口中.

public interface ConfigurableApplicationContext extends ApplicationContext {void refresh() throws IOException, ClassNotFoundException;void registerShutdownHook();void close() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException;ConfigurableListableBeanFactory getBeanFactory();
}

接口定义了refresh方法,用来刷新spring的容器,而getBeanFactory负责返回一个beanfactory,这个ConfigurableListableBeanFactory是继承了ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory这三个接口.

ConfigurableListableBeanFactory接口:继承AutowireCapableBeanFactory接口AutowireCapableBeanFactory接口又继承了beanfactory接口

configurableListableBeanFactory应用上下文环境可以通过bean的name或者clazz获取指定的bean
同时也能获取到BeanDefinition,并且包含一个实例化所有bean对象的方法preInstantiateSingleton()

public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {BeanDefinition getBeanDefinition(String beanName);void preInstantiateSingletons();
}

其中的ConfigurableBeanFactory继承了HierarchicalBeanFactory, SingletonBeanRegistry,这个类还定义了addBeanPostProcessor(添加后置处理器),addEmbeddedValueResolver(添加占位符解析器),还有一个解析器的方法.

这个接口就是负责用来管理spring的各个模块方法的,同时继承了SingletonRegistry可以注册bean对象的方法.

public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {String SCOPE_SINGLETON = "singleton";String SCOPE_PROTOTYPE = "prototype";void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);void addEmbeddedValueResolver(StringValueResolver var1);String resolveEmbeddedValue(String var1);
}

而AutowireCapableBeanFactory接口继承了BeanFactory用来扩展bean后置处理器的位置,定了了两个方法,这两个方法从applicationcontext中可以看到,在bean初始化的时候可以对bean进行扩展

public interface AutowireCapableBeanFactory extends BeanFactory {Object applyBeanPostProcessorsBeforeInitialization(Object var1, String var2);Object applyBeanPostProcessorsAfterInitialization(Object var1, String var2);
}

有了定义上下文的接口,那么就要有实现类,spring用AbstractApplicationContext来实现了该接口,并且定义了spring的运行流程,同时将抽象方法继续丢给子类来实现功能.,并且在refresh将刷新spring的上下文,在刷新时就是bean的生命流程的开始,我们可以将所有创建bean的流程.方法定义到刷新方法中,将其组合成一个完整的bean创建过程,同时也可以在其中进行扩展.

AbstractApplicationContext

/*** 上下文抽象类* 定义了spring的上下文*/
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {//事件广播器在ioc容器的名字public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";//事件广播器对象private ApplicationEventMulticaster applicationEventMulticaster;@Overridepublic void refresh() throws IOException, ClassNotFoundException {//首先创建beanFactory,并加载BeanDefinitionrefreshBeanFactory();//拿到这个beanFactory对象ConfigurableListableBeanFactory beanFactory = getBeanFactory();// 添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContextbeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));//在实例化之前做实现的事情invokeBeanFactoryPostProcessors(beanFactory);//注册将要在实例化之前做的事情registerBeanPostProcessors(beanFactory);//提前实例化所有单例对象beanFactory.preInstantiateSingletons();//  初始化事件广播器initApplicationEventMulticaster();//  注册事件监听器registerListeners();// 发布容器刷新完成事件finishRefresh();//注册销毁钩子registerShutdownHook();}//初始化广播器private void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();//获取一个简单的应用广播器,将这个广播器注册到ioc中this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);}//监听器注册private void registerListeners() {//拿到所有的监听器Collection<ApplicationListener> listeners = getBeanOfType(ApplicationListener.class).values();//将监听器注册到广播器中for (ApplicationListener<?> listener : listeners) {this.applicationEventMulticaster.addApplicationListener(listener);}}private void finishRefresh() {//将该application进行包装到事件上下文刷新器中publishEvent(new ContextRefreshedEvent(this));}//广播器发布该指定事件public void publishEvent(Object event) {Assert.assertNotNull("Event must not be null", event);Object applicationEvent;if (event instanceof ApplicationEvent) {this.applicationEventMulticaster.multicastEvent((ApplicationEvent) event);}}protected abstract void refreshBeanFactory() throws IOException, ClassNotFoundException;//    protected abstract ConfigurableListableBeanFactory getBeanFactory();//注册一个销毁钩子public void registerShutdownHook() {Runtime.getRuntime().addShutdownHook(new Thread(this::close));}@Overridepublic void close() {try {//发布容器关闭事件publishEvent(new ContextClosedEvent(this));((DefaultListableBeanFactory) getBeanFactory()).destroySingletons();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}//在进行BeanFactoryPostProcessor查询的时候同时实例化对象进行使用private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) throws IOException {Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = getBeanOfType(BeanFactoryPostProcessor.class);for (BeanFactoryPostProcessor br : beanFactoryPostProcessorMap.values()) {br.postProcessBeanFactory(beanFactory);}}//在进行BeanPostProcessor查询的时候同时实例化对象,并且将对象装入到容器中private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {Map<String, BeanPostProcessor> beanPostProcessorMap = getBeanOfType(BeanPostProcessor.class);for (BeanPostProcessor br : beanPostProcessorMap.values()) {//将找到的beanpostprocessor bean对象注册到容器中,等待使用beanFactory.addBeanPostProcessor(br);}}@Overridepublic Object getBean(String name) {return getBeanFactory().getBean(name);}@Overridepublic Object getBean(String name, Object... args) {return getBeanFactory().getBean(name, args);}@Overridepublic <T> Map<String, T> getBeanOfType(Class<T> type) {return getBeanFactory().getBeanOfType(type);}@Overridepublic String[] getBeanDefinitionNames() {return getBeanFactory().getBeanDefinitionNames();}@Overridepublic String[] getBeanNamesForType(Class<?> type) {return getBeanFactory().getBeanNamesForType(type);}
}

1.bean对象的创建流程

创建beanfactory对象,原来我们使用ioc必须要手工的去创建DefaultListableBeanFactory,然后操作beanfactory,所以我们现在需要spring自己来操作,而我们只需要传入读取xml文件的路径就行了,spring就会自动的去创建beanfactory对象,然后注册BeanDefinition,创建bean,返回bean.

定义一个抽象类,实现传下来的抽象方法用来刷新beanfactory对象,在刷新beanfactory的同时还读取xml文件将bean信息注册到spring中等待使用,并且可以返回该beanfactory.

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {private DefaultListableBeanFactory beanFactory;@Overrideprotected void refreshBeanFactory() throws IOException, ClassNotFoundException {DefaultListableBeanFactory beanFactory = createBeanFactory();loadBeanDefinitions(beanFactory);this.beanFactory = beanFactory;}private DefaultListableBeanFactory createBeanFactory() {return new DefaultListableBeanFactory();}@Overridepublic ConfigurableListableBeanFactory getBeanFactory() {return this.beanFactory;}//读取xml配置文件,加载bean定义信息protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException, ClassNotFoundException;}

再同时留下一个抽象方法给子类实现,用来调用XmlBeanDefinitionReader读取xml配置文件,进行bean注册.

再由一个抽象方法来实现读取xml配置文件的方法,AbstractXmlApplicationContext抽象类用来读取xml配置文件,这样就不用我们手动创建读取类然后去读取,并且该抽象类也留下了一个抽象方法,留下一个传入xml文件路径的方法.

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext {public AbstractXmlApplicationContext() {}@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException, ClassNotFoundException {XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(beanFactory, this);String[] configLocations = getConfigLocation();if (configLocations != null) {xmlReader.loadBeanDefinitions(configLocations);}}protected abstract String[] getConfigLocation();}

最后就是子类ClassPathXmlApplicationContext实现该方法,跟我们平时用spring一样,传入一个xml文件路径就可以将xml配置加载到spring中进行bean注册了.

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {private String[] configLocations;public ClassPathXmlApplicationContext() {}public ClassPathXmlApplicationContext(String configLocation) throws IOException, ClassNotFoundException {this(new String[]{configLocation});}public ClassPathXmlApplicationContext(String[] configLocations) throws IOException, ClassNotFoundException {this.configLocations = configLocations;refresh();}@Overrideprotected String[] getConfigLocation() {return configLocations;}}

这也是设计模板的理念,自上而下的实现每个功能,从一开始的创建bean对象,读取xml文件,传入路径每一步都是一个抽象类实现该实现的功能,值得学习.

最终,我们可以返回一个beanfactory对象,又注册了BeanDefinition.

后面我们只需要通过实现ConfigurableListableBeanFactory接口的 void preInstantiateSingletons()方法,循环实例化所有单例bean对象就行了.

2.bean的扩展接口

在创建bean的时候,我们现在通过上下文实现bean的创建了,那我们现在可以在bean创建的流程中添加我们自己想添加的逻辑,我们也可以在BeanDefinition注册后对bean信息做出修改,也可以在bean实例化后,在进行初始化的时候进行自己的方法处理bean对象或操作.

首先是BeanFactoryPostProcessor接口,该接口负责定义一个方法,在BeanDefinition注册后对bean信息进行修改方法,实现了这个接口后可以自定义方法对bean的信息进行修改.

public interface BeanFactoryPostProcessor {void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws IOException;
}

在AbstractApplicationContext的refresh中可以看到,该方法定义在拿到BeanDefinition后,对BeanFactoryPostProcessor的实现类进行了处理

invokeBeanFactoryPostProcessors(beanFactory)

从ioc容器中拿到所有实现该接口的实现类,并且执行postProcessBeanFactory方法.

  //在进行BeanFactoryPostProcessor查询的时候同时实例化对象进行使用private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) throws IOException {Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = getBeanOfType(BeanFactoryPostProcessor.class);for (BeanFactoryPostProcessor br : beanFactoryPostProcessorMap.values()) {br.postProcessBeanFactory(beanFactory);}}

既然对BeanDefinition可以进行修改,那也就可以对bean进行修改,我们通过定义BeanPostProcessor接口对bean进行初始化的前后进行前置后置操作.

BeanPostProcessor接口定义了两个方法,分别为前置执行的方法和后置执行的方法.

public interface BeanPostProcessor {Object postProcessBeforeInitialization(Object bean, String beanName);Object postProcessAfterInitialization(Object bean, String beanName);
}

但是该方法只在bean初始化方法中才运行,我们需要现在上下文中先将其注册到spring中,以便在执行bean初始化的时候使用,同时在AbstractBeanFactory中定义容器用来存储实现类对象,待使用时取出.

首先是注册,将在spring初始化上下文的时候将该类注册到容器中,通过getType方法拿到该接口的所有实现类对象,然后注册.

 //在进行BeanPostProcessor查询的时候同时实例化对象,并且将对象装入到容器中private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {Map<String, BeanPostProcessor> beanPostProcessorMap = getBeanOfType(BeanPostProcessor.class);for (BeanPostProcessor br : beanPostProcessorMap.values()) {//将找到的beanpostprocessor bean对象注册到容器中,等待使用beanFactory.addBeanPostProcessor(br);}}

其二就是在实例化对象后,执行初始化方法的时候进行使用.

bean对象创建(createInstantiation)

属性填充(applyPropertyValues)

bean初始化(initializBean)

 private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {//进行功能回调Object wrappedBean = invokeAwareMethods(beanName, bean);//实例化前wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);//自定义的bean初始化方法try {invokeInitMethods(beanName, wrappedBean, beanDefinition);} catch (Exception e) {throw new BeanException("Invocation of init method of bean[" + beanName + "] failed", e);}//实例化后wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);return wrappedBean;}

具体方法的实现,分别调用前置和后置的实现方法:

 //前置消息处理public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) {Object result = existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) {Object current = processor.postProcessBeforeInitialization(existingBean, beanName);if (current == null)return result;result = current;}return result;}//初始化方法private void invokeInitMethods(String beanName, Object wrappedBean, BeanDefinition beanDefinition) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {if (wrappedBean instanceof InitializingBean)((InitializingBean) wrappedBean).afterPropertiesSet();//拿到初始化方法的方法名String initMethodName = beanDefinition.getInitMethodName();//如果没有则不执行if (StrUtil.isNotEmpty(initMethodName)) {if (initMethodName == "") {throw new BeanException("Could not find an init method name'" + initMethodName + "'no bean with name'" + beanName + "'");}Method method = beanDefinition.getBeanClass().getMethod(initMethodName);method.invoke(wrappedBean);}}@Overridepublic Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) {Object result = existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) {Object current = processor.postProcessAfterInitialization(existingBean, beanName);if (current == null)return result;result = current;}return result;}

这样就可以在bean进行初始化的时候使用bean进行操作,比如我们创建好了一个读取文件的对象,在初始化时候就进行文件读取,前置的加载文件信息.

在其中还有一个初始化方法(invokemethod),接上面话题,我加载了文件信息后要进行一些处理,比如读取文件然后将其注册到ioc中等等,这些就要在我们初始化方法中进行处理.

定义一个初始化接口,用来标记初始化实现类,同时定义一个初始化方法.

public interface InitializingBean {void afterPropertiesSet();
}

在上面也可以看到,初始化中会验证该bean对象是否实现了初始化接口,如果实现了则直接转型后运行初始化方法.

当然,还有一种方法实现初始化方法,我们可以自己在类中定义一个初始化方法,然后在xml配置文件中声明这个初始化方法名称,这样在xml读取的时候该初始方法会被注册到BeanDefinition中,然后进行使用.

<bean id="123" class="xxx" init-method="初始化方法名"/>

既然有初始化方法,就有销毁方法,通常我们可能在spring关闭的时候会进行一些操作,比如数据库关闭连接等等.

而spring有自己的默认销毁方法,添加了一个ShutdownHook来运行销毁方法和spring的功能关闭.

public void registerShutdownHook() {Runtime.getRuntime().addShutdownHook(new Thread(this::close));}

将定义的close()方法注册到钩子当中,如果spring关闭,则执行调用其他bean对象的销毁方法

 @Overridepublic void close() {try {//发布容器关闭事件publishEvent(new ContextClosedEvent(this));((DefaultListableBeanFactory) getBeanFactory()).destroySingletons();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}

定义在DefaultSingletonBeanRegister中的销毁方法将被调用.

 //销毁所有数据public void destroySingletons() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {Collection<DisposableBean> values = disposableBeanMap.values();//先执行所有销毁方法for (DisposableBean disposableBean : values) {((DisposableBeanAdapter) disposableBean).destroy();}singletonObjects.clear();disposableBeanMap.clear();}

而实现了该销毁方法的对象将执行实现的销毁方法.

定义一个销毁接口,其中定义了一个销毁方法

public interface DisposableBean {void destroy() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
}

定义一个包装接口的适配器,通过该类来决定使用哪种形式来销毁bean.

/*** 销毁方法适配器**/
public class DisposableBeanAdapter implements DisposableBean {private Object bean;private String beanName;private String destroyMethodName;public DisposableBeanAdapter(Object bean, String beanName, BeanDefinition beanDefinition) {this.bean = bean;this.beanName = beanName;this.destroyMethodName = beanDefinition.getDestroyMethodName();}@Overridepublic void destroy() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {if (bean instanceof DisposableBean)((DisposableBean) bean).destroy();if (StrUtil.isNotEmpty(this.destroyMethodName) && !(bean instanceof DisposableBean) && this.destroyMethodName.startsWith("destroy")) {Method method = bean.getClass().getMethod(destroyMethodName);if (method == null) {throw new BeanException("Couldn't find a destroy method name'" + destroyMethodName + "' on bean with name '" + beanName + "'");}method.invoke(bean);}}
}

销毁方法也可以分成接口实现和xml配置实现.

public interface DisposableBean {void destroy() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
}

该接口提供一个销毁方法来实现bean的销毁.

而注册销毁方法则是在bean创建的时候进行注册的,将销毁方法用销毁适配器进行打包后存入容器中.

 //将使用销毁方法的bean注册到容器中protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {//非Singleton类型的bean不执行销毁方法if (!beanDefinition.isSingleton())return;if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));}}

3.回调接口

在使用spring中,有时候我们扩展spring的功能也会用到一些spring本身的方法或者功能,而如果没有对应的对象则不能进行操作,比如我想进行bean的注册,我需要一个beanfactory来使用它的方法进行注册,但是我们的扩展功能有的并不在上下文中,所以无法使用spring提供的功能,这时候就需要回调接口来实现,它会将applicationcontext返回给需要功能的类进行使用.

定义回调接口Aware,该接口中没有任何方法,实际上它像是一个标记,这样就可以被spring所感知.

创建一个applicationcontext回调接口,其中定义了一个set方法,用来对applicationcontext进行赋值.

public interface ApplicationContextAware extends Aware {//实现此接口,既能感知到所属的 ApplicationContextvoid setApplicationContext(ApplicationContext applicationContext);
}

我们可以用实现类实现该接口获得applicaitoncontext.

当然,我们在spring中也要提供回调的功能,我们可以通过创建回调类继承BeanPostProcessor接口,同时在注册回调方法时放入当前的上下文,然后在bean初始化前将进行applicationcontext功能回调.

public class ApplicationContextAwareProcessor implements BeanPostProcessor {private final ConfigurableApplicationContext applicationContext;public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {this.applicationContext = applicationContext;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {if (bean instanceof ApplicationContextAware)((ApplicationContextAware) bean).setApplicationContext(applicationContext);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {return null;}}

该回调接方法将在上下文中进行注册,当bean被创建后在前置方法中会执行该方法,用来赋予bean对象使用spring功能的对象(权限).

同时在进行前置方法前就进行实现功能的回调.

//将是是实现了ApplicationContextAwareProcessor 接口的bean对象将回调的对应功能赋给bean对象private Object invokeAwareMethods(String beanName, Object bean) {if (bean instanceof Aware) {if (bean instanceof BeanFactoryAware) {((BeanFactoryAware) bean).setBeanFactory(this);}if (bean instanceof BeanClassLoaderAware) {((BeanClassLoaderAware) bean).setBeanClassLoader(bean.getClass().getClassLoader());}if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}}return bean;}

4.监听与事件

在spring中也存在着事件,同时也有监听器,而这种其实是观察者模式,什么是观察者模式?

当一个事件发布者发布事件更新的时候,其他相关该事件的监听器会收到,并且做出反应.

首先需要一个事件抽象类(ApplicationEvent)和一个监听接口(ApplicationListener).

事件抽象类:该类继承了JDK的EventObject表示是一个事件类.

其中source是事件的源数据,也就是事件本身的事务.

而我们自定义的事务要实现该抽象类.

public abstract class ApplicationEvent extends EventObject {/*** Constructs a prototypical Event.** @param source the object on which the Event initially occurred* @throws IllegalArgumentException if source is null*/public ApplicationEvent(Object source) {super(source);}
}

同时spring的上下文中也有spring默认的上下文事件

/*** ApplicationContext容器事件**/
public class ApplicationContextEvent extends ApplicationEvent {/*** Constructs a prototypical Event.** @param source the object on which the Event initially occurred* @throws IllegalArgumentException if source is null*/public ApplicationContextEvent(Object source) {super(source);}public final ApplicationContext getApplicationContext() {return (ApplicationContext) getSource();}
}

该接口继承了事件类,同时定义了一个返回上下文事件源的方法。

而spring上下文中还有其他大大小小的事件,比如刷新上下文发布,spring关闭发布等等,这里列出两个例子/

spring关闭动作,用户可以继承该类实现自定义spring关闭发布事件

/*** 关闭动作*/
public class ContextClosedEvent extends ApplicationContextEvent {/*** Constructs a prototypical Event.** @param source the object on which the Event initially occurred* @throws IllegalArgumentException if source is null*/public ContextClosedEvent(Object source) {super(source);System.out.println("运行完毕,Spring关闭!");}
}

spring上下文刷新发布事件,实现该类可以自定义刷新发布事件

/*** 监听刷新**/
public class ContextRefreshedEvent extends ApplicationContextEvent {/*** Constructs a prototypical Event.** @param source the object on which the Event initially occurred* @throws IllegalArgumentException if source is null*/public ContextRefreshedEvent(Object source) {super(source);System.out.println("Spring初始化成功!");}
}

而spring也有自己实现的这些事件,同时会注册到spring中。

spring自己的刷新事件

 private void finishRefresh() {//将该application进行包装到事件上下文刷新器中publishEvent(new ContextRefreshedEvent(this));}

监听接口:该接口泛型中要存放一个继承了事件抽象类的事件类,表示该监听器监听该事件!!

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);
}

同时监听接口中定义了监听方法,当监听事件发布时运行该监听方法。

现在我们有了事务和监听器,但是事务需要发布出去,同时也要添加监听器,我们需要定义一个事务管理器,用来管理监听器.

定义ApplicationEventMulticaster接口,其中提供添加/移除监听器的方法,还有广播事件的广播器.

public interface ApplicationEventMulticaster {//添加监听器void addApplicationListener(ApplicationListener<?> listener);//移除监听器void removeApplicationListener(ApplicationListener<?> listener);//广播器 multicastEvent 最终推送时间消息也会经过这个接口方法来处理谁该接收事件。void multicastEvent(ApplicationEvent event);
}

创建一个实现类,负责对所有相应的监听器进行广播.

AbstractApplicationEventMulticaster:实现了ApplicationEventMulticaster, BeanFactoryAware ,其中通过广播接口定义了增/删监听器的具体实现,同时通过BeanFactoryAware接口回调beanfactory给属性赋值.

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {//存放(Listener)监听器的容器public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();private ConfigurableBeanFactory beanFactory;public AbstractApplicationEventMulticaster() {}public void setBeanFactory(BeanFactory beanFactory) {if (!(beanFactory instanceof ConfigurableBeanFactory)) {throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);} else {this.beanFactory = (ConfigurableBeanFactory) beanFactory;}}@Overridepublic void addApplicationListener(ApplicationListener<?> listener) {applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);}@Overridepublic void removeApplicationListener(ApplicationListener<?> listener) {applicationListeners.remove(listener);}//通过事件,返回所有该事件的的监听器protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {LinkedList<ApplicationListener> allListeners = new LinkedList<>();//将对应该事件的监听器放入容器,然后返回所有与该事件相关的容器for (ApplicationListener<ApplicationEvent> applicationListener : applicationListeners) {if (supportsEvent(applicationListener, event)) {allListeners.add(applicationListener);}}return allListeners;}//监听器是否对事件感兴趣protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {//拿到该监听器的类类型Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();/*** 如果是通过SimpleInstantiationStrategy实现的对象则直接返回该监听器的class对象* 如果是通过CGlib代理的对象,则返回代理类的父类(也还是监听器的class对象)*///判断如何返回这个监听器类型Class<?> targetClass = ClassUtil.isSimlpeInstantiationStrategy(listenerClass) ? listenerClass : listenerClass.getSuperclass();//返回表示当前对象所表示的类或接口直接实现的接口类型//拿到当前监听器的接口类型Type genericInterface = targetClass.getGenericInterfaces()[0];/*** 例如,通过targetClass.getGenericInterfaces()拿到该目标类直接实现的接口类型* 然后通过拿到的接口类型来判断该接口的参数化类型* 如 拿到的是Listener<? extends ApplicationEvent>*     那么通过((ParameterizedType) genericInterface).getActualTypeArguments()[0]可以拿到一个参数化类型,就是<xxx>里面的参数类型**///通过接口类型拿到接口的参数化类型(该接口的实现类类型,也是ApplicationEvent的类或者其子类)Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];//通过拿到的参数类型返回类型的全限定类名String className = actualTypeArgument.getTypeName();//通过拿到的事件类名,创建事件对象Class<?> eventClassName;try {eventClassName = Class.forName(className);} catch (ClassNotFoundException e) {throw new BeanException("wrong event class name: " + className);}// 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所表示的类或接口是否相同,或是否是其超类或超接口。// isAssignableFrom 是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是 Object。//判断传入的事件,和该事件监听器关注的事件是否一致/*** 也就是将传入的ApplicationEvent类型的类是这个实现了该监听器的类的类或者其子类,则该监听器就是负责进行该事件监听的监听器*/return eventClassName.isAssignableFrom(event.getClass());}}

该抽象实现类不仅实现了两个增删的方法,同时还提供了判断监听器是否监听该事件的方法,和返回所有监听器的方法。而将广播事件留给了子类去实现。

默认实现的事件管理器

SimpleApplicationEventMulticaster:该类继承了事件管理器抽象类,同时实现了广播器的方法。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {//定义一个线程池控制发布事件private Executor taskExecutor;public SimpleApplicationEventMulticaster() {}public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {this.setBeanFactory(beanFactory);}public void setTaskExecutor(Executor taskExecutor) {this.taskExecutor = taskExecutor;}@Overridepublic void multicastEvent(ApplicationEvent event) {//获取当前线程池Executor executor = getTaskExecutor();//获取所有监听该事件的监听器的迭代器Iterator var5 = this.getApplicationListeners(event).iterator();//循环读取监听器while (var5.hasNext()) {//拿到监听器ApplicationListener<?> listenter = (ApplicationListener) var5.next();/*** 如果当前线程池不为空,则开启一个新的线程运行该对象,否则使用当前线程运行*/if (executor != null) {executor.execute(() -> {invokeListener(listenter, event);});} else {this.invokeListener(listenter, event);}}}//调用监听器运行方法protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {this.doInvokeListener(listener, event);}//执行监听器private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {listener.onApplicationEvent(event);}protected Executor getTaskExecutor() {return this.taskExecutor;}}

在这个默认实现的管理器中,我们只需要调用广播器方法,同时放入要被广播的事件就可以将事件广播到对应的监听器,并且执行对应的监听方法。

而如果我们要广播一个事件到监听器,则必须要将该事件发布(推)出来。所以我们还需要定义另一个接口来实现事件的发布

ApplicationEventPublisher:事件发布接口,该接口中定义了发布事件的方法。

/*** ApplicationEventPublisher 是整个一个事件的发布接口,所有的事件都需要从这个接口发布出去。**/
public interface ApplicationEventPublisher {//默认实现的发布方法default void publishEvent(ApplicationEvent event) {this.publishEvent((Object) event);}void publishEvent(Object event);
}

其实在一开始的Application那边就已经说过这个接口了,因为spring上下文也需要发布事件让外面知道spring在干嘛,比如spring启动了,spring关闭了。所以Application需要继承这个接口来实现事件的发布.

在AbstractApplicationContext中有其实现方法

 //广播器发布该指定事件public void publishEvent(Object event) {Assert.assertNotNull("Event must not be null", event);Object applicationEvent;if (event instanceof ApplicationEvent) {this.applicationEventMulticaster.multicastEvent((ApplicationEvent) event);}}

如果该对象实现了事件接口,那么该对象就是一个事件,而运行发布方法的时候就会将该对象推到广播器中,让监听器去实现功能。

有了广播器和事件发布器,那么我们就可以完整的运行观察者模式了,但是广播器没有注册,而事件发布器已经在AbstractApplicationContext中实现了,所以我们需要首先定义一个广播器对象,然后注册一个广播器

在初始化的时候,创建一个默认实现的广播器对象,同时将它存入到ioc容器中,也方便后续使用

private ApplicationEventMulticaster applicationEventMulticaster//初始化广播器private void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();//获取一个简单的应用广播器,将这个广播器注册到ioc中this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);}

这样我们就拥有了一个广播器可以用来对里面的监听器发布事件更新的消息了,但是现在里面没有监听器,所以我们要注册监听器。

最后就是注册监听器了,在spring上下文中定义了注册监听器的方法。

 //监听器注册private void registerListeners() {//拿到所有的监听器Collection<ApplicationListener> listeners = getBeanOfType(ApplicationListener.class).values();//将监听器注册到广播器中for (ApplicationListener<?> listener : listeners) {this.applicationEventMulticaster.addApplicationListener(listener);}}

实例化所有继承了监听器的对象,同时注册到管理器当中.

5.FactoryBean

spring中的对象分为两种,一种是单例对象,还有一种是原型对象。

单例好理解,不管获取多少次,都只返回ioc中该bean对象,而原型对象则是生产对象的bean对象,它每次返回的对象都会新创建一个对象返回(地址不同)。

如果需要创建原型对象,则需要创建bean工厂,也就是FactoryBean,该接口是一个泛型接口,泛型就是要返回的对象类型,其中有一个getObject的方法,可以返回一个对象

/*** FactoryBean 中需要提供 3 个方法,获取对象、对象类型,以及是否是单例对象,* 如果是单例对象依然会被放到内存中。*/
public interface FactoryBean<T> {T getObject() throws Exception;Class<?> getObjectType();boolean isSingleton();
}

如果要创建原型类只要实现这个接口就行了.

那么有了FactoryBean接口,那就需要有容器存放,不肯能将原型类型返回的对象放入到单例容器中,也不可能用单例方法去注册,所以要创建一个新的注册和存放方式,用来注册 FactoryBean.

FactoryBeanRegistrySupport:继承了默认实现的单例注册类,同时自身有存放单例FactoryBean的容器,并且提供了返回生成对象的方法,如果返回对象是单例的则放入FactoryBean的容器中进行存储,方便下次调用获取.

/*** FactoryBeanRegistrySupport 类主要处理的就是关于 FactoryBean 此类对象的注册* 操作,之所以放到这样一个单独的类里,就是希望做到不同领域模块下只负责各自* 需要完成的功能,避免因为扩展导致类膨胀到难以维护。* 同样这里也定义了缓存操作 factoryBeanObjectCache,用于存放单例类型的对象,* 避免重复创建。在日常使用用,基本也都是创建的单例对象* doGetObjectFromFactoryBean 是具体的获取 FactoryBean#getObject() 方法,因为既有缓存的处理也有对象的获取,所以额外提供了 getObjectFromFactoryBean进行逻辑包装,* 这部分的操作方式是不和你日常做的业务逻辑开发非常相似。从Redis 取数据,如果为空就从数据库获取并写入 Redis**/
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap();protected Object getCachedObjectForFactoryBean(String beanName) {return this.factoryBeanObjectCache.get(beanName);}protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) {//判断是否是单例对象,不是单例对象则不存储到容器中if (factory.isSingleton()) {Object object = this.factoryBeanObjectCache.get(beanName);/*** 这个位置就将FactoryBean给替换出来了* 取而代之的是getObject方法返回的对象*/if (object == null) {object = doGetObjectFromFactoryBean(factory, beanName);this.factoryBeanObjectCache.put(beanName, object);}return object;} else {return doGetObjectFromFactoryBean(factory, beanName);}}private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName) {try {return factory.getObject();} catch (Exception e) {throw new BeanException("FactoryBean threw exception on object[" + beanName + "] creation", e);}}}

而在AbstractBeanFactory中也做了相应的改动,继承了FactoryBeanRegistrySupport,以前是直接返回bean对象的,现在则进行判断,该对象是否属于单例对象,再进行下一步操作,同时在返回对象的时候也进行判断,如果该bean实现了FactoryBean 接口则返回getObject对象,其实在spring中,如果想返回FactoryBean对象只要在类名前面加上一个&字符就行了,这里不做演示.

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {//bean对象的后置处理器容器private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();//存放占位字符解析器List<StringValueResolver> embeddedValueResolvers = new LinkedList<>();@Overridepublic Object getBean(String name) {return getBean(name, null);}public Object getBean(String name, Object... args) {return doGetBean(name, args);}protected <T> T doGetBean(final String name, final Object[] args) {Object sharedInstance = getSingleton(name);if (sharedInstance != null) {// 如果是 FactoryBean,则需要调用 FactoryBean#getObjectreturn (T) getObjectForBeanInstance(sharedInstance, name);}BeanDefinition beanDefinition = getBeanDefinition(name);Object bean = createBean(name, beanDefinition, args);/*** 开天辟地的创造!!!* 首先如果我们进行bean实例化,所有的BeanFactory和FactoryBean都会被实例化* 并且如果是单例对象都先注册到SingletonObjects容器中* 但是!!!!* 在返回的时候做了小小的变化* 当如果是BeanFactory的时候实例化bean会被直接返回,而如果是FactoryBean的bean对象则要进行处理* 首先判断是否是单例的bean对象,如果是则注册到FactoryBeanCache容器中* 如果不是则直接获取后返回* 这个最后要返回的bean却变成了实现FactoryBean类下的getObject方法返回的一个对象作为bean对象返回!!!!nb*/return (T) getObjectForBeanInstance(bean, name);}private Object getObjectForBeanInstance(Object beanInstance, String beanName) {//如果不是FactoryBean则直接返回if (!(beanInstance instanceof FactoryBean))return beanInstance;Object object = getCachedObjectForFactoryBean(beanName);if (object == null) {FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;object = getObjectFromFactoryBean(factoryBean, beanName);}return object;}protected abstract BeanDefinition getBeanDefinition(String beanName);protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args);@Overridepublic void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {Assert.assertNotNull("beanPostProcessor must not be null", beanPostProcessor);this.beanPostProcessors.remove(beanPostProcessor);this.beanPostProcessors.add(beanPostProcessor);}public List<BeanPostProcessor> getBeanPostProcessors() {return this.beanPostProcessors;}@Overridepublic void addEmbeddedValueResolver(StringValueResolver var1) {this.embeddedValueResolvers.add(var1);}//解析该占位符@Overridepublic String resolveEmbeddedValue(String var1) {String value = null;for (StringValueResolver resolver : embeddedValueResolvers) {value = resolver.resolveStringValue(var1);}return value;}
}

这里的设计已经很好了,它在返回原型对象的时候会进行判断,如果该类是实现了FactoryBean则将原型bean替换出来,返回的其实是getObject方法,值得学习和借鉴.

到此spring的ioc的大致核心功能就结束了,下面是spring aop了.

SpringAop的功能创建

1.定义两个接口,分别为ClassFilter和MethodMatcher

ClassFilter(类过滤器)

/*** 定义类匹配类,用于切点找到给定的接口和目标类*专门用于检查目标类是否是一个合适的切入点*/
public interface ClassFilter {//判断该类是否需要aopboolean matches(Class<?> clazz);}

MethodMatcher(方法匹配器)


/*** 方法匹配器,找到表达式范围内匹配下的目标类和方法。* methodMatcher.matches(method, targetObj.getClass())* 实例主要是检查方法是否合适以便应用切面**/
public interface MethodMatcher {boolean matches(Method method, Class<?> targetClass);}

然后定义一个Pointcut(切入点),将上面两个接口定义到其中,用来返回两个过滤接口

/*** 切入点接口,定义用于获取 ClassFilter、MethodMatcher 的两个类,这两个接口获取都是切点表达式提供的内容* 切入点指的就是需要切入的类中需要切入的具体方法*/
public interface Pointcut {//类文件过滤ClassFilter getClassFilter();//方法匹配器MethodMatcher getMethodMather();
}

创建一个AspectJExpressionPointcut(切面)类,实现上面三个接口

这个类里面包含了PointcutExpression(切面表达式),PointcutPrimitive(原始切入方法),并且重载了两个过滤方法matches,用来判断对象是否在切面表达式内.

/*** 切面实现类* 该类表达切面,通过容器存储了切入的方式,然后通过切入点解析器读取传入的expression,拿到该切面的切入表达式* 同时也定义了方法用来比对目标类是否符合切入表达式来判断时候可以切入该目标**/
public class AspectJExpressionPointcut implements Pointcut, ClassFile, MethodMatcher {/*** PointcutPrimitive原始切入点,这个类其实是一个枚举类,可以枚举所有的切入方式* 这个容器存储的是切入的方法处理方式* 比如set切入还是get切入* 或者我们自定义的切入点表达式切入*/private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet();//默认实现是从自定义的execution位置切入static {SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);}//切入点表达式private final PointcutExpression pointcutExpression;public AspectJExpressionPointcut(String expression) {//切入点解析器,将定义的切入方式集合和当前类的加载器放入PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());//通过切入点解析器,对传入的切入点位置字符串进行解析,然后获得切入点表达式pointcutExpression = pointcutParser.parsePointcutExpression(expression);}//判断该类是否是切点@Overridepublic boolean matches(Class<?> clazz) {return pointcutExpression.couldMatchJoinPointsInType(clazz);}//判断该方法是否为切点@Overridepublic boolean matches(Method method, Class<?> targetClass) {return pointcutExpression.matchesMethodExecution(method).alwaysMatches() && matches(targetClass);}@Overridepublic ClassFile getClassFile() {return this;}@Overridepublic MethodMatcher getMethodMather() {return this;}
}

有了切面后,我们就可以判断一个对象是否在切面表达式里,是否需要aop功能了!!

第二步就是定义TargetObject(目标类)MethodInterceptor(方法拦截器)

TargetSource接口:该接口负责定义目标,并且有一个默认的DefaultTargetSource类

/*** 目标类资源**/
public interface TargetSource extends TargetClassAware {Class<?> getTargetClass();Object getTarget();
}

MethodInterceptor:该接口定义了invoke方法,传入的对象为一个MethodInvocation(方法请求)

/*** 方法拦截器* 该接口用于定义拦截后的方法实现* 实现该接口的类要重写invoke方法,用来切入业务逻辑方法**/
public interface MethodInterceptor extends Interceptor {Object invoke(MethodInvocation reflectiveMethodInvocation) throws Throwable;
}
//方法请求器,用来返回需要进行aop的方法
public interface MethodInvocation extends Invocation {Method getMethod();
}

其默认实现类为ReflectiveMethodInvocation,该类中等于反射目标对象的大致属性,其中就包括被代理方法,可以通过这个类拿到方法进行操作.

/***反射被代理对象的各个属性*/
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {//代理对象protected Object proxy;//目标对象protected final Object target;//目标参数protected Object[] arguments;//目标方法protected final Method method;//目标类类对象private Class<?> targetClass;protected MethodProxy proxyMethod;public ReflectiveMethodInvocation(Object target, Object[] arguments, Method method) {this.target = target;this.arguments = arguments;this.method = method;}public ReflectiveMethodInvocation(Object target, Object[] arguments, Method method, MethodProxy proxyMethod) {this.target = target;this.arguments = arguments;this.method = method;this.proxyMethod = proxyMethod;}public Object proceed() throws Throwable {return method.invoke(target, arguments);}public Object getProxy() {return proxy;}public void setProxy(Object proxy) {this.proxy = proxy;}public Object getTarget() {return target;}public Object[] getArguments() {return arguments;}public void setArguments(Object[] arguments) {this.arguments = arguments;}public Method getMethod() {return method;}public Class<?> getTargetClass() {return targetClass;}public void setTargetClass(Class<?> targetClass) {this.targetClass = targetClass;}@Overridepublic Object getThis() {return this.target;}
}

现在,我们可以找到需要aop的目标了,也拿到了目标的信息,并且可以把目标信息通过MethodInvocation传递到MethodInterceptor中对目标进行操作了,所以将这三个功能打包到一个类中,方便我们下一步的操作

这样我们就能知道是哪个目标(对象)需要进行aop功能操作,同时用MethodInterceptor代替原始方法执行拦截的功能.

这里可能说起来很拗口,举个例子如下:

public class aop{//皮一下,hhh
int main(void){//如果不是表达式内的对象我正常通过反射实现方法if(!matchs(bean.class)){method.invoke(obj,args);}else {//否则执行拦截器拦截该方法,在拦截器里对该方法进行aop操作getMethodInterceptor.invoke(MethodInvocation);}}}

将上面三个功能打包到AdisedSupport中,方便在使用时进行通知

/*** 通知依赖* AdvisedSupport,主要是用于把代理、拦截、匹配的各项属性包装到一个类中* 方便在 Proxy 实现类进行使用。这和你的业务开发中,包装入参是一个道理* TargetSource,是一个目标对象,在目标对象类中提供 Object 入参属性,以及获取目标类 TargetClass 信息。* MethodInterceptor,是一个具体拦截方法实现类,由用户自己实现 MethodInterceptor#invoke 方法,做具体的处理。* MethodMatcher,是一个匹配方法的操作,这个对象由 AspectJExpressionPointcut提供服务。**/
public class AdvisedSupport implements Adviced {//被代理目标对象private TargetSource targetSource;//代理方式,默认使用JDK代理private boolean proxyTargetClass = false;//方法拦截器private MethodInterceptor methodInterceptor;//方法匹配器,其实默认就是实现类AspectJExpressionPointcutprivate MethodMatcher methodMatcher;public TargetSource getTargetSource() {return targetSource;}public void setTargetSource(TargetSource targetSource) {this.targetSource = targetSource;}public MethodInterceptor getMethodInterceptor() {return methodInterceptor;}public void setMethodInterceptor(MethodInterceptor methodInterceptor) {this.methodInterceptor = methodInterceptor;}public MethodMatcher getMethodMatcher() {return methodMatcher;}public void setMethodMatcher(MethodMatcher methodMatcher) {this.methodMatcher = methodMatcher;}public void setProxyTargetClass(boolean proxyTargetClass) {this.proxyTargetClass = proxyTargetClass;}@Overridepublic boolean isProxyTargetClass() {return this.proxyTargetClass;}
}

第三步,如果要对方法进行aop操作,那我们就要拿到该方法,然后才能在方法前后做aop操作,所以我们将这类需要aop操作的对象就用Proxy来代理方法运行,当Proxy的方法拦截器拦截到被代理的方法后就可以进行aop操作.

当然,我们实现代理就要实现它的InvocationHandler(调用处理器),实现invoke方法,这个invoke方法就是代理类的实现方法,同时可以拿到proxy(代理对象),method(代理方法),args(传入参数),而我们要在创建代理后返回一个代理对象,这个对象才是实现了aop功能的bean对象,放入到ioc容器中使用

AopProxy接口定义了一个getProxy()方法,返回一个Object的代理对象.

而代理方法分为JDK代理和CGlib代理,分别实现如下:

JDK

/*** 基于 JDK 实现的代理类,需要实现接口 AopProxy、InvocationHandler,这样就可* 以把代理对象 getProxy 和反射调用方法 invoke 分开处理了。* getProxy 方法中的是代理一个对象的操作,需要提供入参 ClassLoader、AdvisedSupport、和当前这个类 this,因为这个类提供了 invoke 方法。* invoke 方法中主要处理匹配的方法后,使用用户自己提供的方法拦截实现,做反射调用 methodInterceptor.invoke 。* 这里还有一个 ReflectiveMethodInvocation,其他它就是一个入参的包装信息,提供了入参对象:目标对象、方法、入参。**/
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {//通知实现类private final AdvisedSupport advised;public JdkDynamicAopProxy(AdvisedSupport advised) {this.advised = advised;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//拿到方法匹配器,判断这个对象是否是连接点,如果运行拦截器,否则正常反射调用方法if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {//拿到方法拦截器MethodInterceptor methodInterceptor = advised.getMethodInterceptor();//运行拦截器的invoke方法,同时将目标参数打包传过去,方便使用return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), args, method));}//正常运行方法return method.invoke(advised.getTargetSource().getTarget(), args);}//返回一个代理的对象@Overridepublic Object getProxy() {return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getTargetSource().getTargetClass().getInterfaces(), this);}
}

Cglib

/*** CGlib代理* 基于 Cglib 使用 Enhancer 代理的类可以在运行期间为接口使用底层 ASM 字节* 码增强技术处理对象的代理对象生成,因此被代理类不需要实现任何接口。* 关于扩展进去的用户拦截方法,主要是在 Enhancer#setCallback 中处理,用户自己的新增的拦截处理。这里可以看到 DynamicAdvisedInterceptor#intercept 匹配方法后做了相应的反射操作**/
public class Cglib2AopProxy implements AopProxy {//增强依赖private final AdvisedSupport advised;public Cglib2AopProxy(AdvisedSupport advised) {this.advised = advised;}@Overridepublic Object getProxy() {//增强类Enhancer enhancer = new Enhancer();//设置需要被代理的类和类的接口,传入类对象enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());enhancer.setInterfaces(advised.getTargetSource().getTargetClass().getInterfaces());//代理类方法,将需要代理的aop方法实现类传入enhancer.setCallback(new DynamicAdvisedInterceptor(advised));//返回创建的代理对象return enhancer.create();}//静态内部类,实现了CGlib的方法拦截器,用来拦截方法private static class DynamicAdvisedInterceptor implements MethodInterceptor {private AdvisedSupport advised;public DynamicAdvisedInterceptor(AdvisedSupport advised) {this.advised = advised;}//被Cglib拦截的方法,其实也就是代理类要运行的方法public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//Cglib的方法调用CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), objects, method, methodProxy);//如果该方法是连接点则执行aop拦截器的方法if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {return advised.getMethodInterceptor().invoke(methodInvocation);}//如果不是连接点方法,则执行Cglib的代理方法return methodInvocation.proceed();}}private static class CglibMethodInvocation extends ReflectiveMethodInvocation {//初始化目标类的参数public CglibMethodInvocation(Object target, Object[] arguments, Method method, MethodProxy proxyMethod) {super(target, arguments, method, proxyMethod);}//继续实现代理类的方法@Overridepublic Object proceed() throws Throwable {return this.proxyMethod.invoke(this.target, this.arguments);}}
}

这样我们的aop代理对象就创建好了,同时我们需要在进行代理的时候判断使用哪个代理方式,所以创建一个ProxyFactory来选择创建代理对象.

/*** 代理工厂* 决定用哪个代理方式来代理对象**/
public class ProxyFactory {private AdvisedSupport advisedSupport;//拿到增强依赖包public ProxyFactory(AdvisedSupport advisedSupport) {this.advisedSupport = advisedSupport;}//返回创建的代理对象public Object getProxy() {return createAopProxy().getProxy();}private AopProxy createAopProxy() {//如果是代理对象则用Cglib代理,否则JDK代理if (advisedSupport.isProxyTargetClass()) {return new Cglib2AopProxy(advisedSupport);}return new JdkDynamicAopProxy(advisedSupport);}
}

至此,我们手工创建代理对象就完成了,如下:

public class aop{int main(void){//创建一个通知实现类AdvisedSupport sp = new AdvisedSupport();  //对通知实现类进行装配//设置目标对象sp.setTargetSource(new DefaultTargetSource(xxx));  //设置拦截方法sp.setMethodInterceptor(new UserImpMethodInterceptor());//设置类/方法匹配器sp.setMethodMatcher(new AspectJExpressionPointcut(expression));//将装配好的对象入到ProxyFactory中,拿到代理对象工厂ProxyFactory proxyFactory = new ProxyFactory(sp);//生成aop代理对象Object bean = proxyFactory.getProxy();}}

在进行代理对象方法调用的时候就会用方法匹配器判断该方法是否属于切点,如果是则执行用户自定义的拦截器方法,否则正常执行.

现在可以创建代理对象了,也可以判断是否进行aop操作,那么就要将手工的方式改写到spring框架中,融入bean的生命周期里,并且可以像spring那样进行前置通知(Before),后置通知(After),环绕通知(Around),异常通知(Throw).

首先要进行不同时间段的通知(增强),定义一个Advice接口,该接口返回该类或者子类

public interface Advice {Advice getAdvice();
}
BeforeAdvice(前置通知),还有子接口MethodBeforeAdvice
public interface BeforeAdvice extends Advice {
}/*** 前置通知(增强)* 在 Spring 框架中,Advice 都是通过方法拦截器 MethodInterceptor 实现的。环绕 Advice 类似一个拦截器的链路,Before Advice、After Advice等* 不过暂时我们需要那么多就只定义了一个 MethodBeforeAdvice 的接口定义**/
public interface MethodBeforeAdvice extends BeforeAdvice {void before(Method method, Object[] args, Object target) throws Throwable;
}

然后用户实现该接口,实现before该方法,定义前置的操作

public class UserServiceBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) {System.out.println("拦截方法:" + method.getName());}@Overridepublic Advice getAdvice() {return this;}
}

这里我们就完成了用户自定义的前置方法.

既然有了前置的方法,就要有前置的拦截器,在方法前方进行拦截并执行该前置方法,所以我们需要创建一个MethodBeforeAdviceInterceptor类来实现MethodInterceptor,而不是由用户去实现创建拦截器,这个拦截器默认在代理方法前置的执行前置方法.

/*** MethodBeforeAdviceInterceptor 实现了 MethodInterceptor 接口,在 invoke方法中调用 advice 中的 before 方法,传入对应的参数信息。* 而这个 advice.before 则是用于自己实现 MethodBeforeAdvice 接口后做的相应处* 理。其实可以看到具体的 MethodInterceptor 实现类,其实和我们之前做的测试是一样的,只不过现在交给了 Spring 来处理*/
public class MethodBeforeAdviceInterceptor implements MethodInterceptor {//前置增强方式private MethodBeforeAdvice advice;public MethodBeforeAdviceInterceptor() {}public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {this.advice = advice;}@Overridepublic Object invoke(MethodInvocation methodInvocation) throws Throwable {//在执行目标方法前执行增强this.advice.before(methodInvocation.getMethod(), methodInvocation.getArguments(), methodInvocation.getThis());return methodInvocation.proceed();}@Overridepublic Advice getAdvice() {return null;}
}

以上就是通知时间段的处理,我们可以在任何时间段进行aop操作,同时其他几个也可以类似这样的方法进行开发,就不一一演示了.

最后,需要将该aop功能融入到bean的生命周期中,可能实现起来比较费事,暂时提供一个解决思路

定义一个Advisor接口,该接口定义了一个getAdvice的方法,可以返回一个增强类

public interface Advisor {/*** 返回此切面的增强部分,一个增强部分可能是:* 一个接口* 一个前置增强* 一个后置增强* 一个异常增强* 等** @return 返回一个增强方式*/Advice getAdvice();
}

定义一个PointcutAdvisor继承该接口

/*** Advisor 承担了 Pointcut 和 Advice 的组合,Pointcut 用于获取 JoinPoint(连接点),而 Advice 决定了 JoinPoint 执行什么操作(执行的时间)。**/
public interface PointcutAdvisor extends Advisor {//返回当前访问者的切点Pointcut getPointcut();
}

创建一个AspectJExpressionPointcutAdvisor类,实现PointcutAdvisor接口,该类对切面和增强类做了封装

/*** 切面表达式和增强方法包装类* AspectJExpressionPointcutAdvisor 实现了 PointcutAdvisor 接口* 把切面pointcut、拦截(增强)方法 advice 和具体的拦截表达式包装在一起。* 这样就可以在 xml的配置中定义一个 pointcutAdvisor 切面拦截器了。**/
public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor {//切面private AspectJExpressionPointcut pointcut;//具体的增强方法private Advice advice;//表达式private String expression;public AspectJExpressionPointcutAdvisor() {}public AspectJExpressionPointcutAdvisor(String expression) {this.expression = expression;}@Overridepublic Advice getAdvice() {return advice;}public void setAdvice(Advice advice) {this.advice = advice;}//返回一个切面对象@Overridepublic Pointcut getPointcut() {if (this.pointcut == null) {this.pointcut = new AspectJExpressionPointcut(expression);}return this.pointcut;}
}

进行了包装后,我们可以通过这个类拿到切面和增强方法,就可以对代理对象的具体方法进行Before,After等等的位置切入了.

最后我们只需要将aop代理对象融入到spring的bean生命周期中,并且通过读取xml配置文件来配置代理对象和拦截器,这里只提供一个思路.

创建一个DefaultAdvisorAutoProxyCreator类,实现InstantiationAwareBeanPostProcessor和BeanFactory接口,其中InstantiationAwareBeanPostProcessor接口继承了BeanPostProcessor接口,这样就可以在bean实例化前后对bean进行操作了,其实跟FactoryBean的操作类似,我们可以在初始化后进行对目标bean的拦截,并且用代理对象替换它,也可以在初始化前对该对象进行拦截,具体的可以自己实现.

而拿到BeanFactory的回调接口可以方便我们拿到beanfactory对象,让我们可以从ioc容器中拿到我们所有的切面和增强类的封装类(AspectJExpressionPointcutAdvisor),这样我们就可以循环读取该类并且判断每个切面表达式是否包含该bean对象,如果包含则开始创建AdvisedSupport开始组装代理信息,通过拿到的bean.class对象反射获取需要的属性,放入到TargetSource中,组装完毕后放入到ProxyFactory中生成代理工厂,然后通过代理工厂返回一个代理对象,至此,我们的aop就完美的融入到了ioc中.

/*** 融入 Bean 生命周期的自动代理创建者* 这个 DefaultAdvisorAutoProxyCreator 类的主要核心实现在于* postProcessBeforeInstantiation 方法中,从通过 beanFactory.getBeanOfType 获取 AspectJExpressionPointcutAdvisor 开始。* 获取了 advisors 以后就可以遍历相应的 AspectJExpressionPointcutAdvisor 填充对* 应的属性信息,包括:目标对象、拦截方法、匹配器,之后返回代理对象即可。* 那么现在调用方获取到的这个 Bean 对象就是一个已经被切面注入的对象了,当调* 用方法的时候,则会被按需拦截,处理用户需要的信息。**/
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {private DefaultListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) {this.beanFactory = (DefaultListableBeanFactory) beanFactory;}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {//首先判断该bean是否是Advice或者Pointcut的实现类,如果是则直接返回if (isInfrastructureClass(beanClass)) return null;//如果是则拿到所有AspectJExpressionPointcutAdvisor,拿到所有的切面包装对象Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeanOfType(AspectJExpressionPointcutAdvisor.class).values();for (AspectJExpressionPointcutAdvisor advisor : advisors) {//拿到ClassFilter类过滤器对象ClassFile classFile = advisor.getPointcut().getClassFile();//判断这个切面包装类是否对应该bean对象,如果不是则返回if (!classFile.matches(beanClass)) continue;//创建一个整合依赖包AdvisedSupport advisedSupport = new AdvisedSupport();TargetSource targetSource = null;//通过反射创建一个目标对象try {targetSource = new DefaultTargetSource(beanClass.getDeclaredConstructor().newInstance());} catch (Exception e) {e.printStackTrace();}//组装代理信息advisedSupport.setTargetSource(targetSource);advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMather());advisedSupport.setProxyTargetClass(false);//用代理工厂决定用哪个方式进行代理return new ProxyFactory(advisedSupport);}//否则返回接口的默认为nullreturn InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) {return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);}public boolean isInfrastructureClass(Class<?> beanClass) {boolean retVal = Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);if (retVal) {System.out.println("该类实现Advice 或 Pointcut 或 Advisor: " + beanClass.getSimpleName());}return retVal;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}
}

Spring注解功能

在我们实现了spring的两大核心体系iocaop后,我们就可以去添加其他的辅助功能了,现在我们可以使用xml配置文件来配置spring了,但是我们还是觉得xml有些繁琐,至此,我们会按照spring同样具有的注解功能继续进行扩展,同时更加具体的了解bean的生命周期.

最基础的就是@Component@Scope,@Component可以让spring知道这是一个bean对象,以往通过xml需要配置id和class,而现在只需要在类上面加入该注解就可以让ioc自己去创建,同时@Scope可以指定该类是单例的还是原型的.

@Component

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {String value() default "";
}

@Scope

@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)public @interface Scope {String value() default "singleton";
}

现在我们有两个注解了,但是如果要ioc能去识别,我们需要将注解进行解析和扫描还要融入bean的生命周期.

首先我们需要先扫描,spring框架中通常在xml中基础配置

 <context:component-scan base-package="demo02.anno"/>

通过base-package来找到指定的包,并且扫描下面的class文件,将读取到的class文件上的注解进行解析,并且将有@Component注解的类注册到ioc中.

所以在XmlBeanDefinitionReader中我们需要将解析包扫描放到第一步,先进行注解类的注册,然后再进行xml配置的bean注册.

{       Document doc = XmlUtil.readXML(in);Element element = doc.getDocumentElement();NodeList nodeList = element.getChildNodes();//进行注解扫描器寻找,找到则进行注解注册for (int c = 0; c < nodeList.getLength(); c++) {if (!(nodeList.item(c) instanceof Element))continue;if (!("component-scan".equals(nodeList.item(c).getNodeName())))continue;Element componentScan = (Element) nodeList.item(c);String basePackage = componentScan.getAttribute("base-package");if (!StrUtil.isNotEmpty(basePackage)) {throw new BeanException("The value of base-package must not be null or empty");}scanPackage(basePackage);}//正常的xml配置读取...}//调用包扫描器,扫描包并且注册beanDefinitionprivate void scanPackage(String scanPath) {ClassPathBeanDefinitionScanner componentScan = new ClassPathBeanDefinitionScanner(this.getRegistry());componentScan.doScan(scanPath);}

这样我们就可以从xml配置中读取我们需要扫描的包名,剩下的就是对指定包扫描进行类解析,上面有个scanPackage方法,传入了一个包名,该方法中创建了一个扫描器和解析器,下面有讲到.

现在我们需要一个扫描器负责找到所有该包下面的类,并且将带有@Component注解的类注册到ioc中,ClassPathScanningCandidataComponentProvider这个类就是一个Component注解扫描器,该类中的findCandidateComponents方法就是负责去寻找符合要求的类,然后将他装入集合中准备注册.

/*** 对象扫描装配器**/
public class ClassPathScanningCandidateComponentProvider {//找到带有注解的类,并且注册其信息public Set<BeanDefinition> findCandidateComponents(String basePackage) {LinkedHashSet<BeanDefinition> beanDefinitionSet = new LinkedHashSet<>();Class[] classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class);for (Class<?> aClass : classes) {beanDefinitionSet.add(new BeanDefinition(aClass));}return beanDefinitionSet;}
}

有了扫描器,但是这只是单个的包扫描,我们或许会扫描多个不同的包,并且还需要注册到ioc中,并且还要进行@Scope注解的解析,所以还需要一个ClassPathBeanDefinitionScanner将扫描到的对象进行解析然后创建BeanDefinition注册到ioc中.

ClassPathBeanDefinitionScanner

/*** 注解类注册器* 将BeanDefinition注册到ioc中** @author 秦杨* @version 1.0*/
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {private BeanDefinitionRegistry registry;public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {this.registry = registry;}public void doScan(String... basePackages) {for (String basePackage : basePackages) {//通过find方法找到需要注册的bean类Set<BeanDefinition> components = this.findCandidateComponents(basePackage);//拿到beanDefinitionfor (BeanDefinition beanDefinition : components) {//解析作用域String beanScope = resolveBeanScope(beanDefinition);//如果不是空的则直接设置值if (StrUtil.isNotEmpty(beanScope)) {beanDefinition.setScope(beanScope);}registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);}}}//解析bean对象作用域private String resolveBeanScope(BeanDefinition beanDefinition) {Class<?> beanClass = beanDefinition.getBeanClass();Scope scope = beanClass.getAnnotation(Scope.class);return scope.value();}//创建beanNameprivate String determineBeanName(BeanDefinition beanDefinition) {Class<?> beanClass = beanDefinition.getBeanClass();Component component = beanClass.getAnnotation(Component.class);String beanName = component.value();//如果有名字就直接返回bean名字,没有则默认类名首字母小写if (beanName != null) {return beanName;}return StrUtil.lowerFirst(beanClass.getSimpleName());}}

这样我们的Component类就可以被注册到ioc中,成为bean对象,其他的像@Service,@Controller等也是同理,就不一一列举了.

Spring依赖注入

通过包扫描,我们可以对类进行注解创建bean对象,既然可以注解创建bean对象了,那么类中的属性也应该可以使用注解进行注入比如我们经常使用的@Value,@Autowire,一个是对基础属性值进行注入,而@Autowire是对引用类型进行注解.

@Value


@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Value {String value() default "";
}

@Autowire


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
public @interface Autowire {
}

这类的注解不像包扫描那样,这属于字段赋值,不应该在bean的实例化里,而应该在实例化后,属性填充之前,所以我们可以通过BeanPostProcessor在bean的生命周期中进行扩展,在注册bean的时候将我们扫描属性注解的功能注册,并且在bean实例化后进行扫描注入进去.

创建一个InstantiationAwareBeanPostProcessor接口继承BeanPostProcessor,因为我们要区分这两个接口的作用,并且该接口要有自己独特的方法.

我们将实现这个接口,并且重写它的postAfterInstantion方法,对bean进行属性注入.

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {return null;}default boolean postProcessAfterInstantiation(Object bean, String beanName) {return true;}default PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) {return pvs;}default Object getEarlyBeanReference(String beanName, Object bean) {return bean;};
}

创建一个AutowiredAnnotationBeanPostProcessor类,实现InstantiationAwareBeanPostProcessor和BeanFactoryAware,这样可以在该类中使用benafactory的功能了.

/*** 注解后置处理器* 该类实现了InstantiationAwareBeanPostProcessor,该类将在进行属性填充前对属性进行注解检查* 同时通过读到的注解信息进行相应的操作**/
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) {Class<?> beanClass = bean.getClass();Field[] fields = beanClass.getDeclaredFields();//读取每个字段上的Value注解for (Field field : fields) {Value attrValue = field.getAnnotation(Value.class);if (attrValue == null) continue;String value = attrValue.value();//拿到解析到了value值value = beanFactory.resolveEmbeddedValue(value);BeanUtil.setFieldValueForName(field.getName(), value, bean, new BeanDefinition(beanClass));}//读取每个字段上的Autowirefor (Field field : fields) {Autowire attrAutowire = field.getAnnotation(Autowire.class);if (attrAutowire == null) continue;Class<?> thisClassType = field.getType();Object dependentBean = beanFactory.getBean(StrUtil.lowerFirst(thisClassType.getSimpleName()));BeanUtil.setFieldValueForType(dependentBean, bean, new BeanDefinition(beanClass), thisClassType.getSimpleName());}return pvs;}
}

在该类的postProcessPropertyValues中,我们对bean字段的注解进行扫描匹配,然后对相应的值进行提去注入.

最后我们只需要将功能放入到AbstractAutowireCapableBeanFactory中,然后插入到bean的创建中就行了

在AbstractAutowireCapableBeanFactory中创建一个applyBeanPostProcessorsBeforeApplyingPropertyValues方法,该方法里负责进行属性注入

调用实现了InstantiationAwareBeanProcessor的类,使用其方法进行注入.

  //在填充bean属性之前对bean属性进行更新public void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {List<BeanPostProcessor> beanPostProcessors = getBeanPostProcessors();//读取所有后置处理器,筛选出继承InstantiationAwareBeanPostProcessor的类,将bean转换//虽然代理类也有,但是未重写postPropertyValues方法for (BeanPostProcessor processor : beanPostProcessors) {if (processor instanceof InstantiationAwareBeanPostProcessor) {PropertyValues values = ((InstantiationAwareBeanPostProcessor) processor).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName);if (values != null) {for (PropertyValue property : values.getPropertyValues()) {beanDefinition.getPropertyValues().addPropertyValue(property);}}}}}

融入bean生命周期

protected Object doCreateBean(String beanName, BeanDefinition beanDefinition, Object[] args) {Object bean = null;try {bean = createInstantiation(beanName, beanDefinition, args);//如果对象是单例对象则处理循环依赖,将实例化后的半成品对象放入工厂提前暴露出来if (beanDefinition.isSingleton()) {Object finalBean = bean;//将一个返回半成品bean对象的getEarlyBeanReference方法放入到SingletonBeanFactory中,然后在需要的时候可以返回一个半成品bean对象addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));}//实例化后进行判断boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);if (!continueWithPropertyPopulation) {return bean;}//在进行属性填充之前对该bean上的注解信息进行扫描,然后赋值applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);//进行xml属性填充applyPropertyValues(beanName, beanDefinition, bean);//进行执行Bean的前后的BeanPostProcessor处理和初始化方法的执行bean = initializeBean(beanName, bean, beanDefinition);

其实这里是有读取PropertyValue的一个动作,我们也可以把要注入的值放入到里面,然后在接下来的属性填充中匹配注入.

占位符处理

在配置xml中和@Value注解中,有时候会使用到占位符,通过外部配置文件比如properties文件等对程序中的bean进行字段的配置,所以现在要实现这个功能,其实还是围绕着bean的生命周期进行的,我们在进行注入时将用配置文件中的数据对占位符进行替换.

首先讲xml中的占位符替换,xml中替换其实只需要在注册了BeanDefinition后执行BeanFactoryPostProcessor的时候对PropertyValue进行修改就可以了,我们创建一个PropertyPlaceholderConfigurer用来处理占位符,同时该类要继承BeanFactoryPostProcessor

/*** 属性占位符配置器* 可以找到占位符,然后查找properties配置文件中的值进行替换* 依赖于 BeanFactoryPostProcessor 在 Bean 生命周期的属性,可以在 Bean 对象* 实例化之前,改变属性信息。所以这里通过实现 BeanFactoryPostProcessor 接* 口,完成对配置文件的加载以及摘取占位符中的在属性文件里的配置。* 这样就可以把提取到的配置信息放置到属性配置中了,** @author 秦杨* @version 1.0*/
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";//properties文件路径private String location;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws IOException {DefaultResourceLoader resourceLoader = new DefaultResourceLoader();Resource resource = resourceLoader.getResource(location);Properties properties = new Properties();//读取properties配置文件properties.load(resource.getInputStream());//将占位符解析器放入ioc中,方便后面使用StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties);beanFactory.addEmbeddedValueResolver(valueResolver);//拿到所有的BeanDefinition名字String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();for (String beanName : beanDefinitionNames) {BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);//拿到该bean的所有属性PropertyValues propertyValues = beanDefinition.getPropertyValues();//循环读取属性for (PropertyValue prValue : propertyValues.getPropertyValues()) {//如果值不是String类型的值,则直接返回if (!(prValue.getValue() instanceof String)) continue;String fieldName = prValue.getName();String fieldValue = (String) prValue.getValue();//拿到开头和结尾的下标int startIndex = fieldValue.indexOf(DEFAULT_PLACEHOLDER_PREFIX);int lastIndex = fieldValue.lastIndexOf(DEFAULT_PLACEHOLDER_SUFFIX);//如果没有${,或者}占位符号,或为空,或者符号不正确则直接返回if (startIndex <= -1 || lastIndex <= -1 || lastIndex <= startIndex) continue;//将占位符去掉,拿到值的名称String plaValue = fieldValue.substring(startIndex + 2, lastIndex);//在配置文件中拿值String value = properties.getProperty(plaValue);propertyValues.addPropertyValue(new PropertyValue(fieldName, value));}}}public void setLocation(String location) {this.location = location;}//一个内部类,用来解析占位符的private class PlaceholderResolvingStringValueResolver implements StringValueResolver {private Properties properties;public PlaceholderResolvingStringValueResolver(Properties properties) {this.properties = properties;}@Overridepublic String resolveStringValue(String strVal) {strVal = strVal.substring(strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX) + 2, strVal.lastIndexOf(DEFAULT_PLACEHOLDER_SUFFIX));return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties);}}protected String resolvePlaceholder(String placeholder, Properties props) {return props.getProperty(placeholder);}
}

而xml中的占位符就可以这样处理,但是@Value中的字段却不可以,因为这是在bean的实例化之前就进行了属性的修改,而注解要等到bean实例化后才能进行读取,不可能再本末倒置去使用这个扫描器,所以我们需要创建一个解析器用来解析注解的占位符.

需要创建一个StringValueResolver

public interface StringValueResolver {String resolveStringValue(String value);
}

该接口定义了一个解析的方法,同时返回解析到值的key.

同时创建一个实现该接口的解析类,也是上面说过的用来处理占位符的扫描器的内部类

  //一个内部类,用来解析占位符的private class PlaceholderResolvingStringValueResolver implements StringValueResolver {private Properties properties;public PlaceholderResolvingStringValueResolver(Properties properties) {this.properties = properties;}@Overridepublic String resolveStringValue(String strVal) {strVal = strVal.substring(strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX) + 2, strVal.lastIndexOf(DEFAULT_PLACEHOLDER_SUFFIX));return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties);}}protected String resolvePlaceholder(String placeholder, Properties props) {return props.getProperty(placeholder);}
}

这个占位符解析器会存放读取到的properties值,同时具有一个解析方法resolvePlaceholder(strVal, properties),可以解析占位符.

在扫描器加载好properties文件后会创建该解析器,并且存放到AbstractBeanFactory中,然后在实现一个解析方法,从容器中取出解析器对占位符进行解析

  //解析该占位符@Overridepublic String resolveEmbeddedValue(String var1) {String value = null;for (StringValueResolver resolver : embeddedValueResolvers) {value = resolver.resolveStringValue(var1);}return value;}

然后就拿到值进行注入.

至此spring的大致基础功能就学到这了,如果后面学到更多关于spring的将会继续更新.

Spring深入学习笔记相关推荐

  1. Spring MVC 学习笔记 对locale和theme的支持

    Spring MVC 学习笔记 对locale和theme的支持 Locale Spring MVC缺省使用AcceptHeaderLocaleResolver来根据request header中的 ...

  2. Spring Cloud 学习笔记(四)-Spring Cloud Hystrix

    Spring Cloud 学习笔记(四)-Spring Cloud Hystrix 由于前一阵子项目的原因,今天才继续弄上,今天想学习一下Hystrix组件 这个组件还挺抽象的,最开始我一直没太明白, ...

  3. Spring Boot学习笔记-实践建言

    2019独角兽企业重金招聘Python工程师标准>>> 本文延续<Spring Boot学习笔记-快速示例>,从开发指南中摘出一些实践经验可供参考.这也是笔者看到的眼前一 ...

  4. Spring.NET学习笔记10——方法的注入(基础篇) Level 200

    多数用户都会将容器中的大部分对象布署为singleton模式.当一个singleton对象需要和另一个singleton对象协作,或者一个非singleton对象需要和另一个非singleson对象协 ...

  5. Spring.NET学习笔记——前言

    Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序.它提供了很多方面的功能,比如依赖注入.面向方面编程(AOP).数据访问抽象及ASP.NET扩展等等.Sprin ...

  6. Spring Boot学习笔记-进阶(3)

    文章目录 Spring Boot学习笔记-进阶(3) 一.Spring Boot与缓存 二.Spring Boot与消息 三.Spring Boot与检索 四.Spring Boot与任务 异步任务 ...

  7. Spring Boot学习笔记-基础(2)

    Spring Boot学习笔记-基础(2) Spring Boot 优点: – 快速创建独立运行的Spring项目以及与主流框架集成 – 使用嵌入式的Servlet容器,应用无需打成WAR包 – st ...

  8. Spring Boot学习笔记(1)

    文章目录 Spring Boot学习笔记(1) Spring Boot 整合 JSP Spring Boot HTML Thymeleaf 常用语法 Spring Boot 数据校验 Spring B ...

  9. Spring MVC 学习笔记一 HelloWorld

    Spring MVC 学习笔记一 HelloWorld Spring MVC 的使用可以按照以下步骤进行(使用Eclipse): 加入JAR包 在web.xml中配置DispatcherServlet ...

  10. Spring Cloud 学习笔记(2 / 3)

    Spring Cloud 学习笔记(1 / 3) Spring Cloud 学习笔记(3 / 3) - - - 56_Hystrix之全局服务降级DefaultProperties 57_Hystri ...

最新文章

  1. 一个装作异步的代码段
  2. 标称变量(Categorical Features)或者分类变量(Categorical Features​​​​​​​)编码为数值变量(Continuous Features​​​​​​​)
  3. java贪吃蛇教程_用Java做的贪吃蛇,简单版......
  4. html指定表格行列书,js动态生成指定行数的表格
  5. 让服务器apache/iis/nginx支持.apk/ipa文件下载
  6. 《Internet 路由结构(第2版•修订版)》一7.5 常见问题
  7. 用busybox制作并配置根文件系统
  8. 番茄花园win11 32位专业版镜像v2021.08
  9. 我第一次面试自动化测试就被diss了,人生惨遭滑铁卢…
  10. pcm5102a解码芯片音质评测_鱼和熊掌兼得——一台可以换芯片的PCM1794解码评测(上)...
  11. C语言经典编程100题
  12. 通过计算机主机数来划分子网,计算机网络知识梳理(2)——子网掩码及网络划分...
  13. 某某行政处罚文书网 (请求参数: ciphertext)
  14. tensorflow打印模型图_tensorflow实现打印pb模型的所有节点
  15. mysql语句总结_mysql语句总结
  16. java实现shapefile文件的解析
  17. 网易云信消息抄送php,消息功能-服务端API文档-IM即时通讯-网易云信开发文档
  18. 音频质量评价体系那些事
  19. 在云服务器重装系统后vscode连不上服务器的解决
  20. 如何为餐饮商家打造代运营解决方案?

热门文章

  1. IDEA启动Tomcat报Unrecognized option: --add-opens=java.base/java.lang=ALL-UNNAMED
  2. 注册自定义URL协议
  3. Solidity笔记10:合约创建
  4. “就业”or“创业”,大学生毕业如何选择,校园市场是新出路?
  5. C语言基础:翁恺笔记
  6. Mac常备必用的软件-mac软件推荐
  7. chkdsk命令参数介绍
  8. 电场强度,电势以及PN结耗尽层宽度
  9. 酒店工作者学Java逆袭薪资从6k涨到14k
  10. 过拟和处理方法.md