【Spring常识】

-12 利用@ConditionalOnProperty实现不同环境差异化部署

@Configuration
@ConditionalOnProperty(name = "spring.kafka.bootstrap-servers")
public class Myconsumer{}

只有profile配置了spring.kafka.bootstrap-servers,才会创建MyConsumer这个bean,这样就实现了差异化部署(可插拔)

-11 springboot打的jar为啥可执行

springboot打的jar 比较特殊,fat jar ,(jar-in-jar),打出的包有个META-INF /MANIFEST.MF 里面定义了 springboot定义的 JarLauncher (MainClass)用于启动这个fat jar ,同时还有用户定义的StartClass作为入口。

JarLauncher中有个Main函数,当用户使用命令java -jar 启动项目时,实际是把参数传给了这个Main函数去启动。

总的来说,这是一种规范。
see also

-10 IOC、DI

  • IOC 是典型的“黑话”, inversion of control ,听起来不明不白,不知道是怎么个inversion法的。
    本质上 ,IOC是一种编程思想,不是一种具体技术,目的是将对象之间的关系解耦。IOC会将你设计好的对象交给容器控制,而不是传统的在你的对象内部控制。对应到Spring,就是SpringIOC容器里控制着对象,控制了什么,控制了对象的外部资源获取(i.e,控制了依赖哪个对象)。

为甚叫做反转:IOC来帮忙查找和注入依赖对象,对象只是被动接受依赖对象,所以是反转
see also

DI ,依赖注入,是IOC的一种实现;是为了实现组件(对象)之间的依赖关系由容器在运行期间去决定

-9 spring初始化过程简述

TODO

-8 @ConditionalOnMissingClass 容忍类不存在

为啥@ConditionalOnMissingClass可以容忍类不存在,而不是抛出来NoClassDef类似的异常? 因为注解的元数据是通过 ASM技术获取到的,不是普通的JVM加载类机制

see also : developing-auto-configuration

The @ConditionalOnClass and @ConditionalOnMissingClass annotations let @Configuration classes be included based on the presence or absence of specific classes. Due to the fact that annotation metadata is parsed by using ASM, you can use the value attribute to refer to the real class, even though that class might not actually appear on the running application classpath. You can also use the name attribute if you prefer to specify the class name by using a String value.

This mechanism does not apply the same way to @Bean methods where typically the return type is the target of the condition: before the condition on the method applies, the JVM will have loaded the class and potentially processed method references which will fail if the class is not present.

-7 springboot自动配配置原理

    1. 核心注解
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {}
    
    1. selector会执行SpringFactoriesLoader.loadFactoryNames,本质是将类路径(?)下spring.facotriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration 【key】对应的xxxAutoConfiguration 【value】给初始化了
    1. 初始化过程中,@Conditional系列注解会起作用,如果缺失一些包(类)就不会构建bean,否则就构建bean。

-6 spring application 和 application contxt关系

一个spring 应用可以有多个application contxt ,这些application contxt 甚至可以有父子层级的结构,比如WebApplicationContext extend ApplicationContext

-5. 如何追踪每个的Bean启动时的耗时(tracking)并找出topN

  • spring提供了ApplicationStartup这样的hook提供扩展,但如果要到bean维度,而且要找topN,需要自己扩展埋点;
  • 还有一种思路是利用aspectJ在bean初始化的关键路径上埋点

see also: context-functionality-startup

-4. 如何做到运行时销毁一个singleton bean,然后重建它(Reinitialize) ?

场景:一个bean的属性在运行中已经发生了变化(比如配置文件的内容被改),除了给bean重新赋值外,这种销毁+重建也是一个思路

  • DefaultSingletonBeanRegistry.destroySingleton()
    看源码可知: 销毁的实质就是将bean 从bean factory的缓存中删除,然后执行destroy的回调,注意依赖 方不会把依赖的这个bean置为null
  • DefaultSingletonBeanRegistry.registerSingleton() (这一步不做其实也没事)

see also: spring-reinitialize-singleton-bean

-3 如何让一个对象最先/最后初始化/销毁

场景: 应用中有生产者、消费者,我们需要:

  • 让生产者bean 第一个完成初始化,避免流量进来后还没有初始化好触发异常

  • 让消费者bean 最后一个完成初始化,消费者是后台任务,和进入的流量无关,如果不是最后才完成初始化,消费者任务执行可能会由于依赖的bean没有初始化完成导致报错

  • spring application contxt关闭时,让消费者bean最先关闭;若依赖的bean先关闭,消费者任务执行可能异常

  • ContextRefreshedEvent: ApplicationContext初始化(refresh)完成后触发的事件

  • ContextClosedEvent:ApplicationContext开始关闭就触发的事件

