Spring-框架

Spring概述

传统JavaWeb开发困惑及解决方案


问题一:层与层之间的紧密耦合,接口与实现紧密耦合

解决思路:程序代码不需要手动new对象,第三方根据要求为程序提供需要的bean对象(工厂模式)


问题二:通用的事务功能、日志功能够合在业务代码中

解决思路:程序代码中不需要手动new对象,第三方根据要求为程序提供需要的bean对象的代理对象,通过代理将原对象中的方法进行增强

IoC、DI和AOP思想的提出



IoC思想

Inversion of Control,控制反转,强调原来在程序中创建Bean的权利反转给第三方

DI思想

Dependency Injection,依赖注入,强调的Bean之间的关系,这种关系第三方负责设置,即由容器将Bean之间的引用关系设置好

AOP思想

Aspect Oriented Programming,面向切面编程,功能的横向抽取,主要实现方式是Proxy

Spring框架的诞生

Spring框架概述

Spring历史

Spring Framework

BeanFactory快速入门

将一个bean对象设置到bean工厂中


配置清单是BeanFactory创建Bean类的依据

1.

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.12</version>
</dependency>
</dependencies>

2.

1
2
3
4
5
6
7
//接口
public interface UserService {
}

//实现类
public class UserServiceImpl implements UserService {
}
  1. 此处底层通过反射拿到全包名,创建对象
1
<bean id="userService" class="com.jackey.service.Impl.UserServiceImpl"></bean>

4.

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BeansFactoryTest {
public static void main(String[] args) {
//创建一个工厂对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建一个读取器(xml)
XmlBeanDefinitionReader reader= new XmlBeanDefinitionReader(beanFactory);
//读取配置文件给工厂
reader.loadBeanDefinitions("beans.xml");
//根据id获取Bean实例对象 强转为UserService
UserService userService = (UserService) beanFactory.getBean("userService");
System.out.println(userService);
}
}

这里的强制类型转换是安全的,因为在 Spring 中,UserServiceImpl 类实际上是 UserService 接口的一个实现类,并且 Spring 在配置文件中将其声明为 UserService 类型。因此,即使实际类型是 UserServiceImpl,Spring 在运行时会将其视为 UserService 类型,这也是依赖倒置原则的体现,客户端代码不应直接依赖于具体的实现类,而是依赖于抽象接口。

总结起来,强制转换为 UserService 类型是为了符合面向接口编程的规范,使得代码更加灵活和可维护。


依赖倒置原则

依赖倒置原则(Dependency Inversion Principle,简称DIP)是面向对象设计中的重要原则之一,它主要包括以下两个核心概念:

  1. 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
    • 高层模块通常是指应用程序中的业务逻辑层,低层模块是指底层的基础设施或实现细节。
    • 例如,在一个典型的应用中,业务逻辑层不应直接依赖于具体的数据访问类(例如数据库操作类),而应该依赖于抽象的数据访问接口(如数据访问对象接口)。
  2. 抽象不应该依赖于细节,细节应该依赖于抽象。
    • 这意味着编程时应尽量针对接口或抽象类编程,而不是针对具体实现编程。
    • 这样做可以降低模块之间的耦合度,提高代码的灵活性和可扩展性,使得系统更易于维护和修改。

依赖倒置原则的核心思想是通过抽象来减少模块间的直接依赖关系,从而提高代码的灵活性和可维护性。在实际编码中,我们可以通过以下方式来实现依赖倒置原则:

  • 使用接口或抽象类:定义模块之间的交互约定,而不是直接使用具体类。
  • 依赖注入(Dependency Injection,DI):通过容器(如Spring框架)来管理对象之间的依赖关系,将具体类的创建和管理交给容器,从而降低模块之间的耦合度。
  • 工厂模式:通过工厂方法或抽象工厂来创建对象,客户端只需要依赖工厂接口,而不是具体的产品类。

总之,依赖倒置原则是面向对象设计的一个重要指导原则,它可以帮助我们编写松耦合、灵活和可维护的代码,提高软件系统的质量和扩展性。

将一个bean对象设置到另一个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
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//接口
package com.jackey.dao;

public interface UserDao {
}

//实现类
package com.jackey.dao.impl;

import com.jackey.dao.UserDao;
import com.jackey.service.Impl.UserServiceImpl;

public class UserDaoImpl implements UserDao {
}

//接口
package com.jackey.service;

public interface UserService {

}

//实现类
package com.jackey.service.Impl;

import com.jackey.dao.UserDao;
import com.jackey.service.UserService;

public class UserServiceImpl implements UserService {
private UserDao userDao;

//BeanFactory调用该方法,从BeanFactory中获取sserDao设置到此处
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
System.out.println("将userDao设置到此处");
}

@Override
public String toString() {
return "UserServiceImpl{" +
"userDao=" + userDao +
'}';
}
}


//测试类
package com.jackey;

import com.jackey.dao.UserDao;
import com.jackey.service.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

public class BeansFactoryTest {
public static void main(String[] args) {
//创建一个工厂对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建一个读取器(xml)
XmlBeanDefinitionReader reader= new XmlBeanDefinitionReader(beanFactory);
//读取配置文件给工厂
reader.loadBeanDefinitions("beans.xml");


//根据id获取Bean实例对象 强转为UserService
UserService userService = (UserService) beanFactory.getBean("userService");
System.out.println(userService);


//根据id获取Bean实例对象 强转为UserDao
UserDao userDao = (UserDao) beanFactory.getBean("userDao");
System.out.println(userDao);


}
}

//xml文件
<bean id="userService" class="com.jackey.service.Impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
// 第一个userDao 代表UserServiceImpl中的setter方法setUserDao
// 第二个userDao 代表引用UserDao类
</bean>

<bean id="userDao" class="com.jackey.dao.impl.UserDaoImpl"></bean>

/*
结果
将userDao设置到此处
UserServiceImpl{userDao=com.jackey.dao.impl.UserDaoImpl@42d3bd8b}
com.jackey.dao.impl.UserDaoImpl@42d3bd8b
*/

getBean:

实际上获取的是xml文件中id对应的类,并非getBean()中的类

name属性:

