Spring 讲解

Spring简介

Spring框架的介绍

Spring是一个开源框架,Spring是一个轻量级的Java 开发框架。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。

spring框架的优势

​ 1. Spring通过DI、AOP和消除样板式代码来简化企业级Java开发
​ 2. Spring框架之外还存在一个构建在核心框架之上的庞大生态圈,它将Spring扩展到不同的领域,如Web服务、REST、移动开发以及NoSQL
3. 低侵入式设计,代码的污染极低
4、独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺
5、Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦
​ 6、Spring的AOP允许将一些通用任务如安全、事务、日志等进行集中式处理,从而提供了更好的复用
​ 7、Spring的ORM和DAO提供了与第三方持久层框架的的良好整合,并简化了底层的数据库访问
​ 8、Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部

spring框架

Spring-IOC

IOC的使用

IOC基本使用

  1. 导入spring的pom配置
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.4.RELEASE</version>
        </dependency>
    </dependencies>
  1. 创建自己的Bean
public class MyBean {
    private String name;
    public String getName() {return name;}
    public void setName(String name) { this.name = name;}
}
  1. 在spring的配置文件中声明Bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myBean" class="ioc.beans.MyBean">
        <property name="name" value="123"></property>
    </bean>
</beans>
  1. 测试获取Bean
public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
        MyBean myBean = (MyBean)context.getBean("myBean");
        System.out.println(myBean.toString());
    }
}

Bean的注入方式

  • 通过id获取
    • 通过bean的唯一id获取对象
    • 如果没有找到bean会报错
  • 通过类型获取
    • 通过Class的类型获取Bean
    • 如果有多个相同类型需要指定id,否则会报错

Bean的作用域

在spring 4.x 中有四种方式,但是在5.x中只有两种。
1. singleton:单例的,也是spring默认的作用域。整个容器中只有一个实例,随着容器的产生而创建。
2. prototype:多例的,每次使用时都会重新创建一个实例,使用时才产生。
3. request(4.x):请求作用域。
4. session(4.x):会话作用域。

BeanFactory 和 FactoryBean

BeanFactory的介绍

BeanFactory是Spring的基础类,其中定义了对于单个Bean的操作,没有具体实现,需要子类进行实现

FactoryBean的介绍
  1. FactoryBean是spring为我们提供的创建复杂Bean的接口,继承该接口之后可以自定义Bean的创建过程等。
  2. 著名的Mybatis就是使用了FactoryBean来创建连接对象的(具体的之后再说)
FactoryBean的使用
  1. 创建MyFactoryBean实现FactoryBean,并重写方法
public class MyFactoryBean implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return null;
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
    @Override
    public boolean isSingleton() {
        return false;
    }
}
  1. 在MyFactoryBean中定义需要创建的Bean的过程
public class MyFactoryBean implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new MyBean();	//定义创建Bean的过程,并返回最终生成的Bean
    }

    @Override
    public Class<?> getObjectType() {
        return MyBean.class;	//返回最终生成Bean的类型
    }

    @Override
    public boolean isSingleton() {
        return true;	//定义是否时单例的,true为单例,false不是单例
    }
}
  1. 在spring配置文件中声明MyFactoryBean
<bean id="myFactoryBean" class="ioc.beans.MyFactoryBean" />
  1. 测试使用MyFactoryBean创建MyBean
public class MyTest {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
        MyBean myBean = (MyBean)context.getBean("myFactoryBean");
        myBean.setName("123");
        System.out.println(myBean.toString());
    }
}

BeanPostProcessor

BeanPostProcessor介绍

BeanPostProcessor是Spring提供的创建Bean的初始化前/后置处理器,在Bean创建的过程中可以增加很多自定义的操作。
在Spring启动过程中,也会有对BeanPostProcessor的操作过程。

BeanPostProcessor的使用
  1. 定义Bean实现BeanPostProcessor,并添加创建Bean前后的输出
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor创建前的操作");
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor创建后的操作");
        return null;
    }
}
  1. 在配置文件中定义该Bean
