不断的学习,我们才能不断的前进
一个好的程序员是那种过单行线马路都要往两边看的人

Spring

IOC

Spring Ioc指的是控制反转( 就是依赖倒置原则的一种代码设计的思路,具体采用的方法就是依赖注入),就是把原本在程序中手动创建对象的控制权,交给IOC容器来控制,IOC容器负责对象的创建、赋值和管理,降低了系统的耦合度。只需要知道对象的名称即可,通过名字获取Bean对象。可以通过XML配置 和 注解两种方式来实现。 IoC 容器实际上就是个Map,存放各种对象。

IOC初始化过程:

springchu-shi-hua-bean

IOC源码分析: 主要过程:

  1. 解析提供的配置文件路径
  2. 准备工作:记录容器的启动时间、标记状态
  3. obtainFreshBeanFactory 会初始化 BeanFactory、加载 Bean、注册 Bean;获取到beanFactory容器。(把定义的Bean解析成beanDefinition,用于保存bean的信息,内部是beanName-->beanDefinition的一个Map),这个步骤会实例化Bean。
  4. 设置BeanFactory的类加载器,加载一些特殊的Bean:environmentsystemProperties等。
  5. 添加并执行BeanFactoryPostProcessor 里面的postProcessBeanFactory(factory) 方法
  6. 执行初始化之前的方法:postProcessBeforeInitialization
  7. 执行初始化
  8. 执行初始化之后的方法:postProcessAfterInitialization
  9. 初始化所有的单例Bean

实例化只是在内存里面创建一个Bean对象,但是没给这个Bean对象初始化

AOP

AOP是面向切面编程,能够将与业务无关,但与业务模块所共同调用的逻辑或责任的代码进行封装(比如日志管理、权限控制等)。AOP的实现是通过JDK动态代理 和 Cglib动态代理来实现的。如果代理的对象,实现了某个接口,那么就会使用JDK动态代理,否则使用Cglib生成一个代理对象的子类来实现动态代理。
AOP术语定义:

  • 连接点(join point):对应的是具体被拦截的对象,因为spring只支持方法,所以被拦截的对象往往就是特定的方法
  • 切点(point cut):有时候切面不单单应用于单个方法,也有可能是多个类的不同方法,这时候通过正则式和指示器的规则去定义,从而适配链接点。切点就是提供 这样一个功能的概念。
  • 通知(advice):就是按照约定的流程下的方法,分为前置通知(before advice)、后置通知(after advice)、环绕通知(around advice)、事后返回通知(afterReturning advice)和异常通知(afterThrowing advice)
  • 引入(introduction):是指新引入的类和其方法,增强现有的Bean功能
  • 织入(weaving):它是通过一个动态代理,为原有的服务生成代理对象,然后与切点定义匹配的连接点拦截,并按约定将各类通知织入约定流程的过程
  • 切面(aspect):是一个可以定义切点、各类通知和引入的内容,SpringAop将通过它的信息来增强Bean功能或者将对应的方法织入流程。
    aopliu-cheng

需要把切面类注入到Bean 容器里面:在application.yml文件里面增加以下内容:

    <!--方式三,使用注解-->
    <bean id="annotationpointcut" class="com.ljh.log.AnnotationPointCut"/>
    <bean id="annotationpointcut1" class="com.ljh.log.AnnotationPointCut1"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>

Java Demo


@Aspect // 标注这个类是一个切面
public class AnnotationPointCut {

    /**
     * 定义切点,作用就是向Spring描述哪些累的哪些方法需要启用AOP;
     * 括号里面是正则表达式:
     *  execution 表示在执行的时候,拦截里面的正则匹配的方法。
     *  * 表示返回任何类型的方法
     *  com.ljh.service.UserServiceImple 表示指定目标对象的全限定名称
     *  .* 表示指定目标对象的所有方法
     *  (..) 表示任意参数进行匹配
     */
    @Pointcut("execution(* com.ljh.service.UserServiceImple.*(..))")
    public void pointCut(){}

