Spring教程动画文字版

Spring教程动画文字版笔记

这个是笔者在B站看周瑜大神的相关视频时候的笔记
B站 周瑜大神

任意门

P1 什么是BeanFactory?

BeanFactory是一种”Spring容器“,BeanFactory翻译过来就是Bean工厂,顾名思义,它可以用来创建Bean,获取Bean,BeanFactory是Spring非常核心的组件。

BeanFactory与BeanDefinition和Bean对象之间的关系

工作流程:

BeanFactory将利用BeanDefinition来说生成Bean对象,

BeanDefinition相当于BeanFactory的原材料,

Bean对象就相当于BeanFactory所生产出来的产品。

BeanFactory的核心子接口和实现类

ListableBeanFactory

ConfigurableBeanFactory

AutowireCapableBeanFactory

AbstractBeanFactory

DefaultListableBeanFactory

DefaultListableBeanFactory的功能

是最重要的

支持单例Bean、支持Bean别名、支持父子BeanFactory、支持Bean类型转化、支持Bean后置处理、支持FactoryBean、支持自动装配、等等

P2 什么是BeanDefinition?

BeanDefinition标识Bean定义,Spring根据BeanDefinition来创建Bean对象,BeanDefinition有很多的属性用来描述Bean,BeanDefinition是Spring中非常核心的概念

BeanDefinition中重要的属性

  • beanClass:表示一个bean的类型,比如 UserService.class、OrderService.lass、Spring在创建Bean的过程中会根据此属性来实例化得到对象。
  • scope:表示一个bean的作用域,比如 scope=singleton,表示bean是一个单例Bean,scope=prototype,表示bean是一个原型Bean。
  • lsLazy:标识一个bean是不是需要懒加载,原型Bean的isLazy属性不起作用,懒加载的单例bean,会在第一个getBean的时候生成该bean,非懒加载的单例bean,会在Sping启动过程中直 接生成好。
  • dependsOn:表示一个bean在创建之前所依赖的其他bean,在一个bean创建之间,它所依赖的这些bean,得先全部出创建好。
  • primary:表示一个bean是主bean,在Spring中一个类型有多个bean对象,在进行依赖注入时,如果根据类型找到多个bean,此时会判断这些bean是否存在一个主bean,如果存在,则直接将这个bean注入给属性。
  • initMethodName:表示一个bean的初始化方法,一个bean的生命周期过程中有一个步骤叫初始化,Spring会在这个步骤中去调用bean的初始化方法,初始化逻辑由程序员控制,表示程序员可以自定义逻辑对bean进行加工。

这些都会解析为BeanDefinition对象

@Component、@Brean、< bean />

P3 什么是Bean生命周期?

Bean生命周期描述的是Spring中一个Bean创建过程和销毁过程中所经历的步骤,其中Bean创建过程是重点。

程序员可以利用Bean声明周期机制,对Bean进行自定义加工。

Bean声明周期中的几个核心过程

  • BeanDefinition对象创建:Bean定义
  • 构造方法推断:选出一个构造方法
  • 实例化:构造方法反射得到对象
  • 属性填充:给属性进行自动填充
  • 初始化:对其他属性赋值、校验
  • 初始化后:AOP、生成代理对象
BeanDefinition

BeanDefinition表示Bean定义,它定义了某个Bean的类型,Spring就是利用BeanDefinition来创建Bean的,比如需要利用BeanDefinition中beanCalss属性确定Bean类型,从而实例化出来对象。

构造方法推断

一个Bean中有多个构造方法,此时就需要Spring来判断到底使用按个构造方法,这个过程比较复杂,不展开。通过构造方法推断之后确定一个构造方法后,就可以利用构造方法实例化得到一个对象了。

实例化

通过构造方法反射得到一个实例化对象,在Spring中,可以通过BeanPostProcessor机制对实例化进行干预。

属性填充

实例化所得到的的对象,是“不完整”的对象,“不完整”的意思是该对象中的某些属性还没有进行属性填充,也就是Spring还没有自动给某些属性赋值,属性填充就是我们通常说的自动注入、依赖注入。

初始化

在一个对象的属性填充之后,Spring提供了初始化机制,程序员可以利用初始化机制对Bean进行自定义加工,比如可以利用InitializationBean接口,来对Bean中的其他属性进行赋值,或对Bean中的某些属性进行校验。

初始化后

初始化后是Bean创建生命周期的最后一个步骤,我们常说的AOP机制,就是在这步骤中通过BeanPostProcessor机制实现的,初始化之后得到的对象才是真正的Bean对象。

P4 @Autowired是如何工作的?

@Autowired表示某个属性是否需要依赖注入,可以写在属性和方法上。注解中的required属性默认是true,表示如果没有对象可以注入给属性则抛出异常。

注解在属性上

@Service
public class OrderService{
     @Autowired
    private UserService userService;
}

@Autowired加载某个属性上,Spring在进行Bean生命周期过程中,在属性填充这一步,会基于实例化出来的对象,对该对象中加了 @Autowired的属性自动给属性赋值。

Spring会先根据属性的类型去Spring容器中找出该类型的所有Bean对象,如果找出来多个,则再根据属性的名字从多个中再确定一个。如果required属性为true,并且根据属性信息找不到对象,则直接抛出异常。

注意:

完整的底层筛选逻辑步骤是更多的,不仅只有这两步。

注解在方法上

@Service
public class OrderService{
    
    private UserService userService;
  
    @Autowired
    public void setUserService(UserService userService){
        ths.userService = userService;
    }
    
}

当@Autowired注解写在某个方法上时,Spring在Bean生命周期的属性填充阶段,会根据方法的参数类型、参数名字从Spring容器找到对象当做方法入参,自动反射调用该方法。

注解在构造方法上

@Service
public class OrderService{
    
    private UserService userService;
  
    @Autowired
    public void OrderService(UserService userService){
        ths.userService = userService;
    }
    
    public void OrderService(UserService userService,UserService userService1){
        ths.userService = userService;
    }
    
}

@Autowired加载构造方法上时,Spring会在推断构造方法阶段,选择该构造方法来进行实例化,在反射调用构造方法之前,会先根据构造方法参数类型、参数名从Spring容器中找到Bean对象,当做构造方法入参。

P5 @Resource如何工作的?

@Resource注解与@Autowired类似,也是用来进行依赖注入的,@Resource是Java层面所提供的注解,@Autowired是Spring所提供的的注解,它们依赖注入的底层逻辑也不同

@Resource的name属性

@Resource注解中有一个name属性,针对name属性是否有值,@Resource的依赖注入底层流程是不同的。

name有值

@Resource如果name属性有值,nameSpring会直接根据所指定的name值去Spring容器找Bean对象,如果找到了则成功,反之,则报错。

name没有值