name 属性用于指定要设置的 bean 的属性名。在你的配置中,name=”userDao” 表示你要将一个名为 userDao 的属性设置给 userService bean。

在 Java 类中,这对应于 UserServiceImpl 类中的一个名为 userDao 的属性(通常是通过setter方法来设置的)。

为什么只会获取到setter方法,而不会获取到其他方法

在代码片段中,定义了一个名为 userService的 Spring Bean,它是 UserServiceImpl 类的实例。在这段配置中,通过 元素为 userService注入了一个名为 userDao的依赖。

对于 Spring Framework 来说,它是通过反射机制来实现依赖注入的。具体到配置:

  1. 元素中的 class属性指定了要实例化的类,即 UserServiceImpl。

  2. 元素中的 name属性指定了要调用的 setter 方法,这里是 setUserDao方法,它用来设置 userDao 属性的值。

  3. ref 属性指定了依赖的 Bean 名称,即 userDao,这表示 Spring 将会在容器中查找一个名为 userDao 的 Bean,并将它注入到 UserServiceImpl 实例中的 userDao 属性。

在 Spring 中,通过 XML 配置文件注入属性时,Spring 使用 Java 反射来调用目标 Bean 的 setter 方法,并将依赖注入到目标 Bean 中。因此,只有在 XML 配置文件中显式配置的 setter 方法才会被调用,而其他方法不会被触发。

所以,虽然你可能在 UserServiceImpl 类中定义了其他方法,但是在这段配置中,只有名为 setUserDao的 setter 方法被显式调用并注入了 userDao 属性的值。

ApplicationContext快速入门

BeanFactory与ApplicationContext的关系


BeanFactory是延迟加载的,调用getBean方法时才会创建Bean对象

ApplicationContext只要加载配置文件、创建容器,Bean对象就已经创建

BeanFactory的继承体系


xml文件中的标签都会被封装在DefaultListableBeanFactory中的beanDefinitionMap的map集合中

1
private final Map<String, BeanDefinition> beanDefinitionMap;

ApplicationContext继承体系


继承体系中的类根据配置Spring容器的方式(注解、xml)分为两种

基于xml的Spring应用

SpringBean的配置详解


id属性后期会转成Bean的name,可以理解为getBean(id)获取的是id转成的name

lazy-init延迟加载,什么时候getBean什么时候创建Bean对象

Bean的基础配置

Bean的别名配置



设置别名后,别名会出现在singleObjects中的aliasMap,即存放别名的Map集合中,别名会直接指向Beanname也就是配置的Beanid,如果没有配置Beanid,默认情况下,别名name(aaa bbb ccc)第一个是Beanname(aaa),如果没配置beanid 和别名name,那全限定名是Beanname

Bean的范围设置



prototype创建Bean对象,使用完后会销毁,不会放入单例池中,singleton创建完Bean对象会放入单例池,后续仍可被使用

Bean的延迟加载


对于ApplicationContext有效,对BeanFactory无效

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
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
60
61
62
63
64
65
66
package com.jackey.service.Impl;

import com.jackey.dao.UserDao;
import com.jackey.service.UserService;

public class UserServiceImpl implements UserService {



private UserDao userDao;

//BeanFactory调用该方法,从BeanFactory中获取sserDao设置到此处
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
System.out.println("将userDao设置到此处");
}


//Bean的初始化方法
public void init(){
System.out.println("初始化方法...");
}

//Bean的销毁方法
public void destroy(){
System.out.println("销毁方法...");
}


@Override
public String toString() {
return "UserServiceImpl{" +
"userDao=" + userDao +
'}';
}
}


//xml配置
<bean id="userService" class="com.jackey.service.Impl.UserServiceImpl" init-method="init" destroy-method="destroy">
<property name="userDao" ref="userDao"></property>
</bean>

//测试类
package com.jackey.Test;

import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
System.out.println(userService);
applicationContext.close(); //关闭容器
}
}

//运行结果
/*
将userDao设置到此处
初始化方法...
UserServiceImpl{userDao=com.jackey.dao.impl.UserDaoImpl@6fc6f14e}
销毁方法...
*/

初始化方法在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
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
package com.jackey.service.Impl;

import com.jackey.dao.UserDao;
import com.jackey.service.UserService;
import org.springframework.beans.factory.InitializingBean;

public class UserServiceImpl implements UserService , InitializingBean {



private UserDao userDao;

//BeanFactory调用该方法,从BeanFactory中获取sserDao设置到此处
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
System.out.println("将userDao设置到此处");
}


//Bean的初始化方法
public void init(){
System.out.println("初始化方法...");
}

//Bean的销毁方法
public void destroy(){
System.out.println("销毁方法...");
}


@Override
public String toString() {
return "UserServiceImpl{" +
"userDao=" + userDao +
'}';
}

public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet方法执行");
}
}

//运行结果
/*
将userDao设置到此处
afterPropertiesSet方法执行
初始化方法...
UserServiceImpl{userDao=com.jackey.dao.impl.UserDaoImpl@56235b8e}
销毁方法...
*/

afterPropertiesSet方法在属性设置后,在初始化方法执行前执行

Spring实例化的配置

BeanFactory就是工厂,工厂内部创建Bean时又分为两种方式



当使用工厂方式实例化时,工厂内部的方法需要自定义

静态工厂方法实例化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
25
26
27
28
29
30
package com.jackey.factory;

import com.jackey.dao.UserDao;
import com.jackey.dao.impl.UserDaoImpl;

public class StaticMethodBeanFactory {
public static UserDao setUserDao(){
//返回userDao的实现类 交给Spring容器管理类
//可以再bean创建之前执行一些其他业务逻辑
return new UserDaoImpl();
}
}



public class ApplicationContextTest {
public static void main(String[] args) {

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao1 = (UserDao) applicationContext.getBean("userDao1");
System.out.println(userDao1);


}
}


//<bean id="userDao1" class="com.jackey.factory.StaticMethodBeanFactory" factory-method="setUserDao"></bean>
// 当采用此方法配置的时候,Spring容器解析时,与BeanFactory不同的是 不是将StaticMethodBeanFactory变为对象 ,
//而是去寻找这个类中的方法为setUserDao,把方法的返回值当做对象,再以指定的id作为beanname放入容器中

