目錄
兩種方法創建Spring容器
自定義Spring容器及前置操作
Spring掃描邏輯實現
createBean()方法
getBean()方法
依賴注入(DI)
BeanNameAware接口
InitializingBean接口
BeanPostProcessor接口 + AOP的實現
Spring 是一個輕量級的 Java 開發框架,最初由 Rod Johnson 在 2002 年提出,主要解決 企業級開發中的復雜性問題。其核心主要是 IoC(控制反轉) 和 AOP(面向切面編程)。
-
IoC(Inversion of Control)控制反轉
-
對象的創建和依賴的維護由容器(Spring)來管理,而不是由程序員自己
new
出來。 -
核心實現方式:DI(依賴注入)。
-
-
AOP(Aspect Oriented Programming)面向切面編程
-
通過代理方式實現橫切邏輯的解耦(如事務、日志、權限)。
-
核心實現方式:動態代理。
-
而我們可以通過學習手搓Spring的實現來快速了解Spring底層源碼的實現。
兩種方法創建Spring容器
我們想要再啟動類上構建Spring容器可以使用下面兩種方法:
- ClassPathXmlApplicationContext =>?在 XML 文件中定義了
<bean>
元素(或者<context:component-scan>
),Spring 會根據這些配置來創建 Bean。 - AnnotationConfigApplicationContext? => 傳入的是一個 Java 配置類(
AppConfig
)。
public static void main(String[] args) {// ① 使用 XML 配置文件創建容器ClassPathXmlApplicationContext classPathXmlApplicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");// ② 使用 Java 配置類(注解方式)創建容器AnnotationConfigApplicationContext applicationContext =new AnnotationConfigApplicationContext(AppConfig.class);// ③ 從容器中獲取 BeanUserService userService = (UserService) applicationContext.getBean("userService");userService.test();
}
而Java配置類可以這樣定義:
通過@ComponentScan注解來指定Spring掃描哪個路徑下的文件。也可以通過使用@Bean注解的方式直接定義Bean。
import cn.spring.com.spring.ComponentScan;@ComponentScan("cn.spring.com.eleven.service")
public class AppConfig {@Bean // 定義一個Bean,方法名默認就是Bean的idpublic UserService userService() {return new UserService();}
}
自定義Spring容器及前置操作
我們在手搓一個Spring的時候,直接使用Springboot工程創建即可,另外pom.xml不需要導入任何依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.spring.com</groupId><artifactId>spring-eleven</artifactId><version>0.0.1-SNAPSHOT</version><name>Spring-Eleven</name></project>
大致的結構如下:
我們將Spring該實現的注解、接口等放在Spring目錄下,將我們自己的測試類放在eleven目錄下。
隨后我們先創建一個自定義容器類:
這里我們主要實現的是模擬 AnnotationConfigApplicationContext 使用Java配置類創建Spring容器。
package cn.spring.com.spring;public class ElevenApplicationContext {private Class configClass; // 配置類/*** 構造Spring容器(需要傳遞配置類)* @param configClass 配置類*/public ElevenApplicationContext(Class configClass) {this.configClass = configClass;}
}
之后編寫配置類AppConfig:
package cn.spring.com.eleven;public class AppConfig {}
而為了實現Spring容器可以通過配置類掃描哪個路徑下的文件,我們自定義@ComponentScan注解:
value參數代表的是掃描路徑。????????
package cn.spring.com.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 組件掃描注解*/
@Target(ElementType.TYPE) // 類型注解
@Retention(RetentionPolicy.RUNTIME) // 運行時注解
public @interface ComponentScan {String value(); // 掃描的包路徑
}
之后在AppConfig上添加@ComponentScan注解:
這里我準備掃描cn.spring.com.eleven.service路徑下的文件,所以我將要在該目錄下去創建我的Service。
package cn.spring.com.eleven;import cn.spring.com.spring.ComponentScan;@ComponentScan("cn.spring.com.eleven.service")
public class AppConfig {}
創建UserService:
package cn.spring.com.eleven.service;public class UserService{}
而為了將UserService交給Spring容器去管理,我們需要自定義@Component注解:
package cn.spring.com.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 組件注解*/
@Target(ElementType.TYPE) // 類型注解
@Retention(RetentionPolicy.RUNTIME) // 運行時注解
public @interface Component {String value() default ""; // 組件的名稱
}
之后在UserService上添加@Component注解:
@Component("userService")
public class UserService {}
最后將自定義的Java配置類交給自定義容器:
package cn.spring.com.eleven;import cn.spring.com.eleven.service.UserService;
import cn.spring.com.spring.ElevenApplicationContext;public class Test {public static void main(String[] args) {// 傳一個Spring容器的配置文件ElevenApplicationContext applicationContext = new ElevenApplicationContext(AppConfig.class);}
}
而我們創建?ElevenApplicationContext 肯定會調用其構造方法,所以為了實現實現掃描指定路徑下的文件,我們需要拿到@ComponentScan內的value值,根據value值進行掃描那個目錄下的文件。
Spring掃描邏輯實現
在 ElevenApplicationContext 構造方法中編寫掃描邏輯,我們可以將掃描邏輯抽出成一個方法void Scan(),之后在構造方法內調用即可。
首先通過傳遞的 configClass,使用 getDeclaredAnnotation(ComponentScan.class) 方法來拿到ComponentScan注解,隨后.value()拿到內部值并轉換路徑格式:
ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value(); // 掃描路徑
path = path.replace(".", "/");
現在已經拿到了掃描路徑,所以我們需要根據掃描路徑拿到路徑下的所有類。那么可以使用類加載器ClassLoader去拿剛剛獲得的掃描路徑下的資源,隨后轉換成目錄:
// 掃描(使用應用類加載器ClassLoader)
ClassLoader classLoader = ElevenApplicationContext.class.getClassLoader();
// 因為是應用類加載器,會去 F:\project\Spring-Eleven\target\classes\cn\spring\com\eleven\service 下拿到資源
// 這里的resource可以是資源也可以是目錄,這里拿到的是目錄
URL resource = classLoader.getResource(path);
// resource轉換成file目錄,方便調用
File file = new File(resource.getFile());
之后判斷file是否為目錄,如果是則通過 file.listFiles() 方法拿到目錄下的所有文件:
if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {System.out.println(f.getAbsolutePath());}
}
之后在循環file文件,首先根據目錄來拿到類名className,隨后根據類名拿到類對象Class<?> clazz = classLoader.loadClass(className),隨后使用 clazz.isAnnotationPresent(Component.class) 判斷當前類上是否有@Component注解。
// file是否為目錄
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println(f.getAbsolutePath());
if (f.getAbsolutePath().endsWith(".class")) {// 轉換可用的路徑格式// F:\project\Spring-Eleven\target\classes\cn\spring\com\eleven\service\UserService.class// cn.spring.com.eleven.service.UserServiceString className = f.getAbsolutePath().replace("\\", "/");className = className.substring(className.indexOf("cn"), className.indexOf(".class")); // 截取類名className = className.replace("/", "."); // 替換成點號System.out.println(className);// 加載類try {Class<?> clazz = classLoader.loadClass(className);// 判斷是否有@Component注解if (clazz.isAnnotationPresent(Component.class)) {System.out.println("有@Component注解,表示當前類是一個Bean對象");}}
}
在上面我們已經可以判斷該類上是否有@Component注解,由于有@Component注解,代表我們想要將該類生成一個Bean對象,所以需要構建 getBean() 方法。
在這里需要講述Bean的作用域!!!
單例Bean與原型Bean的區別:
①單例bean(singleton):在整個 Spring 容器中,同一個 Bean 定義只有一個實例。
②原型bean(prototype):每次調用 getBean() 時,都會創建一個新的實例。
在Spring中是通過@Scope注解去解釋當前Bean的作用域,所以我們創建該注解:
package cn.spring.com.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 作用域注解*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {/*** 單例Bean還是原型Bean*/String value() default "singleton";
}
之后我們可以在UserService加入注解讓它暫時代表原型Bean:
package cn.spring.com.eleven.service;
import cn.spring.com.spring.*;@Component("userService")
@Scope("prototype")
public class UserService {}
現在UserService要創建的是原型Bean,所以我們在Test下拿到三次不同的UserService的Bean對象。
package cn.spring.com.eleven;import cn.spring.com.eleven.service.UserService;
import cn.spring.com.spring.ElevenApplicationContext;public class Test {public static void main(String[] args) {// 傳一個Spring容器的配置文件ElevenApplicationContext applicationContext = new ElevenApplicationContext(AppConfig.class);Object userService = applicationContext.getBean("userService");Object userService = applicationContext.getBean("userService");Object userService = applicationContext.getBean("userService");}
}
而現在如何讓單例Bean能夠確保getBean()可以得到同一個Bean對象呢?
這個時候我們可以使用單例池來存儲創建出來的單例Bean對象:
// 單例池
// map存儲的都是單例bean =》<bean名字,bean對象>
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
隨后我們創建getBean方法,在方法內傳入BeanName:
/*** 獲取Bean對象* @param beanName Bean的名字* @return Bean對象*/
public Object getBean(String beanName) {}
之后我們需要研究如何使用beanName來判斷是單例Bean還是原型Bean呢?
這個時候Spring底層是使用了 BeanDefinition 定義類:
用來定義Bean對象,內部有Bean的類型以及Bean的作用域,其他的也可以自定義。
package cn.spring.com.spring;/*** Bean定義類*/
public class BeanDefinition {/*** Bean的類型*/private Class aClass;/*** Bean的作用域*/private String scope;public BeanDefinition(Class aClass, String scope) {this.aClass = aClass;this.scope = scope;}public BeanDefinition() {}public Class getaClass() {return aClass;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}public void setaClass(Class<?> clazz) {this.aClass = clazz;}
}
所以現在我們需要再類上有@Component注解的基礎上先通過@Component注解拿到beanName類名字,隨后去判斷類上是否有@Scope注解,一但有@Scope注解則代表該類是原型Bean,之后使用BeanDefinition定義類的類構造方法將BeanName與Scope傳遞。而為了方便以后再任意方法內可輕易的拿到Bean的屬性我們可以定義一個 beanDefinitionMap 對象池:
// BeanDefinition對象池
// map存儲的都是BeanDefinition對象 =》<bean名字,bean定義對象>
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
隨后將BeanDefinition存到 beanDefinitionMap 對象池內,下面是實現代碼:
/*** 在容器內通過配置類掃描Bean* @param configClass 配置類*/
private void scan(Class configClass) {// 解析配置類// 解析@ComponentScan注解 --》掃描路徑 --》掃描包下所有的類ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);String path = componentScan.value(); // 掃描路徑path = path.replace(".", "/");// 掃描(使用類加載器ClassLoader)ClassLoader classLoader = ElevenApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(path);// 轉換成目錄File file = new File(resource.getFile());// file是否為目錄if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {System.out.println(f.getAbsolutePath());if (f.getAbsolutePath().endsWith(".class")) {// 轉換可用的路徑格式// F:\project\Spring-Eleven\target\classes\cn\spring\com\eleven\service\UserService.class// cn.spring.com.eleven.service.UserServiceString className = f.getAbsolutePath().replace("\\", "/");className = className.substring(className.indexOf("cn"), className.indexOf(".class")); // 截取類名className = className.replace("/", "."); // 替換成點號System.out.println(className);// 加載類try {Class<?> clazz = classLoader.loadClass(className);// 判斷是否有@Component注解if (clazz.isAnnotationPresent(Component.class)) {System.out.println("有@Component注解,表示當前類是一個Bean對象");// 是單例bean還是原型bean// 解析類,-》BeanDefinition對象// 首先通過Component拿到Bean的名字Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);String beanName = componentAnnotation.value();// 創建BeanDefinition對象BeanDefinition beanDefinition = new BeanDefinition();// 設置BeanDefinition的類型beanDefinition.setaClass(clazz);// 是否有Scope注解if(clazz.isAnnotationPresent(Scope.class)){// 然后通過Scope注解拿到Bean的作用域Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);String scope = scopeAnnotation.value(); // 拿到Scope注解的值beanDefinition.setScope(scope); // 設置BeanDefinition的作用域} else {beanDefinition.setScope("singleton"); // 默認是單例}// 存到BeanDefinition對象池beanDefinitionMap.put(beanName, beanDefinition);}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}}}
}
createBean()方法
在上面,我們已經在容器掃描指定路徑下的文件,并將需要創建Bean對象的類名 + Bean作用域封裝成BeanDefinition定義類存入?beanDefinitionMap 對象池,那么接下來我們需要通過遍歷 beanDefinitionMap 對象池來拿出單例bean,并且通過?createBean(beanName,beanDefinition) 方法來拿到創建完成的Bean對象,隨后存入 singletonObjects 單例池中:
/*** 構造Spring容器(需要傳遞配置類)* @param configClass 配置類*/
public ElevenApplicationContext(Class configClass) {this.configClass = configClass;// 解析配置類// 解析@ComponentScan注解 --》掃描路徑 --》掃描包下所有的類 -> BeanDefination -> BeanDefinationMapscan(configClass);// 在BeanDefinitionMap中拿出單例Beanfor (String beanName : beanDefinitionMap.keySet()) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);// 找出單例Beanif ("singleton".equals(beanDefinition.getScope())){// 單例BeanObject o = createBean(beanName,beanDefinition);singletonObjects.put(beanName, o);}}
}
所以接下來我們需要創建createBean()方法來創建Bean對象并返回:
我們現在有BeanName以及BeanDefinition,所以先通過?beanDefinition.getaClass() 方法獲取? ? ? ? 需要創建的Bean類型,隨后通過 aClass.getDeclaredConstructor().newInstance() 無參構造方法反射得到實例對象,最后返回該對象:
/*** 創建Bean對象* @param beanName Bean的名稱* @param beanDefinition Bean的定義對象* @return Bean對象*/
private Object createBean(String beanName,BeanDefinition beanDefinition) {// 獲取需要創建的Bean類型Class aClass = beanDefinition.getaClass();// 通過無參構造方法反射得到實例對象Object instance = aClass.getDeclaredConstructor().newInstance();// 返回實例對象return instance;
}
getBean()方法
在獲取Bean對象時,我們需先去判斷在BeanDefinition對象池里是否有Bean對象?如果有就可以通過BeanName來拿到該Bean的BeanDefinition定義類,隨后根據定義類內部的scope屬性判斷該Bean是單例還是原型,如果是單例可去單例池獲取,如果是原型Bean則需要創建新的對象并返回:
/*** 獲取Bean對象* @param beanName Bean的名字* @return Bean對象*/
public Object getBean(String beanName) {// 在BeanDefinition對象池里是否有Bean對象if(beanDefinitionMap.containsKey(beanName)){BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);// 判斷是單例Bean還是原型Beanif("singleton".equals(beanDefinition.getScope())){// 單例Bean,在單例池獲取return singletonObjects.get(beanName);} else {// 原型Bean,每次都創建新的對象return createBean(beanName,beanDefinition);}} else {throw new NullPointerException("Bean不存在");}
}
依賴注入(DI)
依賴:一個類使用到另一個類的功能。例如我們將要實現 UserService 依賴 OrderService。
注入:不需要自己 new,而是由 Spring 容器自動把依賴對象賦給我們。
?? 簡單說:以前我們需要自己去 new 一個 OrderS二vice 對象,這樣做代碼強耦合。但是現在?Spring 容器幫助我們創建 OrderS二vice 并注入到 UserService ,以此來達到解耦的效果。
控制反轉(IoC)的核心思想:如果在 Bean 內部手動 new 一個對象,那這個對象就不在 Spring 的 IoC 容器中管理了。
在Spring中,一般是通過@Autowired注解來實現的,所以我們在UserService來依賴注入一個OrderService:
package cn.spring.com.eleven.service;import cn.spring.com.spring.*;@Component("userService")
@Scope("prototype")
public class UserService {@Autowiredprivate OrderService orderService;
}
因為我們需要再UserService依賴注入一個OrderService,所以需要在createBean()方法內加入DI:
我們可以通過使用 aClass.getDeclaredFields() 方法獲取一個 Field 數組,其內部包含當前類中聲明的所有字段(成員變量),之后需要遍歷字段上是否有@Autowired注解,如果有那么就可以通過屬性名字找Bean對象,隨后setAccessible(true)?來關閉 Java 的訪問檢查機制,允許通過反射來操作 private/protected/default 修飾的字段,最后使用 field.set(instance, bean) 方法把容器里準備好的 bean
塞進 instance
對象的某個字段里:
/*** 創建Bean對象* @param beanName Bean的名稱* @param beanDefinition Bean的定義對象* @return Bean對象*/
private Object createBean(String beanName, BeanDefinition beanDefinition) {// 1. 獲取需要創建的Bean類型Class aClass = beanDefinition.getaClass();try {// 2. 通過無參構造方法反射得到實例對象Object instance = aClass.getDeclaredConstructor().newInstance();// ===================== 依賴注入開始 =====================// 3. 獲取 Bean 內所有的屬性(成員變量)Field[] fields = aClass.getDeclaredFields();for (Field field : fields) {// 4. 判斷屬性上是否有 @Autowired 注解if (field.isAnnotationPresent(Autowired.class)) {// 5. 根據屬性名字來找容器中的 Bean 對象Object bean = getBean(field.getName());if (bean == null) {throw new RuntimeException("依賴注入失敗,沒有找到對應的Bean對象");}// 6. 打破封裝,讓私有屬性可以被賦值field.setAccessible(true);// 7. 把依賴對象注入到當前正在創建的 Bean// instance -> 當前 Bean(例如 UserService)// bean -> 依賴對象(例如 UserRepository)field.set(instance, bean);}}// ===================== 依賴注入結束 =====================return instance;} catch (Exception e) {throw new RuntimeException(e);}
}
BeanNameAware接口
BeanNameAware 是 Spring 容器中 Aware 系列接口 之一,用來讓 Bean 感知到自己在容器中的名字。
-
獲取 Bean 的名字:有些時候,一個類可能被容器加載多次(比如配置了多個別名),實現這個接口后,它就能知道自己被容器叫做啥。
-
區分不同的 Bean 實例:當同一個類被注冊成多個 Bean 時,可以通過名字來區別。
-
調試/日志:在日志打印時,能更直觀地輸出 Bean 的名字,方便定位問題。
-
框架擴展:在某些自定義框架或工具中,可以根據 Bean 名稱實現一些動態邏輯。
package cn.spring.com.spring;/*** 實現該接口的Bean可以感知自己的BeanName* @Author: Eleven*/
public interface BeanNameAware {void setBeanName(String beanName);
}
當一個 Bean 實現了這個接口,Spring 容器在實例化并注入依賴完成后,會調用它的 setBeanName() 方法,把當前 Bean 在容器中的 name 傳進去。所以接下來我們就要實現這個邏輯。
我們先讓UserService去引入該接口:
package cn.spring.com.eleven.service;import cn.spring.com.spring.*;@Component("userService")
@Scope("prototype")
public class UserService implements BeanNameAware {@Autowiredprivate OrderService orderService;private String beanName;@Overridepublic void setBeanName(String beanName) {this.beanName = beanName;}
}
隨后在createBean()方法中去調用:
首先去判斷當前的Bean對象是不是實現了BeanNameAware接口,如果是的話就將該Bean對象強制轉型為 BeanNameAware?類型,之后調用內部的setBeanName()方法,把這個 Bean 在容器里的名字注入進去。
👉 這是典型的 Aware 回調機制:
容器檢查 -> 確認實現接口 -> 調用接口方法 -> 注入容器上下文信息。
/*** 創建Bean對象* @param beanName Bean的名稱* @param beanDefinition Bean的定義對象* @return Bean對象*/
private Object createBean(String beanName,BeanDefinition beanDefinition) {// 獲取需要創建的Bean類型Class aClass = beanDefinition.getaClass();try {// 通過無參構造方法反射得到實例對象Object instance = aClass.getDeclaredConstructor().newInstance();// 依賴注入DIField[] fields = aClass.getDeclaredFields();for (Field field : fields) {// 2. 判斷屬性上是否有@Autowired注解if (field.isAnnotationPresent(Autowired.class)){// 根據屬性名字來找Bean對象Object bean = getBean(field.getName());if (bean == null){throw new RuntimeException("依賴注入失敗,沒有找到對應的Bean對象");}// 依賴注入// 設置屬性為可訪問field.setAccessible(true);// 設置屬性值// instance 是當前正在創建的 Bean 對象// bean 是從容器中拿到的依賴對象field.set(instance, bean);}}// 判斷是否實現了BeanNameAware接口(感知自己的BeanName)if (instance instanceof BeanNameAware){// 強轉并調用setBeanName方法拿到自己的BeanName((BeanNameAware) instance).setBeanName(beanName);}// 返回實例對象return instance;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);} catch (Exception e) {throw new RuntimeException(e);}
}
InitializingBean接口
InitializingBean 接口是 Spring 里面 Bean 生命周期 的一個關鍵接口,跟我們前面說的 BeanNameAware 接口類似,它也是一種 回調接口,不過用途不同。
package cn.spring.com.spring;/*** 實現該接口的Bean可以在Bean初始化后調用afterPropertiesSet方法* @Author: Eleven*/
public interface InitializingBean {public void afterPropertiesSet() throws Exception;
}
調用 afterPropertiesSet() 方法作用是當 Bean 的所有屬性都注入完成之后,Spring 容器會回調這個方法,讓 Bean 有機會執行一些“初始化邏輯”,適合做那些 依賴注入完成后,必須再進行一次額外配置的場景。
我們在UserSerivce內引入該接口:
package cn.spring.com.eleven.service;import cn.spring.com.spring.*;@Component("userService")
@Scope("prototype")
public class UserService implements BeanNameAware, InitializingBean {@Autowiredprivate OrderService orderService;private String beanName;@Overridepublic void setBeanName(String beanName) {this.beanName = beanName;}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("UserService初始化了");}
}
跟上面的 BeanNameAware 接口實現類似,同樣先去判斷當前的Bean對象是不是實現了InitializingBean 接口,如果是的話就將該Bean對象強制轉型為 InitializingBean?類型,之后調用內部的 afterPropertiesSet() 方法。
/*** 創建Bean對象* @param beanName Bean的名稱* @param beanDefinition Bean的定義對象* @return Bean對象*/
private Object createBean(String beanName,BeanDefinition beanDefinition) {// 獲取需要創建的Bean類型Class aClass = beanDefinition.getaClass();try {// 通過無參構造方法反射得到實例對象Object instance = aClass.getDeclaredConstructor().newInstance();// 依賴注入DIField[] fields = aClass.getDeclaredFields();for (Field field : fields) {// 2. 判斷屬性上是否有@Autowired注解if (field.isAnnotationPresent(Autowired.class)){// 根據屬性名字來找Bean對象Object bean = getBean(field.getName());if (bean == null){throw new RuntimeException("依賴注入失敗,沒有找到對應的Bean對象");}// 依賴注入// 設置屬性為可訪問field.setAccessible(true);// 設置屬性值// instance 是當前正在創建的 Bean 對象// bean 是從容器中拿到的依賴對象field.set(instance, bean);}}// 判斷是否實現了BeanNameAware接口(感知自己的BeanName)if (instance instanceof BeanNameAware){// 強轉并調用setBeanName方法拿到自己的BeanName((BeanNameAware) instance).setBeanName(beanName);}// 判斷是否實現了InitializingBean接口(初始化)if (instance instanceof InitializingBean){// 強轉并調用afterPropertiesSet方法(初始化)((InitializingBean) instance).afterPropertiesSet();}// 返回實例對象return instance;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);} catch (Exception e) {throw new RuntimeException(e);}
}
BeanPostProcessor接口 + AOP的實現
BeanPostProcessor?是 Spring 框架里最核心、最常考的擴展接口之一。它的作用就是:在 Bean 初始化前后,做一些“增強”或“加工”。
package cn.spring.com.spring;/** 定義一個Bean的后置處理器*/
public interface BeanPostProcessor {/** 初始化之前調用*/Object postProcessBeforeInitialization(Object bean, String beanName);/** 初始化之后調用*/Object postProcessAfterInitialization(Object bean, String beanName);
}
Spring 給了開發者一個“切入點”,讓我們能在 Bean 初始化的前后,統一做一些額外處理。
常見用途:
修改 Bean 屬性(比如給某些字段設置默認值)
包裝 Bean(比如 AOP 代理就是在這一步完成的)
檢查或替換 Bean(如果發現不合規,可以替換成另一個對象)
實現通用邏輯(比如自動注入日志、監控、事務代理等)
假設我們想要再UserService中設置一個name屬性,然后想要再Bean初始化前給name賦值:
package cn.spring.com.eleven.service;import cn.spring.com.spring.*;@Component("userService")
@Scope("prototype")
public class UserService UserInterface {@Autowiredprivate OrderService orderService;private String beanName;private String name;@Overridepublic void setBeanName(String beanName) {this.beanName = beanName;}public void setName(String newUserService) {this.name = newUserService;}}
那我們就可以使用創建新的類去繼承這個 BeanPostProcessor 接口:
package cn.spring.com.eleven.service;import cn.spring.com.spring.BeanPostProcessor;
import cn.spring.com.spring.Component;
import cn.spring.com.spring.ElevenApplicationContext;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;@Component("ElevenBeanPostProcessor")
public class ElevenBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("初始化之前調用");if(beanName.equals("userService")){((UserService)bean).setName("eleven");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("初始化之后調用");return bean;}
}
之后我們在掃描類的過程就需要去判斷這個類加了@Component并且還引入了 BeanPostProcessor,之后在外面自定義一個BeanPostProcessor對象池存入,方便后序直接查找:
// BeanPostProcessor對象池
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();// 判斷clazz這個類是否實現了BeanPostProcessor接口
if (BeanPostProcessor.class.isAssignableFrom(clazz)){BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();// 把BeanPostProcessor對象添加到BeanPostProcessor對象池beanPostProcessorList.add(beanPostProcessor);
}
之后在createBean()方法內去遍歷BeanPostProcessor對象池找到當前自定義的兩個方法去調用:
/*** 創建Bean對象* @param beanName Bean的名稱* @param beanDefinition Bean的定義對象* @return Bean對象*/
private Object createBean(String beanName,BeanDefinition beanDefinition) {// 獲取需要創建的Bean類型Class aClass = beanDefinition.getaClass();try {// 通過無參構造方法反射得到實例對象Object instance = aClass.getDeclaredConstructor().newInstance();// 依賴注入DIField[] fields = aClass.getDeclaredFields();for (Field field : fields) {// 2. 判斷屬性上是否有@Autowired注解if (field.isAnnotationPresent(Autowired.class)){// 根據屬性名字來找Bean對象Object bean = getBean(field.getName());if (bean == null){throw new RuntimeException("依賴注入失敗,沒有找到對應的Bean對象");}// 依賴注入// 設置屬性為可訪問field.setAccessible(true);// 設置屬性值// instance 是當前正在創建的 Bean 對象// bean 是從容器中拿到的依賴對象field.set(instance, bean);}}// 判斷是否實現了BeanNameAware接口(感知自己的BeanName)if (instance instanceof BeanNameAware){// 強轉并調用setBeanName方法拿到自己的BeanName((BeanNameAware) instance).setBeanName(beanName);}// 調用BeanPostProcessor的postProcessBeforeInitialization方法for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);}// 判斷是否實現了InitializingBean接口(初始化)if (instance instanceof InitializingBean){// 強轉并調用afterPropertiesSet方法(初始化)((InitializingBean) instance).afterPropertiesSet();}// 調用BeanPostProcessor的postProcessAfterInitialization方法for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);}// 返回實例對象return instance;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);} catch (Exception e) {throw new RuntimeException(e);}
}
接下來就需要實現AOP,AOP一般都是實現在初始化后,所以我們要改寫postProcessAfterInitialization()方法:
package cn.spring.com.eleven.service;import cn.spring.com.spring.BeanPostProcessor;
import cn.spring.com.spring.Component;
import cn.spring.com.spring.ElevenApplicationContext;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;@Component("ElevenBeanPostProcessor")
public class ElevenBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("初始化之前調用");return bean;}// 在 Bean 創建完成后,換掉原來的 Bean 對象,返回一個代理對象。@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("初始化之后調用");if(beanName.equals("userService")){// JDK的動態代理Object proxyInstance = Proxy.newProxyInstance(ElevenBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("JDK的動態代理");return method.invoke(bean, args);}});return proxyInstance;}return bean;}
}