如果@Resource中的name属性没有值,则:

  1. 判断该属性名字在Spring容器中是否存在Bean对象;
  2. 如果存在,则成功找到Bean对象进行注入;
  3. 如果不存在,则根据属性类型去Spring容器找Bean对象,找到一个则进行注入。

P6 @Value注解是如何工作的?

@Value和@Resource注解、@Autowired类似,也是用来对属性进行依赖注入的,只不过@Value是用来从Properties文件中来获取值的,并且@Value可以解析SpEL(Spring表达式)。

使用方式

@Value(“zhouyu”)

直接将字符串“zhouyu”,赋值给属性,如果属性类型不是String,或无法进行类型转化,则报错。

@Value("${zhouyu}")

将会把 的 字 符 串 当 做 k e y , 从 P r o p e r t i e s 文 件 中 找 出 对 应 的 v a l u e 赋 值 给 属 性 , 如 果 找 不 到 , 则 会 把 “ {}的字符串当做key,从Properties文件中找出对应的value赋值给属性,如果找不到,则会把“ keyPropertiesvalue{zhouyu}”当做普通字符串注入给属性。

@Value("#{zhouyu}")

注入bean

会将#{}中的字符串当做Sring表达式进行解析,Spring会把“zhouyu”当做beanName,并从Spring容器中找对应bean,如果找到则进行属性注入,没找到则报错。

P7 什么是FactoryBean?

FactoryBean是Spring所提供的的一种较灵活的创建Bean的方式,可以通过实现FactoryBean接口中的getObject()方法来返回一个对象,这个对象就是最终的Bean对象。

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

    //返回的是Bean对象
    @Nullable
    T getObject() throws Exception;

    //返回的是Bean对象的类型
    @Nullable
    Class<?> getObjectType();

    //返回的是否是单例Bean对象
    default boolean isSingleton() {
        return true;
    }
@Component("testFactoryBean")
public class TestFactoryBean implements FactoryBean {
    
    //Bean对象
    @Override
    public Object getObject() throws Exception {
        return new LoginController();
    }

    //Bean对象的类型
    @Override
    public Class<?> getObjectType() {
        return LoginController.class;
    }

    //所定义的Bean是单例还是原型
    @Override
    public boolean isSingleton() {
        return true;
    }
}

FactoryBean的特殊点

上述代码,实际上对应了两个Bean对象:

  1. beanName为“loginController”,bean对象为getObject方法所返回的LoginController对象。
  2. beanName为“testFactoryBean”,bean对象为TestFactoryBean类的实例对象。

FactoryBean对象本身也是一个Bean,同时它相当于一个小型工厂,可以生产出另外的Bean。

BeanFactory是一个Spring容器,是一个大型工厂,它可以生产出各种各样的Bean。

FactoryBean机制被广泛的应用在Spring内部和Spring与第三方框架或组件的整合过程中。

P8 什么是ApplicationContext?

ApplicationContext是比BeanFactory更加强大的Spring容器,它既可以创建bean,获取bean,还支持国际化、事件广播、获取资源等BeanFactory不具备的功能。

ApplicationContext所继承的接口

  • EnvironmentCapable
  • ListableBeanFactory
  • HierarchicalBeanFactory
  • MessageSource
  • ApplicationEventPublisher
  • ResourcePatternResolver
EnvironmentCapable
public interface EnvironmentCapable {

	/**
	 * Return the {@link Environment} associated with this component.
	 */
	Environment getEnvironment();

}

ApplicationContext继承了这个接口,表示拥有了获取环境变量的功能,可以通过ApplicationContext获取操作系统环境变量和JVM环境变量。

ListableBeanFactory
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

	/**
	 * Return the unique id of this application context.
	 * @return the unique id of the context, or {@code null} if none
	 */
	@Nullable
	String getId();

	/**
	 * Return a name for the deployed application that this context belongs to.
	 * @return a name for the deployed application, or the empty String by default
	 */
	String getApplicationName();

	/**
	 * Return a friendly name for this context.
	 * @return a display name for this context (never {@code null})
	 */
	String getDisplayName();

	/**
	 * Return the timestamp when this context was first loaded.
	 * @return the timestamp (ms) when this context was first loaded
	 */
	long getStartupDate();

	/**
	 * Return the parent context, or {@code null} if there is no parent
	 * and this is the root of the context hierarchy.
	 * @return the parent context, or {@code null} if there is no parent
	 */
	@Nullable
	ApplicationContext getParent();

	/**
	 * Expose AutowireCapableBeanFactory functionality for this context.
	 * <p>This is not typically used by application code, except for the purpose of
	 * initializing bean instances that live outside of the application context,
	 * applying the Spring bean lifecycle (fully or partly) to them.
	 * <p>Alternatively, the internal BeanFactory exposed by the
	 * {@link ConfigurableApplicationContext} interface offers access to the
	 * {@link AutowireCapableBeanFactory} interface too. The present method mainly
	 * serves as a convenient, specific facility on the ApplicationContext interface.
	 * <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
	 * after the application context has been closed.</b> In current Spring Framework
	 * versions, only refreshable application contexts behave that way; as of 4.2,
	 * all application context implementations will be required to comply.
	 * @return the AutowireCapableBeanFactory for this context
	 * @throws IllegalStateException if the context does not support the
	 * {@link AutowireCapableBeanFactory} interface, or does not hold an
	 * autowire-capable bean factory yet (e.g. if {@code refresh()} has
	 * never been called), or if the context has been closed already
	 * @see ConfigurableApplicationContext#refresh()
	 * @see ConfigurableApplicationContext#getBeanFactory()
	 */
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

}

ApplicationContext继承了这个接口,表示有了获取所有beanNames、判断某个beanName是否存在beanDefinition对象、统计BeanDefinition个数、获取某个类型对应的所有beanNames等功能。

HierarchicalBeanFactory

Hierarchical:分层的

public interface HierarchicalBeanFactory extends BeanFactory {

	/**
	 * Return the parent bean factory, or {@code null} if there is none.
	 */
	@Nullable
	BeanFactory getParentBeanFactory();

	/**
	 * Return whether the local bean factory contains a bean of the given name,
	 * ignoring beans defined in ancestor contexts.
	 * <p>This is an alternative to {@code containsBean}, ignoring a bean
	 * of the given name from an ancestor bean factory.
	 * @param name the name of the bean to query
	 * @return whether a bean with the given name is defined in the local factory
	 * @see BeanFactory#containsBean
	 */
	boolean containsLocalBean(String name);

}

ApplicationContext继承了这个接口,表示有了获取父BeanFactory、判断某个name是否存在bean对象的功能。

MessageSource
public interface MessageSource {

