From 47227f523af3d5afe145a81cf8736f7b664c047f Mon Sep 17 00:00:00 2001 From: xking Date: Mon, 6 Jan 2025 13:34:03 +0800 Subject: [PATCH] update --- java/spring.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 java/spring.md diff --git a/java/spring.md b/java/spring.md new file mode 100644 index 0000000..c376264 --- /dev/null +++ b/java/spring.md @@ -0,0 +1,94 @@ +# 通过 Spring 5 中 Supplier 来获取 Bean + + + +Spring5 中开始提供了 `Supplier`,可以通过接口回调获取到一个 Bean 的实例,这种方式显然性能更好一些。 + +如下: + +```java +AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); +GenericBeanDefinition definition = new GenericBeanDefinition(); +definition.setBeanClass(Book.class); +definition.setInstanceSupplier((Supplier) () -> { + Book book = new Book(); + book.setName("深入浅出 Spring Security"); + book.setAuthor("江南一点雨"); + return book; +}); +ctx.registerBeanDefinition("b1", definition); +ctx.refresh(); +Book b = ctx.getBean("b1", Book.class); +System.out.println("b = " + b); +``` + +关键就是通过调用 `BeanDefinition` 的 `setInstanceSupplier` 方法去设置回调。当然,上面这段代码还可以通过 *Lambda* 进一步简化: + +```java +public class BookSupplier { + public Book getBook() { + Book book = new Book(); + book.setName("深入浅出 Spring Security"); + book.setAuthor("江南一点雨"); + return book; + } +} +``` + +然后调用这个方法即可: + +```java +AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); +GenericBeanDefinition definition = new GenericBeanDefinition(); +definition.setBeanClass(Book.class); +BookSupplier bookSupplier = new BookSupplier(); +definition.setInstanceSupplier(bookSupplier::getBook); +ctx.registerBeanDefinition("b1", definition); +ctx.refresh(); +Book b = ctx.getBean("b1", Book.class); +System.out.println("b = " + b); +``` + +这是不是更有一点 *Lambda* 的感觉了? + +在 Spring 源码中,处理获取 Bean 实例的时候,有如下一个分支,就是处理 `Supplier` 这种情况的: + +``` +AbstractAutowireCapableBeanFactory#createBeanInstance +protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { + // Make sure bean class is actually resolved at this point. + Class beanClass = resolveBeanClass(mbd, beanName); + if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { + throw new BeanCreationException(mbd.getResourceDescription(), beanName, + "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); + } + Supplier instanceSupplier = mbd.getInstanceSupplier(); + if (instanceSupplier != null) { + return obtainFromSupplier(instanceSupplier, beanName); + } + if (mbd.getFactoryMethodName() != null) { + return instantiateUsingFactoryMethod(beanName, mbd, args); + } + //... + return instantiateBean(beanName, mbd); +} + +@Nullable +private Object obtainInstanceFromSupplier(Supplier supplier, String beanName) { + String outerBean = this.currentlyCreatedBean.get(); + this.currentlyCreatedBean.set(beanName); + try { + if (supplier instanceof InstanceSupplier instanceSupplier) { + return instanceSupplier.get(RegisteredBean.of((ConfigurableListableBeanFactory) this, beanName)); + } + if (supplier instanceof ThrowingSupplier throwableSupplier) { + return throwableSupplier.getWithException(); + } + return supplier.get(); + } +} +``` + +上面 `obtainFromSupplier` 这个方法,最终会调用到第二个方法。第二个方法中的 `supplier.get();` 其实最终就调用到我们自己写的 `getBook` 方法了。 + +如上,这是从 Spring5 开始结合 Lamdba 的一种 Bean 注入方式。 \ No newline at end of file