    // @Before("execution(* com.ljh.service.UserServiceImple.*(..))")
    // public void before(){
    //     System.out.println("执行方法之前");
    // }
    // @After("execution(* com.ljh.service.UserServiceImple.*(..))")
    // public void after(){
    //     System.out.println("执行方法之后");
    // }
    @Before("pointCut()")
    public void before(){
        System.out.println("执行方法之前");
    }
    @After("pointCut()")
    public void after(){
        System.out.println("执行方法之后");
    }

    /**
     * 在环绕方法中,我们可以定义一个参数,代表我们要获取处理的切入点;
     * 环绕通知用于替换目标对象的服务逻辑
     * @param jp
     * @throws Throwable
     */
    @Around("pointCut()")
    public void arround(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕之前");

        Signature signature = jp.getSignature(); //获得签名
        System.out.println("签名为:" + signature);

        Object proceed = jp.proceed();// 执行目标对象原有方法,会输出执行方法之前、执行方法、执行方法之后

        System.out.println("环绕之后");


    }

}

public interface UserService {
    public  void insert();
    public  void update();
    public  void delete();
    public  void select();
}
public class UserServiceImple implements UserService{
    public void insert() {
        System.out.println("成功插入数据");
    }

    public void update() {
        System.out.println("成功更新数据");
    }

    public void delete() {
        System.out.println("成功删除数据");
    }

    public void select() {
        System.out.println("成功选择数据");
    }
}
public class MyTest {
    @Test
    public  void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        // 动态代理接口
        UserService usersvice = context.getBean("usersvice", UserService.class);
        usersvice.delete();
    }
}
// 输出
环绕之前
签名为:void com.ljh.service.UserService.delete()
执行方法之前
成功删除数据
执行方法之后
环绕之后

DeclareParents

DeclareParents用来增强类,主要的原理是动态代理,把增强的接口的实现类,作为接口传入动态代理,然后通过强制转换成增强类,来做增强的处理逻辑。
需求,增加一个功能,删除数据时,只删除id>2的数据,所以需要在原来的功能上进行增强:


public interface Validator {
	// 坚持输入参数是否大于2
	public boolean validate(int a);
}
public class ValidatorImpl implements Validator {
	public boolean validate(int a) {
		System.out.println("引入新的接口:"+ ValidatorImpl.class.getSimpleName());
		return a>2;
	}
}

@Aspect // 标注这个类是一个切面
public class AnnotationPointCut {
    /**
     * 作用是引入新的类来增强服务,第一个value表示要增强的目标对象,第二个defaultImpl表示引入增强功能的类
     */
    @DeclareParents(
            value = "com.ljh.service.UserServiceImple+",
            defaultImpl = ValidatorImpl.class
    )
    public Validator dataValidator;
}



// 测试类
public class MyTest {
    @Test
    public  void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        // 动态代理接口
        UserService usersvice = context.getBean("usersvice", UserService.class);
        Validator validator = (Validator) usersvice;
        if(validator.validate(3)){ // 验证删除的id 是否大于2
            usersvice.delete(3);
        }

    }
}
// 结果
引入新的接口:ValidatorImpl
成功删除数据3

通知获取参数

把参数传递给通知:

@Aspect // 标注这个类是一个切面
public class AnnotationPointCut {
    
     /**
     * 在通知里面传递参数
     * @param point
     * @param a
     */
    @Before("pointCut() && args(a)")
    public void beforeParam(JoinPoint point,int a){
        Object[] args=point.getArgs();
        System.out.println("执行方法之前,并传递参数"+a);
    }
}

多个切面

定义多个切面的时候,切面的执行顺序是混乱的,所以需要指定顺序,只需要在切面类上面添加一个@Order(1) 注解,表示指定执行顺序。

Bean

在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。Bean是由Spring IoC容器实例化,组装和管理的对象。否则,bean仅仅是应用程序中许多对象之一。Bean及其之间的依赖关系反映在容器使用的配置元数据中。

Bean的作用域

  • singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
  • prototype : 每次请求都会创建一个新的 bean 实例。
  • request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
  • session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
  • global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话