区别:静态方法不需要创建工厂对象,直接通过工厂名.方法名创建bean对象(体现在xml配置中)

实例工厂方法实例化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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.jackey.factory;

import com.jackey.dao.UserDao;
import com.jackey.dao.impl.UserDaoImpl;

public class MethodBeanFactory {
public UserDao setUserDao(){
//返回userDao的实现类 交给Spring容器管理类
//可以再bean创建之前执行一些其他业务逻辑
System.out.println("实例工厂");
return new UserDaoImpl();
}
}

package com.jackey.Test;

import com.jackey.dao.UserDao;
import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {

/**
* 实例工厂
*/
ClassPathXmlApplicationContext applicationContext2 = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao2 = (UserDao) applicationContext2.getBean("userDao2");
System.out.println(userDao2);

}
}

<!-- 先配置工厂对象-->
<bean id="MyBeanFactory" class="com.jackey.factory.MethodBeanFactory"></bean>
<!-- 再去配置实例工厂的方法的返回值-->
<bean id="userDao2" factory-bean="MyBeanFactory" factory-method="setUserDao"></bean>

**注意需要先配置工厂对象 再去配置工厂对象调用的方法**

如果工厂方法有参数,通过constructor-arg来配置方法的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MethodBeanFactory {
public UserDao setUserDao(String name, int age){
//返回userDao的实现类 交给Spring容器管理类
//可以再bean创建之前执行一些其他业务逻辑
System.out.println("实例工厂");
return new UserDaoImpl();
}
}

//xml配置
//<bean id="userDao2" factory-bean="MyBeanFactory" factory-method="setUserDao">
// <constructor-arg name="name" value="zhangsan"></constructor-arg>
// <constructor-arg name="age" value="23"></constructor-arg>
//</bean>
//通过constructor-arg来配置方法的参数

实现FactoryBean规范延迟实例化Bean

Spring底层用的多,是一个接口,主要用于定义工厂Bean的规范,换言之就是给自定义工厂制定了一套标准,实现这个接口,就相当于使用了这套标准(方法名不再是自定义),使用这套标准后,就不需要在xml文件中配置factory-method

接口源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package org.springframework.beans.factory;

import org.springframework.lang.Nullable;

public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

//返回一个泛型为T的对象
@Nullable
T getObject() throws Exception;

//返回当前Object的类型
@Nullable
Class<?> getObjectType();

//是否单例
default boolean isSingleton() {
return true;
}
}

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
//测试类
package com.jackey.Test;

import com.jackey.dao.UserDao;
import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {

/**
* 实现FactoryBean 规范实例化Bean
*/
ClassPathXmlApplicationContext applicationContext3 = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao3 = (UserDao) applicationContext3.getBean("userDao3");
System.out.println(userDao3);

}
}

//实现FactoryBean接口的类
public class MyStandBeanFactory implements FactoryBean<UserDao> {
public UserDao getObject() throws Exception {
System.out.println("UserDao3");
return new UserDaoImpl();
}

public Class<?> getObjectType() {
return UserDao.class;
}
}

//xml文件
<!--采用 实现FactoryBean接口 规范实例化UserDao-->
<bean class="com.jackey.factory.MyStandBeanFactory" id="userDao3" ></bean>

//运行结果
/*
静态工厂
实例工厂
UserDao3
com.jackey.dao.impl.UserDaoImpl@64bf3bbf
*/

//出现其他非相关信息是因为在xml文件中配置了其他Bean的实现,只要测试类读取配置文件就会创建Bean到容器中,
//所以其他Bean中的内容也会被执行,但是其他Bean的对象需要再getBean才能得到

执行完后,底层单例池中存放的是工厂对象,不是UserDaoImpl对象,但控制台仍然输出的是UserDaoImpl对象的地址


而UserDaoImpl对象存放在


当真正调用这个Bean的时候,也就是getBean的时候,才会把这个实例化的对象返回,也就是才会调用实现接口中的getObject()方法,才会放入缓存当中,当下一次调用从缓存当中取出


注意区分BeanFactory和FactoryBean:

区别:

角色与作用 :

BeanFactory 是 Spring IoC 容器的基础接口,负责管理和控制 Bean 的生命周期,包括实例化、依赖注入、初始化、销毁等。

FactoryBean 是一个特殊的 Bean,实现了 FactoryBean 接口的类负责生产其他的 Bean 实例,允许在实例化过程中进行更多的控制和定制。

功能差异 :

BeanFactory 提供了核心的 Bean 管理功能,它是一个工厂接口,可以管理和获取各种类型的 Bean 实例。

FactoryBean 提供了一种扩展机制,允许开发者自定义复杂的 Bean 实例化逻辑,它本身也是一个 Bean,但其 getObject() 方法返回的不是它自身,而是它管理的另一个 Bean 的实例。

实现方式 :

BeanFactory 是一个接口,定义了 IoC 容器的基础功能,由各种具体实现类(如 ApplicationContext)来实现。

FactoryBean 是一个具体的类,实现了 FactoryBean 接口,开发者需要通过实现该接口来自定义 Bean 的创建过程。

联系:

BeanFactory 是 FactoryBean 的容器 :

BeanFactory 作为 Spring IoC 容器的基础接口,它可以管理和控制 FactoryBean 实例,FactoryBean 实例本身也是由 BeanFactory 管理的一种特殊的 Bean。

FactoryBean 可以定制 Bean 的创建逻辑 :

FactoryBean 接口的存在允许开发者在 Bean 实例化的过程中进行更多的控制和定制,例如根据条件动态创建不同的 Bean 实例,或者通过复杂的逻辑来决定 Bean 的初始化参数。

使用场景 :

使用 BeanFactory 管理和获取标准的 Bean 实例,适用于大多数的 Bean 管理场景。

使用 FactoryBean 适用于需要复杂逻辑或者定制化配置的 Bean 创建场景,例如动态代理、条件化的 Bean 创建等。

总结来说,BeanFactory 是 Spring IoC 容器的基础接口,负责管理和控制 Bean 的生命周期;而 FactoryBean 是一个特殊的 Bean,负责生产其他的 Bean 实例,可以在实例化过程中进行更多的控制和定制。它们在 Spring 应用中扮演了不同但互补的角色。

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
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.jackey.mapper.impl;