<bean id="myBeanPostProcessor" class="ioc.beans.MyBeanPostProcessor" />
  1. 测试,查看输出结果
public class MyTest {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
        MyBeanPostProcessor myBeanPostProcessor = (MyBeanPostProcessor)context.getBean("myBeanPostProcessor");
        System.out.println(myBeanPostProcessor.toString());
    }
}
  1. 结果
MyBeanPostProcessor创建前的操作
MyBeanPostProcessor创建后的操作
MyBeanPostProcessor创建前的操作
MyBeanPostProcessor创建后的操作
ioc.beans.MyBeanPostProcessor@617faa95

Bean的自动装配的方式

  1. default:不装配
  2. byName:按名称装配,通过set方法装配
  3. byType:按类型装配,通过set方法装配
  4. constructor:和按类型装配相同,只不过不通过set方法,而是通过构造器装配

注解方式注入Bean

需要在配置文件中开启包扫面,然后加入如下的注解即可注入到IOC容器中。
注解类型:

  1. @Component:Spring组件,可以在任何地方使用
  2. @Controller:控制层
  3. @Service:业务层
  4. @Repository:数据层

说明:以上四个注解对于spring而言都是需要注入到IOC容器中,并无什么区别,所谓的区别只是人为的逻辑上的区别。

@Autoware 和 @Resource的区别

@Autoware
  1. 是spring提供的注解,如果没有使用spring该注解是无法生效的。(比如mybatis中就无法使用该注解)
  2. @Autowired注入首先根据byType注入,当类型大于1时在根据byName注入
@Resource
  1. 是jdk提供的注解。
  2. 默认是按类型(byType)匹配,如果没有再按名称(byName)匹配。

IOC的原理

IOC的启动流程

  1. 从主函数中进入
public class MyTest {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
        MyBean myBean = (MyBean)context.getBean("myBean");
        System.out.println(myBean.toString());
    }
}
  1. ClassPathXmlApplicationContext
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		super(parent);
		// 设置配置的文件
		setConfigLocations(configLocations);
		if (refresh) {
			// IOC启动的核心方法
			refresh();
		}
	}
  1. AbstractApplicationContext -> IOC启动的核心refresh方法
@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 容器启动前,设置一些启动时间、关闭/活动的状态
			prepareRefresh();

			// 获取BeanFactory方法(核心方法)
			/* 
			1. 根据文件位置,读取xml文件
			2. 根据xml文件解析出所有配置,解析<Bean>等其他标签
			3. 将解析的Bean转换成Beandefinition对象(此时并没有进行实例化)
			4. 执行完之后,beanDefinitionMap就有我们定义的BeanDefinition对象       疑问:这个beanDefinitionMap应该是DefaultListableBeanFactory中的,而ConfigurableListableBeanFactory是他的父类,为什么会有这个属性?虽然这个方法返回的是DefaultListableBeanFactory,但是也不应该有啊?
			*/
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 为BeanFactory设置一些属性
			prepareBeanFactory(beanFactory);

			try {
				// 允许实现BeanFactoryPostProcessor,为BeanFactoryPostProcessor的创建提供一些个性化的内容
				postProcessBeanFactory(beanFactory);
				// 执行定义的BeanFactoryPostProcessor
				invokeBeanFactoryPostProcessors(beanFactory);
				// 注册所有实现了BeanPostProcessor接口的类,加载到beanFactory中
				registerBeanPostProcessors(beanFactory);
				// 初始化国际化的内容
				initMessageSource();
				// 初始化事件监听多路广播器
				initApplicationEventMulticaster();
				// 一个空方法,暂时没有实现
				onRefresh();
				// 注册监听器
				registerListeners();
				// 初始化所有单例的Bean(该方法才会真正的生成Bean实例)
				finishBeanFactoryInitialization(beanFactory);
				// 最后做一些收尾工作:清空ResourceCaches,推送一些事件等
				finishRefresh();
			}
			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
				destroyBeans();
				cancelRefresh(ex);
				throw ex;
			}
			finally {
				resetCommonCaches();
			}
		}
	}
  1. obtainFreshBeanFactory()
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		return getBeanFactory();
	}