// subscriber bean 由ContextRefreshedEvent触发创建注册
public class SubscriberInitializationListener implements ApplicationListener<ContextRefreshedEvent>, Ordered {
	@Override
	public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
		ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) contextRefreshedEvent
				.getApplicationContext();

			//  手动生成并注册消费者bean 
	}
	@Override
	public int getOrder() {
		return LoggingApplicationListener.DEFAULT_ORDER + 10;
	}
}
// 消费者bean最先关闭
public class SubscriberClosedListener implements ApplicationListener<ContextClosedEvent>, Ordered {
	@Override
	public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
		ApplicationContext applicationContext = contextClosedEvent.getApplicationContext();
		//关闭消费者 bean
	}

	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE + 10;
	}
}
// 让生产者最先初始化
public class PublisherBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, BeanFactoryAware, Ordered {

	private ApplicationContext applicationContext;

	private DefaultListableBeanFactory beanFactory;

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		// 利用environment解析到 topicName + rootBeanDefinition 得到publisher bean元信息 ,最终注册bean
		// Environment environment = applicationContext.getEnvironment();
		// String topicName = environment.resolveRequiredPlaceholders(?);
		// AbstractBeanDefinition rootBeanDefinition = BeanDefinitionBuilder
					.genericBeanDefinition(PublisherBean.class).addPropertyValue("topic.name", topicName)
					.addPropertyValue("publisherId", publisherId).getBeanDefinition();
			beanFactory.registerBeanDefinition(publisherBeanName, rootBeanDefinition);
		return bean;
	}
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = (DefaultListableBeanFactory) beanFactory;
	}

	@Override
	public int getOrder() {
		return OrderEnum.First.getValue();
	}
}

-2 如何解决循环依赖

bean的生成,实例化 》注入属性》 初始化,循环依赖是在【注入属性】这里发生的。

  • 如果只是普通的bean,整个过程即使有循环依赖也无妨,可以推想下过程:
    A 依赖B,B也依赖A ,那先将 A、B都new 好, 然后setXX ,
  • 如果存在aop,即bean proxy,那要使用 三级缓存 (spring的方案)来解决循环依赖
    • A 依赖B,B依赖A,但B需要被代理(可能加了@Async @Transcational或有拦截器),A实际依赖的应该是 B的代理类
    • spring 有三个缓存, singletonObjects 、earlySingletonObjects、singletonFactories;
    • A先实例化,A对象的引用放到earlySingletonObjects,A的工厂对象放到singletonFactories;A依赖了B,那么:
    • B实例化,B的引用放到earlySingletonObjects,B的工厂对象引用放到singletonFactories;
    • A setB , B setA ,A 和B都可以有彼此的引用,但A、B的初始化还要继续
    • singletonFactories中的A 工厂完成A初始化,得到完整A对象;B工厂完成了 B proxy 的初始化,得到完整B;初始化完整后,A、B的引用转移到了 singletonObjects

上面的描述不一定正确,但大体是这样的实现思路,核心点就是利用三个缓存,提前获取bean的引用

掘金好文章

-1 spring controller 中多次reponse.getWriter.write()

下面的代码会报错java.lang.IllegalStateException: getWriter() has already been called for this response

	@RequestMapping(value = "/test")
	public String test(HttpServletRequest request, HttpServletResponse response) throws IOException {

		log.info("test----------------");
		response.getWriter().write("1");
		return "test";
	}

0 关于WebFlux

As of Spring Framework 5, a WebFlux application does not even use the Servlet API 
directly and can run on servers (such as Netty) that are not Servlet containers.

1 BeanFactory V.S. ApplicationContxt

In short, the BeanFactory provides the configuration framework and basic functionality, and the ApplicationContext adds more enterprise-specific functionality. The ApplicationContext is a complete superset of the BeanFactory and is used exclusively in this chapter in descriptions of Spring’s IoC container.

The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory. It adds:

  • Easier integration with Spring’s AOP features
  • Message resource handling (for use in internationalization)
  • Event publication
  • Application-layer specific contexts such as the WebApplicationContext for use in web applications.

2 Resource Abstraction

The location path or paths supplied to an ApplicationContext constructor are resource strings that let the container load configuration metadata from a variety of external resources, such as the local file system, the Java CLASSPATH, and so on.

What’s more, Spring’s Resource abstraction provides a convenient mechanism for reading an InputStream from locations defined in a URI syntax

3 Registration of existing objects created outside the container

In addition to bean definitions that contain information on how to create a specific bean, the ApplicationContext implementations also permit the registration of existing objects that are created outside the container (by users). This is done by accessing the ApplicationContext’s BeanFactory through the getBeanFactory() method, which returns the BeanFactory DefaultListableBeanFactory implementation. DefaultListableBeanFactory supports this registration through the registerSingleton(..) and registerBeanDefinition(..) methods. However, typical applications work solely with beans defined through regular bean definition metadata.