import com.jackey.dao.UserDao;
import com.jackey.mapper.PersonMapper;

import java.util.List;

public class PersonMapperImpl implements PersonMapper {

//为集合注入属性
private List<String> arrayList;


//集合对象设置属性
private List<UserDao> userDaoList;

// 提供set方法
public void setArrayList(List<String> arrayList) {
this.arrayList = arrayList;
}

public void setUserDaoList(List<UserDao> userDaoList) {
this.userDaoList = userDaoList;
}



public void showList(){
arrayList.forEach(s -> System.out.println(s));
System.out.println(userDaoList);
}
}


//xml文件
<!-- 注入集合属性 和 对象集合-->
<bean id="personMapper" class="com.jackey.mapper.impl.PersonMapperImpl">
<property name="arrayList">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
<value>ddd</value>
</list>
</property>
<property name="userDaoList">
<list>
<ref bean="userDao4"></ref>
<ref bean="userDao5"></ref>
<ref bean="userDao6"></ref>
</list>
</property>
</bean>
<!-- 配置bean,引用bean-->
<bean id="userDao4" class="com.jackey.dao.impl.UserDaoImpl"></bean>
<bean id="userDao5" class="com.jackey.dao.impl.UserDaoImpl"></bean>
<bean id="userDao6" class="com.jackey.dao.impl.UserDaoImpl"></bean>

//测试类
package com.jackey.Test;

import com.jackey.dao.UserDao;
import com.jackey.mapper.PersonMapper;
import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {
/**
* 注入集合
*/
ClassPathXmlApplicationContext applicationContext4 = new ClassPathXmlApplicationContext("applicationContext.xml");
PersonMapper personMapper = (PersonMapper) applicationContext4.getBean("personMapper");
System.out.println(personMapper);
personMapper.showList();
}
}

自动装配

以上均为手动装配


bean中的set方法,方法名去掉set,其余部分首字母小写,与xml文件中bean的id相同

1
2
3
4
5
6
7
8
9
10
11
12
//创建Animal接口 和 实现类

//配置xml文件
<bean id="userDao" class="com.jackey.dao.impl.UserDaoImpl"></bean>

//测试代码
/**
* 自动装配
*/
ClassPathXmlApplicationContext applicationContext5 = new ClassPathXmlApplicationContext("applicationContext.xml");
AnimalMapper animalMapper = (AnimalMapper) applicationContext5.getBean("animalMapper");
System.out.println(animalMapper);

Spring的其他配置标签



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
//test 测试类
package com.jackey.Test;

import com.jackey.dao.UserDao;
import com.jackey.mapper.AnimalMapper;
import com.jackey.mapper.PersonMapper;
import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {
/**
* 开发环境 、 测试环境
*/
//指定环境
System.setProperty("spring.profiles.active", "test");
ClassPathXmlApplicationContext applicationContext6 = new ClassPathXmlApplicationContext("applicationContext.xml");
AnimalMapper animalMapper1 = (AnimalMapper) applicationContext6.getBean("animalMapper");
System.out.println(animalMapper1);
}
}

//xml文件

//<!-- 开发环境、测试环境-->
<beans profile="test">
<bean id="animalMapper" class="com.jackey.mapper.impl.AnimalMapperImpl"></bean>
</beans>

xml文件中,除了指定了环境的部分配置生效以外,公共部分也会生效

导入配置文件


起别名


引入外部标签

  1. 在pom.xml文件中导入坐标
  2. 在xml配置文件中的命名空间中引入、配置映射地址

图中灰色部分为新引入的空间

Spring的get方法

在 Spring 框架中,通常推荐通过接口来获取 Bean 对象,而不是直接使用实现类。这样做有几个好处:

  1. 解耦合: 通过接口获取 Bean 可以将组件之间的耦合度降低。如果后续需要替换实现类,只需修改配置文件或者注解即可,而不需要修改调用方的代码。
  2. 动态代理: Spring 在注入 Bean 的时候,通常会使用动态代理。如果你直接通过实现类获取 Bean,可能会得到一个代理对象,而不是原始的实现类对象。因此,通过接口获取 Bean 可以确保你得到的是 Spring 管理的正确的代理对象。
  3. 更易于测试: 在单元测试中,你可以通过模拟实现类的方式来替代真实的 Bean,这样可以更方便地进行测试。如果直接使用实现类,可能需要进行更多的设置和清理工作。

具体到你的代码片段 applicationContext2.getBean(PeopleService.class),假设 PeopleService 是一个接口,这样做就是为了遵循面向接口编程的最佳实践。Spring 在运行时会根据配置文件或者注解,自动选择合适的实现类来实例化并返回给你。

总结来说,通过接口获取 Bean 是一种良好的编程习惯,能够提升代码的灵活性、可维护性和可测试性。

Spring配置非自定义的Bean

非自定义bean:第三方某些jar包中的对象,交给Spring容器管理

DruidDatasource

  1. 导入坐标
  2. 在xml配置文件中配置相关属性

Connection

SimpleDataFormat

SqlSessionFactory


普通创建对象的方法
将对象交给Spring容器管理


测试类

Bean的实例化基本流程





其中有一个beanClass是维护类的全限定名,用于反射去创建对象

内部存储的Bean的定义信息


BeanDefinition源码

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//BeanDefinition源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.beans.factory.config;

import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.AttributeAccessor;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = "singleton";
String SCOPE_PROTOTYPE = "prototype";
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;

void setParentName(@Nullable String var1);

@Nullable
String getParentName();

void setBeanClassName(@Nullable String var1);

@Nullable
String getBeanClassName();

void setScope(@Nullable String var1);

@Nullable
String getScope();

void setLazyInit(boolean var1);

boolean isLazyInit();

void setDependsOn(@Nullable String... var1);

@Nullable
String[] getDependsOn();

void setAutowireCandidate(boolean var1);

boolean isAutowireCandidate();

void setPrimary(boolean var1);

boolean isPrimary();

void setFactoryBeanName(@Nullable String var1);

@Nullable
String getFactoryBeanName();

void setFactoryMethodName(@Nullable String var1);