refreshBeanFactory()

	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			// 创建一个DefaultListableBeanFactory工厂
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			// 加载配置文件,并把配置文件中的Bean转换为BeanDef
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

loadBeanDefinitions() -> 通过XmlBeanDefinitionReader读取xml配置文件并解析

	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
		initBeanDefinitionReader(beanDefinitionReader);
		// 读取配置文件并把Bean转换成Beandefinition对象
		loadBeanDefinitions(beanDefinitionReader);
	}

Spring AOP

基本概念

  1. 切面(aspect):需要嵌入的代码会抽象成一个类,这个类就是一个切面。
  2. 通知(advice):在切面特定的连接点上做的动作,就是通知。有前置通知、后置通知等。
  3. 连接点(join point):每一个方法中可以填入额外代码的地方,叫做连接点。(连接点不一定)
  4. 切入点(point cut):实际切入的地方。连接点可以很多,实际实现的地方叫做切入点,切入点是连接点的子集。
  5. 引入:
  6. 目标对象(target object):
  7. AOP代理(aop proxy):
  8. 织入(weaving):利用连接点、通知、切面代理目标对象的过程,叫做织入。是整个过程。

AOP通知类型

  1. 前置通知(before advice):切入点之前执行。
  2. 后置返回通知(after return advice):切入点正常执行完return之后执行。
  3. 后置异常通知(after throwing advice):在切入点抛出异常退出时执行。
  4. 后置通知(after advice)(总会执行):当连接点执行完成后,无论是否抛异常都会执行。
  5. 环绕通知(around advice):可以在切入点前后任意定义行为。

AOP的应用场景

  1. 日志管理
  2. 权限认证
  3. 安全检查
  4. 事务控制

AOP演示

AOP基本演示

  1. 新建项目,项目结构如下
    在这里插入图片描述

  2. 导入需要的pom包

    <dependencies>
        <!--spring的包,包含了aop-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <!--由于aop底层原理包含cglib,所以要引入cglib的包-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
        <!--支持aop相关注解等-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.6</version>
        </dependency>
        <!--支持切入点表达式等-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

        <!--单元测试包-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
  1. 增加业务方法类MyService,并且用@Service注解交给spring管理
@Service
public class MyService {

    public Integer add(Integer i,Integer j){
        return i + j;
    }

    public Integer sub(Integer i, Integer j){
        return i - j;
    }

    public Integer mul(Integer i, Integer j){
        return i * j;
    }

    public Integer div(Integer i, Integer j){
        return  i/j;
    }
}
  1. 增加切面类LogUtil
@Aspect
@Component
public class LogUtil {
    @Pointcut("execution(* com.aop.service..*(..))")
    public void log(){}

    @Before(value = "log()")
    public void beforeLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        System.out.println(signature + "方法开始执行,参数为:" + Arrays.asList(args));
    }

    @After(value = "log()")
    public void afterLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        System.out.println(signature + "方法结束执行,参数为:" + Arrays.asList(args));
    }

    @Around(value = "log()")
    public Object aroundLog(ProceedingJoinPoint pjp){
        Signature signature = pjp.getSignature();
        Object[] args = pjp.getArgs();
        Object result = null;
        try {
            System.out.println(signature + "方法环绕前置通知执行");
            result = pjp.proceed(args);
            System.out.println(signature + "方法环绕后置通知执行");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }
}
  • @Aspect 注解声明一个切面
  • @Component 将切面交给spring容器管理
  • @Pointcut 配合 execution表达式 定义并抽象切入点
  • @Before,@After,@Around 后面指定切入点,执行相应的通知
  • 其它通知可以用 JoinPoint 获取方法签名和参数列表;环绕通知需要用ProceedingJoinPoint 获取参数列表
  • 环绕通知调用ProceedingJoinPoint.proceed(); 方法执行代理的方法,可以在前后自定义行为
  1. 增加spring的配置类 application.xml
    <!--配置spring包扫描,让spring的注解生效-->
    <context:component-scan base-package="com.aop"></context:component-scan>
    <!--让spring自动配置切面,是@Aspect注解生效-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  1. junit单元测试
