Spring
一. Spring家族的介绍
1.1 如何选择框架
- 对应的开发者社区是否有名,是否活跃
- 框架的模块是否迭代
1.2 家族体系
- 主要的部分:
- Spring Core
- Spring Data
- Spring Boot
- Spring Security
- Spring Session
- Spring Intergration
- Spring REST Docs
- Spring AMQP
- Spring Cloud
- 等
二. IOC
2.1 IOC(Inversion of Control): 控制反转
- Spring Core最核心的部分
- 需要先了解依赖注入(Dependency Inversion)
2.2 依赖注入
-
为什么要用依赖注入?
- 传统的方式是底层决定了上层,如果底层发生改变则所有的都需要改变;这样显然是极大的消耗性能的;
-
使用依赖注入的好处?
- 把底层类作为参数传递给上层类,实现上层对下层的"控制";
未使用依赖注入的时候,比如一个行李箱的设计,是通过轮子的大小,然后定制出底盘大小,通过底盘和轮子又定制出箱体的大小;如果轮子发生了改变则所有的都无法使用了;
使用依赖注入的时候,首先给一个行李箱的概念,通过注入箱体,底盘和轮子的参数然后构造出一个合适的箱体,当轮子或者其他发生改变了,则也可以直接变化;
2.3 IOC、DI、DL的关系
- 图示:
2.4 实现依赖注入的方式:
- Setter
- Interface 实现接口
- Constructor 构造函数
- Annotation 基于注解
依赖倒置原则,就是本来是底层决定上层改为从上层影响下层;这部分被隐藏掉了,我们只看到了实例被创建出来,不需要去了解它如何创建的;
2.5 Spring IOC支持的功能
- 依赖注入
- 依赖检查
- 自动装配
- 支持集合
- 指定初始化方法和销毁方法
- 支持回调方法
最核心的功能是依赖注入和自动装配
2.6 Spring IOC容器的核心接口
- BeanFactory:
- BeanDefinitionRegistry:提供向IOC容器注册BeanDefinition对象的方法
- 它是Spring框架最核心的接口
- 提供IOC的配置机制
- 包含Bean的各种定义,便于实例化Bean
- 建立Bean之间的依赖关系
- Bean生命周期的控制
- ApplicationContext
BeanFactory是Spring框架的基础设施,面向Spring;ApplicationContext面向使用Spring框架的开发者;
2.7 ApplicationContext的功能(继承多个接口)
- BeanFactory:能够管理、装配Bean
- ResourcePatternResolver:能够加载资源文件
- MessageSource:能够实现国际化等功能;
- ApplicationEventPublisher:能够注册监听器,实现监听机制;
2.8 分析加入Bean
@Configuration
public class ApplicationConfig{
@Bean(name="person")
public Person initPerson(){
Person user=new Person();
user.setId(1L);
user.setName("Jack");
return user;
}
}
在这里的代码中:使用@Configuration注解加入IOC容器,而@Bean注解则配置了一个Bean,如果不指定name,则默认为initPerson作为key[Bean的名称];
- 启动实例
@SpringBootApplication
public class FrameworkApplication{
public static void main(String[] args){
ApplicationContext ctx=SpringApplication.run(FrameworkApplication.class);
Person person=ctx.getBean(Person.class);
System.out.println("Name is"+ person.getName());
}
}
运行application拿到ApplicationContext然后再getBean拿到person即可;
使用SpringBoot扫描注入的方式更方面,代码如下:
@Component("person")
public class Person{
@Value("1")
private Long id;
@Value("Jack")
private String name;
...
}
这里使用了@Component配置了此类被扫描,然后@Value注入了每个属性的值;当application启动的时候,就会自动初始化加入IOC容器,所以效果与上面的一样;
@SpringBootApplication注解已经有了@ComponentScan ,所以不需要单独在启动类中加上此注解;
2.9 getBean方法的代码逻辑
- 转换beanName
- 从缓存中加载实例
- 实例化Bean
- 检测parentBeanFactory
- 初始化依赖的Bean
- 创建Bean
3.10 Spring Bean的作用域
- singleton: Spring的默认作用域,容器里拥有唯一的Bean实例
- prototype:针对每个getBean请求,容器都会创建一个Bean实例
- request: 会为每个Http请求创建一个Bean实例
- session: 会为每个session创建一个Bean实例
- globalSession: 会为每个全局Http Session创建一个Bean实例,该作用域仅对Portlet有效;
3.11 SpringBean的生命周期
-
创建过程:
-
销毁过程
- 若实现了DisposableBean接口,则会调用destroy方法
- 若配置了destry-method属性,则会调用其配置的销毁方法;
三. AOP
3.1 关注点分离: 不同的问题交给不同的部分去解决;
- 面向切面编程AOP正式此种技术的体现
- 通用化功能代码的实现,对应的就是所谓的切面;
- 业务功能代码和切面代码分开后,架构将变得高内聚低耦合;
- 确保功能的完整性:切面最终需要被合并到业务中(Weave)
3.2 AOP的三种织入方式
- 编译时织入:需要特殊的Java编译器,如AspectJ
- 类加载时织入:需要特殊的Java编译器,如AspectJ和AspectWerkz
- 运行时织入: Spring采用的方式,通过动态代理的方式,实现简单;
3.3 运行时织入的代码演示
- 引入相关依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</group>
<artifactId>spring-boot-starter-web</artifactId>
<dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop></artifactId>
</dependency>
</dependencies>
- 创建日志类:
@Aspect
@Component
public class RequestLogAspect{
private static final Logger logger=LoggerFactory.getLogger(RequestLogAspect.class);
@Pointcut("execution(public * com.imooc.framework.web..*.*(..))")
public void webLog{};
@Before("webLog()")
public void doBefore(JoinPoint joinPoint){
//接收到请求,记录请求内容
ServletRequestAttributes attributes=(ServletRequestAttributes)RequestContextHolder.get...
//记录下请求内容
logger.info("URL:"+ request.getRqeustURL().toString());
logger.info("IP:"+request.getRemoteAddr());
}
@AfterReturning(returning="ret",pointcut="webLog()")
public void doAfterReturning(Object ret){
//处理完请求,返回内容
logger.info("RESPONSE:"+ret);
}
}
@Aspect说明这是一个切面类,@AfterReturning注解表示方法执行完后的返回值;
3.4 AOP的主要名词概念
- Aspect:通用功能的代码实现
- Target:被织入Aspect的对象
- Join Point:可以作为切入点的机会,所有方法都可以作为切入点
- Pointcut:Aspect实际被应用在的Join Point,支持正则
- Advice:类里的方法以及这个方法如何织入到目标方法的方式
- Weaving:Aop的实现过程;
3.5 Advice的种类
- 前置通知(Before)
- 在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。
- 后置通知(AfterReturning)
- 在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
- 异常通知(AfterThrowing)
- 在连接点抛出异常后执行。
- 最终通知(After)
- 在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
- 环绕通知(Around):
- 环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。
五种通知的执行顺序为: 前置通知→环绕通知→后置通知/异常返回通知→最终通知,可以多次执行来查看。
四. AOP的实现原理
4.1 AOP的实现:JdkProxy和Cglib
- 由AopProxyFactory根据AdvisedSupport对象的配置来决定
- 默认策略如果目标类是接口,则用JDKProxy来实现,否则用后者
- JDKProxy的核心:InvocationHandler接口和Proxy类
- Cglib:以继承的方式动态生成目标类的代理 [如果被final修饰的类无法使用Cglib,因为无法继承]
4.2 解析:
-
JDKProxy:通过Java的内部反射机制实现
-
Cglib:借助ASM实现
-
反射机制在生成类的过程中比较高效
-
ASM在生成类之后的执行过程中比较高效;
-
代理模式: 接口+真实实现类+代理类
4.3 Spring里的代理模式的实现
- 真实实现类的逻辑包含在了getBean方法里
- getBean方法返回的实际上是Proxy的实例
- Proxy实例是Spring采用JDK Proxy或 CGLIB动态生成的;