spring 基础

核心思想

1,IOC 和 AOP 是一种思想,而不是一种技术
2,在 spring 之前就已经存在了,之不是这两种思想在 spring 得到了非常好的技术实现

IOC 和 DI

1,Inversion of Control,控制反转
2,控制:创建(实例化、管理)对象的权力,反转:把控制权交出来(交给 spring 的 IOC 容器)
3,传统方式如果 A 依赖 B,那么会在 A 里创建 B 对象,在 spring 里面使用 @Autowired 即可
4,IOC 和 DI 描述的是同一个事情,只是角度不同
  • IOC 是站在对象的角度,即 控制反转。程序需要什么对象,就到容器去取
  • DI 是站在容器的角度,即 依赖注入。程序需要什么对象,容器就推送给他什么对象

AOP

1,Aspect oriented Programming ⾯向切⾯编程
2,OOP(面向对象) 的延续。面向对象能解决很多的代码重复问题。但是有局限性。比如每个接口需要增加额外的操作(比如接口耗时、日志统计等)
  • 在不改变原有业务逻辑的情况下,横切逻辑代码,从根本上解耦,避免重复代码
  • 切:代码是竖着执行,执行的是业务逻辑,业务逻辑之外的增强逻辑代码,可以想象成一把刀把业务横着切开了
  • 面:横切往往影响很多方法,每个方法都如同一个点,点多了构成一个面

IOC 应用

其实就是创建 bean 对象的一些操作

bean 生命周期

<bean id="transferService" class="com.lagou.service.impl.TransferServiceImpl" scope="singleton" />
  • singleton:单例模式,容器创建时创建 bean,每次使用都是这个 bean
  • prototype:多例模式,每次使用都创建新的 bean

基于 xml

<?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
    https://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--配置service对象-->
        <bean id="userService" class="com.lagou.service.impl.UserServiceImpl" />
    </beans>

方式一:使用无参构造函数(默认)创建对象

使用反射调用无参构造,如果当前类没有无参构造函数会创建失败

<!-- id 必须唯一;class 指定该 bean 的实现类,反射创建的对象 -->
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
    <!-- 指定默认值,将调用 setName 方法 -->
    <property name="name" value="Rose"></property>
</bean>

方式二:使用静态工厂方法创建对象

<!-- 定义 chinese Bean 由 PersonFactory 工厂的 getPerson(这是个静态方法) 方法创建 -->
<bean id="chinese" class="com.mao.staticFactory.PersonFactory" factory-method="getPerson">
    <!-- getPerson 方法传参 -->
    <constructor-arg value="chinese"/>
    <!-- setMsg 传参 -->
    <property name="msg" value="我是中国人"/>
</bean> 

<!-- 创建american Bean -->
<bean id="american" class="com.mao.staticFactory.PersonFactory" factory-method="getPerson">
    <constructor-arg value="american"/>
    <property name="msg" value="我是美国人"></property>
</bean>
public class PersonFactory {
    public static Person getPerson(String arg){
    if(arg.equalsIgnoreCase("chinese")){
        return new Chinese();
    } else {
        return new American();
    }
  }
}

分析 chinese bean 的创建

  • 将调用 PersonFactory.getPerson 完成实例化
  • 根据 constructor-arg = chinese,方法传入了 chinese,所以会返回 new Chinese()
  • 根据 <property name="msg" value="我是中国人"/> 会调用 Chinese 的 setMsg("我是中国人")
  • 得到的 bean:chinese:

方式三:使用普通方法创建对象

方法不是静态的,不用 static 修饰

<!-- 配置工厂Bean,class指定该工厂的实现类,该Bean负责产生其他Bean实例 -->
<bean id="personFactory" class="com.mao.instanceFactory.PersonFactory"/>

<!-- 由实例工厂Bean的getPerson()方法,创建Chinese Bean, -->
<bean id="ch" factory-bean="personFactory" factory-method="getPerson">
  <!-- 为该方法传入参数为chinese -->
  <constructor-arg value="chinese"/>