	/**
	 * Try to resolve the message. Return default message if no message was found.
	 * @param code the message code to look up, e.g. 'calculator.noRateSet'.
	 * MessageSource users are encouraged to base message names on qualified class
	 * or package names, avoiding potential conflicts and ensuring maximum clarity.
	 * @param args an array of arguments that will be filled in for params within
	 * the message (params look like "{0}", "{1,date}", "{2,time}" within a message),
	 * or {@code null} if none
	 * @param defaultMessage a default message to return if the lookup fails
	 * @param locale the locale in which to do the lookup
	 * @return the resolved message if the lookup was successful, otherwise
	 * the default message passed as a parameter (which may be {@code null})
	 * @see #getMessage(MessageSourceResolvable, Locale)
	 * @see java.text.MessageFormat
	 */
	@Nullable
	String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

	/**
	 * Try to resolve the message. Treat as an error if the message can't be found.
	 * @param code the message code to look up, e.g. 'calculator.noRateSet'.
	 * MessageSource users are encouraged to base message names on qualified class
	 * or package names, avoiding potential conflicts and ensuring maximum clarity.
	 * @param args an array of arguments that will be filled in for params within
	 * the message (params look like "{0}", "{1,date}", "{2,time}" within a message),
	 * or {@code null} if none
	 * @param locale the locale in which to do the lookup
	 * @return the resolved message (never {@code null})
	 * @throws NoSuchMessageException if no corresponding message was found
	 * @see #getMessage(MessageSourceResolvable, Locale)
	 * @see java.text.MessageFormat
	 */
	String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

	/**
	 * Try to resolve the message using all the attributes contained within the
	 * {@code MessageSourceResolvable} argument that was passed in.
	 * <p>NOTE: We must throw a {@code NoSuchMessageException} on this method
	 * since at the time of calling this method we aren't able to determine if the
	 * {@code defaultMessage} property of the resolvable is {@code null} or not.
	 * @param resolvable the value object storing attributes required to resolve a message
	 * (may include a default message)
	 * @param locale the locale in which to do the lookup
	 * @return the resolved message (never {@code null} since even a
	 * {@code MessageSourceResolvable}-provided default message needs to be non-null)
	 * @throws NoSuchMessageException if no corresponding message was found
	 * (and no default message was provided by the {@code MessageSourceResolvable})
	 * @see MessageSourceResolvable#getCodes()
	 * @see MessageSourceResolvable#getArguments()
	 * @see MessageSourceResolvable#getDefaultMessage()
	 * @see java.text.MessageFormat
	 */
	String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}

ApplicationContext继承了这个接口,表示拥有了国际化功能、比如可以直接利用MessageSource对象获取某个国际化资源(比如不同国家语言所对应的字符)

ApplicationEventPublisher
@FunctionalInterface
public interface ApplicationEventPublisher {

	/**
	 * Notify all <strong>matching</strong> listeners registered with this
	 * application of an application event. Events may be framework events
	 * (such as ContextRefreshedEvent) or application-specific events.
	 * <p>Such an event publication step is effectively a hand-off to the
	 * multicaster and does not imply synchronous/asynchronous execution
	 * or even immediate execution at all. Event listeners are encouraged
	 * to be as efficient as possible, individually using asynchronous
	 * execution for longer-running and potentially blocking operations.
	 * @param event the event to publish
	 * @see #publishEvent(Object)
	 * @see org.springframework.context.event.ContextRefreshedEvent
	 * @see org.springframework.context.event.ContextClosedEvent
	 */
	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

	/**
	 * Notify all <strong>matching</strong> listeners registered with this
	 * application of an event.
	 * <p>If the specified {@code event} is not an {@link ApplicationEvent},
	 * it is wrapped in a {@link PayloadApplicationEvent}.
	 * <p>Such an event publication step is effectively a hand-off to the
	 * multicaster and does not imply synchronous/asynchronous execution
	 * or even immediate execution at all. Event listeners are encouraged
	 * to be as efficient as possible, individually using asynchronous
	 * execution for longer-running and potentially blocking operations.
	 * @param event the event to publish
	 * @since 4.2
	 * @see #publishEvent(ApplicationEvent)
	 * @see PayloadApplicationEvent
	 */
	void publishEvent(Object event);

}

ApplicationContext继承了这个接口,表示拥有事件发布功能,可以发布事件,这个是ApplicationContext相对于BeanFactory比较突出、常用的功能。

ResourcePatternResolver
public interface ResourcePatternResolver extends ResourceLoader {
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    Resource[] getResources(String var1) throws IOException;
}

ApplicationContext继承了这个接口,表示拥有加载并获取资源的功能、这里的资源可以是文件,图片等某个URL资源都可以。

P9 什么是BeanPostProcessor?

BeanPostProcessor是Spring所提供的一种扩展机制,可以利用该机制对Bean进行定制化加工,在Spring底层源码实现中,也广泛的用到了该机制,BeanPostProcessor通常也叫作Bean的后置处理器

BeanPostProcessor在Spring中是一个接口,我们定义一个后置处理器,就是提供一个类实现该接口,在Spring中还存在了一些接口继承了BeanPostProcessor,这些子接口是在BeanPostProcessor基础上增加了一些其他的功能。

BeanPostProcessor中的方法

public interface BeanPostProcessor {

	/**
	 * Apply this {@code BeanPostProcessor} to the given new bean instance <i>before</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>The default implementation returns the given {@code bean} as-is.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 */
    //初始化前方法,标识可以利用这个方法来对Bean在初始化前进行加工。
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/**
	 * Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
	 * instance and the objects created by the FactoryBean (as of Spring 2.0). The
	 * post-processor can decide whether to apply to either the FactoryBean or created
	 * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
	 * <p>This callback will also be invoked after a short-circuiting triggered by a
	 * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
	 * in contrast to all other {@code BeanPostProcessor} callbacks.
	 * <p>The default implementation returns the given {@code bean} as-is.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 * @see org.springframework.beans.factory.FactoryBean
	 */
        //初始化前方法,标识可以利用这个方法来对Bean在初始化前进行加工。
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

BeanPostProcessor子接口

InstantiationAwareBeanPostProcessor

Instantiation:实例化

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