Bean 生命周期

  • Bean 容器找到配置文件中 Spring Bean 的定义。
  • Bean 容器利用 Java Reflection API 创建一个Bean的实例。实例化Bean对象。
  • 如果涉及到一些属性值 利用 set()方法设置一些属性值。设置Bean对象属性。
  • 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。检查Aware相关的接口。
  • 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
  • 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
  • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法。 前置处理BeanPostProcessor。
  • 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。执行afterPropertiesSet()初始化。
  • 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。检查自定义的初始化。
  • 如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法。后置处理BeanPostProcessor。
  • 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
  • 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
    beanzhou-qi

依赖注入查找Bean的过程

按Type、再按Name;先查找出满足所有该类型的Bean,然后过滤掉AutowiredCondidate=false的bean,然后查看是否满足Qualifier,然后返回Primary Bean , 然后返回优先级最高的Bean、最后ByName 过滤

  • Autowire注入方法和属性的不同:都是注入Bean对象,都是先查找Type 再查找Name;写在属性上,类型作为Type,属性名作为BeanName;写在方法上,方法参数类型作为Type,方法名作为BeanName

  • Resource属性,如果指定了Name,则直接在Bean容器里面按照BeanName查找;如果没有指定Name属性,但是根据属性名或setXXX方法的XXX名字在Bean容器里面有对应的Bean,则直接再容器里面 查找;否则先按Type

  • AutoWire 和 Resouce不同:对于方法:Resource是根据方法setXXX后面的XXX作为BeanName,而Autowired是根据方法名;AutoWire是先查找Type 在查找Name,而Resouce是根据Name再根据Type。

实例化Bean的过程

对于单例模式的Bean,在容器的初始化过程中就会进行实例化。对于原型模式的bean,则采用懒加载的方式去创建。

Bean的解析

首先通过调用ClassPathResource 的构造函数来构造Resource资源文件的实例对象,这样后续的资源处理就可以用Resource 提供的各种服务来操作了,当我们有了Resource 后就可以进行XmlBeanFactory 的初始化;通过XmlBeanDefinitionReader.loadBeanDefinitions(resource) 进行资源的解析。

BeanFactory bf= new XmlBeanFactory (new ClassPathResource (” beanFactoryTest.xml ”));
// 解析配置文件xml或者配java配置类,使用BeanDefinitionReader去处理配置信息,对于XML文件使用的是XMLBeanDefinitionReader的实现类,将配置解析成BeanDefinition接口的实现类,BeanDefinition里面包括class类型信息、Bean的生命周期(单例还是原型)、别名信息、是否懒加载等,解析成BeanDefinition后注入到容器里面,用于后面Bean的创建,这个容器里面key是beanName,value是对应的BeanDefinition对象
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
				new ClassPathResource("simpleConstructorNamespaceHandlerTestsWithErrors.xml", getClass()));
bf.getBean("123");// 进行bean的加载,获取bean对象

Bean的加载

通过对XML 配置文件的解析,获取到了BeanDefinition,则现在需要通过beanName 获取到Bean的实例,进入方法getBean():

  1. 进入dogetBean()方法,首先提取对应的beanname,因为传入的有可能是bean的别名,所以需要找到真的beanName
  2. 直接尝试从缓存获取单例Bean 或者 singletonFactories 中的ObjectFactory 中获取( 因为创建单例Bean的时候有依赖注入,就存在循环依赖的情况,所以在Spring 创建 bean 的原则是不等bean 创建完成就会将创建bean 的ObjectFactory ,提早将ObjectFactory 加入到缓存中, 一旦下个bean 创建时要依赖上个bean 则直接使用ObejctFactory)。
  3. 如果在缓存中得到了bean 的原始状态,则需要对bean 进行实例化,因为缓存里面记录的是Bean的原始状态,并不是最终想要的Bean,直接调用getObjectForBeanInstance。
  4. 如果缓存里面没有获取到对象:
  5. 在单例情况下才会处理循环依赖的问题,如果在原型情况下,isPrototypeCurrentlyInCreation(beanName) 为true,表示原型中有循环依赖,则抛出异常。
  6. 如果当前beanDefinitionMap不包括 beanName对应的beanDefinition;则尝试从父的parentBeanFactory获取bean对象,通过parentBeanFactory.getBean获取。
  7. 将存储XML 配置文件的GernericBeanDefinition 并转换为RootBeanDefinition。
  8. 寻找bean依赖关系,如果有依赖的Bean没有初始化的话,需要先加载依赖的Bean。
    • 会把当前对象放入一个set集合里面,这个集合用来标识已访问的对象。
    • 使用this.getBean 去访问依赖的bean对象。
  9. 针对不同的Bean生命周期进行不同的Bean 创建。都会调用createBean:populateBean(进行bean的属性注入)-》initializeBean(先调用bean前置处理器、afterpropertieset、初始化、调用bean后置处理器),再调用getObjectForBeanInstance。
    • 单例:首先通过getSingleton里面的ObjectFactory获取到bean对象,ObjectFactory里面包含createBean创建bean对象,创建完成后放入一级缓存。最后调用getObjectForBeanInstance获取完整的bean对象实例
    • 原型:直接调用createBean,再调用getObjectForBeanInstance
  10. 如果requiredType 不为空的话,则对Bean 进行结果转换。