</bean>

<!-- 由实例工厂Bean的getPerson()方法,创建American Bean, -->
<bean id="usa" factory-bean="personFactory" factory-method="getPerson">
  <constructor-arg value="american" />
</bean>
public class PersonFactory {
    // 不是静态方法(其实是不是都没关系,因为不管是否是静态的,都能调用到这个方法)
    public Person getPerson(String arg){
    if(arg.equalsIgnoreCase("chinese")){
        return new Chinese();
    } else {
        return new American();
    }
  }
}

基于注解

不再使用 xml 配置文件,使用注解完成 bean 的定义和作用域等属性配置
注解 对应 xml 标签 注解的地方 含义
@Configuration xml配置文件 表名当前类是⼀个配置类
@Bean xml 的 bean 标签 方法 方法名是 id 属性,return 类型是 class 属性
有个属性 initMethod,初始化后执行,效果和 @PostConstruct 一致
对于初始化后执行的操作还有一种方式:实现 InitializingBean 接口,这种性能要高些,因为不是反射完成的
@PostConstruct bean 标签的 init-method 属性 方法 配合 @Bean 使用,初始化 bean 后执行
@Scope bean 标签的 scope 属性 方法 和 @Bean 配合使用,指明作用域,单例还是多例
@Lazy bean 标签的 lazy-init=true 方法 和 @Bean 配合使用,是否懒加载
使用时才实例化 bean,而不是服务启动就实例化所有 bean
@Value bean 的 property 属性标签 属性 实例化该 bean 时,调用 set 方法赋值
1,@Value("#{xxx.xxx}"是把别的 bean 的值进行赋值
2,@Value("${xxx.xxx}") 是把配置文件的值进行赋值(读取配置文件)
3,@Value("xxx") 直接赋值基本类型
@ComponentScan context:component-scan 扫描包范围,该路径下的 bean 交给 spring 管理
1,一般使用 @ComponentScans,全局出现一次
@ComponentScans(value = {@ComponentScan(value = "io.mieux.controller"),@ComponentScan(value = "io.mieux.service")})
2,value 和 basePackages 功能相同
3,basePackageClasses 属性把指定的类添加到容器中
4,includeFilters 添加某些类到容器中,即使这些类没有标注 @Component 注解
5,指定某些类不添加到容器中
@PropertySource context:property-placeholder location="classpath:xxx.properties" 引⼊外部属性配置⽂件,利用其数据构建 bean,一般配合@Configuration、 @ConfigurationProperties、@EnableConfigurationProperties 等注解完成
@Import - 类或注解 也是把指定的类添加到容器中,有三种用法,后面详细讲解
@Component - 类或接口 依赖注入 bean,还有 @Service、@Controller、@Repository,必须在 @ComponentScan 指定的路径下

循环依赖

两个或以上的对象互引用对方,造成创建 bean 时死循环
public class A{
    private B b;
}
public class B{
    private A a;
}
  • 产生原因
    • spring 在实例化 A 的时候,发现其属性引用了 B
    • 实例化 B,但是在 B 内部也引用了 A(此时 A 还没有创建好,ApplicationContext.getBean(A)获取不到,于是就会创建 A)
    • ...
  • spring 解决方案
    • 实例化 A 的时候,创建好了,但还没有完成初始化(成员变量赋值),这时把 A 先暴露出去(三级缓存)
    • 为 A 初始化,发现成员变量依赖 B,B 还没有创建,于是实例化 B,B 实例化完成
    • 为 B 初始化,发现成员变量依赖 A,这时候已经能获取到 A 了,B 初始化完成,这时能拿到 B 了,再完成 A 的初始化

AOP 应用

本质是在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、⽇志代码、事务控制代码、性能监控代码
说人话就是 一些方法要添加相同的操作功能,不到每个方法里添加相同的代码块,单独写一个方法,在每个方法执行时能执行到新添加的这个方法

术语

  • 连接点(Joinpoint):目标方法执行的某个时机,执行前,执行后,抛出异常时等
  • 切点(Pointcut):具体方法(定义类也会落实到类下面的所有方法)
  • 通知(advice):前置、后置、环绕、异常、返回通知,具体增强的逻辑是啥
  • 目标对象(target):被代理的对象
  • 代理对象(proxy):代理对象(动态代理,有接口就 JDK ,没有接口就 CGLIB)
  • 织入(Weaving):编译期、类装载、动态代理织入三种方式,spring 采用动态代理方式,AspectJ 采用编译期和类装载方式
  • 切面(Aspect):是一个类,定义切点、连接点、通知等

基于 xml

基于注解

见以前我写的 demo(https://gitee.com/huanggyaaa/springboot/tree/master/aspect),可以切注解、类、方法、接口,有前置、后置、环绕、返回等通知  
这是基于 springboot 的,springboot 的自动加载功能(@EnableAutoConfiguration)会自动加载事务相关的信息,所以可以不用 @EnableTransactionManagement 注解

声明式事务底层

用两个注解即可完成,@EnableTransactionManagement 和 @Transactional,分析下这俩货的底层做了啥操作
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 这里导入了 TransactionManagementConfigurationSelector 类
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {
    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default 2147483647;
}
// @Import 注解用法之一就是 类 必须实现 ImportSelector 接口并重写 selectImports 方法
public class TransactionManagementConfigurationSelector extends 
    // 省略...
    // 像容器又添加了两个组件:AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration
    protected String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{this.determineTransactionAspectClass()};
        default:
            return null;
        }
    }
    // 省略...
}

