后端学习日记2--Spring Framework:教材阅读篇

序章 制定一下Spring Boot学习方向

我只知道Spring Boot,但是教材直接塞了两大章的Spring Framework,讲的是IoC容器和AOP,我寻思着不是要做项目吗,出于懒惰我直接翻到第四章,才知道为什么要学,摘出来:

Spring Framework 提供了 IoC 容器、AOP、MVC 等众多功能,让开发者可以从烦琐的工作 中抽离出来,只关注在自己的业务逻辑上。

Spring Framework 虽然解决了开发和测试的问题,但在整个系统的生命周期中,上线后的运维也占据了很大的比重,怎么样让系统具备更好的可运维性也是个重要的任务。怎么配置、怎么监控、怎么部署,都是要考虑的事情。
出于这些原因,Spring Boot 横空出世了,它解决了上面说到的各种痛点,再一次将生产力提升了一个台阶。正如 Spring Boot 项目首页 上写的那样,Spring Boot 可以轻松创建独立的生产级 Spring 应用程序,拿来就能用了。这次,Spring Boot 站到了聚光灯下,成了新的主角

这下知道为什么学习之前听Spring Boot听得最多了……

与此同时又去知乎搜了搜学习路线,现在我初步想法是这样的:

Spring Boot和一些组件组合可以实现更多功能,

整合Redis,实现缓存,提升数据库性能

整合RabbitMQ等实现消息应用

Maven打包项目为jar(第四章内容提到过),再用nginx做分布式部署,负载均衡

了解Spring Boot的注解,这个在Spring Framework学习完以后开始了解

再跟个项目把全栈实现一下,差不多了

大部分时间还是学习开发吧

那么这一节还是先关注一下Spring Framework的基础,IoC容器和AOP。

第一章 IoC容器

1 容器

Ioc容器,控制反转容器,听起来怪抽象的。

IoC 容器背后的思想:所有的组件都要被动接受容器的控制,而不是自己去操作,像是把控制权给第三方了一样,所以叫控制反转。

依赖注入是IoC容器的一种实现方式,更能体现IoC的特征。

我们可以把Spring Framework 的 IoC 容器称为 Spring 容器

spring-core 和 spring-beans 模块提供了最基础的功能,其中就包含了 IoC 容器。BeanFactory 是容器的基础接口,我们平时使用的各种容器都是它的实现。

我想起来第一次执行这里就有个Bean

1.1 容器初始化[A]

(cryworld是上节残留的,不要管)

首先在构造类里新建一个beanFactory,然后reader给它注入配置,再引用.getBean()就能实现对hello的生命周期管理了,不需要手动操作(实例化,销毁等)

1.2 ApplicationContext

ApplicationContext 接口继承了 BeanFactory,在它的基础上增加了更多企业 级应用所需要的特性,通过这个接口,我们可以最大化地发挥 Spring 上下文的能力。所以通常情况下,实现类推荐使用ApplicationContext:

public class SecondnoteApplication {
	private ApplicationContext applicationContext;

	public static void main(String[] args) {
		SecondnoteApplication secondnoteApplication = new SecondnoteApplication();
		secondnoteApplication.sayHello();
	}