getObjectForBeanInstance,是根据给出的bean获取到真实的bean,主要是为工厂bean服务的:

  • 如果当前对象不是工厂bean则直接返回。
  • 如果对象是一个FactoryBean且这里要获取对应的Bean,则尝试从factoryBean已创建的对象缓存中获取。
  • 如果缓存里面没有则用Factory去创建出Bean
publ Object getBean(String name) throws BeansException {
	return doGetBean(name , null , nu11 , false) ;
}
protected <T> T doGetBean(
		// 获取到真正的BeanName,因为有可能这个是别名
		String beanName = transformedBeanName(name);
		Object bean;

		/**
		 * 从一级缓存里面获取bean,如果为空而且存在循环依赖,则从二级缓存里面获取早期Bean,如果还为null,则从三级缓存里面获取bean工厂,并用ObjectFactory::getObject方法获取到早期对象,并存入二级缓存,删除三级缓存。
		 * 因为在单例Bean 里面会处理循环依赖的情况,而Spring 创建bean的原理就是不等Bean创建完成就会创建Bean的ObjectFactory,并提早
		 *  放到缓存里面,一旦下一个Bean依赖上一个bean的时候,则直接使用ObjectFactory
		 */
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			// 返回对应的实例,有时候存在诸如BeanFactory 的情况并不是直拨返回实例本身而是返回指定方法返回的实例
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			// 原型Bean 存在循环依赖的情况,直接抛出异常
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			// 如果beanDefinitionMap中, 也就是在所有已经加载的类中不包括beanName 则尝试从parentBeanFactory 中检测
			// Check if bean definition exists in this factory.
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				// 递归到父类BeanFactory 去寻找
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}

			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}
			try {
				// 将存储的XML 配置文件的GernericBeanDefinition 转换为RootBeanDefinition , 如果指定
				//BeanName 是子Bean 的话同时会合并父类的相关属性性
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);
				// Guarantee initialization of beans that the current bean depends on.
				String[] dependsOn = mbd.getDependsOn(); // 如果存在依赖的话,递归实例话依赖的bean
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}
				// 实例化依赖的bean 后便可以实例化mbd 本身了
				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

加载单例Bean

这个方法首先尝试从singletonObjects 里面获取实例,如果获取不到再从earlySingletonObjects里面获取,如果还获取不到,再尝试从singletonFactories 里面获取beanName 对应的ObjectFactory ,然后调用这个ObjectFactory 的getObject 来创建bean ,并放到earlySingletonObjects里面去,并且从singletonFacotories 里面remove 掉这个ObjectFactorγ ,而对于后续的所有内存操作都只为了循环依赖检测时候使用,也就是在allowEarlyReference 为true的情况下才会使用。

简单解释如下。

  • singletonObjects :用于保存BeanName 和创建bean 实例之间的关系, bean name 一> bean Instance 。

  • singletonFactories(三级缓存) :用于保存BeanName 和创建bean 的工厂之间的关系, bean name 一> ObjectFactory ,一旦最终对象被创建(通过objectFactory.getObject()),此引用信息将删除

  • earlySingletonObjects(二级缓存) :也是保存BeanName 和创建bean 实例之间的关系,与singletonObjects 的不同之处在于,用于存储在创建Bean早期对创建的原始bean的一个引用,注意这里是原始bean,即使用工厂方法或构造方法创建出来的对象,一旦对象最终创建好,此引用信息将删除。其目的是用来检测循环引用

  • registeredSingletons :用来保存当前所有已注册的bean 。

