• Core Container这个模块是spring框架中最核心的部分,其他所有模块都是依赖它运行的。看到容器(装对象的),根据这样的一个结构设计来看,spirng是用来管对象的技术

  • 是AOP(面向切面编程)模块设计型的概念。具体是做什么的呢?它可以在不惊动原始程序的基础上,给它增强功能,aspect也是对aop思想进行了实现

  • Data Access 是和我们与数据库交互相关的内容

  • web 和spring mvc相关

  • test 用于快速测试,单例测试:就是对一个一个的方法进行测试的模式

核心概念

IOC控制反转/DI

在controller中要使用Service 所以我们需要由既要创建controller,又要使得controller创建的时候要带上Service,将controller内部加上Service的过程就是DI(依赖注入)

配置文件注入方式

id是方便IOC容器查找的
class指明这个类在项目中的位置

1
2
3



从容器中拿到对应的类

1
2
3
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //读取配置文件
BlogService bookService = (BlogService) context.getBean("blogService");//从容器中拿到内容
System.out.println(bookService);

Service和Dao之间的关系怎么描述,怎么注入?

首先在service中提供对应的set方法

1
2
3
public void setBlog(Blog blog) {
this.blog=blog;
}

然后在配置中提供描述

1
2
3
4
5
6
7
8
9
10

//单例模式

//多实体类






这样在启动项目后就能拿到带有blog属性的blogService了

在上面中我们提到单例模式:单例模式是设计模式的一种,用于控制示例生成的模式,当一个实例生成后,往后程序中所有对这个实例的引用都是同一个

设计这种模式的原因是为了提高性能,少创建实例,垃圾回收,缓存快速获取,因为要是多例,它会无穷无尽,用一次造一个,而spring在帮我们管理对象的时候,其实就是管理那些可以复用的对象。

Bean的实例化

  • 构造方法

一、构造方法实例化

构造方法实例化是最常见的 Bean 实例化方式,Spring 容器会通过调用类的构造方法来创建 Bean 实例。以下是 Java 代码和 XML 配置示例:

java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 定义一个简单的类,用于创建 Bean 实例
public class UserService {
// 无参构造方法
public UserService() {
System.out.println("UserService 实例被创建");
}

public void doSomething() {
System.out.println("UserService 执行操作");
}
}

//有参模板
public class Student {
private String name;
private int age;

// 有参构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Student 实例通过有参构造方法被创建");
}

public void showInfo() {
System.out.println("姓名: " + name + ", 年龄: " + age);
}
}

xml配置

1
2
3
4
5
6
7
8
9
10
11
12












  • 静态工厂实例化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 定义一个静态工厂类
public class UserServiceFactory {
// 静态工厂方法,用于创建 UserService 实例
public static UserService createUserService() {
System.out.println("通过静态工厂方法创建 UserService 实例");
return new UserService();
}
}

// UserService 类保持不变
public class UserService {
public UserService() {
System.out.println("UserService 实例被创建");
}

public void doSomething() {
System.out.println("UserService 执行操作");
}
}

xml配置

1
2
3
4




  • 实例工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 定义一个实例工厂类
public class UserServiceInstanceFactory {
// 实例工厂方法,用于创建 UserService 实例
public UserService createUserService() {
System.out.println("通过实例工厂方法创建 UserService 实例");
return new UserService();
}
}

// UserService 类保持不变
public class UserService {
public UserService() {
System.out.println("UserService 实例被创建");
}

public void doSomething() {
System.out.println("UserService 执行操作");
}
}

xml配置方式

1
2
3
4
5
6
7







静态工厂和实例工厂的区别就是静态工厂无需实例化,所以在xml中也无需配置

各种注入方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59



















1
2
3




Apple
Banana
Orange






Element1
Element2
Element3




100
200
300














bean的生命周期

在 Spring 框架中,Bean 的生命周期是指一个 Bean 从被 Spring 容器创建到最终被销毁的整个过程,
理解 Bean 的生命周期对于正确使用 Spring 框架以及进行一些高级特性开发(如自定义后置处理器等)非常重要

  • 实例化

创建对象(内存分配)

  • 执行构造方法

  • 执行属性注入

  • 执行bean的初始化方法

  • 使用bean

  • 关闭容器

执行bean的销毁方法

一些与生命周期相关的方法

Aware Spring 会在依赖注入之后,调用相应的回调方法

BeanPostProcessor 前置处理,可以利用postProcessBeforeInitialization 方法对 Bean 进行一些预处理操作

InitializingBean Spring 会调用其 afterPropertiesSet 方法进行初始化操作。或者在xml中指定初始方法

1
2
3
4
5
public class UserService {
public void initMethod() {
System.out.println("UserService 调用自定义初始化方法 initMethod");
}
}
1

BeanPostProcessor 的 postProcessAfterInitialization可以利用这个方法对 Bean 进行一些后处理操作

销毁容器如果 Bean 实现了 DisposableBean,Spring会调用其destroy方法
同样也可以使用xml配置

1
2
3
4
5
public class UserService {
public void destroyMethod() {
System.out.println("UserService 调用自定义销毁方法 destroyMethod");
}
}
1

在实际过程中,我们的程序时运行在java虚拟机上的,但是虚拟机会在bean销毁前退出,也就是会导致destroyMethod方法失效 解决方法有ctx.close(),但是这个属于暴力关闭,不够安全和优雅。 2.注册关闭钩子registerShutdownHook。但是因为我们做的是web应用,容器会交给tomcat关闭。所以一般不用写。

使用properties来进行软编码

properties配置文件

