Spring

一、Spring概述

  • 一个开源免费的框架 , 容器。
  • Java SE/EE full-stack(一站式)轻量级开源框架。非侵入式的。
  • 控制反转 IoC , 面向切面 Aop。
  • 对事物的支持 , 对框架的支持

1.1 两个容器:BeanFactory与ApplicationContext

  1. BeanFactory:

    • 是ioc的基础容器,提供完整的IoC服务支持。
    • 如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。即在需要获取对象时该对象才被创建。

例子:

    @org.junit.Test
    public void testBean() {
        XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        System.out.println("我的输出呢");
        UserT userT = (UserT) beanFactory.getBean("aliasUserT");
        System.out.println(userT);
    }

结果:

我的输出呢
UserT无参构造
User{name='东', sex='男'}

  1. ApplicationContext:

    • ApplicationContext是在BeanFactory的基础上构建的。是相对比较高级的容器实现。
    • 继承了BeanFactory接口,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级,比如事件发布、国际化信息支持等。
    • ApplicationContext所管理的对象是在容器启动后默认全部初始化
    • 因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。

例子:

    public static void main(String[] args) {
        // ApplicationContext在容器启动后,默认全部初始化
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println("我的输出呢");
        User user = (User) context.getBean("aliasUser");
        System.out.println(user);
    }

结果:

UserT无参构造
我的输出呢
User{name='东', sex='1'}

总结:
在这里插入图片描述

注解开发

配置文件中添加:

<!-- 注解扫描包-->
    <context:component-scan base-package="com.dong"></context:component-scan>
  1. @Autowired

由Spring提供,只按照byType注入

  1. @Resource

由J2EE提供,默认按照byName自动注入

@Resource有两个重要的属性:name和type

二、 IOC和DI

三、AOP

3.1 实现原理:

  • SpringAOP底层实现原理就是两种代理模式
  • 目标对象如果实现接口,使用JDK代理,如果没有实现接口,使用Cglib代理(创建子类对象)

3.2 基本概念:

  1. 切点(Point cut):AOP可以提供了一组规则,按照这些规则去切(匹配)连接点,匹配出来的就叫切点(被增强的方法)
  2. 通知(Advice):增强的逻辑
  3. 织入(weaving):将通知织入到目标方法的过程
  4. 切面(Aspect):将通知织入到切点形成切面
    切面 = 切点+通知 = 目标方法+增强代码

3.3 通知配置

  1. 基于xml
<!--   AOP配置-->
    <aop:config>

        <!-- 配置切点:指定哪些方法被增强
            不同包下,用..表示层级
            切点表达式配置:应该限制在最小范围
        -->
        <aop:pointcut id="pc" expression="execution(void com..service.AccountService.*(*))"/>

        <!-- 配置切面,配置织入-->
        <aop:aspect ref="txManager">
            <!-- 在切点方法执行前,执行增强方法start-->
            <aop:before pointcut-ref="pc" method="start"/>
            <!-- 后置通知,发生异常时不执行-->
            <aop:after-returning pointcut-ref="pc" method="commit"/>
            <!--  后置通知,发生异常时-->
            <aop:after-throwing pointcut-ref="pc" method="rollback"/>
            <!--  后置通知(最终),发生异常时也要执行-->
            <aop:after pointcut-ref="pc" method="clear"/>
        </aop:aspect>
		
		
        <aop:aspect ref="txManager">
            <!-- 环绕通知-->
            <aop:around method="around" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>
  1. 基于注解
	
// 注入通知
@Component
@Aspect // 配置切面
public class TxManager {

    // 切点
    @Pointcut("execution(* com..service.*Service.*(..))")
    public void pc(){}

    // 使用在方法上,表示该方法为前置通知方法
    @Before("pc()")
    public void start(){
        System.out.println("开启事务");
    }

    @AfterReturning("pc()")
    public void commit(){
        System.out.println("提交事务");
    }

    @AfterThrowing("pc()")
    public void rollback(){
        System.out.println("回滚事务");
    }

    @After()
    public void clear(){
        System.out.println("释放资源");
    }