	/**
	 * Apply this BeanPostProcessor <i>before the target bean gets instantiated</i>.
	 * The returned bean object may be a proxy to use instead of the target bean,
	 * effectively suppressing default instantiation of the target bean.
	 * <p>If a non-null object is returned by this method, the bean creation process
	 * will be short-circuited. The only further processing applied is the
	 * {@link #postProcessAfterInitialization} callback from the configured
	 * {@link BeanPostProcessor BeanPostProcessors}.
	 * <p>This callback will be applied to bean definitions with their bean class,
	 * as well as to factory-method definitions in which case the returned bean type
	 * will be passed in here.
	 * <p>Post-processors may implement the extended
	 * {@link SmartInstantiationAwareBeanPostProcessor} interface in order
	 * to predict the type of the bean object that they are going to return here.
	 * <p>The default implementation returns {@code null}.
	 * @param beanClass the class of the bean to be instantiated
	 * @param beanName the name of the bean
	 * @return the bean object to expose instead of a default instance of the target bean,
	 * or {@code null} to proceed with default instantiation
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see #postProcessAfterInstantiation
	 * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getBeanClass()
	 * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getFactoryMethodName()
	 */
    //实例化前
	@Nullable
	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	/**
	 * Perform operations after the bean has been instantiated, via a constructor or factory method,
	 * but before Spring property population (from explicit properties or autowiring) occurs.
	 * <p>This is the ideal callback for performing custom field injection on the given bean
	 * instance, right before Spring's autowiring kicks in.
	 * <p>The default implementation returns {@code true}.
	 * @param bean the bean instance created, with properties not having been set yet
	 * @param beanName the name of the bean
	 * @return {@code true} if properties should be set on the bean; {@code false}
	 * if property population should be skipped. Normal implementations should return {@code true}.
	 * Returning {@code false} will also prevent any subsequent InstantiationAwareBeanPostProcessor
	 * instances being invoked on this bean instance.
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see #postProcessBeforeInstantiation
	 */
    //实例化后
	default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}

	/**
	 * Post-process the given property values before the factory applies them
	 * to the given bean, without any need for property descriptors.
	 * <p>Implementations should return {@code null} (the default) if they provide a custom
	 * {@link #postProcessPropertyValues} implementation, and {@code pvs} otherwise.
	 * In a future version of this interface (with {@link #postProcessPropertyValues} removed),
	 * the default implementation will return the given {@code pvs} as-is directly.
	 * @param pvs the property values that the factory is about to apply (never {@code null})
	 * @param bean the bean instance created, but whose properties have not yet been set
	 * @param beanName the name of the bean
	 * @return the actual property values to apply to the given bean (can be the passed-in
	 * PropertyValues instance), or {@code null} which proceeds with the existing properties
	 * but specifically continues with a call to {@link #postProcessPropertyValues}
	 * (requiring initialized {@code PropertyDescriptor}s for the current bean class)
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @since 5.1
	 * @see #postProcessPropertyValues
	 */
    //属性注入后
	@Nullable
	default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
			throws BeansException {

		return null;
	}
}

P10 AOP是如何工作的?

AOP就是面向切面编程,是一种非常适合在无需修改业务代码的情况下,对某个或某些业务增加统一的功能,比如日志记录、权限控制、事务管理等,能很好地使得代码解耦,提供开发效率。

AOP中的核心概念

  • Advice:可以理解为通知、建议,在Spring中通过定义Advice来定义代理逻辑
  • Pointcut:是切点,标识Advice对应的代理逻辑应用在哪个类、哪个方法上。
  • Advisor:等于Advice + Poincut,标识代理逻辑和切点的一个整体,程序员可以通过定义或封装一个Advisor,来定义切点和代理逻辑。
  • Weaving:标识织入,将Advice代理逻辑在源代码级别嵌入到切点的过程,就叫织入。
  • Target:标识目标对象,也就是被代理对象,在AOP生成的代理对象中会持有目标对象。
  • Join Point:标识连接点,在Spring AOP中,就是方法的执行点。

AOP的工作原理

AOP是发生在Bean的生命周期过程中的:

  1. Spring 生成bean对象时,先实例化出来一个对象,也就是target对象
  2. 再对target对象进行属性填充
  3. 在初始化步骤中,会判断target对象有没有对应的切面
  4. 如果有切面,就表示当前target对象需要进行AOP
  5. 通过Cglib或JDK动态代理机制生成一个代理对象,最为最终的bean对象
  6. 代理对象中有一个target属性指向了target对象

P11 Spring之Bean的生命周期步骤详解

UserService类------->推断构造方法 ---------->对象------------->依赖注入------------>初始化前(@PostConstruct)--------->初始化(InitializingBean)-------------->初始化后(AOP)-------->代理对象--------->放入单例池Map -------->Bean

单例池: Map<‘bean名称’,bean对象>

Spring之Bean的生命周期步骤详解

P12 Spring之推断构造方法底层原理详解

Spring在自动推断构造方法的时候 ,推断原则:

  • 优先使用无参构造方法,此时无论该类有多少个构造方法;
  • 如果类中没有无参构造方法,只有一个构造方法,那么就用这个;
  • 如果有多个构造方法,么有无参构造方法,则报错;
  • 如果有多个构造方法,你可以指定Spring使用哪个构造方法,这样不会报错,使用方法是在指定的构造方法上面增加注解:@Autowired

@Autowired加载构造方法上时,Spring会在推断构造方法阶段,选择该构造方法来进行实例化,在反射调用构造方法之前,会先根据构造方法参数类型、参数名从Spring容器中找到Bean对象,当做构造方法入参(先根据类型,如果有多个,再根据参数名称寻找)。

@Autowired
public UserService(OrderService orderService){
  this.orderService = orderService;
}

这种情况下,如果spring容器中有多个 OrderService类的bean,但是名称不同,那么spring在这种情况下,就会先根据类型,再根据名称确定需要导入构造方法中的OrderService类型的bean对象。这个就是常说的,byType------>byName

P13 Spring之依赖注入底层原理详解

byType------>byName

属性赋值,构造方法参数赋值

P14 Spring之初始化前、初始化、初始化后详解

初始化前(@PostConstruct)--------->初始化(InitializingBean)-------------->初始化后(AOP)

P15 Spring之AOP底层实现原理详解

代理对象里面的属性是空的,源码角度是aop之后代理对象生成后面就没有属性注入了。

等到执行代理对象方法的时候,发现属性是有值的了,为什么呢?

UserServiceProxy--------------> UserService代理对象 ------->UserServiceProxy.target =UserService的普通对象 ------------->完成

UserService代理对象.test()--------------------->父类的test方法

//Cglib动态代理

class UserServiceProxy extends UserService(){

    UserService target;

    public void test(){
    先执行切面 @Before逻辑
    target.test();
    }

}

所以:等到执行代理对象方法的时候,发现属性是有值的了,因为UserService普通对象里面的OrderService属性是有值的!,是在UserService的bean生成过程中的属性注入的时候,注入的。

P16 Spring之事物及传播机制底层原理详解

下面数据会插入到数据库中吗?

@ComponentScan("com.zhouyu")
@EnableTransactionManagement
public class AppConfig{
    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(datasource());
    }
}

@Component
public class UserService{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional
    public void test(){
        jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");
        throw new NullPointerException();
    }
}

答案:会

为什么没回滚????

因为,配置AppConfig缺少注解:@Configuration,增加以后,就可以了,为什么呢为什么这个在注解会影响到Spring事务呢? ======》查看本文档P23