// 从三次缓存里面获取对象
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
  	// isSingletonCurrentlyInCreation 判断当前bean是否正在创建中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			// 单例Bean为空,或者存在循环依赖的情况
			synchronized (this.singletonObjects) { // 加锁
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					// 当某些方法记需要前初始化的时候则会调用addSingletonFactory 方法将对应的
					// ObjectFactory 初始化策存储在 singletonFactories
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						// 调用预先设定的getObject 方法
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
    
// 首先从一级缓存里面获取对象,
//如果没有把当前对象放入正在创建bean对象的set集合里面
// 通过ObjectFactory创建bean的原生对象,
// 从正在创建bean对象的set集合里面删除当前对象
// 当前对象放入到一级缓存,并二级缓存、三级缓存里面删除当前对象
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized(this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }

            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }

            this.beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = this.suppressedExceptions == null;
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet();
            }

            try {
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            } catch (IllegalStateException var16) {
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw var16;
                }
            } catch (BeanCreationException var17) {
                BeanCreationException ex = var17;
                if (recordSuppressedExceptions) {
                    Iterator var8 = this.suppressedExceptions.iterator();

                    while(var8.hasNext()) {
                        Exception suppressedException = (Exception)var8.next();
                        ex.addRelatedCause(suppressedException);
                    }
                }

                throw ex;
            } finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }

                this.afterSingletonCreation(beanName);
            }

            if (newSingleton) {
                this.addSingleton(beanName, singletonObject);
            }
        }

        return singletonObject;
    }
}

如何解决循环依赖

所谓循环依赖是指, 两个或者多个Bean相互之间持有对方引用,比如BeanA引用了BeanB,BeanB又引用了BeanC,BeanC引用BeanA这种。

在Spring中有构造器循环依赖和sttter循环依赖,而只能解决通过setter方式且引用的Bean的作用域是Singleton级别

构造器循环依赖

Spring 容器将每一个正在创建的bean 标识符放在一个“当前创建bean 池, bean 标识符在创建过程中将一直保持在这个池中,因此如果在创建bean 过程中发现自己已经在“当前创建bean 池” 里时,将抛出BeanCurrentlylnCreationException 异常表示循环依赖;而对于创建完毕的bean 将从“ 当前创建bean 池”中清除掉。

setter循环依赖

通过setter 注入方式构成的循环依赖。对于setter 注入造成的依赖是通过Spring 容器提前暴露刚完成构造器注入但未完成其他步骤(如setter 注入)的bean 来完成的,而且只能解决单例作用域的bean 循环依赖通过提前暴露一个单例工厂方法,从而使其他bean 能引用到该bean 。这样返回了Bean实例的引用,后期对bean的任何处理,不会改变bean的引用地址。
实际实现上提前暴露一个单例工厂方法,返回了一个在创建中的Bean,这样其他Bean就能够获得他的引用。

Demo: A 依赖B,B依赖C,C依赖A:具体步骤如下。

  1. Spring 容器创建单例“testA ” bean ,首先根据无参构造器创建bean ,并暴露一个“0bjectFactory”用于返回一个提前暴露一个创建中的bean ,并将“testA”标识符放到“当前创建bean 池”, 然后进行setter 注入“testB ” 。

  2. Spring 容器创建单例“ testB ” bean ,首先根据无参构造器创建bean ,并暴露一个“ ObjectFactory”用于返回一个提前暴露一个创建中的bean ,并将“testB”标识符放到“当前创建bean 池”,然后进行setter 注入“testC ” 。

  3. Spring 容器创建单例“ testC ” bean ,首先根据无参构造器创建bean ,并暴露一个“ ObjectFactory”用于返回一个提前暴露一个创建中的bean ,并将“testC”标识符放到“当前创建bean 池”,然后进行setter 注入“testA” 。进行注入“testA”时由于提前暴露了“0bjectFactory”工厂,从而使用它返回提前暴露一个创建中的bean 。

  4. 最后在依赖注入“testB ”和“testA ”,完成setter 注入。

