IoC

原理

https://www.jianshu.com/p/ad05cfe7868e

IoC控制反转,是一种设计思想,将以往通过手动创建对象的控制权交由Spring管理,降低对象之间的耦合性。其控制反转,指的是控制对象创建的权力,然后将控制权反转交给外部环境管理。

所有的Bean在项目启动时生成,加载到Spring容器中统一管理,我们需要使用的时候再去容器中取Bean。而不是单独的去new一个相关类来进行调用。

IoC只是一种思想,而依赖注入DI是具体实现。

实现

底层xml配置文件,编写对应属性kv,如k - 类名,v - 类的全路径。

创建一个工厂类解析xml,获取对应类的全路径,并通过反射创建该类对象。

最后从工厂中调用创建好的对象即可。

现在我们开发都是使用注解注入Bean替代xml配置。

AOP

简述

AOP面向切面编程,能将与业务无关,却被业务模块共同调用的服务进行封装(如工具类等),然后进行统一调用,降低模块之间的耦合性,利于拓展和维护。

AOP基于代理模式实现,其分为静态代理和动态代理。

  • AspectJ AOP 静态代理
  • Spring AOP 动态代理(JDK、CGLIB)

Spring AOP

我们要代理的对象实现了接口时,Spring AOP使用 JDK动态代理 去创建代理对象;对于没有实现接口的对象,Spring AOP使用 CGLIB动态代理 去生成一个被代理对象的子类来作为代理(继承)

Spring默认使用的 JDK动态代理。

JDK动态代理原理(反射)

  • 编写一个代理类:实现 InvocationHandler 接口,将原类方法通过重写 invoke() 进行代理实现,invoke()中可以自行添加内容。

    1
    2
    3
    4
    /**
    * proxy 代理类,method 代理方法,args[] 方法参数
    */
    invoke(Object proxy, Method method, Object[] args)
  • 编写工厂类生成代理对象:Proxy.newProxyinstance,获取全部接口,将接口方法通过代理进行增强,最后返回代理对象。

    1
    2
    // loader 类加载器,interfaces[] 全部接口,handler 自行实现的代理类
    newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)

JDK动态代理基于反射实现,工厂类实现了原类的全部接口,并对接口的所有方法进行了代理。通过代理对象执行原类方法时,代理对象会通过反射回调我们实现的代理类的 invoke(),也就是代理方法被调用。

特点:

  • JDK动态代理通过反射实现,是JDK原生的。
  • JDK动态代理,被代理类必须实现接口,且只能代理接口中定义的方法,如果代理类自行编写了不存在于接口的方法,那么JDK动态代理不会实现该方法。

CGLIB动态代理原理(字节码)

底层对代理类的字节码进行操作,生成该类的一个子类,并重写父类的所有可重写方法,也就是生成自定义的代理方法(自行生成子类继承父类,对子类进行代理)。然后通过字节码操作生成代理类。

特点:

  • CGLIB代理的类无需实现接口,CGLIB会自行生成子类继承原类来代理。
  • 由于final修饰的类无法被继承,所以CGLIB不能代理final类。
  • CGLIB代理是生成子类,对原类方法进行重写,生成相应的代理方法,如果原方法由final 或 private修饰,我们无法重写方法,所以final和private修饰的方法不能被代理。

AspectJ AOP(静态代理)

Spring AOP是运行时增强,基于代理;Aspect AOP是编译时增强,基于字节码操作。

Bean

Bean就是IoC容器管理的对象。

Bean生命周期

Bean实例化,根据实现的接口,调用对应方法完成相应操作,如在BeanFactory中获取容器实例,加载Bean所在应用的上下文引用等。

Bean初始化完成后,可以被应用程序使用,它们一直驻留在应用上下文中,,直到应用上下文被销毁。

可以执行destroy()销毁Bean。

Bean作用域

  • singleton:单例,唯一Bean实例
  • prototype:多实例,每次请求都创建一个Bean
  • request:每次Http请求都创建一个Bean,只在当前Http request内有效
  • session:每次新session的Http请求会创建一个Bean,只有当前HttpSession内有效
  • global - session:全局Session,一个Bean对应一个实例

Bean线程安全问题

单例可能会设计多线程修改变量。改变Bean作用域,选择多实例,每个请求都创建一个Bean。或使用ThreadLcoal存储变量。

Spring事务

@Transaction

Spring事务对应数据库事务,也是ACID,隔离级别也一致,多了一种默认的级别default,和数据库的级别同步。

1
@Transactional(rollbackFor = Exception.class)

注意Spring事务默认是运行时异常才回滚,我们可以设置为任何异常都回滚。

AOP

@Transaction也是通过AOP动态代理实现的。由于是动态代理,只有注解方法被类以外方法调用时事务才生效,如果本类方法直接调用则事务无法生效。

MyBatis的#{}、${}

  • #{}:sql参数占位符,mybatis会认定传入参数是一个字符串,将其字段加上’ ‘,替换我们需要的参数。经过预编译,防止sql注入。
  • ${}:变量占位符,直接传值,不做处理。

@SpringBootApplication

1
2
3
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(···)

主启动类注解由三个注解组成:

  • @SpringBootConfiguration:代表主启动注解是一个配置类

  • @ComponentScan:默认扫描主启动类包下的全部注入组件(@Component等),可自定义不扫描的Bean。

  • @EnableAutoConfiguration:开启自动装配

    在META-INF/spring.factories下配置自动加载类,一般都是 **AutoConfiguration 结尾的类,通过@Conditional注解按需加载实现自动装配。