@Nullable
String getFactoryMethodName();

ConstructorArgumentValues getConstructorArgumentValues();

default boolean hasConstructorArgumentValues() {
return !this.getConstructorArgumentValues().isEmpty();
}

MutablePropertyValues getPropertyValues();

default boolean hasPropertyValues() {
return !this.getPropertyValues().isEmpty();
}

void setInitMethodName(@Nullable String var1);

@Nullable
String getInitMethodName();

void setDestroyMethodName(@Nullable String var1);

@Nullable
String getDestroyMethodName();

void setRole(int var1);

int getRole();

void setDescription(@Nullable String var1);

@Nullable
String getDescription();

ResolvableType getResolvableType();

boolean isSingleton();

boolean isPrototype();

boolean isAbstract();

@Nullable
String getResourceDescription();

@Nullable
BeanDefinition getOriginatingBeanDefinition();
}




基本流程概括


Spring的后处理器

bean实例化->属性赋值->bean前置处理->bean初始化->bean后置处理->使用

BeanFactoryPostProcessor

修改BeanDefinition

自定义类实现该接口,再将该类交给Spring容器管理

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
//自定义类实现BeanFactoryPostProcessor接口
package com.jackey.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("当完成beanDefinitionMap填充后,回调该方法");
// 修改beanDefinition,在实例化bean之前改变bean
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("userService");
// 设置bean的全限定名 改变bean
beanDefinition.setBeanClassName("com.jackey.mapper.impl.AnimalMapperImpl");

}
}

//测试类
package com.jackey.Test;

import com.jackey.dao.UserDao;
import com.jackey.mapper.AnimalMapper;
import com.jackey.mapper.PersonMapper;
import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {

ClassPathXmlApplicationContext applicationContext7 = new ClassPathXmlApplicationContext("processorContext.xml");
Object userService = applicationContext7.getBean("userService");
System.out.println(userService);

}
}

//配置文件xml

// <bean id="userService" class="com.jackey.service.Impl.UserServiceImpl"></bean>
// <!-- <bean id="animalMapper" class="com.jackey.service.Impl.UserServiceImpl"></bean>-->
// <bean id="processor" class="com.jackey.processor.MyBeanFactoryPostProcessor"></bean>

// 运行结果
// 当完成beanDefinitionMap填充后,回调该方法
// com.jackey.mapper.impl.AnimalMapperImpl@7f0eb4b4


设置bean的全限定名 改变bean,发现bean的名字是UserService,但是bean的value变成了AnimalMapperImpl

存疑– 乌龙 xml中注册bean的全限定名写错了

当将AnimalMapperImpl也配置进xml文件中,会发现UserService中的内容是AnimalMapperImpl的

而AnimalMapper中的内容是UserServiceImpl的,也就是bean中的内容发生了自动变换,两个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
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
//自定义类实现BeanFactoryPostProcessor接口
package com.jackey.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("当完成beanDefinitionMap填充后,回调该方法");
// 修改beanDefinition,在实例化bean之前改变bean
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("userService");
// 设置bean的全限定名 改变bean
beanDefinition.setBeanClassName("com.jackey.mapper.impl.AnimalMapperImpl");

}
}

//测试类
package com.jackey.Test;

import com.jackey.dao.UserDao;
import com.jackey.mapper.AnimalMapper;
import com.jackey.mapper.PersonMapper;
import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {

ClassPathXmlApplicationContext applicationContext7 = new ClassPathXmlApplicationContext("processorContext.xml");
Object userService = applicationContext7.getBean("userService");
System.out.println(userService);

}
}

//配置文件xml

// <bean id="userService" class="com.jackey.service.Impl.UserServiceImpl"></bean>
// <bean id="animalMapper" class="com.jackey.service.Impl.UserServiceImpl"></bean>
// <bean id="processor" class="com.jackey.processor.MyBeanFactoryPostProcessor"></bean>

// 运行结果
// 当完成beanDefinitionMap填充后,回调该方法
// afterPropertiesSet方法执行
// com.jackey.mapper.impl.AnimalMapperImpl@57a3af25

注册BeanDefinition

不需要在xml文件中配置bean,通过bean工厂后处理器同样可以完成实例化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
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
//自定义类MyBeanFactoryPostProcessor实现BeanFactoryPostProcessor接口
package com.jackey.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {


System.out.println("当完成beanDefinitionMap填充后,回调该方法");

/**
* 注册beanDefinition
*/
//使用beanDefinition接口的实现 RootBeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.jackey.dao.impl.PersonDaoImpl");
//强转成DefaultListBeanFactory 内部维护的方法多 为了使用registerBeanDefinition方法
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
//注册 将beanDefinition放入到BeanDefinitionMap中
defaultListableBeanFactory.registerBeanDefinition("personDao",beanDefinition);

}
}

/*
测试类
*/
package com.jackey.Test;
import com.jackey.dao.UserDao;
import com.jackey.mapper.AnimalMapper;
import com.jackey.mapper.PersonMapper;
import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {

//PostProcessor后处理器,注册beanDefinition
ClassPathXmlApplicationContext classPathXmlApplicationContext8 = new ClassPathXmlApplicationContext("processorContext.xml");
Object personDao = classPathXmlApplicationContext8.getBean("personDao");
System.out.println(personDao);
}
}


除了以上方式外,Spring提供了专门注册BeanDefinition的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//BeanDefinitionRegistryPostProcessor源码
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.beans.factory.support;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}

完善Bean的实例化基本流程

使用Spring的BeanDefinitionRegistryPostProcessor案例

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//创建类MyComponentBeanFactoryPostProcessor 实现 将beanDefinition注册
package com.jackey.processor;

import com.jackey.utils.BaseClassScanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;

import java.util.Map;

public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//通过扫描工具去扫描指定包及其子包下的所有类,收集使用了@MyComponent注解的类
Map<String, Class> myComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation("com.jackey");
//遍历Map,组装BeanDefinition进行注册
myComponentAnnotationMap.forEach((beanName, clazz) -> {
//反射获得BeanClassName com.jackey.beans.DemoBean
String beanClassName = clazz.getName();
//创建BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition(beanClassName);
//注册
beanDefinitionRegistry.registerBeanDefinition(beanClassName,beanDefinition);


});
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

}
}