prototype 范围的依赖处理

对于“prototype”作用域bean, Spring 容器无法完成依赖注入,因为Spring 容器不进行缓存“prototype”作用域的bean ,因此无法提前暴露一个创建中的bean。

Spring MVC

Spring MVC 是一种设计模式,分为Model、View、Controller层。Spring后端的 Service层(处理业务)、Dao层(数据库操作)属于Model层;Controller层(控制层,返回数据给前台页面)属于控制层;View 层负责渲染页面返回给前端。主要步骤如下:

  1. 客户端(浏览器)发送请求,直接请求到 DispatcherServlet
  2. DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler
  3. 解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。
  4. HandlerAdapter 会根据 Handler 来调用真正的处理器来处理请求,并处理相应的业务逻辑。
  5. 处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View
  6. ViewResolver 会根据逻辑 View 查找实际的 View
  7. DispaterServlet 把返回的 Model 传给 View(视图渲染)。
  8. View 返回给请求者(浏览器)

HandlerMapping 处理器映射器,查找具体的Handler,也就是处理器
HandlerAdapter 处理器适配器,适配具体的Controller进行业务处理
ViewResolver 视图解析器,根据返回的view名字,找到具体的模板

springmvc

为什么要在SpringMVC里面使用适配器模式?

Spring MVC 中的 Controller 种类众多,不同类型的 Controller 通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet 直接获取对应类型的 Controller,需要自行的if-else来判断,如何新增一个Controller的话,需要修改代码。不符合开闭原则。

Spring 事务

Spring 事务分为编程式事务(手动写在代码中)、声明式事务(注解、XML配置两种方式)。 Spring 事务有三个重要的接口PlatformTransactionManager、TransactionDefinition、TransactionStatus:

  • PlatformTransactionManager接口可以被看作是事务上层的管理者,包含获取事务、提交事务、回滚事务
  • TransactionDefinition接口包括事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)
  • TransactionStatus 表示事务的运行状态

@Transaction写在public方法上才有效;写在class上面表示,事务对当前class所有的public方法都生效。

如果一个类或者一个类中的 public 方法上被标注@Transactional 注解的话,Spring 容器就会在启动的时候为其创建一个代理类,在调用被@Transactional 注解的 public 方法的时候,实际调用的是,TransactionInterceptor 类中的 invoke()方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。

若同一类中的其他没有 @Transactional 注解的方法内部调用有 @Transactional 注解的方法,有@Transactional 注解的方法的事务会失效。这是由于Spring AOP代理的原因造成的,因为只有当 @Transactional 注解的方法在类以外被调用的时候,Spring 事务管理才生效。

因为进行动态代理的时候,会生成一个新的代理对象,这个代理对象的代理方法内部 先执行aop增强的逻辑,然后在执行原有的逻辑,也就是this.method, 但是这个原有的逻辑就不会被aop管理了,因为this 所在这个包名+类名是代理后的对象,而不是原来需要aop切面的那个包,所以在同一个类里面调用,是不会被AOP管理的。解决方案有换成两个service,或者从bean工厂里面获取对象、直接获取代理对象

Spring事务的隔离级别有:

  • ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别

  • ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读

  • ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

  • ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

  • ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰。但是这将严重影响程序的性能。

Spring 事务的传播行为:

事务传播行为是为了解决业务层方法之间互相调用的事务问题。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。

支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRED(默认): 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

不支持当前事务的情况:

  • **TransactionDefinition.PROPAGATION_REQUIRES_NEW:**如果当前存在事务,则把当前事务挂起;如果当前没有事务, 创建一个新的事务。
  • **TransactionDefinition.PROPAGATION_NOT_SUPPORTED:**如果当前存在事务,则把当前事务挂起; 如果当前没有事务,以非事务方式运行。
  • **TransactionDefinition.PROPAGATION_NEVER:**如果当前存在事务,则抛出异常。 如果当前没有事务,以非事务方式运行。

其他情况:

  • TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