public class MyTest {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

    @Test
    public void test01(){
        MyService myService = (MyService)context.getBean("myService");
        System.out.println(myService.add(1,2));
    }
}
  1. 测试结果
    在这里插入图片描述

AOP基本演示注意点

  1. 切入点表达式的格式
    - @Pointcut(“execution(返回值 包名.方法名(参数列表))”)
    - 返回值可以写确定返回值,也可以用号匹配所有返回值
    - 包名可以写全名;也可以用. . 或
    通配;. .匹配多层路径;匹配一层路径;当用开头时则匹配全部路径;
    - 方法名可以写全名;也可以用*匹配所有;
    - 参数列表可以写全名;也可以用. .匹配所有
    - 切入点表达式也支持逻辑运算符&&,||,!
  2. 执行顺序
    环绕前置通知 -> @Before -> 环绕后置通知 -> 环绕返回通知 -> @After -> @AfterReturning/@AfterThrowing
  3. 如何传参:通过joinPoint获取参数列表,并且JoinPoint必须是第一个参数;环绕通知用ProceedingJoinPoint
  4. 如何获取结果 :通知上增加returnning=“result”,然后参数列表上增加返回参数Object result,两者名字需要相同
  5. afterThrowing如何获取异常信息:注解上增加throwing=“e”,e必须和参数列表中名称一致
  6. 通知方法在定义的时候,对访问修饰符和返回值都没有什么特殊的要求,只是参数不能随便增加,需要JoinPoint/ProceedingJoinPoint或者和注解中配置的参数配合使用。
  7. 可以用@PointCut + 空方法,对多个相同表达式进行抽象
  8. 环绕通知中如果捕获了异常,普通通知中是无法捕获的。
  9. 同时有两个切面,如何保证执行顺序???
    默认是根据首字母顺序执行,但是可以在切面类上用@Order(100)指定权重,值越小越优先

AOP与事务

事务的种类

  1. 编程式事务:需要编程人员手动开启事务,提交和回滚,代码侵入性太强。
  2. 声明式事务:建立在AOP基础上,通过对事务方法的拦截,在方法执行前后开启事务并设置提交或回滚。

事务的隔离级别

隔离级别是指若干个并发事务之间的隔离程度。

  1. ISOLATION_DEFAULT:默认值,表示使用数据库默认的隔离级别。
  2. ISOLATION_READ_UNCOMMITTED:事务可以读取到另一个事务修改还没有提交的数据。存在脏读、不可重复读、幻读的情况。
  3. ISOLATION_READ_COMMITTED:事务可以读取到另一个事务已经提交的数据。解决脏读,存在不可重复读、幻读的情况。
  4. ISOLATION_REPEATABLE_READ:一个事务可以重复读取一条数据,且每次返回记录都相同。解决脏读、不可重复读,存在幻读的情况。
  5. ISOLATION_SERIALIZABLE:所有事务依次执行,性能极差。解决脏读、不可重复读、幻读的情况。

事务的传播行为

