Spring(一)Spring的作用,概览,看源码:BeanFactory和FactoryBean

文章目录

  • 前言,Spring的作用,模拟下Spring的思路
  • Spring概览
  • 看源码-BeanFactory
    • BeanFactory
    • 什么是DI,IOC,AOP? 实现原理是什么?
    • spring-cloud-openfeign 看BeanFactory
    • "取地址符" FACTORY_BEAN_PREFIX(&)
    • Bean的生命周期
  • FactoryBean
    • AOP 的原理
    • spring-cloud-openfeign 看FactoryBean
    • 修改FeignClientsRegistrar,使@FeignClient不需要name属性
    • 大致看一看FeignClientFactoryBean.getObject()

前言,Spring的作用,模拟下Spring的思路

Spring正如其名,给Java带来了春天;企业中如果使用Spring,可以大大降低开发的成本.
虽然日常工作中我们只要会使用Spring的功能就可以了
但是偶尔有些定制化需求或者BUG时,我们也可以修改其源码
Spring本身也是很优秀的代码,可以学习下设计模式
这节主要从这几个方面学习: Spring的作用,概览,看源码:BeanFactory和FactoryBean

Spring可以看作一个对象容器
如果没有Spring,我们怎么写一个工程?
假设有一个类使用频率很高,就需要在各个地方去new它;
但这样耦合度太高啦,假如某天不想要这个对象啦,想把这个对象换成另一个实现,就很麻烦,一个个去替换修改代码,这时候考虑通过对象工厂创建对象;
但这样代码跟工厂的耦合度又高了,假如哪天不想要这个工厂怎么办?不如干脆用反射

        Class<Pet> clazz = Pet.class;
        Pet pet = clazz.newInstance();
        clazz.getFields();
        clazz.getDeclaredFields();

其实Spring就是使用反射的方式去创建对象的,下面简单模拟一下核心流程:
(Spring为了支持扩展性,提供了很多接口,见下面的描述)

/**
 * @author liweizhi
 * @date 2020/7/5
 *
 * 扩展性非常重要:
 * 1. 抽象
 * 2. 设计模式
 *
 * Spring给出了扩展:
 * 1. 在容器初始化之前的processer
 * 2. 在创建对象之前的processer
 * 3. 在不同的阶段发出不同的事件,listener
 * 4. 面向接口编程
 */
public class DemoTest {
    Class<UserController> aClass;
    UserController userController;