@ComponentScan("com.zhouyu")
@EnableTransactionManagement
@Configuration
public class AppConfig{
    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(datasource());
    }
}

@Transactional注解:内部逻辑:

1.新建一个数据库链接 conn;

2.设置 autoCommit = false; //不能再自动提交了,因为考虑到会存在回滚的操作

spring七中事务传播机制
REQUIRED (默认)(TransactionDefinition.PROPAGATION_REQUIRED)支持当前事务,如果有事务就加入到当前事务,没有则会创建一个新的事务。
SUPPORTS (TransactionDefinition.PROPAGATION_SUPPORTS)支持当前事务,如果有事务就加入到当前事务,如果没有事务的话,以非事务的方式执行。
MANDATORY(强制性的)(TransactionDefinition.PROPAGATION_MANDATORY)支持当前事务,当前如果没有事务就抛出异常
REQUIRES_NEW (TransactionDefinition.PROPAGATION_REQUIRES_NEW)不管是否有事务,都会创建一个新事物,并挂起当前事务。
NOT_SUPPORTED (TransactionDefinition.PROPAGATION_NOT_SUPPORTED)以非事务的方式执行,如果当前存在事务,就将当前事务挂起。
NEVER (TransactionDefinition.PROPAGATION_NEVER)以非事务方式进行,如果存在事务则抛出异常
NESTED(嵌套) (TransactionDefinition.PROPAGATION_NESTED)如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,创建一个新的事务。

https://blog.csdn.net/weixin_43587472/article/details/112300696

注意:spring事务的传播在类内部方法中调用时是失效的

因为 spring 事务管理是通过 AOP 来实现的,外部类调用时会先调用代理类,代理类再调用实际类

而类内部调用时,是实际类内部调用,不经过代理类,而事务管理实在代理类层面实现,所以类内部调用事务管理会失效。

也就是说,只要涉及到AOP的地方,只有代理对象调用的方法上面的注解(该注解涉及到AOP)才会生效。

@Component
public class UserService{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional
    public void test(){
        jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");
        a();
    }
    
    @Transactional(progation=Progation.NEVER)
    public void a(){
      jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");  
    }
}

public class Test{
    @Autowired
    UserService userService;
    public static void main(String[] args) {
        userService.test();
    }
}

上述这种情况下,根据事务传播机制,调用a方法的时候,a上面的事务不会生效,解决办法有两种:

  1. 把a方法独立出一个类,这样可以把这个类注入UserService中,注入的是独立出来的类的代理类。所以AOP生效,注解生效;

  2. 自己注入自己,上述代码修改为:

    @Component
    public class UserService{
        @Autowired
        private JdbcTemplate jdbcTemplate;
        
        @Autowired
        private UserService userService;
        
        @Transactional
        public void test(){
            jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");
            userService.a();
        }
        
        @Transactional(progation=Progation.NEVER)
        public void a(){
          jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");  
        }
    }
    

    这里自己注入自己,注入的是Spring bean,也就是代理对象,注解生效。

sdd

p17 Spring之Configuration底层原理详解

p18 @Value你真的会用吗

参考P6

可以注入bean

@Component
public class UserService{
    @Value("#{zhouyu}")
    private OrderService test;
    
    public void test(){
        System.out.println(test);
    }
}

@Value还可以放到注解上面

进行赋值,这样这个@Value的复制操作,就不需要复制很多遍了,只要用注解的赋值即可。Springboot就是这么做的

解决的场景就是一个@Value属性的值需要在很多地方用到,基本方法就是

@Value("#{local.host.port}")
private String test;

在很多地方,都复制过去,比较省事的方法就是这样解决。

@Target({ElementType.TYPE......})
@Retention(RetentionPolicy.RUNTIME)
@Value("${local.host.port}")
public @interface LocalServerPort{
    
}

//使用
@Component
public class UserService{
    @LocalServerPort
    private String test;
    
    public void test(){
        System.out.println(test);
    }
}

P19 @Bean的autowired属性

一种不用@Autowired注入bean的方法:

public class OrderService {
}



public class UserService {
        //这里没有注解哦
    private OrderService orderService;
    
    public OrderService getOrderService() {
        return orderService;
    }

    //这个不能少
    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }


    public void test(){
        System.out.println(orderService);
    }

}


@ComponentScan("com.zhouyu")
public class AppConfig {

    @Bean(autowire = Autowire.BY_NAME)
    //@Bean(autowire = Autowire.BY_TYPE)
    public  UserService userService(){
        return new UserService();
    }
}

@Bean的autowire属性已经标记为过期,这种方法不好控制具体哪些属性可以赋值,哪些不能赋值。

原理是在生成UserService这个bean的过程中,回去找这个bean中属性的setter方法,进行根据type或者name进行注入。

P20 @Bean的autowireCandidate属性

candidate:候选人

autowireCandidate:默认是true,有什么用处呢

举个例子

public class OrderService {
}

public class UserService {

    @Autowired
    private OrderService orderService;

    public void test(){
        System.out.println(orderService);
    }
}

@ComponentScan("com.zhouyu")
public class AppConfig {

    @Bean
   public OrderService orderService(){
       return new OrderService();
   }

}

如果调用 user.servcie.test方法,没有问题。

如果是这种呢

@ComponentScan("com.zhouyu")
public class AppConfig {

    @Bean
   public OrderService orderService(){
       return new OrderService();
   }

    @Bean
    public OrderService orderService1(){
        return new OrderService();
    }
   
}

@Autowired我们知道是先byType在byName,这里就是byName可以确定,所以bean的名称是orderService和orderService1,注入的是orderService,也没有问题。

如果是这种呢

@ComponentScan("com.zhouyu")
public class AppConfig {

    @Bean
   public OrderService orderService2(){
       return new OrderService();
   }

    @Bean
    public OrderService orderService1(){
        return new OrderService();
    }

}

可以看到有问题了,两个bean的名称没有orderService。那么怎么修改呢?

有一种修改方式

@ComponentScan("com.zhouyu")
public class AppConfig {

    @Bean
   public OrderService orderService2(){
       return new OrderService();
   }

    @Bean(autowireCandidate = false)
    public OrderService orderService1(){
        return new OrderService();
    }

}

这里两个bean名称都不同,但是在根据type选择的时候,已经排除了一个(autowireCandidate = false), 只有一个,所有没问题。

autowireCandidate意思是说:当前bean能不能作为依赖注入的候选者false的话,也就是这个bean不能被其他bean依赖注入。

P21 @Bean与@Component

在@Component注解中写@Bean也会生效的,比如:

@ComponentScan("com.zhouyu")
public class AppConfig {
}

public class OrderService {
}

@Component
public class UserService {

    @Bean
    public OrderService orderService(){
        return new OrderService();
    }
}

OrderService这个bean也会出现在Spring容器中。

