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
| <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(); XmlBeanDefinitionReader reader= new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions("beans.xml"); UserService userService = (UserService) beanFactory.getBean("userService"); System.out.println(userService); } }
|
这里的强制类型转换是安全的,因为在 Spring 中,UserServiceImpl 类实际上是 UserService 接口的一个实现类,并且 Spring 在配置文件中将其声明为 UserService 类型。因此,即使实际类型是 UserServiceImpl,Spring 在运行时会将其视为 UserService 类型,这也是依赖倒置原则的体现,客户端代码不应直接依赖于具体的实现类,而是依赖于抽象接口。
总结起来,强制转换为 UserService 类型是为了符合面向接口编程的规范,使得代码更加灵活和可维护。
依赖倒置原则
依赖倒置原则(Dependency Inversion Principle,简称DIP)是面向对象设计中的重要原则之一,它主要包括以下两个核心概念:
- 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
- 高层模块通常是指应用程序中的业务逻辑层,低层模块是指底层的基础设施或实现细节。
- 例如,在一个典型的应用中,业务逻辑层不应直接依赖于具体的数据访问类(例如数据库操作类),而应该依赖于抽象的数据访问接口(如数据访问对象接口)。
- 抽象不应该依赖于细节,细节应该依赖于抽象。
- 这意味着编程时应尽量针对接口或抽象类编程,而不是针对具体实现编程。
- 这样做可以降低模块之间的耦合度,提高代码的灵活性和可扩展性,使得系统更易于维护和修改。
依赖倒置原则的核心思想是通过抽象来减少模块间的直接依赖关系,从而提高代码的灵活性和可维护性。在实际编码中,我们可以通过以下方式来实现依赖倒置原则:
- 使用接口或抽象类:定义模块之间的交互约定,而不是直接使用具体类。
- 依赖注入(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;
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(); XmlBeanDefinitionReader reader= new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions("beans.xml");
UserService userService = (UserService) beanFactory.getBean("userService"); System.out.println(userService);
UserDao userDao = (UserDao) beanFactory.getBean("userDao"); System.out.println(userDao);
} }
<bean id="userService" class="com.jackey.service.Impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.jackey.dao.impl.UserDaoImpl"></bean>
|
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 来说,它是通过反射机制来实现依赖注入的。具体到配置:
元素中的 class属性指定了要实例化的类,即 UserServiceImpl。
元素中的 name属性指定了要调用的 setter 方法,这里是 setUserDao方法,它用来设置 userDao 属性的值。
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;
public void setUserDao(UserDao userDao) { this.userDao = userDao; System.out.println("将userDao设置到此处"); }
public void init(){ System.out.println("初始化方法..."); }
public void destroy(){ System.out.println("销毁方法..."); }
@Override public String toString() { return "UserServiceImpl{" + "userDao=" + userDao + '}'; } }
<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(); } }
|
初始化方法在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;
public void setUserDao(UserDao userDao) { this.userDao = userDao; System.out.println("将userDao设置到此处"); }
public void init(){ System.out.println("初始化方法..."); }
public void destroy(){ System.out.println("销毁方法..."); }
@Override public String toString() { return "UserServiceImpl{" + "userDao=" + userDao + '}'; }
public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet方法执行"); } }
|
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(){ 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对象(体现在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(){ 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){ System.out.println("实例工厂"); return new UserDaoImpl(); } }
|
实现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";
@Nullable T getObject() throws Exception;
@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) {
ClassPathXmlApplicationContext applicationContext3 = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao3 = (UserDao) applicationContext3.getBean("userDao3"); System.out.println(userDao3);
} }
public class MyStandBeanFactory implements FactoryBean<UserDao> { public UserDao getObject() throws Exception { System.out.println("UserDao3"); return new UserDaoImpl(); }
public Class<?> getObjectType() { return UserDao.class; } }
<!--采用 实现FactoryBean接口 规范实例化UserDao--> <bean class="com.jackey.factory.MyStandBeanFactory" id="userDao3" ></bean>
|
执行完后,底层单例池中存放的是工厂对象,不是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;
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); } }
<!-- 注入集合属性 和 对象集合--> <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
|
<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
| 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); } }
<beans profile="test"> <bean id="animalMapper" class="com.jackey.mapper.impl.AnimalMapperImpl"></bean> </beans>
|
xml文件中,除了指定了环境的部分配置生效以外,公共部分也会生效
导入配置文件

起别名

引入外部标签

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

Spring的get方法
在 Spring 框架中,通常推荐通过接口来获取 Bean 对象,而不是直接使用实现类。这样做有几个好处:
- 解耦合: 通过接口获取 Bean 可以将组件之间的耦合度降低。如果后续需要替换实现类,只需修改配置文件或者注解即可,而不需要修改调用方的代码。
- 动态代理: Spring 在注入 Bean 的时候,通常会使用动态代理。如果你直接通过实现类获取 Bean,可能会得到一个代理对象,而不是原始的实现类对象。因此,通过接口获取 Bean 可以确保你得到的是 Spring 管理的正确的代理对象。
- 更易于测试: 在单元测试中,你可以通过模拟实现类的方式来替代真实的 Bean,这样可以更方便地进行测试。如果直接使用实现类,可能需要进行更多的设置和清理工作。
具体到你的代码片段 applicationContext2.getBean(PeopleService.class),假设 PeopleService 是一个接口,这样做就是为了遵循面向接口编程的最佳实践。Spring 在运行时会根据配置文件或者注解,自动选择合适的实现类来实例化并返回给你。
总结来说,通过接口获取 Bean 是一种良好的编程习惯,能够提升代码的灵活性、可维护性和可测试性。
Spring配置非自定义的Bean
非自定义bean:第三方某些jar包中的对象,交给Spring容器管理