    // 环绕通知方法
    // @Around()
    public void around(ProceedingJoinPoint joinPoint){
        try {
            // 开启事务
            start();
            // 执行目标对象的方法  method.invoke(target, args);
            joinPoint.proceed();
            // 提交事务
            commit();
        }catch (Throwable e){
            // 回滚事务
            rollback();
        }finally {
            // 释放资源
            clear();
        }
    }
}

  • JoinPoint和ProceedingJoinPoint区别

    • ProceedingJoinPoint只适用于环绕通知,因为只有环绕通知,才能控制目标方法的运行.
    • JoinPoint 适用于其它的四大通知类型,可以用来记录运行的数据。
    • ProceedingJoinPoint 中有特殊的方法proceed()。
    • 如果使用"JoinPoint" 则必须位于参数的第一位。
  • 获取切点的返回值
    ProceedingJoinPoint的proceed()方法

  • 获取切点的参数
    joinPoint.getArgs() 返回的是Object类型的数组

3.4 切点表达式的演变

execution(int com.ujiuye.service.LoggerServiceImpl.add())
当前切点为add一个方法

execution(int com.ujiuye.service.LoggerServiceImpl.*())
*:表示任意个字符,代指方法名可以是任意的

execution(* com.ujiuye.service.LoggerServiceImpl.*())
*:表示任意个字符,代指返回值类型可以是任意的

execution(* com.ujiuye.service.*ServiceImpl.*())
*:表示任意个字符,代指业务类型可以是任意的

execution(* com..service.*ServiceImpl.*())
..:表示任意层级的目录

execution(* com..service.*ServiceImpl.*(..))
..:表示方法的参数可以任意

execution(* *..*.*()):错误示范

3.5 事务管理

无论哪种配置,都要在配置文件中配置事务管理器数据源

  1. 基于xml配置

①:配置事务管理器(就是一个bean),需要数据源
②:配置事务管理方式,指定给哪些功能(方法)配置事务
③:配置AOP,将事务织入到切点中

	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/java_0425?useSSL=false"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>


    <!-- 事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 具体事务管理方式-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- name:指定目标方法名称-->
            <tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
        </tx:attributes>
    </tx:advice>

    <!-- 配置AOP-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(boolean com.dong.service.AccountServiceImpl.transfer(..))"/>
        <!-- 将事务织入到切点中-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
    </aop:config>
  1. 基于注解

①:配置事务管理器(就是一个bean),需要数据源
②:spring配置文件中开启注解支持
③:类、接口或者方法上添加@Transactional

代码:

<!-- 开启注解配置-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional(
            transactionManager = "transactionManager",
            isolation = Isolation.DEFAULT,
            propagation = Propagation.REQUIRED,
            readOnly = false
    )
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public boolean transfer(String fromName, String toName, double money) {
        Account account1 = accountDao.queryByName(fromName);
        Account account2 = accountDao.queryByName(toName);
        account1.setMoney(account1.getMoney() - money);
        int i1 = accountDao.updateAccount(account1);
        int i = 3 / 0;
        account2.setMoney(account2.getMoney() + money);
        int i2 = accountDao.updateAccount(account2);

        return i1 > 0;
    }
}
  • 事务的传播行为
    • REQUIRED : 支持当前事务。如果当前不存在事务,则创建一个事务。
    • SUPPORTS:支持当前事务。如果当前不存在事务,则以非事务方式执行。
    • MANDATORY:支持当前事务。如果当前不存在事务,则抛出异常
    • REQUIRED_NEW:创建一个新事务。如果当前存在事务,则把当前事务挂起。
    • NOT_SUPPORTED:不支持当前事务。如果当前存在事务,则将当前事务挂起。
    • NEVER:不支持当前事务。如果当前存在事务,则抛出异常。
    • NESTED:支持当前事务,如果当前存在事务,则在当前事务中嵌套一个事务,否则类似于REQUIRED

3.6 整合mybatis