//类 扫描工具 扫描注解@MyComponent
package com.jackey.utils;

import com.jackey.anno.MyComponent;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BaseClassScanUtils {

//设置资源规则
private static final String RESOURCE_PATTERN = "/**/*.class";

public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {

//创建容器存储使用了指定注解的Bean字节码对象
Map<String, Class> annotationClassMap = new HashMap<String, Class>();

//spring工具类,可以获取指定路径下的全部类
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern);
//MetadataReader 的工厂类
MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
//用于读取类信息
MetadataReader reader = refractory.getMetadataReader(resource);
//扫描到的class
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
//判断是否属于指定的注解类型
if(clazz.isAnnotationPresent(MyComponent.class)){
//获得注解对象
MyComponent annotation = clazz.getAnnotation(MyComponent.class);
//获得属value属性值
String beanName = annotation.value();
//判断是否为""
if(beanName!=null&&!beanName.equals("")){
//存储到Map中去
annotationClassMap.put(beanName,clazz);
continue;
}

//如果没有为"",那就把当前类的类名作为beanName
annotationClassMap.put(clazz.getSimpleName(),clazz);

}
}
} catch (Exception exception) {
}

return annotationClassMap;
}

public static void main(String[] args) {
Map<String, Class> stringClassMap = scanMyComponentAnnotation("com.jackey");
System.out.println(stringClassMap);
}
}

//测试类
package com.jackey.Test;

import com.jackey.beans.DemoBean;
import com.jackey.dao.UserDao;
import com.jackey.mapper.AnimalMapper;
import com.jackey.mapper.PersonMapper;
import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {

/**
* 自定义注解MyComponent
*/
ClassPathXmlApplicationContext applicationContext9 = new ClassPathXmlApplicationContext("MyComponentContext.xml");
DemoBean demoBean = applicationContext9.getBean(DemoBean.class);
System.out.println(demoBean);

}
}


//bean
package com.jackey.beans;

import com.jackey.anno.MyComponent;

@MyComponent("demoBean")
public class DemoBean {
}

//运行结果
//com.jackey.beans.DemoBean@72b6cbcc

BeanPostProcessor



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
//BeanPostProcessor

package com.jackey.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+":postProcessBeforeInitialization");
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+":postProcessAfterInitialization");
return bean;
}
}

//测试类
package com.jackey.Test;

import com.jackey.beans.DemoBean;
import com.jackey.dao.UserDao;
import com.jackey.mapper.AnimalMapper;
import com.jackey.mapper.PersonMapper;
import com.jackey.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beanpostprocessContext.xml");


}
}

//xml文件

<bean class="com.jackey.mapper.impl.AnimalMapperImpl"></bean>
<bean class="com.jackey.dao.impl.PersonDaoImpl"></bean>
<bean class="com.jackey.processor.MyBeanPostProcessor"></bean>

/*
运行结果
com.jackey.mapper.impl.AnimalMapperImpl#0:postProcessBeforeInitialization
com.jackey.mapper.impl.AnimalMapperImpl#0:postProcessAfterInitialization
com.jackey.dao.impl.PersonDaoImpl#0:postProcessBeforeInitialization
com.jackey.dao.impl.PersonDaoImpl#0:postProcessAfterInitialization
*/

由此可以得知,当在xml中配置了Bean,并将Bean后处理器同样配置到xml当中,Spring容器会自动在Bean的初始化过程中将Bean后处理器中的方法加入到Bean中,不论配置几个Bean。

在Spring框架中,Bean后处理器(BeanPostProcessor)是一种特殊类型的Bean,它允许在容器实例化Bean并在依赖注入(DI)发生之前或之后执行自定义的初始化和销毁逻辑。Bean后处理器需要在Spring容器中显式配置,并且其实例化和初始化过程是由Spring容器管理的。

如果在XML配置文件中将Bean后处理器配置为一个Bean,并将其注册到Spring容器中,那么Spring容器在实例化和初始化其他普通Bean时,会自动检测是否存在注册的Bean后处理器。如果存在,Spring容器会将该Bean后处理器应用于容器中所有符合条件的Bean。

具体来说:

  • 在XML配置文件中配置Bean后处理器时,需要使用 <bean> 元素来定义该后处理器的Bean。
  • Spring容器会自动检测实现了 BeanPostProcessor 接口的Bean,并将其应用于容器中的所有Bean。
  • Bean后处理器的主要方法是 postProcessBeforeInitializationpostProcessAfterInitialization,分别在Bean的初始化前后调用,允许开发者执行额外的逻辑操作。

因此,无论你在XML配置中有多少个普通Bean,只要你配置了相应的Bean后处理器,并且将其注册到Spring容器中,Spring会确保在所有符合条件的Bean的初始化过程中调用该后处理器的方法。这确保了你可以在Bean初始化前后进行自定义的操作,而不需要显式地在每个Bean的配置中添加额外的初始化逻辑。

实例化Bean过程中,方法的执行顺序如下

再完善Bean实例化的基本流程

SpringBean的生命周期


Bean的实例属性填充




执行流程


虽然是半成品,但是可以先把这个半成品设置为自己的属性,后面再往这个半成品里填东西(也就是把这个半成品补全)

三级缓存

1
2
3
4
5
6
7
8
//一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

//二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);

//三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

一般情况下 ,三级缓存存储的bean是未被引用过的半成品bean,二级缓存存储的是已经被引用的半成品bean

二级缓存用于存储被引用的对象,如果没被引用直接会从三到一,不走二


三级缓存源码剖析流程.pdf

  1. 实例化bean,将bean的ObjectFactory对象存入到三级缓存中


2. 在三级缓存中寻找userService,放入二级缓存


3.


4.


5.

常用的Aware接口

SpringIoC流程总结


以下内容尚未完善

?Spring xml方式整合第三方框架(整合模式) P57-P66

Spring整合Mybatis

原始写法

1
2
3
4
5
6
7
8
//加载配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//创建sqlSession对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
List<Purchase> purchaseList = mapper.listAll();