AutoProxyRegistrar组件

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    // 省略 ...
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
       // 省略 ...
                    if (mode == AdviceMode.PROXY) {
                        // 这里又注册了一个组件
                        AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                    }
        }
        // 省略 ...
    }
}
public abstract class AopConfigUtils {
    // 省略 ...
    @Nullable
    public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
        return registerAutoProxyCreatorIfNecessary(registry, (Object)null);
    }
    // 省略 ...
    @Nullable
    public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
        return registerOrEscalateApcAsRequired(
            // 最终注册了 InfrastructureAdvisorAutoProxyCreator 的 bean
            // 这个类继承了 AbstractAutoProxyCreator
            InfrastructureAdvisorAutoProxyCreator.class, registry, source);
    }
    // 省略 ...
}

ProxyTransactionManagementConfiguration 组件

@Configuration(proxyBeanMethods = false)
@Role(2)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    public ProxyTransactionManagementConfiguration() {
    }

    @Bean(name = {"org.springframework.transaction.config.internalTransactionAdvisor"})
    @Role(2)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
        // 事务增强器
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        // 向事务增强器中注⼊ 属性解析器 transactionAttributeSource
        advisor.setTransactionAttributeSource(transactionAttributeSource);
        // 向事务增强器中注⼊ 事务拦截器 transactionInterceptor
        advisor.setAdvice(transactionInterceptor);
        if (this.enableTx != null) {
            advisor.setOrder((Integer)this.enableTx.getNumber("order"));
        }

        return advisor;
    }

    @Bean
    @Role(2)
    // 属性解析器 transactionAttributeSource,作用之一就是 解析@Transaction注解
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    @Bean
    @Role(2)
    // 事务拦截器 transactionInterceptor
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        // interceptor.invoke 调用 interceptor.invokeWithinTransaction,完成原有的逻辑和增强的通知执行
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }

        return interceptor;
    }
}
  • 可以看到 @EnableTransactionManagement 导入了两个类:AutoProxyRegistrarProxyTransactionManagementConfiguration
  • AutoProxyRegistrar 组件引入了 InfrastructureAdvisorAutoProxyCreator 类,这个类继承了 AbstractAutoProxyCreator 是一个后置处理器
  • ProxyTransactionManagementConfiguration 组件完成 AOP 通知操作
posted @ 2021-08-17 11:00  huanggy  阅读(44)  评论(0编辑  收藏  举报