也就是说@bean注解不是一定要和@Configuration注解一起用,只是建议。

P22 @Bean之自定义注解

@Bean除了可以写在方法上,也能写在某个注解上,使得可以自定义注解来模拟@Bean的功能

自定义一个多例bean:可以这样实现,需要两个注解:

@bean
@Scope("prototype")
public UserService userService(){
    return new UserService();
}

也可以这样:相当于合并注解:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Bean
@Scope("prototype")
public @interface PrototypeBean {
}

@PrototypeBean
public UserService userService(){
    return new UserService();
}

p23 @Bean和@Configuration的关系

@Configuration

举个例子:

public class OrderService {
}

public class UserService {
}


@ComponentScan("com.zhouyu")
public class AppConfig {

    @Bean
    public OrderService orderService(){
        return new OrderService();
    }

    @Bean
    public UserService userService(){
        System.out.println(orderService());
        System.out.println(orderService());
        return new UserService();
    }

}

//测试:
public class MainTest {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(applicationContext.getBean("appConfig"));
    }

}

可以看到,两个OrderService的bean打印出来是不一样的,说明这不是同一个bean

com.iflytek.picc.test.OrderService@69b794e2
com.iflytek.picc.test.OrderService@3f200884
com.iflytek.picc.test.AppConfig@5b0abc94
    
Process finished with exit code 0

AppConfig 增加@Configuration 注解以后,发现结果:

com.iflytek.picc.test.OrderService@3cc2931c
com.iflytek.picc.test.OrderService@3cc2931c
com.iflytek.picc.test.AppConfig$$EnhancerBySpringCGLIB$$412d9b9b@63355449
    
Process finished with exit code 0

可以看到是同一个bean,此外,我们打印的Appconfig这个bean出现了变化,具体就是增加了@Configuration以后,Appconfig这个配置类,变成了一个cglib的代理对象。

这里就解释了为什么P16里面,缺失**@Configuration**注解后,会导致事务的注解失效。因为 datasource生成的时候不是同一个bean。

原理:

@Configuration注解 增加以后,

  • Appconfig这个配置类,变成了一个cglib的代理对象

  • 代理对象会负责去执行orderService()方法,会先判断这个对应的bean有没有 创建,

    • 没有的话,才会执行这个方法;
    • 有的话,就会直接返回这个bean,

    这个就解释了为什么增加了注解以后,返回的两个bean是一样的。

P24 @ComponentScan你真的会用吗 ?

1.扫描@Component注解

@ComponentScan:定义扫描路径;扫描@Component注解,默认情况下Spring会把扫描路径下所有加了@Component注解的类扫描出来,并生成对应的Bean对象。

2.自定义过滤条件,符合条件的才是Bean

另外还有两个属性:

Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};

举个例子:includeFilters 可以把一个注解注释的类 变成Bean

@ComponentScan("com.zhouyu")
@Configuration
public class AppConfig {

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Zhouyu {
}

@Zhouyu
public class UserService {
}

public class MainTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(applicationContext.getBean("userService"));
    }

}

执行结果:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userService' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:816)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1288)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1109)
	at com.zhouyu.springdemo.service.MainTest.main(MainTest.java:16)

Process finished with exit code 1

好的,我们修改下@ComponentScan注解:

@ComponentScan(value = "com.zhouyu",includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,value = Zhouyu.class))
@Configuration
public class AppConfig {

}

再次测试:

com.zhouyu.springdemo.service.UserService@2f177a4b

Process finished with exit code 0

解释:

includeFilters

@ComponentScan(value = “com.zhouyu”,includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,value = Zhouyu.class))

扫描的路径是value的值,可以用参数includeFilters(“包含过滤器”)指定符合某种条件下的类成为是bean,比如这个就是符合的条件是上面用了注解是@Zhouyu的类。

也就是说:可以自定义过滤条件,符合条件的才是Bean

  • ANNOTATION:可以指定某个注解,表示类上有这个注解的才是Bean;
  • ASSIGNABLE_TYPE:可以指定某个类,表示只有这个类才是Bean;
  • ASPECTJ:可以指定一个Apsectj表达式,表示符合这个表达式的才是Bean;
  • REGEX:可以指定一个正则表达式,表示符合这个表达式的才是Bean;
  • CUSTOM:可以指定有一个符合TypeFilter实现类,自定义匹配逻辑;

3.自定义过滤条件,符合条件的不是Bean

excludeFilters

相反,这个是排除过滤器,指定符合某个条件的类不能是bean。

p25 @CompenentScan之扫描索引

跟@ComponentScan注解没关系,跟扫描有关系;

默认情况下,Spring扫描时有可能会扫描很多的类,具体看扫描路径下的类的数量,这个过程可能比较慢;

在Spring中提供了一种加快扫描的方式,也就是扫描索引,不过这个索引扫描需要程序员自己去创建一个META-INF/spring.components文件,并在这个文件中指定哪些类是Bean,对于Spring而言,它就会去读取这个文件中的内容,并把文件中的类当做Bean,速度会比较快,当然需要程序员自己的建立这个索引。

增加了META-INF/spring.components 以后,Spring不会在去扫描@ComponentScan注解指定的了路径。

应该是SPI机制。

P26 @Conditional你真的会用吗?

例子

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	/**
	 * All {@link Condition} classes that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();

}

这个注解的值是一个实现Condition接口的类的数组,Condition

package org.springframework.context.annotation;

import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.type.AnnotatedTypeMetadata;

@FunctionalInterface
public interface Condition {

	/**
	 * Determine if the condition matches.
	 * @param context the condition context
	 * @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
	 * or {@link org.springframework.core.type.MethodMetadata method} being checked
	 * @return {@code true} if the condition matches and the component can be registered,
	 * or {@code false} to veto the annotated component's registration
	 */
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

举个例子:

自定义实现Condition接口的类

public class ZhouyuCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }
}
@Component
@Conditional(value ={ZhouyuCondition.class} )
public class UserService {
}

就是说 UserService这个要成为一个Bean,需要满足条件:@Conditional(value ={ZhouyuCondition.class} ),也就是Condition接口的match方法返回true,所以上面的例子肯定不会是一个Bean。

说明

@Conditional :条件注解:

  • TYPE:Spring在扫描的时候,如果发现一个类上存在@Conditional ,那么就会获取改注解中所配置的Condition接口的实现类,并调用match方法,判断是否匹配,匹配则把该类生成Bean。
  • METHOD:当把@Conditional 注解加在一个@Bean注解的方法上时,Spring在解析@Bean方法时,会发现一个@Conditional 注解,从而去判断是否符合当前条件,符合才会生成这个对应的Bean。
Condition接口