Bean metadata and manually supplied singleton instances need to be registered as early as possible, in order for the container to properly reason about them during autowiring and other introspection steps. While overriding existing metadata and existing singleton instances is supported to some degree, the registration of new beans at runtime (concurrently with live access to the factory) is not officially supported and may lead to concurrent access exceptions, inconsistent state in the bean container, or both.

4 Aliasing a Bean outside the Bean Definition

根据下面的配置, subsystemA-dataSource 和 subsystemB-dataSource 都是别名, 实际指向的是同一个 myApp-dataSource.

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

5 Instantiating Beans

既可以提供Class,也可以提供静态工厂方法,以及实例工厂方法

6 Determining a Bean’s Runtime Type

The runtime type of a specific bean is non-trivial to determine. A specified class in the bean metadata definition is just an initial class reference, potentially combined with a declared factory method or being a FactoryBean class which may lead to a different runtime type of the bean, or not being set at all in case of an instance-level factory method (which is resolved via the specified factory-bean name instead). Additionally, AOP proxying may wrap a bean instance with an interface-based proxy with limited exposure of the target bean’s actual type (just its implemented interfaces).

The recommended way to find out about the actual runtime type of a particular bean is a BeanFactory.getType call for the specified bean name. This takes all of the above cases into account and returns the type of object that a BeanFactory.getBean call is going to return for the same bean name.

7 Dependency Injection

  • Constructor-based dependency injection, and it resembles Calling a static factory method with specific arguments to construct the bean
  • Setter-based dependency injection

8 Spring supports constructor-based and setter-based DI

The ApplicationContext supports constructor-based andsetter-based DI for the beans it manages. It also supports setter-based DI after some dependencies have already been injected through the constructor approach.You configure the dependencies in the form of a BeanDefinition, which you use in conjunction with PropertyEditor instances to convert properties from one format to another. However, most Spring users do not work with these classes directly (that is, programmatically) but rather with XML bean definitions, annotated components (that is, classes annotated with @Component, @Controller, and so forth), or @Bean methods in Java-based @Configuration classes. These sources are then converted internally into instances of BeanDefinition and used to load an entire Spring IoC container instance.

9 Constructor-based or setter-based DI

The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.

Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.

Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.

10 Prototype

The non-singleton prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made. That is, the bean is injected into another bean or you request it through a getBean() method call on the container. As a rule, you should use the prototype scope for all stateful beans and the singleton scope for stateless beans.

In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean. The container instantiates, configures, and otherwise assembles a prototype object and hands it to the client, with no further record of that prototype instance. Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype beans hold. To get the Spring container to release resources held by prototype-scoped beans, try using a custom bean post-processor, which holds a reference to beans that need to be cleaned up.

In some respects, the Spring container’s role in regard to a prototype-scoped bean is a replacement for the Java new operator. All lifecycle management past that point must be handled by the client. (For details on the lifecycle of a bean in the Spring container, see Lifecycle Callbacks.)

11 method injection

A 是个singleton bean , B 是个 prototype bean, A依赖注入了B. 实际上, 从 A 获取到的B,也是个 singleton bean, 因为 只注入了一次.

假如希望每次从A 获取 B都是一个新bean, 则可使用 method injection. (Lookup 注解)

12 Initial Web Configuration

To support the scoping of beans at the request, session, application, and websocket levels (web-scoped beans), some minor initial configuration is required before you define your beans. (This initial setup is not required for the standard scopes: singleton and prototype.)

How you accomplish this initial setup depends on your particular Servlet environment.

If you access scoped beans within Spring Web MVC, in effect, within a request that is processed by the Spring DispatcherServlet, no special setup is necessary. DispatcherServlet already exposes all relevant state.

但是,如果使用了JSF等其他框架,可能就需要自己添加配置了. 可以通过 RequestContextListenerRequestContextFilter来配置.

DispatcherServlet, RequestContextListener, and RequestContextFilter all do exactly the same thing, namely bind the HTTP request object to the Thread that is servicing that request. This makes beans that are request- and session-scoped available further down the call chain

13 Application Scope

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>

The Spring container creates a new instance of the AppPreferences bean by using the appPreferences bean definition once for the entire web application. That is, the appPreferences bean is scoped at the ServletContext level and stored as a regular ServletContext attribute. This is somewhat similar to a Spring singleton bean but differs in two important ways: It is a singleton per ServletContext, not per Spring ApplicationContext (for which there may be several in any given web application), and it is actually exposed and therefore visible as a ServletContext attribute.

14 @Transactional(readOnly = true)

这种注解用法是告诉Spring事务管理器,我这个方法没有写事务,只有读事务, 所以Spring会作出一些优化.
这里强调下:

  • 如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性;
  • 如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持
    read-only="true"表示该事务为只读事务,比如上面说的多条查询的这种情况可以使用只读事务
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值