这段代码是使用 MyBatis 进行数据库操作的一个示例。让我们逐步解释每一行代码的作用:

  1. InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    • 这一行通过 MyBatis 提供的 Resources 类加载名为 “mybatis-config.xml” 的配置文件,返回一个 InputStream 对象 is。这个配置文件通常包含了数据库连接信息、映射器配置等。
  2. SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    • 创建一个 SqlSessionFactoryBuilder 对象 builderSqlSessionFactoryBuilder 是用来创建 SqlSessionFactory 的构建器。
  3. SqlSessionFactory sqlSessionFactory = builder.build(is);
    • 使用 builder 构建 SqlSessionFactory 对象 sqlSessionFactorySqlSessionFactory 是 MyBatis 的核心接口之一,负责创建 SqlSession 对象,从而与数据库进行交互。
  4. SqlSession sqlSession = sqlSessionFactory.openSession();
    • 通过 sqlSessionFactory 创建一个 SqlSession 对象 sqlSessionSqlSession 提供了执行 SQL 命令、获取映射器等操作的方法。
  5. PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
    • 通过 sqlSessiongetMapper 方法获取一个 PurchaseMapper 接口的实现类实例 mapperPurchaseMapper 是一个由 MyBatis 根据配置和接口生成的映射器接口,用于执行数据库操作。
  6. List<Purchase> purchaseList = mapper.listAll();
    • 调用 mapperlistAll 方法,这个方法可能是在 PurchaseMapper 接口中定义的,用于查询数据库并返回一个 List<Purchase>,即购买记录列表。

总结一下,这段代码的主要流程是:

  • 加载 MyBatis 的配置文件 mybatis-config.xml
  • 创建并配置 SqlSessionFactory,它是 MyBatis 的核心接口,用于创建数据库会话 SqlSession
  • 通过 SqlSession 获取映射器 mapper,并使用 mapper 执行数据库操作,这里是获取所有购买记录的列表。

这种方式利用了 MyBatis 的对象关系映射(ORM)能力,将数据库操作封装在映射器接口中,使得代码更加模块化和易于维护。

整合

  1. 导入依赖坐标


2. 编写Mapper和Mapper.xml
3. 配置SqlSessionFactoryBean和MapperScannerConfigurer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
//实现了FactoryBean的接口

//.......

//主要作用是提供SqlSessionFactory
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}

return this.sqlSessionFactory;
}
//..........
}



可以替代

Spring整合MyBatis的原理

基于注解的Spring应用

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
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">

<!--开启注解组件扫描:扫描指定包及其子包下的类,识别使用使用@Component注解-->
<context:component-scan base-package="com.jackey"/>


</beans>


package com.jackey.dao.impl;

import com.jackey.dao.UserDao;
import org.springframework.stereotype.Component;

//将对象实例化 并以指定的名字存储到Spring容器当中
@Component("userDao")
public class UserDaoImpl implements UserDao {
}

若注解未指定beanname,默认beanname为类名且首字母小写



除此以外的三层架构中,其他类使用@Component注解标注

Bean依赖注入注解开发


@Value一般用于读取配置文件的属性

@Autowired更具类型进行注入,如果统一类型的Bean有多个,尝试根据名字进行二次匹配,匹配不成功则注入失败

@Qualifier可以和@Autowired结合一起使用,作用是根据名称注入相应的Bean,也可以单独使用注入方法的参数

@Resource 不指定名称参数的时候,根据类型注入,指定名称就根据名称注入

非自定义Bean注解开发



在方法参数的属性注入对象引用,按照类型注入可以直接注入,省略@Autowired

Bean配置类的注解开发



当使用注解去配置配置文件,需要更换加载配置文件的方式



同样也可以引入其他配置类到核心配置类当中


@Mapper标记的接口不是由Spring管理的Bean,但是可以在Spring管理的Service层中通过 @Autowired注解获取到MyBatis生成的Mapper接口的代理对象,从而在Service层中使用Mapper的方法来操作数据库。

Spring配置其他注解


以下内容未完善

?Spring注解的原理解析 p76-79



?Spring注解整合第三方框架 p80


AOP简介


AOP相关概念

AOP思想实现的方案

模拟AOP的基础代码(动态代理)

1
2
3
<bean id="peopleService" class="com.jackey.service.impl.PeopleServiceImpl"></bean>
<bean id="myAdvice" class="com.jackey.advice.MyAdvice"></bean>
<bean class="com.jackey.processor.MockAopBeanPostProcessor"></bean>
1
2
3
4
5
6
7
8
9
10
11
12
public class PeopleServiceImpl implements PeopleService {
@Override
public void show1() {
System.out.println("show1....");
}

@Override
public void show2() {
System.out.println("show2....");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
package com.jackey.advice;

public class MyAdvice {
public void beforeAdvice() {
System.out.println("增强前置方法");
}

public void afterAdvice() {
System.out.println("增强后置方法");
}
}

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
60
61
62
63
package com.jackey.processor;

import com.jackey.advice.MyAdvice;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MockAopBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {

private ApplicationContext applicationContext;


@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//目的:对PeopleServiceImpl中的show1、show2方法进行增强,增强方法在MyAdvice中
//问题1:如何筛选service.impl包下的所有类的所有方法都可以增强,解决方案if-else
//问题2:MyAdvice怎么获取到? 解决方案:从Spring容器中区获取MyAdvice方法
if (bean.getClass().getPackage().getName().equals("com.jackey.service.impl")) {
//生成当前Bean的Proxy对象
Object beanProxy = Proxy.newProxyInstance(
//类加载器
bean.getClass().getClassLoader(),
//接口
bean.getClass().getInterfaces(),

new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

MyAdvice myAdvice = applicationContext.getBean(MyAdvice.class);

//执行增强对象的before方法
myAdvice.beforeAdvice();

//执行目标对象的方法 参数1 目标对象 目标对象方法参数
Object result = method.invoke(bean,args);

//执行增强对象的after方法
myAdvice.afterAdvice();

//返回对象的返回值
return result;
}
}
);
return beanProxy;
}else
//如果进不了if则原封不动的返回bean
return bean;
}

//实现ApplicationContextAware接口 以获取注入到applicationContext的对象实例
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

将目标对象bean交给Spring容器管理,再通过bean后处理器将bean中的方法增强,使用动态代理将增强对象的方法给目标对象增强