DruidDatasource

- 导入坐标
- 在xml配置文件中配置相关属性
Connection


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
|
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
| 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 beanDefinition = configurableListableBeanFactory.getBeanDefinition("userService");
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);
} }
|

设置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
| 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 beanDefinition = configurableListableBeanFactory.getBeanDefinition("userService");
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);
} }
|
注册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
| 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 = new RootBeanDefinition(); beanDefinition.setBeanClassName("com.jackey.dao.impl.PersonDaoImpl"); DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory; 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) { 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
|
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
| 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 { Map<String, Class> myComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation("com.jackey"); myComponentAnnotationMap.forEach((beanName, clazz) -> { String beanClassName = clazz.getName(); BeanDefinition beanDefinition = new RootBeanDefinition(beanClassName); beanDefinitionRegistry.registerBeanDefinition(beanClassName,beanDefinition);
}); }
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
} }
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) {
Map<String, Class> annotationClassMap = new HashMap<String, Class>();
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); try { String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN; Resource[] resources = resourcePatternResolver.getResources(pattern); MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver); for (Resource resource : resources) { MetadataReader reader = refractory.getMetadataReader(resource); String classname = reader.getClassMetadata().getClassName(); Class<?> clazz = Class.forName(classname); if(clazz.isAnnotationPresent(MyComponent.class)){ MyComponent annotation = clazz.getAnnotation(MyComponent.class); String beanName = annotation.value(); if(beanName!=null&&!beanName.equals("")){ annotationClassMap.put(beanName,clazz); continue; }
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) {
ClassPathXmlApplicationContext applicationContext9 = new ClassPathXmlApplicationContext("MyComponentContext.xml"); DemoBean demoBean = applicationContext9.getBean(DemoBean.class); System.out.println(demoBean);
} }
package com.jackey.beans;
import com.jackey.anno.MyComponent;
@MyComponent("demoBean") public class DemoBean { }
|
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
|
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");
} }
<bean class="com.jackey.mapper.impl.AnimalMapperImpl"></bean> <bean class="com.jackey.dao.impl.PersonDaoImpl"></bean> <bean class="com.jackey.processor.MyBeanPostProcessor"></bean>
|
由此可以得知,当在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后处理器的主要方法是
postProcessBeforeInitialization 和 postProcessAfterInitialization,分别在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
- 实例化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");
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 进行数据库操作的一个示例。让我们逐步解释每一行代码的作用:
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
- 这一行通过 MyBatis 提供的
Resources 类加载名为 “mybatis-config.xml” 的配置文件,返回一个 InputStream 对象 is。这个配置文件通常包含了数据库连接信息、映射器配置等。
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
- 创建一个
SqlSessionFactoryBuilder 对象 builder。SqlSessionFactoryBuilder 是用来创建 SqlSessionFactory 的构建器。
SqlSessionFactory sqlSessionFactory = builder.build(is);
- 使用
builder 构建 SqlSessionFactory 对象 sqlSessionFactory。SqlSessionFactory 是 MyBatis 的核心接口之一,负责创建 SqlSession 对象,从而与数据库进行交互。
SqlSession sqlSession = sqlSessionFactory.openSession();
- 通过
sqlSessionFactory 创建一个 SqlSession 对象 sqlSession。SqlSession 提供了执行 SQL 命令、获取映射器等操作的方法。
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
- 通过
sqlSession 的 getMapper 方法获取一个 PurchaseMapper 接口的实现类实例 mapper。PurchaseMapper 是一个由 MyBatis 根据配置和接口生成的映射器接口,用于执行数据库操作。
List<Purchase> purchaseList = mapper.listAll();
- 调用
mapper 的 listAll 方法,这个方法可能是在 PurchaseMapper 接口中定义的,用于查询数据库并返回一个 List<Purchase>,即购买记录列表。
总结一下,这段代码的主要流程是:
- 加载 MyBatis 的配置文件
mybatis-config.xml。
- 创建并配置
SqlSessionFactory,它是 MyBatis 的核心接口,用于创建数据库会话 SqlSession。
- 通过
SqlSession 获取映射器 mapper,并使用 mapper 执行数据库操作,这里是获取所有购买记录的列表。
这种方式利用了 MyBatis 的对象关系映射(ORM)能力,将数据库操作封装在映射器接口中,使得代码更加模块化和易于维护。
整合

- 导入依赖坐标

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> {
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;
@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 { if (bean.getClass().getPackage().getName().equals("com.jackey.service.impl")) { 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);
myAdvice.beforeAdvice();
Object result = method.invoke(bean,args);
myAdvice.afterAdvice();
return result; } } ); return beanProxy; }else return bean; }
@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
| <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency>
<?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>
package com.jackey.test;
import com.jackey.service.PeopleService; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest { public static void main(String[] args) {
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 2 3 4
| public interface UserService { void addUser(String username); void deleteUser(String username); }
|
- 编写目标类 : 编写实现了该接口的目标类,例如
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); } }
|
- 实现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; } }
|
- 创建代理对象 : 使用
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) {
Target target = new Target(); MyAdvice2 myAdvice = new MyAdvice2();
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Target.class); enhancer.setCallback(new MethodInterceptor() { @Override 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域的创建