传播行为是指多个类的方法执行,每个方法都有事务注解,多个事务之间如何协调工作。
假设A类方法a(),B类方法b(),a方法中调用了b方法:

  1. PROPAGATION_REQUIRED(默认):如果当前存在事务,则加入;如果不存在事务,则重新创建一个事务。
    如果a,b都有事务注解,b事务会加入a事务,a或b出现异常两个方法都会回滚

  2. PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则挂起。
    如果a,b都有事务注解,b重新创建一个事务并将a事务挂起,如果b出现异常只会回滚b事务,对a事务没有影响

  3. PROPAGATION_SUPPORTS:如果当前存在事务则加入;如果不存在则以非事务的方式运行
    如果a有事务,b会加入,异常同时回滚;如果a没有事务,b则以非事务运行,异常都不会回滚

  4. PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前有事务,挂起当前事务

  5. PROPAGATION_NEVER:以非事务方式运行,如果当前有事务,抛出异常

  6. PROPAGATION_MANDATORY:如果当前存在事务则加入;如果当前没有事务,则抛出异常

  7. PROPAGATION_NESTED:总是创建事务,如果当前存在事务则创建的是当前事务的嵌套子事务,否则是个新事务

    总是要创建一个事务,和PROPAGATION_REQUIRES_NEW的区别就是,如果a,b都有事务,用PROPAGATION_REQUIRES_NEW的时候,b出异常a不会回滚。用PROPAGATION_NESTED的时候,因为虽然是两个事务,但是事务是嵌套的关系,所以b出现异常a也会回滚

事务失效的情况

  1. 调用一个类中非事务方法,非事务方法内部调用了当前类的事务方法,因为事务是在aop的时候处理的,所以不会产生事务

Spring常见问题

IOC 和 DI的关系

控制反转是就是应用本身不负责依赖对象的创建和维护,依赖对象的创建及维护是由外部容器负责的,这样控制权就有应用转移到了外部容器,控制权的转移就是控制反转。

​ 依赖注入是指:在程序运行期间,由外部容器动态地将依赖对象注入到组件中如:一般,通过构造函数注入或者setter注入。

Spring IOC容器初始化过程

Spring IOC容器的初始化简单的可以分为三个过程:

​ 第一个过程是Resource资源定位。这个Resouce指的是BeanDefinition的资源定位。这个过程就是容器找数据的过程,就像水桶装水需要先找到水一样。

​ 第二个过程是BeanDefinition的载入过程。这个载入过程是把用户定义好的Bean表示成Ioc容器内部的数据结构,而这个容器内部的数据结构就是BeanDefition。

​ 第三个过程是向IOC容器注册这些BeanDefinition的过程,这个过程就是将前面的BeanDefition保存到HashMap中的过程。

Spring Bean的生命周期

BeanFactory 和 FactoryBean的区别

  • BeanFactory是个Factory,也就是IOC容器或对象工厂,在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的,提供了实例化对象和拿对象的功能。

使用场景:

  • 从Ioc容器中获取Bean(byName or byType)

  • 检索Ioc容器中是否包含指定的Bean

  • 判断Bean是否为单例

  • FactoryBean是个Bean,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。

    使用场景

    • ProxyFactoryBean

BeanFactory 和 ApplicationContext的区别

相同:

  • Spring提供了两种不同的IOC 容器,一个是BeanFactory,另外一个是ApplicationContext,它们都是Java interface,ApplicationContext继承于BeanFactory(ApplicationContext继承ListableBeanFactory。
  • 它们都可以用来配置XML属性,也支持属性的自动注入。
  • 而ListableBeanFactory继承BeanFactory),BeanFactory 和 ApplicationContext 都提供了一种方式,使用getBean(“bean name”)获取bean。

不同:

  • 当你调用getBean()方法时,BeanFactory仅实例化bean,而ApplicationContext 在启动容器的时候实例化单例bean,不会等待调用getBean()方法时再实例化。
  • BeanFactory不支持国际化,即i18n,但ApplicationContext提供了对它的支持。
  • BeanFactory与ApplicationContext之间的另一个区别是能够将事件发布到注册为监听器的bean。
  • BeanFactory 的一个核心实现是XMLBeanFactory 而ApplicationContext 的一个核心实现是ClassPathXmlApplicationContext,Web容器的环境我们使用WebApplicationContext并且增加了getServletContext 方法。
  • 如果使用自动注入并使用BeanFactory,则需要使用API注册AutoWiredBeanPostProcessor,如果使用ApplicationContext,则可以使用XML进行配置。
  • 简而言之,BeanFactory提供基本的IOC和DI功能,而ApplicationContext提供高级功能,BeanFactory可用于测试和非生产使用,但ApplicationContext是功能更丰富的容器实现,应该优于BeanFactory