其中的关键点:增强对象再xml文件中配置,通过实现接口ApplicationContextAware,来回调注入到applicationContext中对象

基于xml配置的AOP





修改命名空间


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
60
//pom.xml
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>

//applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">

<!-- 注解配置aop-->
<!-- 目标对象-->
<bean id="peopleService" class="com.jackey.service.impl.PeopleServiceImpl"></bean>
<!-- 增强对象-->
<bean id="myAdvice" class="com.jackey.advice.MyAdvice"></bean>
<!-- aop配置-->
<aop:config>
<!-- 配置切点表达式,目的是指定哪些方法要被增强-->
<aop:pointcut id="myPointCut" expression="execution(void com.jackey.service.impl.PeopleServiceImpl.show1())"/>
<!-- 配置织入,目的是将目标方法与增强方法织入-->
<aop:aspect ref="myAdvice">
<aop:before method="beforeAdvice" pointcut-ref="myPointCut"></aop:before>
<aop:after method="afterAdvice" pointcut-ref="myPointCut"></aop:after>
</aop:aspect>
</aop:config>

</beans>

//Test类
package com.jackey.test;

import com.jackey.service.PeopleService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
public static void main(String[] args) {


/**
* xml配置实现aop
*/
ClassPathXmlApplicationContext applicationContext2 = new ClassPathXmlApplicationContext("applicationContext.xml");
//调用目标对象的方法
PeopleService bean = applicationContext2.getBean(PeopleService.class);
bean.show1();
}
}

配置详解






xml方式AOP的配置解析

首先进行自定义命名空间解析


然后为标签制定一个解析器

parse解析器只做一件事,往Spring容器中注入一个,而它的本质是一个BeanPostProcessor,在对应的After方法中生成Bean的代理对象的动作,最终把代理对象存储到容器当中 简言之是一个生成AOP的Proxy的过程

AOP底层两种生成Proxy方式


jdk动态代理

怎么理解jdk动态代理技术,目标类有接口,基于接口动态生成实现类的代理对象?

JDK动态代理技术是Java中一种利用反射机制在运行时动态生成代理类的技术。它基于目标类实现的接口来创建代理对象,主要通过以下步骤实现:

  1. 定义接口 : 首先,你需要定义一个接口,这个接口是目标类实现的接口。例如:
1
2
3
4
public interface UserService {
void addUser(String username);
void deleteUser(String username);
}
  1. 编写目标类 : 编写实现了该接口的目标类,例如UserServiceImpl
1
2
3
4
5
6
7
8
9
public class UserServiceImpl implements UserService {
public void addUser(String username) {
System.out.println("Adding user: " + username);
}

public void deleteUser(String username) {
System.out.println("Deleting user: " + username);
}
}
  1. 实现InvocationHandler接口 : 创建一个实现了InvocationHandler接口的类,这个类中实现代理对象的具体操作逻辑。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
private final Object target;

public MyInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method invocation");
Object result = method.invoke(target, args);
System.out.println("After method invocation");
return result;
}
}
  1. 创建代理对象 : 使用Proxy类的newProxyInstance方法动态地创建代理对象。这个方法接受一个类加载器、一个接口数组和一个InvocationHandler实例作为参数,返回一个实现了指定接口的代理对象。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
InvocationHandler handler = new MyInvocationHandler(userService);
UserService proxy = (UserService) Proxy.newProxyInstance(
Main.class.getClassLoader(),
new Class[] { UserService.class },
handler
);

proxy.addUser("Alice");
proxy.deleteUser("Bob");
}
}

在上述例子中,Proxy.newProxyInstance方法根据传入的接口数组和InvocationHandler实例动态地生成了一个实现了UserService接口的代理对象proxy。当调用proxy的方法时,实际会调用MyInvocationHandler中的invoke方法,在方法调用前后可以执行额外的逻辑,例如日志记录、权限检查等。

总结来说,JDK动态代理技术利用Java的反射机制,在运行时动态生成一个实现了指定接口的代理类对象,从而可以在方法调用前后插入一些通用逻辑,而不需要修改原始类的代码。

生成的 Proxy 在某种意义上可以看作是 UserService 接口的实现类。虽然它并不是你手动编写的典型意义上的实现类,但它确实实现了 UserService 接口定义的所有方法。

在使用动态代理时,你通过 Proxy.newProxyInstance 方法传入了一个接口数组和一个 InvocationHandler 实例。Java 在运行时会动态生成一个代理类,这个代理类实现了这些接口,并且在方法调用时会委托给 InvocationHandler 实现中的 invoke 方法处理具体的逻辑。

**因此,虽然在代码中你没有显式地编写一个类声明 **implements UserService,但动态代理技术使得在运行时生成了一个实现了 UserService 接口的类,这个类就是通过 Proxy.newProxyInstance 生成的 Proxy 对象。

Cglib动态代理

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
package com.jackey.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibTest {
public static void main(String[] args) {

//CGlib基于父类(目标类)生成Proxy

//目标对象
Target target = new Target();
//通知对象
MyAdvice2 myAdvice = new MyAdvice2();

//编写CGlib的代码

//创建增强器对象
Enhancer enhancer = new Enhancer();
//增强器设置父类
//生成的代理对象就是Target的子类
enhancer.setSuperclass(Target.class);
//设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
//intercept相当于jdk的Proxy的invoke方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//前置通知
myAdvice.beforeAdvice();

Object result = method.invoke(target, objects);//执行目标方法

//后置通知
myAdvice.afterAdvice();

return null;
}
});

//生成代理对象
Target proxy = (Target) enhancer.create();

//测试
proxy.show();

}
}

基于注解配置的AOP

基本使用




@Before属性时前置通知增强的目标对象方法的位置

配置详解


切点表达式的抽取



配置核心配置类


解析核心配置类,创建容器

原理解析

Spring事务编程概述

基于xml声明式事务控制






以下内容尚未完善

原理剖析p99?

基于注解声明式事务控制



全注解配置:

将xml文件中的所有配置全部配置到核心类中




更换解析方式

Spring整合web环境

javaweb三大组件及环境特点

Spring整合web环境的思路与实现



contextInitialized监听Servlet Context域的创建