@Transactional(rollbackFor = Exception.class)注解,可以在事务遇到运行时异常和非运行时异常都发送回滚。如果不加这个注解,只会在运行时异常进行回滚。

public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;

    void commit(TransactionStatus var1) throws TransactionException;

    void rollback(TransactionStatus var1) throws TransactionException;
}

public interface TransactionDefinition {
  // 七大传播行为
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
  // 5大隔离级别
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1; // 超时时间
}

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    boolean hasSavepoint();
    void flush();
}

事务失效的情况

  1. @Transactional 应用在非 public 修饰的方法上。
  2. @Transactional 注解属性 propagation 设置错误;比如PROPAGATION_NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务则挂起当前事务。
  3. @Transactional 注解属性 rollbackFor 设置错误。默认不支持非运行时异常,如果发送这类异常是不会进行回滚的。可以设置为@Transactional(Exception.class)
  4. 同一个类中方法调用,导致@Transactional失效,比如同一个类里面方法A调用方法B(B有事务注解,A没有),会导致事务失效,因为事务的实现是AOP,而AOP只有在被当前类以外的代码调用时,才会由Spring生成代理对象,解决方法是用一个Service调用另外一个Service。
  5. 异常被 try-catch 捕获了,导致@Transactional失效
  6. 存储引擎不支持事务

spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了.

SpringCache

在修改缓存代码的时候,需要在每个使用缓存的部分 开头获取分布式redission 的锁,中间部分写入缓存,在末尾finally里面释放锁,而SpringCache正是简化了这样的开发,只需要一个注解即可。
但是SpringCache适合读多,写少的数据,因为SpringCache里面只有get方法加了锁的,无法很好解决缓存击穿的问题对于实时性要求高的数据,需要特殊处理,比如使用分布式锁来实现

SpringCache会自动配置缓存管理器,

  • @Cacheable:触发缓存写入的操作: @Cacheable(value = {"detail_Blog_cache_"},key = "#id",sync = true)
  • @CacheEvict:触发缓存删除的操作 : @CacheEvict(value = ("detail_Blog_cache_"),key = "#blog.blogId")
  • @CachePut:更新缓存,而不会影响方法的执行
  • @Caching:重新组合要应用于一个方法的多个缓存操作,即对一个方法添加多个缓存操作
  • @CacheConfig:在类级别共享一些与缓存有关的常见设

步骤:

  1. 开启缓存,启动类上标注@EnableCaching
  2. 要缓存的对象必须实现Serializable接口。
  3. 使用@Cacheable 进行缓存,
    • 如果redis缓存里面没有数据,则会根据规则自动进行缓存;
    • 如果缓存里面有数据,连方法内部都不会进入
  4. 默认行为:
    1. 缓存命中,连方法都不会进入
    2. 缓存的key默认自动生成
    3. 缓存的value默认采用jdk序列化,缓存的java对象必须实现Serializable接口
    4. 默认的ttl 时间是配置写死的

自定义行为:

  1. 自定义ttl失效时间
  2. 将数据转换成json格式

不足
缓存穿透:不存在的key,SpringCache通过配置缓存null值来解决
缓存击穿:大量并发进行同时访问一个刚刚过期的key,解决方案:加锁?默认是没有加锁的, 需要添加sync = true,但是这个是本地锁,不是分布式锁,而且只有get方法有锁。所以这部分需要额外使用分布式锁来解决
缓存雪崩:大量的key同时过期。解决方案:随机化过期时间。

面试问题

单例Bean是线程不安全的

当多个线程操作同一个对象的时候,对这个对象的成员变量的写操作会存在线程安全问题。有两种解决方案:

  • 单例Bean内部定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。
  • 改变 Bean 的作用域为 “prototype” 或者 request;每次请求都会创建一个新的 bean 实例,就不会存在线程安全的问题。

Spring中Bean的生命周期和作用域?

Spring的几种事务?跟Mysql的事务有什么区别?

Spring的aop和ioc?

Spring的BeanFactory和FactoryBean的区别?

BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

讲讲Spring中一个Bean的加载流程?

Spring的事务的隔离级别?

Autowired注解的原理?

spring的生命周期?怎么解决循环依赖?


目录