1
2
3
4
5
# 数据库连接配置
jdbc.url=jdbc:mysql://localhost:3306/testdb
jdbc.username=root
jdbc.password=123456
jdbc.driverClassName=com.mysql.cj.jdbc.Driver

xml文件中先加载引入配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

















容器

容器相关属性

注解开发

定义bean

@Component这是一个通用的注解,用于将一个类标记为 Spring 容器管理的 Bean。它是 @Repository、@Service 和 @Controller 的父注解。

1
2
3
4
5
6
7
8
import org.springframework.stereotype.Component;

@Component
public class MyComponent {
public void doSomething() {
System.out.println("MyComponent is doing something.");
}
}

注解注入方式

使用@Scope定义bean的作用范围,PostConstruct和PreDestroy定义周期

1
2
3
4
5
6
7
8
9
10
11
12
@Repository
@Scope("singleton")
public Class BooDaoImpl implements BookDao{
@PostConstruct
public void init(){
System.out.println("book init....");
}
@PreDestroy
public void destroy(){
System.out.println("book destroy....");
}
}
  • @Autowired

  • 作用:@Autowired 是 Spring 中最常用的依赖注入注解,它可以对构造函数、字段、Setter 方法和配置方法进行标注,以实现自动装配。默认情况下,@Autowired 按照类型进行自动装配,如果有多个匹配的 Bean,会尝试根据名称进行匹配。

  • 示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
// 字段注入
@Autowired
private UserDao userDao;

public void createUser() {
userDao.saveUser();
}
}
//指定名称注入
@Service
public class PaymentService {
@Autowired
@Qualifier("alipayPayment")
private Payment payment;

public void doPayment() {
payment.pay();
}
}
  • @Resource
    作用:@Resource 是 JSR - 250 规范的注解,它可以根据名称或类型进行注入。默认情况下,先根据名称进行匹配,如果找不到匹配的 Bean,再根据类型进行匹配。

  • 示例代码

1
2
3
4
5
6
7
8
9
@Service
public class ProductService {
@Resource(name = "productDao")
private ProductDao productDao;

public void addProduct() {
productDao.saveProduct();
}
}

3.@Value 用于简单的值的注入或者是外部配置文件 :后面是默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class AppConfig {
@Value("${app.name}")
private String appName;

@Value("${app.version:1.0}")
private String appVersion;

public String getAppName() {
return appName;
}

public String getAppVersion() {
return appVersion;
}
}

public class ProductService {
@Resource(name = "productDao")
private ProductDao productDao;
@Value("小明")
private String name;
public void addProduct() {
productDao.saveProduct();
}
}
1
app.name=MySpringApp

AOP
我们说过spring有两大特征,一个是ioc,一个是aop。
AOP用于在代码不变的基础下增强功能

在 AOP 中有以下几个概念:

  • AspectJ:切面,只是一个概念,没有具体的接口或类与之对应,是 Join point,Advice 和 Pointcut 的一个统称。

  • Join point:连接点,指程序执行过程中的一个点,例如方法调用、异常处理等。在 Spring AOP 中,仅支持方法级别的连接点。

  • Advice:通知,即我们定义的一个切面中的横切逻辑,有“around”,“before”和“after”三种类型。在很多的 AOP 实现框架中,Advice 通常作为一个拦截器,也可以包含许多个拦截器作为一条链路围绕着 Join point 进行处理。

  • Pointcut:切点,用于匹配连接点,一个 AspectJ 中包含哪些 Join point需要由 Pointcut 进行筛选。

  • Introduction:引介,让一个切面可以声明被通知的对象实现任何他们没有真正实现的额外的接口。例如可以让一个代理对象代理两个目标类。

  • Weaving:织入,在有了连接点、切点、通知以及切面,如何将它们应用到程序中呢?没错,就是织入,在切点的引导下,将通知逻辑插入到目标方法上,使得我们的通知逻辑在方法调用时得以执行。

  • AOP proxy:AOP 代理,指在 AOP 实现框架中实现切面协议的对象。在 Spring AOP 中有两种代理,分别是 JDK 动态代理和 CGLIB 动态代理。

  • Target object:目标对象,就是被代理的对象。

Spring AOP 是基于 JDK 动态代理和 Cglib 提升实现的,两种代理方式都属于运行时的一个方式所以它没有编译时的一个处理,那么因此 Spring 是通过 Java 代码实现的。
各类写法

注意要在主类上开起@EnableAspectJAutoProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

// 定义切面类
@Aspect
@Component
public class UserServiceAspect {

// 定义切入点,匹配 UserService 接口的所有方法
@Pointcut("execution(* com.example.service.UserService.*(..))")
public void userServiceMethods() {}

// 前置通知:在目标方法执行之前执行
@Before("userServiceMethods()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}

// 后置通知:在目标方法执行之后执行,无论目标方法是否抛出异常
@After("userServiceMethods()")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}

// 返回通知:在目标方法正常返回后执行,可以获取目标方法的返回值
@AfterReturning(pointcut = "userServiceMethods()", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println("Method " + joinPoint.getSignature().getName() + " returned: " + result);
}

// 异常通知:在目标方法抛出异常后执行,可以获取抛出的异常
@AfterThrowing(pointcut = "userServiceMethods()", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, RuntimeException ex) {
System.out.println("Method " + joinPoint.getSignature().getName() + " threw exception: " + ex.getMessage());
}

// 环绕通知:包围目标方法的执行,可以在目标方法执行前后进行额外的操作,甚至可以决定是否执行目标方法
@Around("userServiceMethods()")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Around advice: Before method execution");
Object result = pjp.proceed();
System.out.println("Around advice: After method execution");
return result;
}
}