Condition接口中方法match的两个参数:

  • ConditionContext:可以拿到一些Spring容器相关的东西

    • BeanFactory:比如可以用来判断当前BeanFactory是否存在某个Bean

      public interface ConditionContext {
      	BeanDefinitionRegistry getRegistry();
      	@Nullable
      	ConfigurableListableBeanFactory getBeanFactory();
      	Environment getEnvironment();
      	ResourceLoader getResourceLoader();
      	@Nullable
      	ClassLoader getClassLoader();
      }
      
    • Enviroment:比如可以用来判断当前环境变量中是否存在某个key

    • ClassLoader:比如可以用来判断当前是否存在某个类

  • AnnotatedTypeMetadata:@Conditional注解所在类上的元数据信息:比如可以利用它来判断当前类上是否存在某个注解

P27 @Autowired你真的会用吗

参考:[P4 @Autowired是如何工作的?](## P4 @Autowired是如何工作的?)

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

	/**
	 * Declares whether the annotated dependency is required.
	 * <p>Defaults to {@code true}.
	 */
	boolean required() default true;

}

注解可以

  • 加在属性上、
  • 普通方法(无论是不是setter)上、
  • 构造方法上、
  • 某个方法的参数前面(绝大部分没啥用,springframework框架会忽略,只有某些特定框架有用),
  • 某个注解上面;

P28 @Autowired之Set注入你不知道的

普通方法(无论是不是setter)上

也是先type在name

P29 @Autowired之构造方法注入你不知道的

参考:[P12 Spring之推断构造方法底层原理详解](## P12 Spring之推断构造方法底层原理详解)

举个例子:

@Component
public class UserService {

    private OrderService orderService;

    public UserService(OrderService orderService) {
        this.orderService = orderService;
        System.out.println(1);
    }

    public UserService(OrderService orderService,OrderService orderService1) {
        this.orderService = orderService;
        System.out.println(2);
    }
}

在产生UserService这个Bean的时候,会报错,因为不知道选择哪个。

当没有定义构造方法时每个类里都有一个默认的无参的构造方法,此时该类就只有一个构造方法;而当你显示定义类的构造方法时,那就没有那个默认的构造方法了,该类所以的构造方法就是定义了的那些构造方法。

Spring在自动推断构造方法的时候 ,推断原则:

  • 优先使用无参构造方法(默认),此时无论该类有多少个构造方法;
  • 如果类中没有无参构造方法,只有一个构造方法,那么就用这个;
  • 如果有多个构造方法,么有无参构造方法,则报错;
  • 如果有多个构造方法,你可以指定Spring使用哪个构造方法,这样不会报错,使用方法是在指定的构造方法上面增加注解:@Autowired

@Autowired加载构造方法上时,Spring会在推断构造方法阶段,选择该构造方法来进行实例化,在反射调用构造方法之前,会先根据构造方法参数类型、参数名从Spring容器中找到Bean对象,当做构造方法入参(先根据类型,如果有多个,再根据参数名称寻找)。

@Autowired
public UserService(OrderService orderService){
  this.orderService = orderService;
}

这种情况下,如果spring容器中有多个 OrderService类的bean,但是名称不同,那么spring在这种情况下,就会先根据类型,再根据名称确定需要导入构造方法中的OrderService类型的bean对象。这个就是常说的,byType------>byName

P30 @Autowired之方法参数前的作用

举个例子:

    public UserService(@Autowired OrderService orderService) {
        this.orderService = orderService;
    }

某个方法的参数前面(绝大部分没啥用,springframework框架会忽略,只有spring-test模块中的对于Junit-Jupiter框架有用 支持中,支持了参数前@Autowired注解

Spring源码解释:

Autowired Parameters
Although @Autowired can technically be declared on individual method or constructor parameters since Spring Framework 5.0, most parts of the framework ignore such declarations. The only part of the core Spring Framework that actively supports autowired parameters is the JUnit Jupiter support in the spring-test module (see the TestContext framework  reference documentation for details).
Multiple Arguments and 'required' Semantics

P31 @Autowired之自定义使用

也就是说集成到其他自定义注解里面,扩展使用,自定义注解也拥有了@Autowired注解的功能。

举个例子:

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Autowired
public @interface ZhouyuAutowired {
}

@Component
public class UserService {

    @ZhouyuAutowired
    private OrderService orderService;

    public UserService(OrderService orderService) {
        this.orderService = orderService;

    }

}

P32 @Autowired之static属性分析

static字段或方法是不会进行依赖注入的

@Component
public class UserService {

    @ZhouyuAutowired
    private static OrderService orderService;

    public UserService(OrderService orderService) {
        this.orderService = orderService;

    }

}

类似这样,会报错。

P33 @Lazy注解你真的会用吗

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {

	/**
	 * Whether lazy initialization should occur.
	 */
	boolean value() default true;

}

分类和作用

  • TYPE:
    • 当把**@Lazy注解写在某个类上(该类上有@Component注解)时,表示该Bean,是一个懒加载**的Bean,表示该Bean是在用到时才去创建,而不是Spring启动时创建;
    • 当把**@Lazy注解写在一个@Configuration注解所在类时,表示该类内部所定义所有@Bean都是懒加载的Bean**;
  • FIELD:当把**@Lazy注解加在一个字段上**时,Spring会给该属性赋值一个由CGLIB所生成的一个代理对象,当该代理对象执行某个方法时,才会真正的根据类型的类型和名称从Spring容器去找到某个Bean对象,并执行该Bean对象所对应的方法;
  • METHOD:当把**@Lazyz注解写在一个@Autowired注解所在的方法**上时,那么Spring会给该方法的所有入参赋值一个代理对象;
  • PARAMETER:当把@Lazy注解写在一个@Autowired注解所在方法的某个参数前时,那么Spring会给该入参赋值一个代理对象;
  • CONSTRUCTOR:
    • 当把@Lazy注解写在一个@Autowired注解所在构造方法上时,那么Spring会给该方法的所有入参赋值一个代理对象;
    • 注意:可以解决循环依赖

默认bean不是懒加载的

@Component
public class UserService {
}

public class MainTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println("userService在Spring容器启动后是否存在:"+applicationContext.getBeanFactory().containsSingleton("userService"));
        System.out.println(applicationContext.getBean("userService"));
    }

}

输出:

userService在Spring容器启动后是否存在:true
com.zhouyu.springdemo.service.UserService@68c9d179

Process finished with exit code 0

加上注解@Lazy:

@Component
@Lazy
public class UserService {
}

结果,可以看到Bean对象userService是后续用到的时候才生成的。

userService在Spring容器启动后是否存在:false
16:01:50.610 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
com.zhouyu.springdemo.service.UserService@2dfaea86

Process finished with exit code 0

