ナビゲーション:
コード: https://wwmg.lanzouk.com/ictoK135ye2f
目次
1.1.6 Bean宣言クラス:BeanDefinition
1.1.7 Bean 名コールバック インターフェイス: BeanNameAware
1.1.8 初期化インターフェース: InitializingBean
1.1.9 Bean ポストプロセッサ インターフェイス: BeanPostProcessor
1.2.5 Beanポストプロセッサインタフェース実装クラス:MyBeanPostProcessor
2. コンテナ クラスの改善: VinceApplicationContext
1. 準備
1.0プロジェクトディレクトリ
1.1 Spring パッケージのクラス
com.vince.spring パッケージの下のクラス:
1.1.1 コンテナクラス
Spring コンテナの作成方法と比較して、シミュレートする必要があるのは AnnotationConfigApplicationContext クラスです。
- ClassPathXmlApplicationContext:指定された構成に従って Spring コンテナー ApplicationContext を作成するように Spring に指示するには、XML 構成ファイルを渡す必要があります。作成後、getBean を通じてコンテナーから対応する Bean オブジェクトを取得できます。
- AnnotationConfigApplicationContext:これもコンテナですが、Java 構成クラスの形式で渡されます。@ComponentScan をクラスに追加して、Spring などでスキャンする必要があるパスを定義できます。@Bean (Bean タグに等しい) のメソッドとこのアノテーションを使用して、Bean をコンテナに追加することもできます。
public class Test { public static void main(String[] args) { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml"); classPathXmlApplicationContext.getBean("user"); AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class); SpringCodeApplicationContext applicationContext = new SpringCodeApplicationContext(AppConfig.class); } }
getBean()
Spring フレームワークを参照すると、このコンテナ クラスには構築メソッド、受信パラメータ、およびメソッドが必要であることがわかります。
パッケージcom.vince.spring;
//模拟AnnotationConfigApplicationContext
public class VinceApplicationContext{
private Class configClass;
//构造方法,参数是配置类的字节码文件
public SpringCodeApplicationContext(Class configClass) {
//1.赋值配置类成员变量
this.configClass = configClass;
// 2.解析配置类
// 通过反射,判断配置类字节码有没有注解@ComponentScan
// 如果有,就根据@ComponentScan传入的扫描路径,扫描那个包下的Bean
// 3.实例化bean
}
//容器里拥有getBean()方法,获取Bean
public Object getBean(String beanName){
return null;
}
1.1.2 @ComponentScan アノテーション
パッケージcom.vince.spring;
/**
* 用于扫描指定包路径的Bean
*/
@Retention(RetentionPolicy.RUNTIME)//注解生命周期,不仅被保存到class文件中,jvm加载class文件之后,仍然存在
//@Target用来表示注解作用范围,超过这个作用范围,编译的时候就会报错。
@Target(ElementType.TYPE)//注解范围是:接口、类、枚举、注解
public @interface ComponentScan {
String value() default "";
}
1.1.3 @Component アノテーション
パッケージcom.vince.spring;
/**
* 注解为Bean
*/
@Retention(RetentionPolicy.RUNTIME)//注解生命周期,不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(ElementType.TYPE)//注解范围是:接口、类、枚举、注解
public @interface Component {
String value() default "";
}
1.1.4 @Autowired アノテーション
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
}
1.1.5 @Scope アノテーション
@Retention(RetentionPolicy.RUNTIME)//注解生命周期,不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(ElementType.TYPE)//注解范围是:接口、类、枚举、注解
public @interface Scope {
String value() default "";
}
1.1.6 Bean宣言クラス:BeanDefinition
BeanDefinition は、以下を含む Bean の構成メタデータを定義します。
- Beanのクラス名
- 親 Bean 名 (プライマリ Bean かどうか) を設定します。
- Beanの動作設定情報、スコープ、自動バインディングモード、ライフサイクルコールバック、遅延ロード、初期メソッド、破棄メソッドなど。
- Bean間の依存関係設定、依存関係
- 構築パラメータ、プロパティ設定
//Bean的声明类,用来描述Bean;里面定义的Bean的类型(Class格式)和作用域
public class BeanDefinition {
private Class type;
private String scope;
@Override
public String toString() {
return "BeanDefinition{" +
"type=" + type +
", scope='" + scope + '\'' +
'}';
}
public Class getType() {
return type;
}
public void setType(Class type) {
this.type = type;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
1.1.7 Bean 名コールバック インターフェイス: BeanNameAware
//回调接口,主要setter方法给Bean的beanName变量赋值。Aware译为明白的,意识到的。
public interface BeanNameAware {
public void setBeanName(String beanName);
}
1.1.8 初期化インターフェース: InitializingBean
//初始化Bean的接口,有个方法,
public interface InitializingBean {
//在属性填充后执行
public void afterPropertiesSet();
}
1.1.9 Bean ポストプロセッサ インターフェイス: BeanPostProcessor
このインターフェースの 2 つのメソッドは初期化の前後に実行され、JDK の Proxy は実装クラスを通じて使用できます。
//Bean后置处理器接口
public interface BeanPostProcessor {
//在Bean 的初始化方法(如 @PostConstruct 注解的方法)被调用之前被自动调用
public Object postProcessBeforeInitialization(String beanName,Object bean);
//在 Bean 的初始化方法被调用之后被自动调用
public Object postProcessAfterInitialization(String beanName,Object bean);
}
1.2 ユーザービジネス関連クラス
com.vince.service パッケージの下のクラス:
1.2.1 テストクラス作成コンテナ
パッケージcom.vince.spring;
public class Test {
public static void main(String[] args){
//模拟AnnotationConfigApplicationContext容器,构造参数传入配置类
VinceApplicationContext applicationContext = new VinceApplicationContext(AppConfig.class);
UserInterface userService = (UserInterface)applicationContext.getBean("userService");
userService.test();
}
}
1.2.2 構成クラス AppConfig
パッケージcom.vince.spring;
// com/springCode/service/UserService.java
@ComponentScan("com.springCode.service")
public class AppConfig {
}
1.2.3 ユーザーサービス
パッケージcom.vince.service;
public interface UserInterface {
public void test();
}
@Component
//@Scope("prototype") //多例
public class UserService implements BeanNameAware , InitializingBean ,UserInterface{
@Autowired
private OrderService orderService;
private String beanName;
@Override
public void test(){
System.out.println(orderService);
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
public void afterPropertiesSet() {
System.out.println("初始化方法");
}
}
1.2.4 OrderService、依存関係注入用
@Component
//@Scope("prototype") //多例
public class OrderService {
public void submitOrder(){
System.out.println("下单。。。");
}
}
1.2.5 Beanポストプロセッサインタフェース実装クラス:MyBeanPostProcessor
//Bean后置处理器实现类,两个方法在初始化之前和之后执行,可以通过实现类使用JDK的Proxy.newProxyInstance();基于反射实现aop。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(String beanName, Object bean) {
if ("userService".equals(beanName)) {
System.out.println("BeanPostProcessor实现类的postProcessBeforeInitialization()方法");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(String beanName, Object bean) {
if ("userService".equals(beanName)) {
//代理对象,通过JDK的Proxy.newProxyInstance();基于反射实现aop。
// 第一个参数类加载器,第二个参数接口的class对象,第三个参数重写invoke()的InvocationHandler实现类
Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(),
bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("切面逻辑,BeanPostProcessor实现类的postProcessAfterInitialization()方法");
return method.invoke(bean,args);
}
});
return proxyInstance;
}
return bean;
}
}
2. コンテナ クラスの改善: VinceApplicationContext
2.0 Bean プロセスの作成
コンテナ クラス コンストラクターにアノテーションを付けて Bean を作成します。
- Beanスキャンパスの取得:構成クラスのClassオブジェクトを取得し、リフレクションに基づいて@ComponentScanアノテーションに設定されているスキャンパスを取得します。
- 走査Bean:走査パス内の各クラスのバイトコードファイルを走査し、クラスローダでクラスオブジェクトにロードし、リフレクションに基づいて@Componentなどのアノテーションを判定し、存在する場合、このクラスはBeanクラスです。
- インスタンス化後に BeanPostProcessor リストを保存します。Beanクラスが BeanPostProcessor インターフェースの実装クラスの場合、Bean クラスをインスタンス化し、メンバー変数 List<BeanPostProcessor> に保存します。
- BeanDefinition オブジェクトの作成と割り当て: BeanDefinition オブジェクトを作成して値を割り当て、Bean クラスのクラス オブジェクトに type 変数を割り当て、スコープにスコープ変数を割り当てます (リフレクションによって @Scope アノテーション値を取得します)。BeanDefinition には、Bean オブジェクトやスコープなどのメタ情報が格納されます。
- beanDefinitionMap へのキーと値のペアの追加: Bean 名とそれに対応する BeanDefinition オブジェクトを、キーと値のペアの形式でコンテナ クラスのメンバー変数 beanDefinitionMap に追加します。beanDefinitionMap はスレッドセーフであり、タイプは ConcurrentHashMap です。
- beanDefinitionMap を走査して Bean を作成する: beanDefinitionMap のキーを走査し、createBean()メソッドを呼び出して Bean を作成します。createBean() メソッドの具体的なロジックは次のとおりです。
- インスタンス化:リフレクションに基づいて構築方法を推論し、BeanクラスのClassオブジェクトに従ってインスタンスを作成し、シングルトンの場合はシングルトンプールに格納します。このメソッドは getBean() をインスタンス化するときにも使用されるため、プライベート メソッドとして抽出されます。このメソッドは最初に Bean のスコープを決定します。シングル インスタンスの場合は、最初に Bean 名に従ってシングルトン プールから取得されます。シングルトン プールがない場合は、createBean を呼び出します。 () メソッドは、Bean 名と対応する BeanDefinition に従って、Object 型の Bean オブジェクトを作成します。
- 属性の充填:リフレクションに基づいて、Bean クラスの Class オブジェクトに従ってすべての属性を取得し、@Autowired などのアノテーションが付けられた属性のプライベート メンバー アクセス制限を開き、getBean (名前またはクラス)メソッド。getBean() メソッド:スコープを決定します。単一インスタンスの場合は、createBean() を呼び出してシングルトン プールに入れて戻ります。プロトタイプの場合は、createBean() を呼び出して直接戻ります。簡略化のため、ここでは 1 レベル キャッシュのみを考慮し、2 レベルおよび 3 レベル キャッシュについては当面考慮しません。
- ハンドル対応コールバック: Bean インスタンスが BeanNameAware インターフェースを実装している場合 (instanceof によって判断)、Bean によって書き換えられた setBeanName() メソッドを呼び出して、Bean インスタンスの beanName 変数に値を割り当てます。
- すべての BeanPostProcessor 事前初期化メソッドを実行します。BeanPostProcessor リストを走査し、各 BeanPostProcessor オブジェクトの postProcessBeforeInitialization() メソッドを実行します。このメソッドは、JDK の Proxy.newProxyInstance() を通じて動的プロキシを実装し、ターゲット オブジェクトのプロキシ オブジェクトを返すことができます。
- 初期化: Bean インスタンスが InitializingBean インターフェースを実装している場合 (instanceof によって判断)、Bean によって書き換えられた afterPropertiesSet() メソッドを呼び出して初期化ロジックを処理します。afterPropertiesSet は「プロパティが設定された後」と訳されます。
- すべての BeanPostProcessor 初期化後メソッドを実行します。BeanPostProcessor リストを走査し、各 BeanPostProcessor オブジェクトの postProcessAfterInitialization() メソッドを実行します。このメソッドは、JDK の Proxy.newProxyInstance() を通じて動的プロキシを実装し、ターゲット オブジェクトのプロキシ オブジェクトを返すことができます。
- それをシングルトン プールに入れます。シングルトンの場合は、オブジェクト タイプ Bean のプロキシ オブジェクトをシングルトン プールの singletonObjects に入れます。
2.1 コードの実装
Spring パッケージの VinceApplicationContext クラス
2.1.1 メンバー変数
private Class configClass;//配置类的Class对象
//Bean名和它对应的BeanDefinition键值对;
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
//单例池;map存各Bean名所对应的Bean实例
private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();
//存放扫描包路径下的所有BeanPostProcessor类
private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
2.1.2 構築メソッド、パラメータは構成クラス
//构造方法,参数是配置类的Class对象
public VinceApplicationContext(Class configClass) {
//1.赋值配置类成员变量
this.configClass = configClass;
// 2.通过反射,判断配置类Class对象有没有注解@ComponentScan;如果有,就解析ComponentScan对象
// --->BeanDefinition -->beanDefinitionMap
if (configClass.isAnnotationPresent(ComponentScan.class)) {
//解析ComponentScan对象
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
// 获取ComponentScan对象的value,也就是用户传入的扫描路径。例如@ComponentScan("com.vince.service"),扫描这个包下的Bean
String path = componentScanAnnotation.value();// com.vince.service
// 将包名中的点号替换为斜杠
path = path.replace(".","/");//com/vince/service
// 获取容器类的类加载器
ClassLoader classLoader = VinceApplicationContext.class.getClassLoader();
// 类加载器获取扫描包的绝对路径
URL resource = classLoader.getResource(path);//file:/D:/xxx/com/vince/service
// 把URL绝对路径对象封装成File对象
File file = new File(resource.getFile());
// 如果File对象是目录,则遍历判断扫描目录下的文件是不是bean;
if (file.isDirectory()) {
File[] files = file.listFiles();
//遍历“扫描路径”下所有文件中,找到Bean,创建对应BeanDefinition对象,并存到map里;
// 判断Bean方式:基于反射,判断该字节码文件对应的类有没有@Component注解
for (File f: files) {
String fileName = f.getAbsolutePath();
if (fileName.endsWith(".class")) {
//将文件名转为类名;D:\xxx\com\vince\service\AppConfig.class ----> AppConfig
//这里其实不应该写死成“com”,只是方便起见,其实应该用更复杂的逻辑截取全名为类名
String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
className = className.replace("\\",".");
try {
//根据类的全限定名加载类
Class<?> clazz = classLoader.loadClass(className);
//如果该类有@Component注解
if (clazz.isAnnotationPresent(Component.class)) {
System.out.println("容器类构造方法里,扫描@ComponentScan('xx'),发现xx路径下这个类是Bean:"+clazz.getName());
//a.如果@Component("类名")有设置类名,Bean名就是设置的这个类名
Component component = clazz.getAnnotation(Component.class);
String beanName = component.value();
//判断这个Bean类是不是BeanPostProcessor接口的实现类,如果是的话就通过反射创建实例,并放进处理器列表里。
//注意不能用instanceof,因为instanceof是实例与类的关系比较,isAssignableFrom是类和接口的关系比较
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
beanPostProcessorList.add(instance);
System.out.println("这个Bean是BeanPostProcessor接口的实现类,需要加入list里,以供所有Bean初始化之前和之后增强");
}
//b.如果@Component没有设置类名,就将首字母小写后的Bean类名设为Bean名
if ("".equals(beanName)) {
beanName = Introspector.decapitalize(clazz.getSimpleName());//工具类将字符串校验后首字母小写
}
// 创建一个BeanDefinition对象,用来描述Bean,里面赋值这个Bean的类型和作用域
BeanDefinition beanDefinition = new BeanDefinition();
//反射获取@Scope注解指定的作用域,赋值给BeanDefinition对象的scope变量
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
}else{
beanDefinition.setScope("singleton");
}
//把这个Bean的Class对象赋值给BeanDefinition对象的type变量
beanDefinition.setType(clazz);
//把所有Bean名和它对应的BeanDefinition对象映射关系,存到map里统一管理
beanDefinitionMap.put(beanName,beanDefinition);
System.out.println("Bean名:"+beanName+",BeanDefinition对象:"+beanDefinition);
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
// 3.实例化bean,如果作用域是单例,则从单例池中取;取不到就创建新的Bean对象,并存入单例池
for (String beanName:beanDefinitionMap.keySet()) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if ("singleton".equals(beanDefinition.getScope())) {
//实例化Bean
Object bean = createBean(beanName,beanDefinition);
singletonObjects.put(beanName,bean);
}
}
}
2.1.3 createBean()方法
//根据Bean名和BeanDefinition对象实例化Bean的方法;私有,只供本类内部调用
//BeanDefinition对象的成员变量type就是Bean的类型(Class格式)
private Object createBean(String beanName,BeanDefinition beanDefinition){
// 获取Bean的Class格式的类型
Class clazz = beanDefinition.getType();
try {
//1.基于反射创建Bean的实例;
Object instance = clazz.getConstructor().newInstance();
//2.依赖注入
//遍历Bean类Class对象的所有属性;
for (Field f : clazz.getDeclaredFields()) {
//找出有@Autowired注解的属性,给这些属性进行填充
if (f.isAnnotationPresent(Autowired.class)) {
//破除 private 修饰符访问限制;这样就可以访问注入的这个依赖里面的私有成员
f.setAccessible(true);
//给该属性填充属性名对应的Bean对象。
// 该属性通过getBean能获得Bean,因为上面构造方法里已经扫描了所有的Bean,并创建了BeanDefinition对象加入到“声明map”里了。
//void set(Object obj, Object value)
//obj 表示要设置属性值的对象;value 表示要为该成员变量设置的新值。
f.set(instance,getBean(f.getName()));
}
}
// 3.Aware回调。如果Bean实例实现了BeanNameAware接口,就调用重写的setter方法,给Bean实例的beanName变量赋值
if (instance instanceof BeanNameAware) {
((BeanNameAware) instance).setBeanName(beanName);
}
// 遍历后置处理器列表,执行后置处理器的before方法,在Bean 的初始化方法(如 @PostConstruct 注解的方法)被调用之前被自动调用
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessBeforeInitialization(beanName,instance);
}
// 初始化
if (instance instanceof InitializingBean) {
((InitializingBean) instance).afterPropertiesSet();
}
//Bean后置处理器接口的after方法,在 Bean 的初始化方法被调用之后被自动调用
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessAfterInitialization(beanName,instance);
}
return instance;
} catch (InstantiationException e) { //异常处理
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
2.1.4 getBean() メソッド
//容器类的getBean()方法,根据作用域获取Bean
public Object getBean(String beanName){
//从map里获取BeanDefinition对象
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) {
//如果BeanDefinition对象为空,说明这个bean没有创建成功,抛异常;
throw new NullPointerException();
}else{
//如果BeanDefinition对象不为空,则判断作用域后获取Bean
String scope = beanDefinition.getScope();
if ("singleton".equals(scope)) {
//如果是单例则从单例池根据Bean名取Bean;
Object bean= singletonObjects.get(beanName);
if (bean == null) {
//如果单例池查到的是null,则新创建Bean,再给单例池赋值
bean = createBean(beanName, beanDefinition);
singletonObjects.put(beanName,bean);
}
//返回Bean
return bean;
}else{
// 多例,每次直接创建新的
return createBean(beanName, beanDefinition);
}
}
}