	public SecondnoteApplication() {
		// applicationContext != new ApplicationContext() ;
		applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    }
	public void sayHello() {
		Hello hello = applicationContext.getBean("hello", Hello.class);
        // 替代 new Hello();
		System.out.println("hello.hello()");
	}
1.3 容器的继承

2. Bean

2.1 概念

JavaBeans 是 Java 中一种特殊的类,可以将多个对象封装到一个对象(Bean)中。 特点是可序列化,提供无参构造器,提供 Getter 方法和 Setter 方法访问对象的 属性。名称中的 Bean 是用于 Java 的可重用软件组件的惯用叫法。

Bean 是指 Java 中的可重用软件组件,Spring 容器也遵循这一惯例,因此将容器中管理的可重用组件称为 Bean。容器会根据所提供的元数据来创建并管理这些 Bean,其中也包括它们之间的依赖关系。Spring 容器对 Bean 并没有太多的要求,无须实现特定接口或依赖特定库,只要是最普通的 Java 对象即可,这类对象也被称为 POJO (Plain Old Java Object)

2.2 依赖注入

所谓“依赖注入”,很重要的一块就是管理依赖。在 Spring 容器中,“管理依赖”主要 就是管理 Bean 之间的依赖。有两种基本的注入方式——基于构造方法的注入和基于 Setter 方法的注入

手动配置依赖在 Bean 少时还能接受,当 Bean 的数量变多后,这种配置就会变得非常繁琐。在合适的场合,可以让 Spring 容器替我们自动进行依赖注入,这种机制称为自动织入。

开启自动织入后,仍可以手动设置依赖,手动设置的依赖优先级高于自动织入 ,自动织入无法注入基本类型和字符串;

2.3 容器配置(元数据)[B]

Spring 容器的元数据配置本质上就是配置 Bean(AOP 和事务的配置背后也是配置各种 Bean)。

Spring Framework 提供了 <beans/> 这个 Schema 来配置 Bean。

Spring Framework 2.0 引入了 @Required 注解 ,Spring Framework 2.5 又引 入了 @Autowired、@Component、@Service 和 @Repository 等重要的注解, 使用这些注解能简化 Bean 的配置。

从 Spring Framework 3.0 开始,我们可以使用 Java 类代替 XML 文件,使用 @Configuration、@Bean 和 @ComponentScan 等一系列注解,基本可以满足日 常所需。
通过 AnnotationConfigApplicationContext 可以构建一个支持基于注解和 Java 类的 Spring 上下文

2.4 Bean在容器中的生命周期

A+B得到了一个可正常使用的对象。

回调方法是一种编程模式,主要用于实现异步操作和事件处理。 它涉及将一个函数作为参数传递给另一个函数,并在特定事件发生或异步操作完成时被调用。这种机制允许程序在等待某些操作完成的同时继续执行其他任务,从而提高程序的响应性能和并发处理能力。

无论是初始化还是销毁,Spring 都会按照如下顺序依次进行调用:

(1) 添加了 @PostConstruct 或 @PreDestroy 的方法;
(2) 实现了 InitializingBean 的 afterPropertiesSet() 方法,或 DisposableBean 的 destroy() 方法;
(3) 在 <bean/> 中配置的 init-method 或 destroy-method,@Bean 中配置 的 initMethod 或 destroyMethod。

第二章 AOP

1 概念

AOP面向切面编程,和OOP不同但并不对立,它通过切入点来匹配程序中的特定连接点,在这些连接点上执行通知,这种通知可以是在连接点前后执行,也可以是将连接点包围起来。

而Spring将二者融合,并且在多年发展中,一直使用动态代理技术实现,图解如下:

我们可以为某个对象提供一个代理,控制对该对象的访问,代理可以在两个有调用关系的对象之间起到中介的作用——代理封装了目标对象,调用者调用了代理的方法, 代理再去调用实际的目标对象

动态代理就是在运行时动态地为对象创建代理的技术。它允许你在运行时创建一个实现了一组给定接口的新类。比方说本来有一个写好的类class M,它的方法就是去做A;你可以动态创建一个新的类class N,修改A的行为但又不改变A,可以反复阅读下面的代码示例来感受。

在 Spring 中,由 AOP 框架创建、用来实现切面的对象被称为 AOP 代理(AOP Proxy)。

Spring 容器在为 Bean 注入依赖时,会自动将被依赖 Bean 的 AOP 代理注入进来,这就让我们感觉是在使用原始的 Bean,其实不然。
被切面拦截的对象称为目标对象(target object)或通知对象(advised object)因为 Spring 用了动态代理,所以目标对象就是要被代理的对象。

2 JDK动态代理研究

以 JDK 动态代理为例,假设我们希望在代码示例 3-1 的方法执行前后增加两句日志,可以采用下面这套代码,Hello类是一个接口类,SpringHello类完成了对它的实现。

随后,我们可以像代码示例 3-2 那样实现一个 InvocationHandler[动态代理的核心接口之一]

于是对代理对象(3-3的target)的调用都会转为调用 3-2中的invoke 方法,传入的参数中就包含了所调用的方法和实际的参数。

3-3使用Proxy.newProxyInstance() ,为 SpringHello类【也即接口实现类】的 Bean实例(3-3的original),创建一个使用了LogHandler的代理

Instance:实例;Invoke:调用

public interface Hello { void say();}
public class SpringHello implements Hello {   
    @Override
    public void say() { 
    System.out.println("Hello Spring!"); } 
}

代码示例 3-1

public class LogHandler implements InvocationHandler { 
    private Hello source;