P34 @Lazy加在字段上的作用有哪些(解决循环依赖

延迟给属性赋值

FIELD:当把**@Lazy注解加在一个字段上时,Spring会给该属性赋值一个由CGLIB所生成的一个代理对象**,当该代理对象执行某个方法时,才会真正的根据类型的类型和名称从Spring容器去找到某个Bean对象,并执行该Bean对象所对应的方法;

举个例子:

@Component
public class OrderService {

    public void test(){
        System.out.println("orderService.test");
    }

}

@Component
public class UserService {

    @Autowired
    @Lazy
    private OrderService orderService;

    public void test(){
        System.out.println(orderService);
        orderService.test();//代理逻辑,bean对象.test()
    }

}

public class MainTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println("userService在Spring容器启动后是否存在:"+applicationContext.getBeanFactory().containsSingleton("userService"));
        UserService userService = (UserService)applicationContext.getBean("userService");
        System.out.println(userService);
        userService.test();
    }

}

debug看下:

在这里插入图片描述
可以明显看到这个时候的orderService对象时CGLIB动态代理生成的代理对象。

在属性上面增加@Lazy注解可以在解决循环依赖的问题上。

循环举个例子:依赖,

@ComponentScan(value = "com.zhouyu")
@Configuration
public class AppConfig {
}

@Component
public class OrderService {

    @Autowired
    private UserService userService;
    
    public void test(){
        userService.test();
    }
}

@Component
public class UserService {

    @Autowired
    private OrderService orderService;

    public void test(){
        System.out.println(orderService);
    }

}

public class MainTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println("userService在Spring容器启动后是否存在:"+applicationContext.getBeanFactory().containsSingleton("userService"));
        UserService userService = (UserService)applicationContext.getBean("userService");
        System.out.println(userService);
        userService.test();
    }

}

这种情况下默认是解决了循环依赖的,是用三级缓存的。我们加点东西:开启异步执行:

@ComponentScan(value = "com.zhouyu")
@Configuration
@EnableAsync  //新增的
public class AppConfig {
}

@Component
public class OrderService {

    @Autowired
    private UserService userService;

    @Async  //新增的
    public void test(){
        userService.test();
    }
}

@Component
public class UserService {

    @Autowired
    private OrderService orderService;

    public void test(){
        System.out.println(orderService);
    }

}

public class MainTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println("userService在Spring容器启动后是否存在:"+applicationContext.getBeanFactory().containsSingleton("userService"));
        UserService userService = (UserService)applicationContext.getBean("userService");
        System.out.println(userService);
        userService.test();
    }

}

执行结果:

16:33:35.992 [main] WARN org.springframework.context.annotation.AnnotationConfigApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'orderService': Bean with name 'orderService' has been injected into other beans [userService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'orderService': Bean with name 'orderService' has been injected into other beans [userService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:623)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
	at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)
	at com.zhouyu.springdemo.service.MainTest.main(MainTest.java:14)

Process finished with exit code 1

可以看到出现了循环依赖问题,这里就是spring的三级缓存也解决不了的问题。好的,我们增加一个@Lazy注解:

@Component
public class OrderService {

    @Autowired
    @Lazy
    private UserService userService;

    @Async
    public void test(){
        userService.test();
    }
}

其他类不变,重新执行结果:

userService在Spring容器启动后是否存在:true
com.zhouyu.springdemo.service.UserService@75c56eb9
com.zhouyu.springdemo.service.OrderService@56dc1551

Process finished with exit code 0

可以看到,这种循环依赖的问题,已经解决了。(周瑜老师这里没说,笔者猜测:应该是加载时间延迟,可以在两个Bean都生成好了之后,才注入的,可以规避这个问题的吧。

P35 @Lazy在方法上、参数前有哪些作用

举个例子:

@Component
public class UserService {

    private OrderService orderService;

    @Autowired
    @Lazy
    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }

    public void test(){
        System.out.println(orderService);
    }

}

我们知道:

当@Autowired注解写在某个方法上时,Spring在Bean生命周期的属性填充阶段,会根据方法的参数类型、参数名字从Spring容器找到对象当做方法入参,自动反射调用该方法。

如果这个时候加上了@Lazy注解,那么这个时候传入的bean(orderService)是一个代理对象,也就是属性orderService先指向一个代理对象,在真正使用这个orderService的方法时,才会调用bean对象的实例,去执行对应的逻辑。

@Lazy注解加在方法的参数前,也是先指向一个代理对象,和上面说的一样。举个例子:

@Component
public class UserService {

    private OrderService orderService;

    @Autowired
    public void setOrderService(@Lazy OrderService orderService,OrderService orderService1) {
        this.orderService = orderService;
    }

    public void test(){
        System.out.println(orderService);
    }

}

我们debug看下,执行到setOrderService方法的时候:
在这里插入图片描述

P36 @Lazy在构造方法上,有哪些作用?

  • 当把@Lazy注解写在一个@Autowired注解所在构造方法上时,那么Spring会给该方法的所有入参赋值一个代理对象;

  • 此外,总所周知,由构造方法引发的循环依赖,Spring是无法解决的,可以用@Lazy注解解决

举个例子:

@Component
public class UserService {

    private OrderService orderService;

    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }

    public void test(){
        System.out.println(orderService);
    }
}

@Component
public class OrderService {

    private UserService userService;

    public OrderService(UserService userService) {
        this.userService = userService;
    }
}

public class MainTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println("userService在Spring容器启动后是否存在:"+applicationContext.getBeanFactory().containsSingleton("userService"));
        UserService userService = (UserService)applicationContext.getBean("userService");
        System.out.println(userService);
    }

}

结果:

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderService' defined in file [D:\IDEA-workSpace\就是玩\SpringDemo\target\classes\com\zhouyu\springdemo\service\OrderService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService' defined in file [D:\IDEA-workSpace\就是玩\SpringDemo\target\classes\com\zhouyu\springdemo\service\UserService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'orderService': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:799)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1356)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1203)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
	at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)
	at com.zhouyu.springdemo.service.MainTest.main(MainTest.java:14)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService' defined in file [D:\IDEA-workSpace\就是玩\SpringDemo\target\classes\com\zhouyu\springdemo\service\UserService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'orderService': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:799)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1356)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1203)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1307)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:886)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:790)
	... 14 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'orderService': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1307)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:886)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:790)
	... 28 more

Process finished with exit code 1

很明显,构造器引发的循环依赖 Spring默认无法解决。

我们在任一构造方法上面增加@Lazy注解:

@Component
public class UserService {

    private OrderService orderService;

    @Lazy
    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }

    public void test(){
        System.out.println(orderService);
    }

}

再次执行:

userService在Spring容器启动后是否存在:true
com.zhouyu.springdemo.service.UserService@35a3d49f
com.zhouyu.springdemo.service.OrderService@1b410b60

Process finished with exit code 0

可以看到,循环依赖解决了。

未完待续…

仅学习使用,侵权速删!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值