Spring Bean的生命周期

总结:

(1)实例化Bean:

对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

(2)设置对象属性(依赖注入):

实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。

(3)处理Aware接口:

接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:

①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;

②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。

③如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

(4)BeanPostProcessor:

如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

(5)InitializingBean 与 init-method:

如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

(6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

(7)DisposableBean:

当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

(8)destroy-method:

最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

Spring Aop的实现原理

Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

​ Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。

​ 如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

Spring是如何管理事务的

Spring事务管理主要包括3个接口,Spring的事务主要是由它们(PlatformTransactionManager,TransactionDefinition,TransactionStatus)三个共同完成的。

1. PlatformTransactionManager:事务管理器–主要用于平台相关事务的管理

主要有三个方法:

  • commit 事务提交;
  • rollback 事务回滚;
  • getTransaction 获取事务状态。

2. TransactionDefinition:事务定义信息–用来定义事务相关的属性,给事务管理器PlatformTransactionManager使用

这个接口有下面四个主要方法:

  • getIsolationLevel:获取隔离级别;
  • getPropagationBehavior:获取传播行为;
  • getTimeout:获取超时时间;
  • isReadOnly:是否只读(保存、更新、删除时属性变为false–可读写,查询时为true–只读)

事务管理器能够根据这个返回值进行优化,这些事务的配置信息,都可以通过配置文件进行配置。

3. TransactionStatus:事务具体运行状态–事务管理过程中,每个时间点事务的状态信息。

例如它的几个方法:

  • hasSavepoint():返回这个事务内部是否包含一个保存点,
  • isCompleted():返回该事务是否已完成,也就是说,是否已经提交或回滚
  • isNewTransaction():判断当前事务是否是一个新事务

声明式事务的优缺点

  • 优点:不需要在业务逻辑代码中编写事务相关代码,只需要在配置文件配置或使用注解(@Transaction),这种方式没有侵入性。
  • 缺点:声明式事务的最细粒度作用于方法上,如果像代码块也有事务需求,只能变通下,将代码块变为方法。

Spring不同事务的传播特性有哪些?

Spring中用到了哪些设计模式

  • 代理模式—在AOP中被用的比较多。
  • 单例模式—在spring配置文件中定义的bean默认为单例模式。
  • 模板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
  • 工厂模式—BeanFactory用来创建对象的实例。
  • 适配器–spring aop
  • 装饰器–spring data hashmapper
  • 观察者-- spring 事件驱动模型
  • 回调–Spring Aware回调接口

Spring是如何解决循环依赖的

https://blog.csdn.net/qq_36381855/article/details/79752689

Bean的作用域

(1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。

(2)prototype:为每一个bean请求提供一个实例。

(3)request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。

(4)session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。

Spring框架中有哪些不同类型的事件

(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。

(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。

(3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。

(4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。

(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

Spring中通知有哪些

(1)前置通知(Before advice):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

(2)返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。

(3)抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。

(4)后通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

(5)环绕通知(Around Advice):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。 环绕通知是最常用的一种通知类型。

Spring的自动装配

在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。

在Spring框架xml配置中共有5种自动装配:

(1)no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。

(2)byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。

(3)byType:通过参数的数据类型进行自动装配。

(4)constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。

(5)autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。

基于注解的方式:

使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;

如果查询的结果不止一个,那么@Autowired会根据名称来查找;

如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

@Autowired可用于:构造函数、成员变量、Setter方法

注:@Autowired和@Resource之间的区别

(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值