    public LogHandler(Hello source) {this.source = source;} 
    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Ready to say something."); 

        try {
        return method.invoke(source, args); } 
        finally { System.out.println("Already say something."); } } }

代码示例3-2

Object类是Java中所有类的父类,相当于是Java中的”万类之王“,处于最顶层。所以在Java中,所有的类默认都继承自Object类。

这个类的设计:

1.构造函数LogHandler(Hello source)  用于传入source(被代理的对象)

2.它用了InvocationHandler接口,需要重写的方法是invoke,invoke的传入参量有三个

然后return里面的method.invoke是正经调用source(Hello接口实现类)的方法

3-3中的最后一个传入参数是new LogHandler(original),调用的方法是target.say()

在 Java 中,接口的实现类可以被当作接口类型的实例来传递,这是多态的一种体现。

于是执行的输出是:invoke中的Ready to say something,然后是source.say(),然后是Already say something.

public class Application {
public static void main(String[] args) { 
    Hello original = new SpringHello();
    // target是代理对象,截获original,在它前后做操作,操作方法在LogHandler里
    Hello target = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(),original.getClass().getInterfaces(),new LogHandler(original));
    target.say(); } }

代码示例3-3

3 配置AOP的两种方式

Spring Framework 同时支持 @AspectJ 注解和 XML Schema 两种方式来使用 AOP

教材更推荐使用注解方式,所以另一种以后遇到再学吧

注意:

 (1) 添加 @Aspect 注解只是告诉 Spring“这个类是切面”,但并没有把它声明为 Bean,因此需要我们手动进行配置,例如添加 @Component 注解,或者在 Java 配置类中进行声明。
(2) Spring Framework 会对带有 @Aspect 注解的类做特殊对待,因为其本身就是一个切面,所以不会被别的切面自动拦截。

声明了切面后,就可以配置具体的切入点和通知了

3.1 切入点

切入点声明由两部分组成——切入点表达式和切入点方法签名。

前者用来描述要匹配的连接点,后者可以用来引用切入点,方便切入点的复用,具体如下:

execution 用得非常多,日后遇到了可以百度下用法。

PCD:切入点表示符

由于 Spring AOP 的实现基于动态代理,因而只能匹配普通方法的执行,像静态初始化、静态方法、构造方法、属性赋值等操作都是拦截不到的。所以说相比AspectJ 而 言,Spring AOP 的PCD功能弱很多,但在大部分场景下也基本够用。

3.2 通知

Spring AOP 中有多种通知类型,可以帮助我们在方法的各个执行阶段进行拦截,例如,可 以在方法执行前、返回后、抛出异常后添加特定的操作,也可以完全替代方法的实现,甚至为一个类添加原先没有的接口实现。

前置:

@before 注解可以用来声明一个前置通知,注解中可以引用事先定义好的切入点,也可以直接传入一个切入点表达式,在被拦截到的方法开始执行前,会先执行通知中的代码

后置:

在方法执行后,可能正常返回,也可能抛出了异常。如果想要拦截正常返回的调用,可以使用
@AfterReturing 注解。

如果想要拦截抛出异常的调用,可以使用 @AfterThrowing 注解,这个注解的用法和@AfterReturing 极为类似,如果不关注执行是否成功,只是想在方法结束后做些动作,可以使用 @After 注解。添加了@After 注解的方法必须要能够处理正常与异常这两种情况,但它又获取不到返回值或异常对象,所以一般只被用来做一些资源清理的工作。

环绕:

如果我们希望统计 say() 方法的执行时间,可以添加 @Around 注解来声明环绕通知,这个方法的签名需要特别注意,它的第一个参数必须 是 ProceedingJoinPoint 类型的,方法的返回类型是被拦截方法的返回类型,或者直 接用 Object 类型。

引入:

不常用

3.3 合成举例:

4 去别的地方看点例子~

半小时后注:懒得看了,以后开发需要弄日志再来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值