仅供参考学习。
一、手写Spring启动流程
我们刚使用spring时,spring提供的容器是基于xml配置文件的方式:
ClassPathXmlAplicationContext context = new ClassPathXmlAplicationContext("spring-bean.xml");
context.getBean("userService");
ClassPathXmlApplicationContext根据spring的配置文件spring-bean.xml来实例化一个spring容器,然后可以通过beanName到spring容器取到具体的bean对象。
下面是基于注解的方式实例化一个spring容器,这篇文章也将以这种方式实现手写spring的核心功能。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
context.getBean("userService");
只不过是用一个配置类AppConfig来作为参数来实例化spring容器。
包的约定如下:
com.hyj.spring包:手写spring实现代码
com.hyj.test包:验证手写spring功能代码
注:只是简易实现基本的原理,不保证健壮性,仅考虑最简单的情况。
定义spring容器类
package com.hyj.spring;
public class HyjApplicationContext {
private Class configClass;
public HyjApplicationContext(Class configClass) {
this.configClass = configClass;
}
public Object getBean(String beanName) {
// ...
return null;
}
}
从上一节可以知道,它至少需要:
- 一个参数,配置类类型
- 一个构造方法,构造方法的参数是配置类configClass,
- 提供一个getBean方法。
这时候需要一个AppConfig配置类,这个配置类需要告诉我们需要扫描的包路径,那么还需要定义扫描的注解@ComponentScan
定义配置类AppConfig
package com.hyj.spring;
@ComponentScan("com.hyj.test.service")
public class AppConfig {
}
定义扫描的注解@ComponentScan
package com.hyj.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value();
}
这时候假设有一个业务类UserService需要放入spring容器中,需要定义一个表示该类需要纳入Spring bean容器的注解@Component
package com.hyj.test.service;
import com.hyj.spring.Component;
@Component("userService")
@Scope("prototype")
public class UserService {
}
定义表示Bean的注解@Component
package com.hyj.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value();
}
定义Bean作用域的注解@Scope
package com.hyj.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value();
}
定义BeanDefinition用于解析后的bean定义
package com.hyj.spring;
public class BeanDefinition {
private Class Clazz;//bean的类型
private String scope; // bean的作用域
//bean是否是赖加载等待...
public Class getClazz() {
return Clazz;
}
public void setClazz(Class clazz) {
Clazz = clazz;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
spring启动流程
1.初始化Spring容器AnnotationConfigApplicationContext时,传入Appconfig类;
2.通过Appconfig类的@ComponentScan注解告知spring需要扫描的包路径;
3.扫描指定包路径下的所有文件,使用类加载器加载这些classes文件;
4.对有@Component注解的类进行解析成 BeanDefinition(Bean的定义对象);
5.实例化所有单例bean放入单例池中;
6.最后在getBean方法中根据具体的bean定义对象来实例化具体的bean对象;
Bean容器实现
package com.hyj.spring;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class HyjApplicationContext {
// Spring配置类
private Class configClass;
// 单例池
private ConcurrentHashMap<String, Object> singletonPool = new ConcurrentHashMap<>();
// 解析到的BeanDefinition集合
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public HyjApplicationContext(Class configClass) {
this.configClass = configClass;
// 解析配置类上的@ComponentScan注解,获取扫描的包路径
String packagePath = getScanPackagePath(configClass);
// 扫描包路径下的class文件并解析成BeanDefinition
scanAndParseToBeanDefinitions(packagePath);
// 单例bean进行实例化,并放入单例池中
singletonInstanceToPool();
}
private void singletonInstanceToPool() {
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
String beanName = entry.getKey();
BeanDefinition beanDefinition = entry.getValue();
if (beanDefinition.getScope().equals("singleton")) {
Object singletonObj = createBean(beanDefinition);
singletonPool.put(beanName, singletonObj);
}
}
}
private Object createBean(BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getClazz();
Object o = null;
try {
o = clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return o;
}
private void scanAndParseToBeanDefinitions(String packagePath) {
URL resource = HyjApplicationContext.class.getClassLoader().getResource(packagePath);//使用App加载器获取资源文件URL
File file = new File(resource.getFile());
if (file.isDirectory()) {
for (File f : file.listFiles()) { //便利所有文件
String absolutePath = f.getAbsolutePath();
if (absolutePath.endsWith(".class")) {//只扫描class文件
String fullyQualifiedClassName = getFullyQualifiedClassName(absolutePath);
try {
Class<?> aClass = HyjApplicationContext.class.getClassLoader().loadClass(fullyQualifiedClassName);
// 这个类上有@Component注解,表示当前类应该是一个bean
if (aClass.isAnnotationPresent(Component.class)) {
// 需要在扫描的时候就实例化这个bean吗?这和这个bean的作用域有关系.
// 解析这个类,把类的定义信息解析额出来 ---> BeanDefinition
parseClassToBeanDefinition(aClass);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
private void parseClassToBeanDefinition(Class<?> aClass) {
String beanName = aClass.getDeclaredAnnotation(Component.class).value();
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(aClass);
if (aClass.isAnnotationPresent(Scope.class)) {//如果这个类上有@Scope注解
beanDefinition.setScope(aClass.getDeclaredAnnotation(Scope.class).value());
} else {
beanDefinition.setScope("singleton"); //没有@Scope注解默认就是单例
}
beanDefinitionMap.put(beanName, beanDefinition); //解析到的beanDefinition放Map里
}
private String getFullyQualifiedClassName(String absolutePath) {
String fullyQualifiedClassName = absolutePath
.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))
.replace(File.separator, ".");// com.hyj.test.service.UserService
return fullyQualifiedClassName;
}
private String getScanPackagePath(Class configClass) {
return ((ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class))
.value() //扫描包名 com.hyj.test.service
.replace(".", File.separator); //扫描的相对路径 com/hyj/test/service
}
public Object getBean(String beanName) {
// 首先看BeanDefinition结合中是否有这个bean定义对象
// 如果有,并且如果是单例,则直接从单例池中取;
// 如果不是单例,创建bean对象
if (beanDefinitionMap.containsKey(beanName)) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("singleton")) {
return singletonPool.get(beanName);
} else {
// 创建bean对象
Object bean = createBean(beanDefinition);
return bean;
}
} else {
// 不存在对应的bean对象
throw new NullPointerException();
}
}
}
其中类加载器有3种,本文使用Application的类加载器:
BootStrap:负责加载jre/lib目录
Ext:负责加载jre/ext/lib目录
App:负载classpath路径
验证测试
package com.hyj.test;
import com.hyj.spring.AppConfig;
import com.hyj.spring.HyjApplicationContext;
public class AppMain {
public static void main(String[] args) {
HyjApplicationContext applicationContext = new HyjApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("userService"));
System.out.println(applicationContext.getBean("userService"));
}
}
通过打印的对象是否相同来验证scope是否生效,如果生效表明整个启动过程成功。
二、手写Spring依赖注入DI
如果UserService里面还有成员变量对象,这时候就需要根据依赖来注入对应的属性。
比如UserService类如下:
package com.hyj.test.service;
import com.hyj.spring.Autowired;
import com.hyj.spring.Component;
import com.hyj.spring.Scope;
@Component("userService")
//@Scope("prototype")
public class UserService {
@Autowired
private OrderService orderService;
public void printFiled() {
System.out.println(orderService);
}
}
其中,OrderService如下:
package com.hyj.test.service;
import com.hyj.spring.Component;
import com.hyj.spring.Scope;
@Component("orderService")
public class OrderService {
}
用到了@Autowired注解,那么我们就来自定义一个:
定义自动注入注解@Autowired
package com.hyj.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Autowired {
/* String value() default ""; */
}
注意target指向成员变量或者方法。
接下来改造spring容器类HyjApplicationContext里的createBean方法(根据BeanDefinition来创建)。
改造创建Bean的方法
HyjApplicationContext里的createBean方法:
//
private Object createBean(BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getClazz();
Object beanObj = null;
try {
beanObj = clazz.getDeclaredConstructor().newInstance();
// 依赖注入
for (Field declaredField : clazz.getDeclaredFields()) {
if (declaredField.isAnnotationPresent(Autowired.class)) { //判断成员变量是否有@Autowired注解
Object fieldBean = getBean(declaredField.getName()); // 根据beanName获取对应bean
declaredField.setAccessible(true);
declaredField.set(beanObj, fieldBean);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return beanObj;
}
public Object getBean(String beanName) {
// 首先看BeanDefinition结合中是否有这个bean定义对象
// 如果有,并且如果是单例,则直接从单例池中取;
// 如果不是单例,创建bean对象
if (beanDefinitionMap.containsKey(beanName)) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("singleton")) {
return singletonPool.get(beanName);
} else {
// 创建bean对象
Object bean = createBean(beanDefinition);
return bean;
}
} else {
// 不存在对应的bean对象
throw new NullPointerException();
}
}
}
验证依赖注入
public class AppMain {
public static void main(String[] args) {
HyjApplicationContext applicationContext = new HyjApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.printFiled();
}
}
查看userService的成员变量orderService是否有值。
三、手写Spring之Aware回调
如果在UserService里有一个beanName的属性需要注入,此时如果用@Autowired注入显然不合适,Spring的bean容器中没有这这个bean,可以使用Aware进行回调。
@Component("userService")
public class UserService {
@Autowired
private OrderService orderService;
private String beanName;
public void printFiled() {
System.out.println(orderService);
}
}
定义BeanNameAware接口
package com.hyj.spring;
public interface BeanNameAware {
void setBeanName(String beanName);
}
实现BeanNameAware接口
package com.hyj.test.service;
import com.hyj.spring.Autowired;
import com.hyj.spring.BeanNameAware;
import com.hyj.spring.Component;
import com.hyj.spring.Scope;
@Component("userService")
public class UserService implements BeanNameAware {
@Autowired
private OrderService orderService;
private String beanName;
public void printFiled() {
System.out.println(orderService);
System.out.println(beanName);
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Spring容器createBean方法改造
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getClazz();
Object beanObj = null;
try {
beanObj = clazz.getDeclaredConstructor().newInstance();
// 依赖注入
for (Field declaredField : clazz.getDeclaredFields()) {
if (declaredField.isAnnotationPresent(Autowired.class)) { //判断成员变量是否有@Autowired注解
Object fieldBean = getBean(declaredField.getName()); // 根据beanName获取对应bean
declaredField.setAccessible(true);
declaredField.set(beanObj, fieldBean);
}
}
//Aware回调
if (beanObj instanceof BeanNameAware) {
((BeanNameAware) beanObj).setBeanName(beanName);
}
} catch (Exception e) {
e.printStackTrace();
}
return beanObj;
}
验证
public class AppMain {
public static void main(String[] args) {
HyjApplicationContext applicationContext = new HyjApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.printFiled();
}
}
验证userService的属性beanName是否有值。
四、手写Spring初始化机制
Spring创建bean的最后完成bean初始化时会做一些事情,这里就涉及到spring的初始化技术。
定义InitializingBean
package com.hyj.spring;
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
实现InitializingBean接口
package com.hyj.test.service;
import com.hyj.spring.*;
@Component("userService")
public class UserService implements BeanNameAware, InitializingBean {
@Autowired
private OrderService orderService;
private String beanName;
public void printFiled() {
System.out.println(orderService);
System.out.println(beanName);
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
public void afterPropertiesSet() throws Exception {
// 可以做一些初始化的工作 验证,赋值
System.out.println("Bean初始化。。。");
}
}
Spring容器createBean方法改造
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getClazz();
Object beanObj = null;
try {
// 实例化
beanObj = clazz.getDeclaredConstructor().newInstance();
// 依赖注入
for (Field declaredField : clazz.getDeclaredFields()) {
if (declaredField.isAnnotationPresent(Autowired.class)) { //判断成员变量是否有@Autowired注解
Object fieldBean = getBean(declaredField.getName()); // 根据beanName获取对应bean
declaredField.setAccessible(true);
declaredField.set(beanObj, fieldBean);
}
}
//Aware回调
if (beanObj instanceof BeanNameAware) {
((BeanNameAware) beanObj).setBeanName(beanName);
}
// 初始化
if (beanObj instanceof InitializingBean) {
((InitializingBean)beanObj).afterPropertiesSet();
}
} catch (Exception e) {
e.printStackTrace();
}
return beanObj;
}
验证
public class AppMain {
public static void main(String[] args) {
HyjApplicationContext applicationContext = new HyjApplicationContext(AppConfig.class);
}
}
五、手写Spring后置处理器BeanPostProcessor
当在创建bean的各个时期做某些操作时,可以使用spring的后置处理器来解决,把处理的操作作为业务代码,只需要继承BeanPostProcessor接口就行,spring会在创建bean的指定的各个时期调用接口的方法。
定义BeanPostProcessor接口
package com.hyj.spring;
public interface BeanPostProcessor {
default Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
default Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
为验证这个处理器,可以在userService中新增一个message字段作为测试字段
@Component("userService")
public class UserService implements BeanNameAware, InitializingBean {
@Autowired
private OrderService orderService;
private String beanName; // 为验证Aware回调
private String message; // 为验证后置处理器BeanPostProcessor
public void printFiled() {
System.out.println(orderService);
System.out.println(beanName);
System.out.println(message);
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public void afterPropertiesSet() throws Exception {
// 可以做一些初始化的工作 验证,赋值
System.out.println("userService Bean初始化。。。");
}
}
实现一个BeanPostProcessor的实现
此时可以按照业务需要实现自己的BeanPostProcessor处理器。
package com.hyj.test.service;
import com.hyj.spring.BeanPostProcessor;
import com.hyj.spring.Component;
@Component("hjyBeanPostProcessor")
public class HyjBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// 可以做某些处理
System.out.println(beanName + "初始化前的操作。。。");
if (beanName.equals("userService")) {//这里假设只对某个bean进行某些处理操作
((UserService)bean).setMessage("beanName : " + beanName + ", 调用了postProcessBeforeInitialization方法");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println(beanName + "初始化后的操作。。。");
return bean;
}
}
并且把这个后置处理器纳入spring的bean容器管理。
那么,在spring扫描bean的时候,对BeanPostProcessor的这类的bean做特殊处理(先实例化然后暂存起来),以便后面createBean的各个时期来使用BeanPostProcessor来作为后置处理器处理一些额外的业务。
在Spring容器类HyjApplicationContext类中改造对应方法:
private void scanAndParseToBeanDefinitions(String packagePath) {
URL resource = HyjApplicationContext.class.getClassLoader().getResource(packagePath);//使用App加载器获取资源文件URL
File file = new File(resource.getFile());
if (file.isDirectory()) {
for (File f : file.listFiles()) { //便利所有文件
String absolutePath = f.getAbsolutePath();
if (absolutePath.endsWith(".class")) {//只扫描class文件
String fullyQualifiedClassName = getFullyQualifiedClassName(absolutePath);
try {
Class<?> aClass = HyjApplicationContext.class.getClassLoader().loadClass(fullyQualifiedClassName);
// 这个类上有@Component注解,表示当前类应该是一个bean
if (aClass.isAnnotationPresent(Component.class)) {
// 需要在扫描的时候就实例化这个bean吗?这和这个bean的作用域有关系.
// 解析这个类,把类的定义信息解析额出来 ---> BeanDefinition
// 对BeanPostProcessor的所有bean进行特殊处理
if (BeanPostProcessor.class.isAssignableFrom(aClass)) {//这个类是否实现类类当前接口
BeanPostProcessor instance = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance();
beanPostProcessorList.add(instance);
}
parseClassToBeanDefinition(aClass);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
createBean时进行后置处理
最后,就可以在创建bean的各个时期前后做加工处理,可以放在任何时期。
这里就放在 初始化 的前后做相关操作。
在Spring容器类HyjApplicationContext的createBean方法里改造:
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getClazz();
Object beanObj = null;
try {
// 实例化
beanObj = clazz.getDeclaredConstructor().newInstance();
// 依赖注入
for (Field declaredField : clazz.getDeclaredFields()) {
if (declaredField.isAnnotationPresent(Autowired.class)) { //判断成员变量是否有@Autowired注解
Object fieldBean = getBean(declaredField.getName()); // 根据beanName获取对应bean
declaredField.setAccessible(true);
declaredField.set(beanObj, fieldBean);
}
}
//Aware回调
if (beanObj instanceof BeanNameAware) {
((BeanNameAware) beanObj).setBeanName(beanName);
}
// 初始化前需要做的事,使用BeanPostProcessor处理
// 这个地方可以定义各种类型的BeanPostProcessor
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
beanObj = beanPostProcessor.postProcessBeforeInitialization(beanObj, beanName);
}
// 初始化
if (beanObj instanceof InitializingBean) {
((InitializingBean)beanObj).afterPropertiesSet();
}
// 初始化后需要做的事,使用BeanPostProcessor处理
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
beanObj = beanPostProcessor.postProcessAfterInitialization(beanObj, beanName);
}
} catch (Exception e) {
e.printStackTrace();
}
return beanObj;
}
验证
public static void main(String[] args) {
HyjApplicationContext applicationContext = new HyjApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.printFiled();
}
打印如下:
orderService初始化前的操作。。。
orderService初始化后的操作。。。
userService初始化前的操作。。。
userService Bean初始化。。。
userService初始化后的操作。。。
hjyBeanPostProcessor初始化前的操作。。。
hjyBeanPostProcessor初始化后的操作。。。
com.hyj.test.service.OrderService@5451c3a8
userService
beanName : userService, 调用了postProcessBeforeInitialization方法
六、手写Spring之AOP实现
如果想在业务类UserService的方法printFiled上做AOP的,可以基于上一节中的BeanPostProcessor,在bean初始化后返回一个代理bean对象,那么userService再调用printFiled方法时就可以先执行代理加的操作,然后在执行原有操作。
使用jdk的动态代理,需要一个接口
package com.hyj.test.service;
public interface UserInterface {
public void printFiled();
}
UserService改造,仅继承上面接口:
@Component("userService")
//@Scope("prototype")
public class UserService implements BeanNameAware, InitializingBean, UserInterface {
@Autowired
private OrderService orderService;
private String beanName; // 为验证Aware回调
private String message; // 为验证后置处理器BeanPostProcessor
public void printFiled() {
System.out.println(orderService);
System.out.println(beanName);
System.out.println(message);
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public void afterPropertiesSet() throws Exception {
// 可以做一些初始化的工作 验证,赋值
System.out.println("userService Bean初始化。。。");
}
}
此文就不再新搞一个BeanPostProcessor了,还是用原来HyjBeanPostProcessor改造,在初始化后(postProcessAfterInitialization方法)用jdk代理生成一个代理对象返回。
bean初始化后返回代理对象
@Component("hjyBeanPostProcessor")
public class HyjBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// 可以做某些处理
System.out.println(beanName + "初始化前的操作。。。");
if (beanName.equals("userService")) {//这里假设只对某个bean进行某些处理操作
((UserService)bean).setMessage("beanName : " + beanName + ", 调用了postProcessBeforeInitialization方法");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println(beanName + "初始化后的操作。。。");
if ("userService".equals(beanName)) { // 仅对指定的bean进行代理
Object proxyInstance = Proxy.newProxyInstance(HyjBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(beanName + "执行代理逻辑before");
Object invoke = method.invoke(bean, args);
System.out.println(beanName + "执行代理逻辑after");
return invoke;
}
});
return proxyInstance;
}
return bean;
}
}
验证
public static void main(String[] args) {
HyjApplicationContext applicationContext = new HyjApplicationContext(AppConfig.class);
// UserService userService = (UserService) applicationContext.getBean("userService");
// userService.printFiled();
UserInterface userInterface = (UserInterface) applicationContext.getBean("userService");
userInterface.printFiled();
}
打印结果:
orderService初始化前的操作。。。
orderService初始化后的操作。。。
userService初始化前的操作。。。
userService Bean初始化。。。
userService初始化后的操作。。。
hjyBeanPostProcessor初始化前的操作。。。
hjyBeanPostProcessor初始化后的操作。。。
【重点关注】userService执行代理逻辑before
com.hyj.test.service.OrderService@2c7b84de
userService
beanName : userService, 调用了postProcessBeforeInitialization方法
【重点关注】userService执行代理逻辑after
网友评论