X、bean的生命周期

  1. 根据Bean的配置情况,实例化一个Bean。
  2. 根据Spring上下文对实例化的Bean进行依赖注入,即对Bean的属性进行初始化。
  3. 如果Bean实现了BeanNameAware接口,将调用它实现的setBeanName(String beanId)方法,此处参数传递的是Spring配置文件中Bean的ID。
  4. 如果Bean实现了BeanFactoryAware接口,将调用它实现的setBeanFactory()方法,此处参数传递的是当前Spring工厂实例的引用。
  5. 如果Bean实现了ApplicationContextAware接口,将调用它实现的setApplicationContext(ApplicationContext)方法,此处参数传递的是Spring上下文实例的引用。
  6. 如果Bean关联了BeanPostProcessor接口,将调用预初始化方法postProcessBeforeInitialization(Object obj, String s)对Bean进行操作。
  7. 如果Bean实现了InitializingBean接口,将调用afterPropertiesSet()方法。
  8. 如果Bean在Spring配置文件中配置了init-method属性,将自动调用其配置的初始化方法。
  9. 如果Bean关联了BeanPostProcessor接口,将调用postProcessAfterInitialization(Object obj, String s)方法,由于是在Bean初始化结束时调用After方法,也可用于内存或缓存技术。
  10. 当Bean不再需要时,将经过销毁阶段,如果Bean实现了DisposableBean接口,将调用其实现的destroy方法将Spring中的Bean销毁。
  11. 如果在配置文件中通过destroy-method属性指定了Bean的销毁方法,将调用其配置的销毁方法进行销毁
  • 实例化 Instantiation
  • 属性赋值 Populate
  • 初始化 Initialization
  • 销毁 Destruction
    在这里插入图片描述

bean的循环依赖

两个单例bean互相依赖。
通过三级缓存解决
在这里插入图片描述

一级缓存:singletonObjects
二级缓存:earlySingletonObjects
三级缓存:singletonFactories

先获取bean

getBean()
在这里插入图片描述

进入之后,再进调用DefaultSingletonBeanRegistrygetSingleton(beanName)方法,
然后先从一级缓冲中获取

  • 如果一级没有获取到,并且当前单例对象正在创建中(已经实例化,但是未初始化),则从二级缓存中取
  • 如果二级缓存也没获取到,就从三级缓存找
  • 如果在三级缓存找到了就,添加到二级缓存,之后再从三级缓存中移除

最后返回
在这里插入图片描述

没获取到则创建bean

缓存中没有获取到则会创建bean实例(createBean()doCreateBea()):
在这里插入图片描述

实际上的,bean通过反射实例化的步骤(doCreateBean()
在这里插入图片描述

执行到doCreateBean中的下面
在这里插入图片描述

添加到三级缓存为ObjectFactory<?>类型,同时在
在这里插入图片描述

getEarlyBeanReference
在这里插入图片描述

填充属性,给属性赋值(populateBean()
在这里插入图片描述
在这里插入图片描述

使用三级缓存解决单例bean的循环依赖,在第一个实体类createBeanInstance()方法创建完bean实例,和populateBean()填充属性之间,添加三级缓存实例创建工厂,实现提前暴漏,这样在第二个实体类填充第一个实体类属性时,通过getSingleton()方法获取三级缓存中提前暴漏的第一个实体类创建的bean,从而解决循环依赖。
在这里插入图片描述

参考原文链接:https://blog.csdn.net/Maybe_9527/article/details/112303239

spring循环依赖流程分析:https://blog.csdn.net/hao134838/article/details/121239018?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-121239018-blog-112303239.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-121239018-blog-112303239.pc_relevant_aa&utm_relevant_index=1

IOC流程解析,循环依赖:https://blog.csdn.net/qq_39339965/article/details/124416399?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-124416399-blog-112303239.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-124416399-blog-112303239.pc_relevant_aa&utm_relevant_index=2

Aop实现原理

BeanFactoryAware

BeanPostProcessors

配置文件给bean的属性替换时机

BeanFactoryPostProcessor

spring所创建的所有对对象,都会被声明为BeanDefinition

Aware接口

是为了让bean获取spring容器的服务:

spring 提供的aware的接口:

BeanNameAware :可以获取容器中bean的名称

BeanFactoryAware:获取当前bean factory这也可以调用容器的服务

ApplicationContextAware: 当前的applicationContext, 这也可以调用容器的服务

MessageSourceAware:获得message source,这也可以获得文本信息

applicationEventPulisherAware:应用事件发布器,可以发布事件,

ResourceLoaderAware: 获得资源加载器,可以获得外部资源文件的内容;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值