    @Before
    public void init() {
        try {
            aClass = UserController.class;
            userController = aClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Test
    public void test01() throws Exception {
        Field userServiceField = aClass.getDeclaredField("userService");
        // 因为userService属性是私有的,所以设置这个 让JVM跳过检查;这里算是个障眼法,因为字段的访问标识符是在编译期字节码就确定的;
        userServiceField.setAccessible(true);

        Class<?> fieldType = userServiceField.getType();
        Object fieldInstance = fieldType.newInstance();
//        userServiceField.set(userController, userService);

        String name = userServiceField.getName();
        name = name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
        String setMethodName = "set" + name;
        Method method = aClass.getMethod(setMethodName, UserService.class);
        method.invoke(userController, fieldInstance);

        System.out.println(userController);
    }

    @Test
    public void test02() {
        Arrays.stream(aClass.getDeclaredFields()).forEach(field -> {
            MyAutowired annotation = field.getAnnotation(MyAutowired.class);
            if (annotation != null) {
                field.setAccessible(true);
                Class<?> fieldType = field.getType();
                Object fieldInstance = null;
                try {
                    /**
                     * 这里对象是直接newInstance出来的
                     * Spring中,这个bean是定义在注解或者xml中的
                     * 已经初始化过的单例对象放入容器中,用两个map维护,分别按id和Type存储
                     */
                    fieldInstance = fieldType.newInstance();
                    field.set(userController, fieldInstance);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println(userController);
    }
}
/**
 * @author liweizhi
 * @date 2020/7/5
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}

@Data
public class UserController {
    @MyAutowired
    private UserService userService;

    private String localName;
}
@Data
public class UserService {
    private String name;
}

Spring概览

  1. BeanDefinationReader
  2. BeanFactory
  3. Environment
  4. BeanFactoryPostProcessor (创建对象前)
  5. BeanPostProcessor (创建对象后)
  6. FactoryBean
    Spring(一)Spring的作用,概览,看源码:BeanFactory和FactoryBean_第1张图片

看源码-BeanFactory

context上下文: 划分一个范围,在范围内活动的数据, 这个范围和数据就是上下文;

新建一个project,只进入spring-context包(4.x,没有webflux,看起来干净些),可以看到有这些包:
Spring(一)Spring的作用,概览,看源码:BeanFactory和FactoryBean_第2张图片

BeanFactory

Spring容器的根接口
对象作用域:常见的有Singleton单例, Prototype(原型,每次新对象) ,还有request和session现在几乎不用啦
是一个注册中心
依赖注入,Dependency Injection
子工厂可以访问父工厂,当在本工厂找不到时,就去父工厂找; 子容器可以覆盖父容器的同名对象;(具体应用举例:spring-cloud-openfeign)

下面是BeanFactory的类注释

/**
 * The root interface for accessing a Spring bean container.
 * This is the basic client view of a bean container;
 * further interfaces such as {@link ListableBeanFactory} and
 * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
 * are available for specific purposes.
 *
 * 

This interface is implemented by objects that hold a number of bean definitions, * each uniquely identified by a String name. Depending on the bean definition, * the factory will return either an independent instance of a contained object * (the Prototype design pattern), or a single shared instance (a superior * alternative to the Singleton design pattern, in which the instance is a * singleton in the scope of the factory). Which type of instance will be returned * depends on the bean factory configuration: the API is the same. Since Spring * 2.0, further scopes are available depending on the concrete application * context (e.g. "request" and "session" scopes in a web environment). * *

The point of this approach is that the BeanFactory is a central registry * of application components, and centralizes configuration of application * components (no more do individual objects need to read properties files, * for example). See chapters 4 and 11 of "Expert One-on-One J2EE Design and * Development" for a discussion of the benefits of this approach. * *

Note that it is generally better to rely on Dependency Injection * ("push" configuration) to configure application objects through setters * or constructors, rather than use any form of "pull" configuration like a * BeanFactory lookup. Spring's Dependency Injection functionality is * implemented using this BeanFactory interface and its subinterfaces. * *

Normally a BeanFactory will load bean definitions stored in a configuration * source (such as an XML document), and use the {@code org.springframework.beans} * package to configure the beans. However, an implementation could simply return * Java objects it creates as necessary directly in Java code. There are no * constraints on how the definitions could be stored: LDAP, RDBMS, XML, * properties file, etc. Implementations are encouraged to support references * amongst beans (Dependency Injection). * *

In contrast to the methods in {@link ListableBeanFactory}, all of the * operations in this interface will also check parent factories if this is a * {@link HierarchicalBeanFactory}. If a bean is not found in this factory instance, * the immediate parent factory will be asked. Beans in this factory instance * are supposed to override beans of the same name in any parent factory. * * **Bean的生命周期** *

Bean factory implementations should support the standard bean lifecycle interfaces * as far as possible. The full set of initialization methods and their standard order is: *

    *
  1. BeanNameAware's {@code setBeanName} *
  2. BeanClassLoaderAware's {@code setBeanClassLoader} *
  3. BeanFactoryAware's {@code setBeanFactory} *
  4. EnvironmentAware's {@code setEnvironment} *
  5. EmbeddedValueResolverAware's {@code setEmbeddedValueResolver} *
  6. ResourceLoaderAware's {@code setResourceLoader} * (only applicable when running in an application context) *
  7. ApplicationEventPublisherAware's {@code setApplicationEventPublisher} * (only applicable when running in an application context) *
  8. MessageSourceAware's {@code setMessageSource} * (only applicable when running in an application context) *
  9. ApplicationContextAware's {@code setApplicationContext} * (only applicable when running in an application context) *
  10. ServletContextAware's {@code setServletContext} * (only applicable when running in a web application context) *
  11. {@code postProcessBeforeInitialization} methods of BeanPostProcessors *
  12. InitializingBean's {@code afterPropertiesSet} *
  13. a custom init-method definition *
  14. {@code postProcessAfterInitialization} methods of BeanPostProcessors *
* *

On shutdown of a bean factory, the following lifecycle methods apply: *

    *
  1. {@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors *
  2. DisposableBean's {@code destroy} *
  3. a custom destroy-method definition *
* * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams * @since 13 April 2001 * @see BeanNameAware#setBeanName * @see BeanClassLoaderAware#setBeanClassLoader * @see BeanFactoryAware#setBeanFactory * @see org.springframework.context.ResourceLoaderAware#setResourceLoader * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher * @see org.springframework.context.MessageSourceAware#setMessageSource * @see org.springframework.context.ApplicationContextAware#setApplicationContext * @see org.springframework.web.context.ServletContextAware#setServletContext * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization * @see InitializingBean#afterPropertiesSet * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization * @see DisposableBean#destroy * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName */

什么是DI,IOC,AOP? 实现原理是什么?

DI : Dependency Injection,依赖注入;我需要啥,他就给我注入啥
IOC: 控制反转,以前需要对象时我们自己new,现在交给Spring做依赖注入,这就是控制反转
AOP: 面向切面编程,一个点且多个面,一个面切多个点
实现原理: 反射,动态代理
Spring中如何实现: BeanPostProcessor层,运行期增强

运行时增强
编译时增强: aspectJ,lombok
载入时增强

spring-cloud-openfeign 看BeanFactory

https://github.com/spring-cloud/spring-cloud-openfeign,切换到release tag(v2.1.5.RELEASE)
feign作用:简化Http接口调用
FeignClientFactoryBean
FeignClientRegistrar
Spring(一)Spring的作用,概览,看源码:BeanFactory和FactoryBean_第3张图片

“取地址符” FACTORY_BEAN_PREFIX(&)

Used to dereference a {@link FactoryBean} instance and distinguish it from
beans created by the FactoryBean. For example, if the bean named
{@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
will return the factory, not the instance returned by the factory.

Java里面没有指针,但是有很多和指针类似的概念

C语言中,假设int a = 1;
int *b = &a;
就是b存储了a的内容 “1” 在内存中的地址

对应到Spring的BeanFactory中,一般我们都是取FactoryBean中维护的对象
假如需要取FactoryBean本身,就需要在前面加FACTORY_BEAN_PREFIX表示区分
AbstractApplicationContext.finishBeanFactoryInitialization()中,会调用beanFactory.preInstantiateSingletons();
然后会调用DefaultListableBeanFactory.preInstantiateSingletons()
里面就是这么调用:

if (isFactoryBean(beanName)) {
	Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
	// do sth else
}else {
	getBean(beanName);
}

Bean的生命周期

  1. 经历一堆XXXAware,把bean需要的spring组件 通过调用setXX 给bean
  2. BeanPostProcessor postProcessBeforeInitialization
  3. InitializingBean (init)
  4. 用户自定义的init-method
  5. BeanPostProcessor postProcessAfterInitialization
  6. DisposableBean
  7. 用户自定义的Destroy-method

FactoryBean

/**
 * Interface to be implemented by objects used within a {@link BeanFactory} which
 * are themselves factories for individual objects. If a bean implements this
 * interface, it is used as a factory for an object to expose, not directly as a
 * bean instance that will be exposed itself.
 *
 * 

NB: A bean that implements this interface cannot be used as a normal bean. * A FactoryBean is defined in a bean style, but the object exposed for bean * references ({@link #getObject()}) is always the object that it creates. * *

FactoryBeans can support singletons and prototypes, and can either create * objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean} * interface allows for exposing more fine-grained behavioral metadata. * *

This interface is heavily used within the framework itself, for example for * the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the * {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for * custom components as well; however, this is only common for infrastructure code. * *

{@code FactoryBean} is a programmatic contract. Implementations are not * supposed to rely on annotation-driven injection or other reflective facilities. * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in the * bootstrap process, even ahead of any post-processor setup. If you need access to * other beans, implement {@link BeanFactoryAware} and obtain them programmatically. * *

The container is only responsible for managing the lifecycle of the FactoryBean * instance, not the lifecycle of the objects created by the FactoryBean. Therefore, * a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()} * will not be called automatically. Instead, a FactoryBean should implement * {@link DisposableBean} and delegate any such close call to the underlying object. * *

Finally, FactoryBean objects participate in the containing BeanFactory's * synchronization of bean creation. There is usually no need for internal * synchronization other than for purposes of lazy initialization within the * FactoryBean itself (or the like). * * @author Rod Johnson * @author Juergen Hoeller * @since 08.03.2003 * @param the bean type * @see org.springframework.beans.factory.BeanFactory * @see org.springframework.aop.framework.ProxyFactoryBean * @see org.springframework.jndi.JndiObjectFactoryBean */

AOP 的原理

ProxyFactoryBean

This interface is heavily used within the framework itself, for example for
the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
{@link org.springframework.jndi.JndiObjectFactoryBean}

spring-cloud-openfeign 看FactoryBean

org.springframework.cloud.openfeign.FeignClientFactoryBean

修改FeignClientsRegistrar,使@FeignClient不需要name属性

org.springframework.cloud.openfeign.FeignHttpClientUrlTests
看测试用例:
这个测试用例是自己定义了一个API,然后通过Feign访问那个API(getHello)

	@Test
	public void testBeanUrlNoProtocol() {
		Hello hello = this.beanClientNoProtocol.getHello();
		assertThat(hello).as("hello was null").isNotNull();
		assertThat(hello).as("first hello didn't match")
				.isEqualTo(new Hello("hello world 1"));
	}

我们也可以在在其基础上,写个访问baidu的Feign-service:

	@Autowired
	private BaiduUrlClient baiduUrlClient;

	// 这就是我们自定义的Feign-service了
	@FeignClient(name = "baiduUrl", url = "https://www.baidu.com")
	protected interface BaiduUrlClient {
		@RequestMapping(method = RequestMethod.GET, value = "/")
		String getHello();
	}

	// 这里测试用例是写的固定扫描几个类,加上我们自定义的类 BaiduUrlClient.class 
	// 项目中一般都是扫描包
	@Configuration
	@EnableAutoConfiguration
	@RestController
	@EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class,
			BeanUrlClientNoProtocol.class, BaiduUrlClient.class })
	@Import(NoSecurityConfiguration.class)
	protected static class TestConfig {
		// xxx 代码
	}

	// 测试代码
	@Test
	public void testBaiduUrl() {
		String hello = this.baiduUrlClient.getHello();
		System.out.println("testBaiduUrl========================\n" + hello);
	}

上面的测试中,@FeignClient(name = “baiduUrl”, url = “https://www.baidu.com”)
,尽管我们只需要一个URL,不需要name,但是不输入name就会抛异常
可以修改一下源码,去掉这个机制
org.springframework.cloud.openfeign.FeignClientsRegistrar
我们一步步来,先把测试用例中的Feign-service的@FeignClient的name去掉,看哪里报错,然后就改那里

	// 这就是我们自定义的Feign-service了
	@FeignClient(url = "https://www.baidu.com")
	protected interface BaiduUrlClient {
		@RequestMapping(method = RequestMethod.GET, value = "/")
		String getHello();
	}

经过测试,改掉FeignClientsRegistrar的getClientName()和registerFeignClient()

	private String getClientName(Map<String, Object> client) {
		// xxxcode
		// 主要是在最后,给个默认值
		// 这里简单测试,随便写个
		return "defaultName";
		/*throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
				+ FeignClient.class.getSimpleName());*/
	}
	
	private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientFactoryBean.class);
		validate(attributes);
		definition.addPropertyValue("url", getUrl(attributes));
		definition.addPropertyValue("path", getPath(attributes));
		String name = getName(attributes);
		// 主要是再这里,加一个判断,如果name为空,就给个默认值
		if (name == null || name.length() == 0) {
			name = "defaultName";
		}
		// xxx code
	}

然后改掉FeignClientFactoryBean的afterPropertiesSet()

	// 这里有个参数校验,去掉他
	@Override
	public void afterPropertiesSet() throws Exception {
//		Assert.hasText(this.contextId, "Context id must be set");
//		Assert.hasText(this.name, "Name must be set");
	}

现在就可以不输入name参数了
当然,改完后要跑一遍测试用例,保证其他的用例也不受影响

上面我们看到,FeignClientsRegistrar的registerFeignClient()中:
String className = annotationMetadata.getClassName();
definition.addPropertyValue(“type”, className);
这个type是个String类型的

但是FeignClientsRegistrar的type是Class类型呀,setType(Class type)接收的也是Class类型
那这是在哪里做的类型转换?Spring帮我们做的

大致看一看FeignClientFactoryBean.getObject()

头一次也是看的有点懵,但是大致思路是可以看出来的

FeignClientFactoryBean:

	@Override
	public Object getObject() throws Exception {
		return getTarget();
	}

	/**
	 * @param  the target type of the Feign client
	 * @return a {@link Feign} client created with the specified data and the context
	 * information
	 */
	<T> T getTarget() {
		/*
			FeignContext:
			A factory that creates instances of feign classes.
			It creates a Spring ApplicationContext per client name, and extracts the beans that it needs from there.
			点击FeignContext,可以看到是在FeignAutoConfiguration中创建
		*/
		FeignContext context = this.applicationContext.getBean(FeignContext.class);
		// 这个builder是建造者模式,点击查看feign()
		Feign.Builder builder = feign(context);

		// xxx code 剩下的就不重要了
	}

	protected Feign.Builder feign(FeignContext context) {
		// 主要是这个get方法
		FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
		Logger logger = loggerFactory.create(this.type);

		// @formatter:off
		Feign.Builder builder = get(context, Feign.Builder.class)
				// required values
				.logger(logger)
				.encoder(get(context, Encoder.class))
				.decoder(get(context, Decoder.class))
				.contract(get(context, Contract.class));
		// @formatter:on
		configureFeign(context, builder);
		return builder;
	}
	
	protected <T> T get(FeignContext context, Class<T> type) {
		// 主要是这个getInstance方法,会调用NamedContextFactory的getInstance()
		T instance = context.getInstance(this.contextId, type);
		if (instance == null) {
			throw new IllegalStateException(
					"No bean found of type " + type + " for " + this.contextId);
		}
		return instance;
	}

NamedContextFactory:

	private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();

	public <T> T getInstance(String name, Class<T> type) {
		AnnotationConfigApplicationContext context = getContext(name);
		if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
				type).length > 0) {
			return context.getBean(type);
		}
		return null;
	}

	protected AnnotationConfigApplicationContext getContext(String name) {
		if (!this.contexts.containsKey(name)) {
			synchronized (this.contexts) {
				if (!this.contexts.containsKey(name)) {
					this.contexts.put(name, createContext(name));
				}
			}
		}
		return this.contexts.get(name);
	}

你可能感兴趣的:(Spring原理,spring,java)