手寫Spring底層機制的實現【初始化IOC容器+依賴注入+BeanPostProcesson機制+AOP】

摘要:建議先看“JAVA----Spring的AOP和動態代理”這個文章,解釋都在代碼中!

一:提出問題依賴注入

1.單例

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:comtext="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 https://www.springframework.org/schema/context/spring-context.xsd"><!--   1. 如果我們是普通的java項目, beans.xml 放在src下2. 如果我們是java maven 項目, beans.xml 放在 src/main/resources  --><comtext:component-scan base-package="com.campus.spring.component"/>
</beans>

UserAction

package com.campus.spring.component;import org.springframework.stereotype.Component;@Component
public class UserAction {
}

UserDao

package com.campus.spring.component;import org.springframework.stereotype.Component;//可以使用 @Repository
@Component
public class UserDao {public void hi(){System.out.println("hi。。。。。。。。。。。。。。");}
}

UserService

package com.campus.spring.component;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;// 可以使用@Service
@Component
public class UserService {@Autowiredprivate UserDao userDao;public void m1(){userDao.hi();}
}

package com.campus.spring;import com.campus.spring.component.UserAction;
import com.campus.spring.component.UserDao;
import com.campus.spring.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class AppMain {public static void main(String[] args) {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");UserAction userAction =(UserAction) ioc.getBean("userAction");UserAction userAction2 =(UserAction) ioc.getBean("userAction");System.out.println("userAction="+ userAction);System.out.println("userAction2="+ userAction2);UserDao userDao = (UserDao) ioc.getBean("userDao");System.out.println("userDao="+ userDao);UserService userService = (UserService) ioc.getBean("userService");System.out.println("userService="+ userService);}
}

運行結果:

userAction=com.campus.spring.component.UserAction@54d9d12d
userAction2=com.campus.spring.component.UserAction@54d9d12d
userDao=com.campus.spring.component.UserDao@38425407
userService=com.campus.spring.component.UserService@43bc63a3
注:userAction和userAction2的結果一樣;
如果我們是普通的java項目, beans.xml 放在src下;
如果我們是java maven 項目, beans.xml 放在 src/main/resources ;

2.多例

UserAction

package com.campus.spring.component;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;@Scope(value = "prototype")
@Component
public class UserAction {
}

其他代碼一樣

運行結果
userAction=com.campus.spring.component.UserAction@dd05255
userAction2=com.campus.spring.component.UserAction@6a78afa0
userDao=com.campus.spring.component.UserDao@2f4948e4
userService=com.campus.spring.component.UserService@1f2586d6
注:在默認情況下 我們配置@Component @Controller @Service @Repository 是單例 @Scope(value = "prototype") 表示以多實例形式,返回UserAction bean

問題一:加入 @Autowired ,Spring容器時如何實現依賴注入 和? (單例和多列)怎么實現的?

二:提出問題BeanPostProcessor

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:comtext="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 https://www.springframework.org/schema/context/spring-context.xsd"><comtext:component-scan base-package="com.campus.spring.component"/><!--配置后置處理器--><bean class="com.campus.spring.process.MyBeanPostProcessor" id="myBeanPostProcessor"/>
</beans>

UserAction

package com.campus.spring.component;import org.springframework.stereotype.Component;@Component
public class UserAction {
}

UserDao

package com.campus.spring.component;import org.springframework.stereotype.Component;//可以使用 @Repository
@Component
public class UserDao {public void hi(){System.out.println("hi。。。。。。。。。。。。。。");}
}

UserService

package com.campus.spring.component;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;//也可以使用@Service
@Component
public class UserService {//定義屬性//思考:加入 @Autowired , Spring容器時如何實現依賴注入?//也可以使用@Resource@Autowiredprivate UserDao userDao;public void m1() {userDao.hi();}//這里我們需要指定init() 是初始化方法@PostConstructpublic void init() {System.out.println("UserService-init()");}}

AppMain

package com.campus.spring;import com.campus.spring.component.UserAction;
import com.campus.spring.component.UserDao;
import com.campus.spring.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class AppMain {public static void main(String[] args) {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");UserAction userAction =(UserAction) ioc.getBean("userAction");UserAction userAction2 =(UserAction) ioc.getBean("userAction");System.out.println("userAction="+ userAction);System.out.println("userAction2="+ userAction2);UserDao userDao = (UserDao) ioc.getBean("userDao");System.out.println("userDao="+ userDao);UserService userService = (UserService) ioc.getBean("userService");System.out.println("userService="+ userService);}
}

MyBeanPostProcessor(后置處理器)

package com.campus.spring.process;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;/*** 編寫的一個后置處理器*/
//@Component
public class MyBeanPostProcessor implements BeanPostProcessor {/*** 在Bean的 init初始化方法前調用* @param bean 就是ioc 容器返回的bean 對象, 如果這里被替換會修改,則返回的bean 對象也會被修改* @param beanName 就是ioc 容器配置的bean 的名稱* @return  beanName 就是返回的bean 對象* @throws BeansException*/public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("postProcessBeforeInitialization 被 調 用 " + beanName + " bean= " + bean.getClass());return bean;}/**
*	在Bean的 init初始化方法后調用
*	@param bean : 就是ioc 容器返回的bean 對象, 如果這里被替換會修改,則返回的bean 對象也會被修改
*	@param beanName: 就是ioc 容器配置的bean 的名稱
*	@return Object: 就是返回的bean 對象
*/public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("postProcessAfterInitialization 被 調 用 " + beanName + " bean= " + bean.getClass());return bean;}
}

運行結果(后置處理器有4組):

postProcessBeforeInitialization 被 調 用 userDao bean= class com.campus.spring.component.UserDao
postProcessAfterInitialization 被 調 用 userDao bean= class com.campus.spring.component.UserDao
postProcessBeforeInitialization 被 調 用 userService bean= class com.campus.spring.component.UserService
UserService-init()
postProcessAfterInitialization 被 調 用 userService bean= class com.campus.spring.component.UserService
postProcessBeforeInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
postProcessAfterInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
postProcessBeforeInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
postProcessAfterInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
userAction=com.campus.spring.component.UserAction@2362f559
userAction2=com.campus.spring.component.UserAction@b2c9a9c
userDao=com.campus.spring.component.UserDao@4c178a76
userService=com.campus.spring.component.UserService@fa4c865

注:如果把UserAction.java中的@Scope(value = "prototype") 去掉,則后置處理器有3組;

運行結果:
postProcessBeforeInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
postProcessAfterInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
postProcessBeforeInitialization 被 調 用 userDao bean= class com.campus.spring.component.UserDao
postProcessAfterInitialization 被 調 用 userDao bean= class com.campus.spring.component.UserDao
postProcessBeforeInitialization 被 調 用 userService bean= class com.campus.spring.component.UserService
UserService-init()
postProcessAfterInitialization 被 調 用 userService bean= class com.campus.spring.component.UserService
userAction=com.campus.spring.component.UserAction@3d285d7e
userAction2=com.campus.spring.component.UserAction@3d285d7e
userDao=com.campus.spring.component.UserDao@40005471
userService=com.campus.spring.component.UserService@2cd76f31

問題二:原生 Spring容器實現? BeanPostProcessor??

三:問題三

beans.xml

AppMain

<?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:comtext="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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><!--   1. 如果我們是普通的java項目, beans.xml 放在src下2. 如果我們是java maven 項目, beans.xml 放在 src/main/resources  --><comtext:component-scan base-package="com.campus.spring.component"/><comtext:component-scan base-package="com.campus.spring.aop"/><!--配置后置處理器--><bean class="com.campus.spring.process.MyBeanPostProcessor" id="myBeanPostProcessor"/><!--啟用基于注解方式的AOP功能--><aop:aspectj-autoproxy/>
</beans>

SmartAnimalable接口

package com.campus.spring.aop;public interface SmartAnimalable {float getSum(float i, float j);float getSub(float i, float j);
}

SmartAnimalAspect

package com.campus.spring.aop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;/*** 這是一個切面類*/
@Component
@Aspect
public class SmartAnimalAspect {//給SmartDog配置前置,返回,異常,最終通知//前置通知@Before(value = "execution(public float com.campus.spring.aop.SmartDog.getSum(float, float))")public void showBeginLog(JoinPoint joinPoint) {//通過連接點對象joinPoint 可以獲取方法簽名Signature signature = joinPoint.getSignature();System.out.println("SmartAnimalAspect-切面類showBeginLog()[使用的myPointCut()]-方法執行前-日志-方法名-" + signature.getName() + "-參數 "+ Arrays.asList(joinPoint.getArgs()));}//返回通知@AfterReturning(value = "execution(public float com.campus.spring.aop.SmartDog.getSum(float, float))", returning = "res")public void showSuccessEndLog(JoinPoint joinPoint, Object res) {Signature signature = joinPoint.getSignature();System.out.println("SmartAnimalAspect-切面類showSuccessEndLog()-方法執行正常結束-日志-方法名-" + signature.getName() + " 返回的結果是=" + res);}//異常通知@AfterThrowing(value = "execution(public float com.campus.spring.aop.SmartDog.getSum(float, float))", throwing = "throwable")public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {Signature signature = joinPoint.getSignature();System.out.println("SmartAnimalAspect-切面類showExceptionLog()-方法執行異常-日志-方法名-" + signature.getName() + " 異常信息=" + throwable);}//最終通知@After(value = "execution(public float com.campus.spring.aop.SmartDog.getSum(float, float))")public void showFinallyEndLog(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();System.out.println("SmartAnimalAspect-切面類showFinallyEndLog()-方法最終執行完畢-日志-方法名-" + signature.getName());}}

SmartDog

package com.campus.spring.aop;import org.springframework.stereotype.Component;@Component
public class SmartDog implements SmartAnimalable {public float getSum(float i, float j) {float res = i + j;System.out.println("SmartDog-getSum-res=" + res);return res;}public float getSub(float i, float j) {float res = i - j;System.out.println("SmartDog-getSub-res=" + res);return res;}
}

AppMain

package com.campus.spring;import com.campus.spring.aop.SmartAnimalable;
import com.campus.spring.component.UserAction;
import com.campus.spring.component.UserDao;
import com.campus.spring.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class AppMain {public static void main(String[] args) {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");UserAction userAction =(UserAction) ioc.getBean("userAction");UserAction userAction2 =(UserAction) ioc.getBean("userAction");System.out.println("userAction="+ userAction);System.out.println("userAction2="+ userAction2);UserDao userDao = (UserDao) ioc.getBean("userDao");System.out.println("userDao="+ userDao);UserService userService = (UserService) ioc.getBean("userService");System.out.println("userService="+ userService);//測試 AOPSmartAnimalable smartDog = ioc.getBean(SmartAnimalable.class);smartDog.getSum(10, 2);}
}
運行結果:
postProcessBeforeInitialization 被 調 用 userDao bean= class com.campus.spring.component.UserDao
postProcessAfterInitialization 被 調 用 userDao bean= class com.campus.spring.component.UserDao
postProcessBeforeInitialization 被 調 用 userService bean= class com.campus.spring.component.UserService
UserService-init()
postProcessAfterInitialization 被 調 用 userService bean= class com.campus.spring.component.UserService
postProcessBeforeInitialization 被 調 用 smartAnimalAspect bean= class com.campus.spring.aop.SmartAnimalAspect
postProcessAfterInitialization 被 調 用 smartAnimalAspect bean= class com.campus.spring.aop.SmartAnimalAspect
postProcessBeforeInitialization 被 調 用 smartDog bean= class com.campus.spring.aop.SmartDog
postProcessAfterInitialization 被 調 用 smartDog bean= class jdk.proxy2.$Proxy20
postProcessBeforeInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
postProcessAfterInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
postProcessBeforeInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
postProcessAfterInitialization 被 調 用 userAction bean= class com.campus.spring.component.UserAction
userAction=com.campus.spring.component.UserAction@24c22fe
userAction2=com.campus.spring.component.UserAction@93081b6
userDao=com.campus.spring.component.UserDao@cd1e646
userService=com.campus.spring.component.UserService@7ba8c737
SmartAnimalAspect-切面類showBeginLog()[使用的myPointCut()]-方法執行前-日志-方法名-getSum-參數 [10.0, 2.0]
SmartDog-getSum-res=12.0
SmartAnimalAspect-切面類showSuccessEndLog()-方法執行正常結束-日志-方法名-getSum 返回的結果是=12.0
SmartAnimalAspect-切面類showFinallyEndLog()-方法最終執行完畢-日志-方法名-getSum

注:postProcessBeforeInitialization 被 調 用 smartDog bean= class com.campus.spring.aop.SmartDog
postProcessAfterInitialization 被 調 用 smartDog bean= class jdk.proxy2.$Proxy20

解釋:當創建smartDog 對象/組件的時候,postProcessBeforeInitialization 的時候還是SmartDog,也就是初始化,但是postProcessAfterInitialization 后置處理器變成了代理對象,因為AOP返回的是代理對象

問題三:AOP 與?BeanPostProcessor 關系

簡單分析AOP 與?BeanPostProcessor 關系

  1. AOP 實現 Spring 可以通過給一個類,加入注解 @EnableAspectJAutoProxy ?來指定, 比如?

package com.campus.spring.aop;import org.springframework.context.annotation.EnableAspectJAutoProxy;@EnableAspectJAutoProxy
public class Test {
}
  1. 我們來追一下@EnableAspectJAutoProxy

  1. 看一下 AnnotationAwareAspectJAutoProxyCreator 的類圖

  1. AOP 底層是基于 BeanPostProcessor 機制的.
  2. 即在 Bean 創建好后,根據是否需要 AOP 處理,決定返回代理對象,還是原生 Bean
  3. 在返回代理對象時,就可以根據要代理的類和方法來返回
  4. 其實這個機制并不難,本質就是在 BeanPostProcessor 機制 + 動態代理技術
  5. 下面我們就準備來實現 AOP 機制

四:Spring 整體架構分析

第一步:編寫自己 Spring 容器,實現掃描包, 得到 bean 的 class 對象

1.分析示意圖

知識擴展:類加載器
java 的類加載器 3 種

1.Bootstrap 類加載器--------------對應路徑 jre/lib
2.Ext 類加載器--------------------對應路徑 jre/lib/ext
3.App 類加載器-------------------對應路徑 classpath
● classpath 類路徑,就是 java.exe 執行時,指定的路徑,比如

?D:\program\hspjdk8\bin\java.exe????????????????????????????? "-javaagent:D:\program\JavaIDEA

2020.2\lib\idea_rt.jar=55992:D:\program\JavaIDEA????? 2020.2\bin"???? -Dfile.encoding=UTF-8????? -classpath D:\program\hspjdk8\jre\lib\charsets.jar;D:\program\hspjdk8\jre\lib\deploy.jar;D:\program\hspjdk8\jre\lib\ ext\access-bridge-64.jar;D:\program\hspjdk8\jre\lib\ext\cldrdata.jar;D:\program\hspjdk8\jre\lib\ext\dnsns. jar;D:\program\hspjdk8\jre\lib\ext\jaccess.jar;D:\program\hspjdk8\jre\lib\ext\jfxrt.jar;D:\program\hspjdk8 \jre\lib\ext\localedata.jar;D:\program\hspjdk8\jre\lib\ext\nashorn.jar;D:\program\hspjdk8\jre\lib\ext\sune

c.jar;D:\program\hspjdk8\jre\lib\ext\sunjce_provider.jar;D:\program\hspjdk8\jre\lib\ext\sunmscapi.jar;D:\ program\hspjdk8\jre\lib\ext\sunpkcs11.jar;D:\program\hspjdk8\jre\lib\ext\zipfs.jar;D:\program\hspjdk8\jr e\lib\javaws.jar;D:\program\hspjdk8\jre\lib\jce.jar;D:\program\hspjdk8\jre\lib\jfr.jar;D:\program\hspjdk8\jr e\lib\jfxswt.jar;D:\program\hspjdk8\jre\lib\jsse.jar;D:\program\hspjdk8\jre\lib\management-agent.jar;D:\p rogram\hspjdk8\jre\lib\plugin.jar;D:\program\hspjdk8\jre\lib\resources.jar;D:\program\hspjdk8\jre\lib\rt.j ar;D:\java_projects\hsp-myspring\target\classes com.hspedu.spring.AppMain

2.代碼(解釋在代碼中)

com.campus.spring.annotation.Component(注釋)
package com.campus.spring.annotation;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 ""; // 通過value 可以給注入的bean/對象指定名字
}
com.campus.spring.annotation.ComponentScan注釋
package com.campus.spring.annotation;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() default ""; // 表示可以傳入一個 value 屬性,指定要掃描的包
}
com.campus.spring.component.MonsterDao.java
package com.campus.spring.component;import com.campus.spring.annotation.Component;@Component(value = "monsterDao")
public class MonsterDao {}
?com.campus.spring.component.MonsterService.java
package com.campus.spring.component;import com.campus.spring.annotation.Component;/***	1. 使用@Component("monsterService") 修飾*	2. 給該MonsterService 注入到容器設置beanName 為 monsterService*	3. 如果沒有設置,默認可以以類名首字母小寫來玩*/@Component(value="monsterService")
public class MonsterService {
}
com.campus.spring.ioc.SpringApplicationContext.java
package com.campus.spring.ioc;import com.campus.spring.annotation.Component;
import com.campus.spring.annotation.ComponentScan;import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;/*
SpringApplicationContext 類的作用類似于 Spring原生ioc容器*/
public class SpringApplicationContext {private Class configClass;public SpringApplicationContext(Class configClass) {this.configClass = configClass;System.out.println(this.configClass);//獲取要掃描的包//1.先得到 SpringConfig配置的@ComponentScan(value = "com.campus.spring.ComponentScan")ComponentScan componentScan = (ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);System.out.println(componentScan);//@com.campus.spring.annotation.ComponentScan("com.campus.spring.component")//2.通過componentScan的value =》即要掃描的包String path = componentScan.value();System.out.println("要掃描的包= "+path);//得到要掃描的包下面的所有資源(類.class)//1.得到類的加載器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2.通過類的加載器獲取要掃描的包的資源 url =》 類似于一個路徑path = path.replace(".", "/");System.out.println("轉換后的路徑: " + path);URL resource = classLoader.getResource(path);System.out.println("resource= "+resource);//3.將要加載的資源(.class)路徑下的文件進行遍歷String decodedPath = null;try {decodedPath = URLDecoder.decode(resource.getFile(), "UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}File file = new File(decodedPath);if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String fileAbsolutePath = f.getAbsolutePath();//這里我們只處理.class文件if (fileAbsolutePath.endsWith(".class")) {//1. 獲取到類名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));//System.out.println("className=" + className);//2. 獲取類的完整的路徑(全類名)//解讀 path.replace("/",".") =》 com.campus.spring.component.String classFullName = path.replace("/", ".") + "." + className;//3. 判斷該類是不是需要注入容器, 就看該類是不是有注解 @Component @Service..try {// aClass.isAnnotationPresent(Component.class) 判斷該類是否有 @ComponentClass<?> clazz = classLoader.loadClass(classFullName);if (clazz.isAnnotationPresent(Component.class)) {System.out.println("是一個Spring bean= "+clazz+" 類名= "+className);}else {System.out.println("不是一個Spring bean= "+clazz+" 類名= "+className);}}catch (Exception e) {e.printStackTrace();}}}}}//編寫方法返回對容器中對象public Object getBean(String name) {return null;}}
com.campus.spring.ioc.SpringConfig.java
package com.campus.spring.ioc;/*
這是一個配置類,作用類似于我們原生的spring 的 beans.xml 容器配置文件
* */import com.campus.spring.annotation.ComponentScan;@ComponentScan(value = "com.campus.spring.component")
public class SpringConfig {
}
com.campus.spring.AppMain.java
package com.campus.spring;import com.campus.spring.ioc.SpringApplicationContext;
import com.campus.spring.ioc.SpringConfig;public class AppMain {public static void main(String[] args) throws ClassNotFoundException {//創建自己的容器SpringApplicationContext SpringApplicationContext = new SpringApplicationContext(SpringConfig.class);}
}
運行結果
class com.campus.spring.ioc.SpringConfig
componentScan= @com.campus.spring.annotation.ComponentScan("com.campus.spring.component")
要掃描的包= com.campus.spring.component
轉換后的路徑: com/campus/spring/component
resource= file:/E:/%e6%a1%86%e6%9e%b6%e6%8a%80%e6%9c%af%e5%ad%a6%e4%b9%a0/myspring/target/classes/com/campus/spring/component
是一個Spring bean= class com.campus.spring.component.MonsterService 類名= MonsterService
是一個Spring bean= class com.campus.spring.component.MonsterDao 類名= MonsterDao

第二步:掃描將 bean 信息封裝到 BeanDefinition 對象, 并放入到 Map

分析:bean的作用域可能是singletion,也可能是prototype;所以需要一個注解來指定是singletion還是prototype

代碼

pom.xml需要引入一個(為了“StringUtils”的使用,因為StringUtils.uncapitalize(className
)可以讓className首字母大小寫)
 <dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency>
com.campus.spring.component.MonsterService(加了:“@Scope(value = "prototype")”)
package com.campus.spring.component;import com.campus.spring.annotation.Component;
import com.campus.spring.annotation.Scope;/***	1. 使用@Component("monsterService") 修飾*	2. 給該MonsterService 注入到容器設置beanName 為 monsterService*	3. 如果沒有設置,默認可以以類名首字母小寫來玩(底層還有很多細節)*/
@Scope(value = "prototype")
@Component(value="monsterService")
public class MonsterService {
}
com.campus.spring.ioc.SpringApplicationContext
package com.campus.spring.ioc;import com.campus.spring.annotation.Component;
import com.campus.spring.annotation.ComponentScan;
import com.campus.spring.annotation.Scope;
import org.springframework.util.StringUtils;import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.concurrent.ConcurrentHashMap;/*
SpringApplicationContext 類的作用類似于 Spring原生ioc容器*/
public class SpringApplicationContext {private Class configClass;//定義屬性BeanDefinitionMap -> 存放BeanDefinition對象private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//定義屬性SingletonObjects -> 存放單例對象private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();public SpringApplicationContext(Class configClass) {this.configClass = configClass;System.out.println(this.configClass);//獲取要掃描的包//1.先得到 SpringConfig配置的@ComponentScan(value = "com.campus.spring.ComponentScan")ComponentScan componentScan = (ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);System.out.println("componentScan= "+componentScan);//@com.campus.spring.annotation.ComponentScan("com.campus.spring.component")//2.通過componentScan的value =》即要掃描的包String path = componentScan.value();System.out.println("要掃描的包= "+path);//得到要掃描的包下面的所有資源(類.class)//1.得到類的加載器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2.通過類的加載器獲取要掃描的包的資源 url =》 類似于一個路徑path = path.replace(".", "/");System.out.println("轉換后的路徑: " + path);URL resource = classLoader.getResource(path);System.out.println("resource= "+resource);//3.將要加載的資源(.class)路徑下的文件進行遍歷String decodedPath = null;try {decodedPath = URLDecoder.decode(resource.getFile(), "UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}File file = new File(decodedPath);if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String fileAbsolutePath = f.getAbsolutePath();//這里我們只處理.class文件if (fileAbsolutePath.endsWith(".class")) {//1. 獲取到類名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));//System.out.println("className=" + className);//2. 獲取類的完整的路徑(全類名)//解讀 path.replace("/",".") =》 com.campus.spring.component.String classFullName = path.replace("/", ".") + "." + className;//3. 判斷該類是不是需要注入容器, 就看該類是不是有注解 @Component @Service..try {// aClass.isAnnotationPresent(Component.class) 判斷該類是否有 @ComponentClass<?> clazz = classLoader.loadClass(classFullName);if (clazz.isAnnotationPresent(Component.class)) {System.out.println("是一個Spring bean= "+clazz+" 類名= "+className);//先得到beanName//1. 得到Component注解Component componentAnnotation =clazz.getDeclaredAnnotation(Component.class);//2. 的配置value值String beanName = componentAnnotation.value();if ("".equals(beanName)) {//如果沒有寫value//將該類的類名首字母小寫作為beanNamebeanName = StringUtils.uncapitalize(className);}//3.將Bean的信息封裝到BeanDefinition對象->放入到BeanDefinitionMapBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClazz(clazz);//4. 獲取Scope值if (clazz.isAnnotationPresent(Scope.class)) {//如果配置了Scope, 獲取他配置的值Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);//scopeAnnotation= @com.campus.spring.annotation.Scope("prototype")//System.out.println("scopeAnnotation= "+scopeAnnotation);beanDefinition.setScope(scopeAnnotation.value());//scopeAnnotation value = prototype// System.out.println("scopeAnnotation value = "+scopeAnnotation.value());} else {//如果沒有配置Scope, 就默認的值singletonbeanDefinition.setScope("singleton");}//蔣beanDefinition 對象放入到MapbeanDefinitionMap.put(beanName, beanDefinition);}else {System.out.println("不是一個Spring bean= "+clazz+" 類名= "+className);}}catch (Exception e) {e.printStackTrace();}}}}}//編寫方法返回對容器中對象public Object getBean(String name) {return null;}}

補充:isAnnotationPresent?方法是Java反射機制中的一個重要方法,它用于檢查某個類、方法或字段是否存在指定類型的注解。如果存在,則返回?true;如果不存在,則返回?false;

getDeclaredAnnotation?方法用于獲取直接修飾在元素(類、方法、構造函數、字段)上的指定注解。它不會獲取繼承的注解

其他代碼不變
運行后:
com.campus.spring.ioc.SpringApplicationContext另外一個寫法
package com.campus.spring.ioc;import com.campus.spring.annotation.Component;
import com.campus.spring.annotation.ComponentScan;
import com.campus.spring.annotation.Scope;
import org.springframework.util.StringUtils;import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.concurrent.ConcurrentHashMap;/*
SpringApplicationContext 類的作用類似于 Spring原生ioc容器*/
public class SpringApplicationContext {private Class configClass;//定義屬性BeanDefinitionMap -> 存放BeanDefinition對象private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//定義屬性SingletonObjects -> 存放單例對象private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();public SpringApplicationContext(Class configClass) {setBeanDefinitionsScan(configClass);System.out.println("beanDefinitionMap= " + beanDefinitionMap);}public void setBeanDefinitionsScan(Class configClass){this.configClass = configClass;System.out.println(this.configClass);//獲取要掃描的包//1.先得到 SpringConfig配置的@ComponentScan(value = "com.campus.spring.ComponentScan")ComponentScan componentScan = (ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);System.out.println("componentScan= "+componentScan);//@com.campus.spring.annotation.ComponentScan("com.campus.spring.component")//2.通過componentScan的value =》即要掃描的包String path = componentScan.value();System.out.println("要掃描的包= "+path);//得到要掃描的包下面的所有資源(類.class)//1.得到類的加載器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2.通過類的加載器獲取要掃描的包的資源 url =》 類似于一個路徑path = path.replace(".", "/");System.out.println("轉換后的路徑: " + path);URL resource = classLoader.getResource(path);System.out.println("resource= "+resource);//3.將要加載的資源(.class)路徑下的文件進行遍歷String decodedPath = null;try {decodedPath = URLDecoder.decode(resource.getFile(), "UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}File file = new File(decodedPath);if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String fileAbsolutePath = f.getAbsolutePath();//這里我們只處理.class文件if (fileAbsolutePath.endsWith(".class")) {//1. 獲取到類名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));//System.out.println("className=" + className);//2. 獲取類的完整的路徑(全類名)//解讀 path.replace("/",".") =》 com.campus.spring.component.String classFullName = path.replace("/", ".") + "." + className;//3. 判斷該類是不是需要注入容器, 就看該類是不是有注解 @Component @Service..try {// aClass.isAnnotationPresent(Component.class) 判斷該類是否有 @ComponentClass<?> clazz = classLoader.loadClass(classFullName);if (clazz.isAnnotationPresent(Component.class)) {System.out.println("是一個Spring bean= "+clazz+" 類名= "+className);//先得到beanName//1. 得到Component注解Component componentAnnotation =clazz.getDeclaredAnnotation(Component.class);//2. 的配置value值String beanName = componentAnnotation.value();if ("".equals(beanName)) {//如果沒有寫value//將該類的類名首字母小寫作為beanNamebeanName = StringUtils.uncapitalize(className);}//3.將Bean的信息封裝到BeanDefinition對象->放入到BeanDefinitionMapBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClazz(clazz);//4. 獲取Scope值if (clazz.isAnnotationPresent(Scope.class)) {//如果配置了Scope, 獲取他配置的值Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);//scopeAnnotation= @com.campus.spring.annotation.Scope("prototype")//System.out.println("scopeAnnotation= "+scopeAnnotation);beanDefinition.setScope(scopeAnnotation.value());//scopeAnnotation value = prototype// System.out.println("scopeAnnotation value = "+scopeAnnotation.value());} else {//如果沒有配置Scope, 就默認的值singletonbeanDefinition.setScope("singleton");}//蔣beanDefinition 對象放入到MapbeanDefinitionMap.put(beanName, beanDefinition);}else {System.out.println("不是一個Spring bean= "+clazz+" 類名= "+className);}}catch (Exception e) {e.printStackTrace();}}}}}//編寫方法返回對容器中對象public Object getBean(String name) {return null;}}
運行結果:
class com.campus.spring.ioc.SpringConfig
componentScan= @com.campus.spring.annotation.ComponentScan("com.campus.spring.component")
要掃描的包= com.campus.spring.component
轉換后的路徑: com/campus/spring/component
resource= file:/E:/%e6%a1%86%e6%9e%b6%e6%8a%80%e6%9c%af%e5%ad%a6%e4%b9%a0/myspring/target/classes/com/campus/spring/component
是一個Spring bean= class com.campus.spring.component.MonsterService 類名= MonsterService
是一個Spring bean= class com.campus.spring.component.MonsterDao 類名= MonsterDao
beanDefinitionMap= {monsterService=BeanDefinition{scope='prototype', clazz=class com.campus.spring.component.MonsterService}, monsterDao=BeanDefinition{scope='singleton', clazz=class com.campus.spring.component.MonsterDao}}
Hello World

第三部:初始化 bean 單例池,并完成 getBean 方法 , createBean 方法

3.1.初始化bean單例池 ,createBean 方法

3.1.1.代碼

思路:

通過beanDefinitionMap ,

初始化singletonObjects 單例池

封裝成方法

遍歷所有的beanDefinition對象

//定義屬性SingletonObjects -> 存放單例對象 
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();   
private Object createBean(BeanDefinition beanDefinition) {//得到Bean的clazz對象Class clazz = beanDefinition.getClazz();try {//使用反射得到實例Object instance = clazz.getDeclaredConstructor().newInstance();return instance;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();}//如何反射創建對象失敗return null;}public SpringApplicationContext(Class configClass) {setBeanDefinitionsScan(configClass);//通過beanDefinitionMap , 初始化singletonObjects 單例池//封裝成方法//遍歷所有的beanDefinition對象//這里是java基礎->集合和枚舉Enumeration<String>keys=beanDefinitionMap.keys();while(keys.hasMoreElements()){//得到beanNameString beanName = keys.nextElement();//通過beanName 得到對應的beanDefinition對象BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//判斷該bean是singleton還是prototypeif ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//將該bean實例放入到singletonObjects 集合Object bean = createBean(beanDefinition);singletonObjects.put(beanName, bean);}System.out.println("==============================================");System.out.println("singletonObjects 單例池=" + singletonObjects);System.out.println("beanDefinitionMap=" + beanDefinitionMap);System.out.println("==============================================");}}
運行結果:

3.2.完成 getBean 方法(返回對容器中對象)

        //判斷傳入的beanName是否在beanDefinitionMap中存在..if (beanDefinitionMap.containsKey(name)) {//如果存在BeanDefinition beanDefinition = beanDefinitionMap.get(name);//得到beanDefinition的scope, 分別進行處理if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//說明是單例配置, 就直接從單例池獲取return singletonObjects.get(name);} else {//如果不是單例的,我就調用createBean, 反射一個對象return createBean(name, beanDefinition);}} else {//如果不存在//拋出一個空指針異常-自定義-Java基礎異常throw new NullPointerException("沒有該bean");}}
AppMain.java
package com.campus.spring;import com.campus.spring.component.MonsterDao;
import com.campus.spring.component.MonsterService;
import com.campus.spring.ioc.SpringApplicationContext;
import com.campus.spring.ioc.SpringConfig;public class AppMain {public static void main(String[] args) throws ClassNotFoundException {//創建自己的容器SpringApplicationContext SpringApplicationContext = new SpringApplicationContext(SpringConfig.class);MonsterService monsterService =(MonsterService)SpringApplicationContext.getBean("monsterService");MonsterService monsterService2 =(MonsterService)SpringApplicationContext.getBean("monsterService");System.out.println("monsterService=" + monsterService);System.out.println("monsterService2=" + monsterService2);MonsterDao monsterDao =(MonsterDao)SpringApplicationContext.getBean("monsterDao");MonsterDao monsterDao2 =(MonsterDao)SpringApplicationContext.getBean("monsterDao");System.out.println("monsterDao=" + monsterDao);System.out.println("monsterDao2=" + monsterDao2);}
}
運行結果
class com.campus.spring.ioc.SpringConfig
componentScan= @com.campus.spring.annotation.ComponentScan("com.campus.spring.component")
要掃描的包= com.campus.spring.component
轉換后的路徑: com/campus/spring/component
resource= file:/E:/%e6%a1%86%e6%9e%b6%e6%8a%80%e6%9c%af%e5%ad%a6%e4%b9%a0/myspring/target/classes/com/campus/spring/component
是一個Spring bean= class com.campus.spring.component.MonsterService 類名= MonsterService
是一個Spring bean= class com.campus.spring.component.MonsterDao 類名= MonsterDao
==============================================
singletonObjects 單例池={}
beanDefinitionMap={monsterService=BeanDefinition{scope='prototype', clazz=class com.campus.spring.component.MonsterService}, monsterDao=BeanDefinition{scope='singleton', clazz=class com.campus.spring.component.MonsterDao}}
==============================================
==============================================
singletonObjects 單例池={monsterDao=com.campus.spring.component.MonsterDao@27fa135a}
beanDefinitionMap={monsterService=BeanDefinition{scope='prototype', clazz=class com.campus.spring.component.MonsterService}, monsterDao=BeanDefinition{scope='singleton', clazz=class com.campus.spring.component.MonsterDao}}
==============================================
Hello World
monsterService=com.campus.spring.component.MonsterService@421faab1
monsterService2=com.campus.spring.component.MonsterService@2b71fc7e
monsterDao=com.campus.spring.component.MonsterDao@27fa135a
monsterDao2=com.campus.spring.component.MonsterDao@27fa135a

注:MonsterDao是單例,所以返回的對象一樣(MonsterDao和MonsterDao2)

第四步: 完成依賴注入

Autowired注解

package com.campus.spring.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {//boolean required() default true;
}

MonsterDao.java

package com.campus.spring.component;import com.campus.spring.annotation.Component;@Component(value = "monsterDao")
public class MonsterDao {public void hi(){System.out.println("MonsterDao-- hi()");}
}

MonsterService.java

package com.campus.spring.component;import com.campus.spring.annotation.Autowired;
import com.campus.spring.annotation.Component;
import com.campus.spring.annotation.Scope;/***	1. 使用@Component("monsterService") 修飾*	2. 給該MonsterService 注入到容器設置beanName 為 monsterService*	3. 如果沒有設置,默認可以以類名首字母小寫來玩*/
@Scope(value = "prototype")
@Component(value="monsterService")
public class MonsterService {@Autowiredprivate MonsterDao monsterDao;public void m1() {monsterDao.hi();}}

實現代碼:

    for (Field declaredField : clazz.getDeclaredFields()) {//2. 判斷這個字段是否有@Autowiredif (declaredField.isAnnotationPresent(Autowired.class)) {//提示一下//處理@Autowired 的required ,很簡單//Autowired annotation = declaredField.getAnnotation(Autowired.class)//annotation.required()=> 然后根據true, 是false 進行其它處理..//3. 得到這個字段名字String name = declaredField.getName();//4. 通過getBean方法來獲取要組裝對象Object bean = getBean(name);//5. 進行組裝declaredField.setAccessible(true);//因為屬性是pirvate, 需要暴破declaredField.set(instance, bean);}}
SpringApplicationContext.java
package com.campus.spring.ioc;import com.campus.spring.annotation.Autowired;
import com.campus.spring.annotation.Component;
import com.campus.spring.annotation.ComponentScan;
import com.campus.spring.annotation.Scope;
import org.springframework.util.StringUtils;import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;/*
SpringApplicationContext 類的作用類似于 Spring原生ioc容器*/
public class SpringApplicationContext {private Class configClass;//定義屬性BeanDefinitionMap -> 存放BeanDefinition對象private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//定義屬性SingletonObjects -> 存放單例對象private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();public SpringApplicationContext(Class configClass) {setBeanDefinitionsScan(configClass);//通過beanDefinitionMap , 初始化singletonObjects 單例池//封裝成方法//遍歷所有的beanDefinition對象//這里是java基礎->集合和枚舉Enumeration<String> keys = beanDefinitionMap.keys();while (keys.hasMoreElements()) {//得到beanNameString beanName = keys.nextElement();//通過beanName 得到對應的beanDefinition對象BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//判斷該bean是singleton還是prototypeif ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//將該bean實例放入到singletonObjects 集合Object bean = createBean(beanName, beanDefinition);singletonObjects.put(beanName, bean);}System.out.println("==============================================");System.out.println("singletonObjects 單例池=" + singletonObjects);System.out.println("beanDefinitionMap=" + beanDefinitionMap);System.out.println("==============================================");}}public void setBeanDefinitionsScan(Class configClass) {this.configClass = configClass;System.out.println(this.configClass);//獲取要掃描的包//1.先得到 SpringConfig配置的@ComponentScan(value = "com.campus.spring.ComponentScan")ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);System.out.println("componentScan= " + componentScan);//@com.campus.spring.annotation.ComponentScan("com.campus.spring.component")//2.通過componentScan的value =》即要掃描的包String path = componentScan.value();System.out.println("要掃描的包= " + path);//得到要掃描的包下面的所有資源(類.class)//1.得到類的加載器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2.通過類的加載器獲取要掃描的包的資源 url =》 類似于一個路徑path = path.replace(".", "/");System.out.println("轉換后的路徑: " + path);URL resource = classLoader.getResource(path);System.out.println("resource= " + resource);//3.將要加載的資源(.class)路徑下的文件進行遍歷String decodedPath = null;try {decodedPath = URLDecoder.decode(resource.getFile(), "UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}File file = new File(decodedPath);if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String fileAbsolutePath = f.getAbsolutePath();//這里我們只處理.class文件if (fileAbsolutePath.endsWith(".class")) {//1. 獲取到類名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));//System.out.println("className=" + className);//2. 獲取類的完整的路徑(全類名)//解讀 path.replace("/",".") =》 com.campus.spring.component.String classFullName = path.replace("/", ".") + "." + className;//3. 判斷該類是不是需要注入容器, 就看該類是不是有注解 @Component @Service..try {// aClass.isAnnotationPresent(Component.class) 判斷該類是否有 @ComponentClass<?> clazz = classLoader.loadClass(classFullName);if (clazz.isAnnotationPresent(Component.class)) {System.out.println("是一個Spring bean= " + clazz + " 類名= " + className);//先得到beanName//1. 得到Component注解Component componentAnnotation =clazz.getDeclaredAnnotation(Component.class);//2. 的配置value值String beanName = componentAnnotation.value();if ("".equals(beanName)) {//如果沒有寫value//將該類的類名首字母小寫作為beanNamebeanName = StringUtils.uncapitalize(className);}//3.將Bean的信息封裝到BeanDefinition對象->放入到BeanDefinitionMapBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClazz(clazz);//4. 獲取Scope值if (clazz.isAnnotationPresent(Scope.class)) {//如果配置了Scope, 獲取他配置的值Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);//scopeAnnotation= @com.campus.spring.annotation.Scope("prototype")//System.out.println("scopeAnnotation= "+scopeAnnotation);beanDefinition.setScope(scopeAnnotation.value());//scopeAnnotation value = prototype// System.out.println("scopeAnnotation value = "+scopeAnnotation.value());} else {//如果沒有配置Scope, 就默認的值singletonbeanDefinition.setScope("singleton");}//蔣beanDefinition 對象放入到MapbeanDefinitionMap.put(beanName, beanDefinition);} else {System.out.println("不是一個Spring bean= " + clazz + " 類名= " + className);}} catch (Exception e) {e.printStackTrace();}}}}}private Object createBean(String beanName, BeanDefinition beanDefinition) {//得到Bean的clazz對象Class clazz = beanDefinition.getClazz();try {//使用反射得到實例Object instance = clazz.getDeclaredConstructor().newInstance();for (Field declaredField : clazz.getDeclaredFields()) {//2. 判斷這個字段是否有@Autowiredif (declaredField.isAnnotationPresent(Autowired.class)) {//提示一下//處理@Autowired 的required ,很簡單//Autowired annotation = declaredField.getAnnotation(Autowired.class)//annotation.required()=> 然后根據true, 是false 進行其它處理..//3. 得到這個字段名字String name = declaredField.getName();//4. 通過getBean方法來獲取要組裝對象Object bean = getBean(name);//5. 進行組裝declaredField.setAccessible(true);//因為屬性是pirvate, 需要暴破declaredField.set(instance, bean);}}return instance;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();}//如何反射創建對象失敗return null;}//編寫方法getBean(String name),編寫方法返回對容器中對象public Object getBean(String name) {//判斷傳入的beanName是否在beanDefinitionMap中存在..if (beanDefinitionMap.containsKey(name)) {//如果存在BeanDefinition beanDefinition = beanDefinitionMap.get(name);//得到beanDefinition的scope, 分別進行處理if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//說明是單例配置, 就直接從單例池獲取return singletonObjects.get(name);} else {//如果不是單例的,我就調用createBean, 反射一個對象return createBean(name, beanDefinition);}} else {//如果不存在//拋出一個空指針異常-小伙伴也可以自定義-Java基礎異常throw new NullPointerException("沒有該bean");}}
}

AppMain.java

package com.campus.spring;import com.campus.spring.component.MonsterDao;
import com.campus.spring.component.MonsterService;
import com.campus.spring.ioc.SpringApplicationContext;
import com.campus.spring.ioc.SpringConfig;public class AppMain {public static void main(String[] args) throws ClassNotFoundException {//創建自己的容器SpringApplicationContext SpringApplicationContext = new SpringApplicationContext(SpringConfig.class);System.out.println("Hello World");//測試一下依賴注入的功能MonsterService monsterService =(MonsterService)SpringApplicationContext.getBean("monsterService");monsterService.m1();}
}

運行結果

Hello World
MonsterDao-- hi()

注:

instance?com.campus.spring.component.MonsterService@41906a77

clazz:? class com.campus.spring.component.MonsterService

declaredField(字段有@Autowired的類信息):private com.campus.spring.component.MonsterDao com.campus.spring.component.MonsterService.monsterDao

name(字段名字): monsterDao
bean(要組裝的對象): com.campus.spring.component.MonsterDao@27fa135a

第五部:bean 后置處理器實現

補充:bean 的生命周期
● 說明: bean 對象創建是由 JVM 完成的,然后執行如下方法
1. 執行構造器
2. 執行 set 相關方法
3. 調用 bean 的初始化的方法(需要配置),初始化之前可能會調用后置處理器的before方法,在初始化之后可能會調用后置處理器的after方法!!!
4. 使用 bean
5. 當容器關閉時候,調用 bean 的銷毀方法(需要配置)

注:在創建好bean實例后,判斷是否需要進行初始化(容器中常用的一個方法是:根據該類是否實現某個接口,來判斷是否需要執行某個業務邏輯,這其實就算java基礎的接口編程實際應用,也就是標記接口

1.“創建好bean實例后,判斷是否需要進行初始化”代碼演示


1.1.InitializingBean接口
package com.campus.spring.processor;/*** 1. 我們根據原生Spring 定義了一個InitializingBean* 2. 該InitializingBean接口有一個方法void afterPropertiesSet() throws Exception;* 3. afterPropertiesSet() 在Bean的 setter后執行,即就是我們原來的初始化方法* 4. 當一個Bean實現這個接口后,就實現afterPropertiesSet() , 這個方法就是初始化方法*/
public interface InitializingBean {void afterPropertiesSet() throws Exception;
}
1.2.SpringApplicationContext.java
package com.campus.spring.ioc;import com.campus.spring.annotation.Autowired;
import com.campus.spring.annotation.Component;
import com.campus.spring.annotation.ComponentScan;
import com.campus.spring.annotation.Scope;
import com.campus.spring.processor.InitializingBean;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.StringUtils;import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;/*
SpringApplicationContext 類的作用類似于 Spring原生ioc容器*/
public class SpringApplicationContext {private Class configClass;//定義屬性BeanDefinitionMap -> 存放BeanDefinition對象private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//定義屬性SingletonObjects -> 存放單例對象private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();public SpringApplicationContext(Class configClass) {setBeanDefinitionsScan(configClass);//通過beanDefinitionMap , 初始化singletonObjects 單例池//封裝成方法//遍歷所有的beanDefinition對象//這里是java基礎->集合和枚舉Enumeration<String> keys = beanDefinitionMap.keys();while (keys.hasMoreElements()) {//得到beanNameString beanName = keys.nextElement();//通過beanName 得到對應的beanDefinition對象BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//判斷該bean是singleton還是prototypeif ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//將該bean實例放入到singletonObjects 集合Object bean = createBean(beanName, beanDefinition);singletonObjects.put(beanName, bean);}System.out.println("==============================================");System.out.println("singletonObjects 單例池=" + singletonObjects);System.out.println("beanDefinitionMap=" + beanDefinitionMap);System.out.println("==============================================");}}public void setBeanDefinitionsScan(Class configClass) {this.configClass = configClass;System.out.println(this.configClass);//獲取要掃描的包//1.先得到 SpringConfig配置的@ComponentScan(value = "com.campus.spring.ComponentScan")ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);System.out.println("componentScan= " + componentScan);//@com.campus.spring.annotation.ComponentScan("com.campus.spring.component")//2.通過componentScan的value =》即要掃描的包String path = componentScan.value();System.out.println("要掃描的包= " + path);//得到要掃描的包下面的所有資源(類.class)//1.得到類的加載器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2.通過類的加載器獲取要掃描的包的資源 url =》 類似于一個路徑path = path.replace(".", "/");System.out.println("轉換后的路徑: " + path);URL resource = classLoader.getResource(path);System.out.println("resource= " + resource);//3.將要加載的資源(.class)路徑下的文件進行遍歷String decodedPath = null;try {decodedPath = URLDecoder.decode(resource.getFile(), "UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}File file = new File(decodedPath);if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String fileAbsolutePath = f.getAbsolutePath();//這里我們只處理.class文件if (fileAbsolutePath.endsWith(".class")) {//1. 獲取到類名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));//System.out.println("className=" + className);//2. 獲取類的完整的路徑(全類名)//解讀 path.replace("/",".") =》 com.campus.spring.component.String classFullName = path.replace("/", ".") + "." + className;//3. 判斷該類是不是需要注入容器, 就看該類是不是有注解 @Component @Service..try {// aClass.isAnnotationPresent(Component.class) 判斷該類是否有 @ComponentClass<?> clazz = classLoader.loadClass(classFullName);if (clazz.isAnnotationPresent(Component.class)) {System.out.println("是一個Spring bean= " + clazz + " 類名= " + className);//先得到beanName//1. 得到Component注解Component componentAnnotation =clazz.getDeclaredAnnotation(Component.class);//2. 的配置value值String beanName = componentAnnotation.value();if ("".equals(beanName)) {//如果沒有寫value//將該類的類名首字母小寫作為beanNamebeanName = StringUtils.uncapitalize(className);}//3.將Bean的信息封裝到BeanDefinition對象->放入到BeanDefinitionMapBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClazz(clazz);//4. 獲取Scope值if (clazz.isAnnotationPresent(Scope.class)) {//如果配置了Scope, 獲取他配置的值Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);//scopeAnnotation= @com.campus.spring.annotation.Scope("prototype")//System.out.println("scopeAnnotation= "+scopeAnnotation);beanDefinition.setScope(scopeAnnotation.value());//scopeAnnotation value = prototype// System.out.println("scopeAnnotation value = "+scopeAnnotation.value());} else {//如果沒有配置Scope, 就默認的值singletonbeanDefinition.setScope("singleton");}//蔣beanDefinition 對象放入到MapbeanDefinitionMap.put(beanName, beanDefinition);} else {System.out.println("不是一個Spring bean= " + clazz + " 類名= " + className);}} catch (Exception e) {e.printStackTrace();}}}}}private Object createBean(String beanName, BeanDefinition beanDefinition) {//得到Bean的clazz對象Class clazz = beanDefinition.getClazz();try {//使用反射得到實例Object instance = clazz.getDeclaredConstructor().newInstance();for (Field declaredField : clazz.getDeclaredFields()) {//2. 判斷這個字段是否有@Autowiredif (declaredField.isAnnotationPresent(Autowired.class)) {//提示一下//處理@Autowired 的required ,很簡單//Autowired annotation = declaredField.getAnnotation(Autowired.class)//annotation.required()=> 然后根據true, 是false 進行其它處理..//3. 得到這個字段名字String name = declaredField.getName();//4. 通過getBean方法來獲取要組裝對象Object bean = getBean(name);//5. 進行組裝declaredField.setAccessible(true);//因為屬性是pirvate, 需要暴破declaredField.set(instance, bean);}}System.out.println("===創建好bean實例===="+ instance);//這里判斷是否要執行Bean初始化方法//1. 判斷當前創建的Bean對象是否實現了InitializingBean//2. instanceof  表判斷某個對象的運行類型是不是 某個類型 或者 某個類型的子類型//3. 這里就使用到接口編程if (instance instanceof InitializingBean) {//3.將instance轉成InitializingBean類型try {((InitializingBean) instance).afterPropertiesSet();} catch (Exception e) {e.printStackTrace();}}return instance;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();}//如何反射創建對象失敗return null;}//編寫方法getBean(String name),編寫方法返回對容器中對象public Object getBean(String name) {//判斷傳入的beanName是否在beanDefinitionMap中存在..if (beanDefinitionMap.containsKey(name)) {//如果存在BeanDefinition beanDefinition = beanDefinitionMap.get(name);//得到beanDefinition的scope, 分別進行處理if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//說明是單例配置, 就直接從單例池獲取return singletonObjects.get(name);} else {//如果不是單例的,我就調用createBean, 反射一個對象return createBean(name, beanDefinition);}} else {//如果不存在//拋出一個空指針異常-小伙伴也可以自定義-Java基礎異常throw new NullPointerException("沒有該bean");}}
}
1.3.運行結果:
注:
MonsterDao 沒有實現了InitializingBean 接口(implements InitializingBean) ,而MonsterService實現了接口(implements InitializingBean),所以MonsterService 初始化方法被調用

2.實現后置處理器方法的調用

BeanPostProcessor接口
package com.campus.spring.processor;/*** 1. 參考原生Spring容器定義一個接口BeanPostProcessor* 2. 該接口有兩個方法postProcessBeforeInitialization 和 postProcessAfterInitialization* 3. 這兩個方法,會對Spring容器的所有Bean生效, 已經是切面編程的概念.*/
public interface BeanPostProcessor {/*** 1. postProcessBeforeInitialization在Bean的初始化方法前調用* @param bean* @param beanName* @return*/default Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}/*** 1. postProcessAfterInitialization在Bean的初始化方法后調用* @param bean* @param beanName* @return*/default Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}
}

注:接口有兩個方法postProcessBeforeInitializationpostProcessAfterInitialization,這兩個方法,會對Spring容器的所有Bean生效,

?com.campus.spring.component.myBeanPostProcessor.java(后置處理器)

package com.campus.spring.component;import com.campus.spring.annotation.Component;/*** 1. 這是我們自己的一個后置處理器* 2. 實現了BeanPostProcessor* 3. 我們可以重寫before和after方法* 4. 在Spring容器中,仍然把HspBeanPostProcessor當做一個Bean對象, 要在注入到容器* 5. @Component 標識* 6. 我們要讓HspBeanPostProcessor成為真正的后置處理器, 需要在容器中加入業務代碼* 7. 還要考慮多個后置處理器對象注入到容器問題*/
@Component
public class myBeanPostProcessor implements com.campus.spring.processor.BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("后置處理器HspBeanPostProcessor Before調用 bean類型="+ bean.getClass() + " bean的名字=" + beanName);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("后置處理器HspBeanPostProcessor After調用 bean類型="+ bean.getClass() + " bean的名字=" + beanName);return bean;}
}
SpringApplicationContext.java
package com.campus.spring.ioc;import com.campus.spring.annotation.Autowired;
import com.campus.spring.annotation.Component;
import com.campus.spring.annotation.ComponentScan;
import com.campus.spring.annotation.Scope;
import com.campus.spring.processor.BeanPostProcessor;
import com.campus.spring.processor.InitializingBean;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.StringUtils;import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;/*
SpringApplicationContext 類的作用類似于 Spring原生ioc容器*/
public class SpringApplicationContext {private Class configClass;//定義屬性BeanDefinitionMap -> 存放BeanDefinition對象private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//定義屬性SingletonObjects -> 存放單例對象private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//定義一個屬性beanPostProcessorList, => 存放后置處理器private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();public SpringApplicationContext(Class configClass) {setBeanDefinitionsScan(configClass);//通過beanDefinitionMap , 初始化singletonObjects 單例池//封裝成方法//遍歷所有的beanDefinition對象//這里是java基礎->集合和枚舉Enumeration<String> keys = beanDefinitionMap.keys();while (keys.hasMoreElements()) {//得到beanNameString beanName = keys.nextElement();//通過beanName 得到對應的beanDefinition對象BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//判斷該bean是singleton還是prototypeif ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//將該bean實例放入到singletonObjects 集合Object bean = createBean(beanName, beanDefinition);singletonObjects.put(beanName, bean);}System.out.println("==============================================");System.out.println("singletonObjects 單例池=" + singletonObjects);System.out.println("beanDefinitionMap=" + beanDefinitionMap);System.out.println("==============================================");}}public void setBeanDefinitionsScan(Class configClass) {this.configClass = configClass;System.out.println(this.configClass);//獲取要掃描的包//1.先得到 SpringConfig配置的@ComponentScan(value = "com.campus.spring.ComponentScan")ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);System.out.println("componentScan= " + componentScan);//@com.campus.spring.annotation.ComponentScan("com.campus.spring.component")//2.通過componentScan的value =》即要掃描的包String path = componentScan.value();System.out.println("要掃描的包= " + path);//得到要掃描的包下面的所有資源(類.class)//1.得到類的加載器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2.通過類的加載器獲取要掃描的包的資源 url =》 類似于一個路徑path = path.replace(".", "/");System.out.println("轉換后的路徑: " + path);URL resource = classLoader.getResource(path);System.out.println("resource= " + resource);//3.將要加載的資源(.class)路徑下的文件進行遍歷String decodedPath = null;try {decodedPath = URLDecoder.decode(resource.getFile(), "UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}File file = new File(decodedPath);if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String fileAbsolutePath = f.getAbsolutePath();//這里我們只處理.class文件if (fileAbsolutePath.endsWith(".class")) {//1. 獲取到類名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));//System.out.println("className=" + className);//2. 獲取類的完整的路徑(全類名)//解讀 path.replace("/",".") =》 com.campus.spring.component.String classFullName = path.replace("/", ".") + "." + className;//3. 判斷該類是不是需要注入容器, 就看該類是不是有注解 @Component @Service..try {// aClass.isAnnotationPresent(Component.class) 判斷該類是否有 @ComponentClass<?> clazz = classLoader.loadClass(classFullName);if (clazz.isAnnotationPresent(Component.class)) {System.out.println("是一個Spring bean= " + clazz + " 類名= " + className);//1. 為了方便,將后置處理器放入到一個ArrayList//2. 如果發現是一個后置處理器, 放入到 beanPostProcessorList//3. 在原生的Spring容器中, 對后置處理器還是走的getBean, createBean//   , 但是需要我們在singletonObjects 加入相應的業務邏輯//4. 因為這里我們是為了講解后置處理去的機制,我就簡化//判斷當前的這個clazz有沒有實現BeanPostProcessor//說明, 這里我們不能使用 instanceof 來判斷clazz是否實現了BeanPostProcessor//原因: clazz不是一個實例對象,而是一個類對象/clazz, 使用isAssignableFrom//小伙伴將其當做一個語法理解if (BeanPostProcessor.class.isAssignableFrom(clazz)) {BeanPostProcessor beanPostProcessor =(BeanPostProcessor) clazz.newInstance();//放入到beanPostProcessorListbeanPostProcessorList.add(beanPostProcessor);continue;}//先得到beanName//1. 得到Component注解Component componentAnnotation =clazz.getDeclaredAnnotation(Component.class);//2. 的配置value值String beanName = componentAnnotation.value();if ("".equals(beanName)) {//如果沒有寫value//將該類的類名首字母小寫作為beanNamebeanName = StringUtils.uncapitalize(className);}//3.將Bean的信息封裝到BeanDefinition對象->放入到BeanDefinitionMapBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClazz(clazz);//4. 獲取Scope值if (clazz.isAnnotationPresent(Scope.class)) {//如果配置了Scope, 獲取他配置的值Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);//scopeAnnotation= @com.campus.spring.annotation.Scope("prototype")//System.out.println("scopeAnnotation= "+scopeAnnotation);beanDefinition.setScope(scopeAnnotation.value());//scopeAnnotation value = prototype// System.out.println("scopeAnnotation value = "+scopeAnnotation.value());} else {//如果沒有配置Scope, 就默認的值singletonbeanDefinition.setScope("singleton");}//蔣beanDefinition 對象放入到MapbeanDefinitionMap.put(beanName, beanDefinition);} else {System.out.println("不是一個Spring bean= " + clazz + " 類名= " + className);}} catch (Exception e) {e.printStackTrace();}}}}}private Object createBean(String beanName, BeanDefinition beanDefinition) {//得到Bean的clazz對象Class clazz = beanDefinition.getClazz();try {//使用反射得到實例Object instance = clazz.getDeclaredConstructor().newInstance();for (Field declaredField : clazz.getDeclaredFields()) {//2. 判斷這個字段是否有@Autowiredif (declaredField.isAnnotationPresent(Autowired.class)) {//提示一下//處理@Autowired 的required ,很簡單//Autowired annotation = declaredField.getAnnotation(Autowired.class)//annotation.required()=> 然后根據true, 是false 進行其它處理..//3. 得到這個字段名字String name = declaredField.getName();//4. 通過getBean方法來獲取要組裝對象Object bean = getBean(name);//5. 進行組裝declaredField.setAccessible(true);//因為屬性是pirvate, 需要暴破declaredField.set(instance, bean);}}System.out.println("-------------------------------------------------------------------------------");System.out.println("===創建好bean實例===="+ instance);System.out.println("-------------------------------------------------------------------------------");//我們在Bean的初始化方法前,調用后置處理器的before方法for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在后置處理器的before方法,可以對容器的bean實例進行處理//然后返回處理后的bean實例, 相當于做一個前置處理Object current =beanPostProcessor.postProcessBeforeInitialization(instance, beanName);if (current != null) {instance = current;}}//這里判斷是否要執行Bean初始化方法//1. 判斷當前創建的Bean對象是否實現了InitializingBean//2. instanceof  表判斷某個對象的運行類型是不是 某個類型 或者 某個類型的子類型//3. 這里就使用到接口編程if (instance instanceof InitializingBean) {//3.將instance轉成InitializingBean類型try {((InitializingBean) instance).afterPropertiesSet();} catch (Exception e) {e.printStackTrace();}}//我們在Bean的初始化方法后,調用后置處理器的after方法for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在后置處理器的after方法,可以對容器的bean實例進行處理//然后返回處理后的bean實例, 相當于做一個后置處理Object current =beanPostProcessor.postProcessAfterInitialization(instance, beanName);if(current != null) {instance = current;}}return instance;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();}//如何反射創建對象失敗return null;}//編寫方法getBean(String name),編寫方法返回對容器中對象public Object getBean(String name) {//判斷傳入的beanName是否在beanDefinitionMap中存在..if (beanDefinitionMap.containsKey(name)) {//如果存在BeanDefinition beanDefinition = beanDefinitionMap.get(name);//得到beanDefinition的scope, 分別進行處理if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//說明是單例配置, 就直接從單例池獲取return singletonObjects.get(name);} else {//如果不是單例的,我就調用createBean, 反射一個對象return createBean(name, beanDefinition);}} else {//如果不存在//拋出一個空指針異常-小伙伴也可以自定義-Java基礎異常throw new NullPointerException("沒有該bean");}}
}
SpringApplicationContext新增代碼:
//定義一個屬性beanPostProcessorList, => 存放后置處理器private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();//我們在Bean的初始化方法前,調用后置處理器的before方法for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在后置處理器的before方法,可以對容器的bean實例進行處理//然后返回處理后的bean實例, 相當于做一個前置處理Object current =beanPostProcessor.postProcessBeforeInitialization(instance, beanName);if (current != null) {instance = current;}}//這里判斷是否要執行Bean初始化方法//1. 判斷當前創建的Bean對象是否實現了InitializingBean//2. instanceof  表判斷某個對象的運行類型是不是 某個類型 或者 某個類型的子類型//3. 這里就使用到接口編程if (instance instanceof InitializingBean) {//3.將instance轉成InitializingBean類型try {((InitializingBean) instance).afterPropertiesSet();} catch (Exception e) {e.printStackTrace();}}//我們在Bean的初始化方法后,調用后置處理器的after方法for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在后置處理器的after方法,可以對容器的bean實例進行處理//然后返回處理后的bean實例, 相當于做一個后置處理Object current =beanPostProcessor.postProcessAfterInitialization(instance, beanName);if(current != null) {instance = current;}}

運行結果:

第六步:實現AOP機制

com.campus.spring.annotation.Aspect注解

package com.campus.spring.annotation;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 Aspect {String value() default "";
}

com.campus.spring.processor.BeanPostProcessor接口

package com.campus.spring.processor;/*** 1. 參考原生Spring容器定義一個接口BeanPostProcessor* 2. 該接口有兩個方法postProcessBeforeInitialization 和 postProcessAfterInitialization* 3. 這兩個方法,會對Spring容器的所有Bean生效, 已經是切面編程的概念.*/
public interface BeanPostProcessor {/*** 1. postProcessBeforeInitialization在Bean的初始化方法前調用* @param bean* @param beanName* @return*/default Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}/*** 1. postProcessAfterInitialization在Bean的初始化方法后調用* @param bean* @param beanName* @return*/default Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}
}

com.campus.spring.processor.myBeanPostProcessor.java(后置處理器)

package com.campus.spring.component;import com.campus.spring.annotation.Component;
import com.campus.spring.processor.BeanPostProcessor;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** 1. 我們自己的一個后置處理器* 2. 實現了BeanPostProcessor* 3. 我們可以重寫before和after方法* 4. 在Spring容器中,仍然把HspBeanPostProcessor當做一個Bean對象, 要在注入到容器* 5. @Component 標識* 6. 我們要讓BeanPostProcessor成為真正的后置處理器, 需要在容器中加入業務代碼* 7. 還要考慮多個后置處理器對象注入到容器問題*/
@Component
public class myBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("后置處理器HspBeanPostProcessor Before調用 bean類型="+ bean.getClass() + " bean的名字=" + beanName);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("后置處理器HspBeanPostProcessor After調用 bean類型="+ bean.getClass() + " bean的名字=" + beanName);//實現AOP, 返回代理對象, 即對Bean進行包裝if ("smartDog".equals(beanName)) {//使用Jdk的動態代理,返回返回bean的代理對象,我的另外一篇AOP文章中有這個知識點Object proxyInstance = Proxy.newProxyInstance(BeanPostProcessor.class.getClassLoader(),bean.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("method=" + method.getName());Object result = null;//假如我們進行前置通知+返回通知     處理的方法是 getSum!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!if ("getSum".equals(method.getName())) {SmartAnimalAspect.showBeginLog();result = method.invoke(bean, args);//執行目標方法//進行返回通知的處理SmartAnimalAspect.showSuccessLog();} else {result = method.invoke(bean, args);//執行目標方法}return result;}});//如果bean是需要返回代理對象的, 這里就直接return proxyInstancereturn proxyInstance;}//如果不需要AOP, 返回 beanreturn bean;}
}

注:

使用Jdk的動態代理,返回返回bean的代理對象,我的另外一篇"JAVA----Spring的AOP和動態代理"文章中有這個知識點!!!

com.campus.spring.component.SmartAnimalAspect

package com.campus.spring.component;import com.campus.spring.annotation.*;/**SmartAnimalAspect當做一個切面類來使用*/public class SmartAnimalAspect {public static void showBeginLog() {System.out.println("前置通知..");}public static void showSuccessLog() {System.out.println("返回通知..");}
}

com.campus.spring.ioc.SmartAnimalable

package com.campus.spring.ioc;public interface SmartAnimalable {float getSum(float i, float j);float getSub(float i, float j);
}


com.campus.spring.component.SmartDog

package com.campus.spring.component;import com.campus.spring.annotation.Component;
import com.campus.spring.ioc.SmartAnimalable;@Component(value = "smartDog")
public class SmartDog implements SmartAnimalable {public float getSum(float i, float j) {float res = i + j;System.out.println("SmartDog-getSum-res=" + res);return res;}public float getSub(float i, float j) {float res = i - j;System.out.println("SmartDog-getSub-res=" + res);return res;}
}

SpringApplicationContext.java、

package com.campus.spring.ioc;import com.campus.spring.annotation.Autowired;
import com.campus.spring.annotation.Component;
import com.campus.spring.annotation.ComponentScan;
import com.campus.spring.annotation.Scope;
import com.campus.spring.processor.BeanPostProcessor;
import com.campus.spring.processor.InitializingBean;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.StringUtils;import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;/*
SpringApplicationContext 類的作用類似于 Spring原生ioc容器*/
public class SpringApplicationContext {private Class configClass;//定義屬性BeanDefinitionMap -> 存放BeanDefinition對象private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//定義屬性SingletonObjects -> 存放單例對象private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//定義一個屬性beanPostProcessorList, => 存放后置處理器private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();public SpringApplicationContext(Class configClass) {setBeanDefinitionsScan(configClass);//通過beanDefinitionMap , 初始化singletonObjects 單例池//封裝成方法//遍歷所有的beanDefinition對象//這里是java基礎->集合和枚舉Enumeration<String> keys = beanDefinitionMap.keys();while (keys.hasMoreElements()) {//得到beanNameString beanName = keys.nextElement();//通過beanName 得到對應的beanDefinition對象BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//判斷該bean是singleton還是prototypeif ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//將該bean實例放入到singletonObjects 集合Object bean = createBean(beanName, beanDefinition);singletonObjects.put(beanName, bean);}System.out.println("==============================================");System.out.println("singletonObjects 單例池=" + singletonObjects);System.out.println("beanDefinitionMap=" + beanDefinitionMap);System.out.println("==============================================");}}public void setBeanDefinitionsScan(Class configClass) {this.configClass = configClass;System.out.println(this.configClass);//獲取要掃描的包//1.先得到 SpringConfig配置的@ComponentScan(value = "com.campus.spring.ComponentScan")ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);System.out.println("componentScan= " + componentScan);//@com.campus.spring.annotation.ComponentScan("com.campus.spring.component")//2.通過componentScan的value =》即要掃描的包String path = componentScan.value();System.out.println("要掃描的包= " + path);//得到要掃描的包下面的所有資源(類.class)//1.得到類的加載器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2.通過類的加載器獲取要掃描的包的資源 url =》 類似于一個路徑path = path.replace(".", "/");System.out.println("轉換后的路徑: " + path);URL resource = classLoader.getResource(path);System.out.println("resource= " + resource);//3.將要加載的資源(.class)路徑下的文件進行遍歷String decodedPath = null;try {decodedPath = URLDecoder.decode(resource.getFile(), "UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}File file = new File(decodedPath);if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String fileAbsolutePath = f.getAbsolutePath();//這里我們只處理.class文件if (fileAbsolutePath.endsWith(".class")) {//1. 獲取到類名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));//System.out.println("className=" + className);//2. 獲取類的完整的路徑(全類名)//解讀 path.replace("/",".") =》 com.campus.spring.component.String classFullName = path.replace("/", ".") + "." + className;//3. 判斷該類是不是需要注入容器, 就看該類是不是有注解 @Component @Service..try {// aClass.isAnnotationPresent(Component.class) 判斷該類是否有 @ComponentClass<?> clazz = classLoader.loadClass(classFullName);if (clazz.isAnnotationPresent(Component.class)) {System.out.println("是一個Spring bean= " + clazz + " 類名= " + className);//1. 為了方便,將后置處理器放入到一個ArrayList//2. 如果發現是一個后置處理器, 放入到 beanPostProcessorList//3. 在原生的Spring容器中, 對后置處理器還是走的getBean, createBean//   , 但是需要我們在singletonObjects 加入相應的業務邏輯//4. 因為這里我們是為了講解后置處理去的機制,我就簡化//判斷當前的這個clazz有沒有實現BeanPostProcessor//說明, 這里我們不能使用 instanceof 來判斷clazz是否實現了BeanPostProcessor//原因: clazz不是一個實例對象,而是一個類對象/clazz, 使用isAssignableFromif (BeanPostProcessor.class.isAssignableFrom(clazz)) {BeanPostProcessor beanPostProcessor =(BeanPostProcessor) clazz.newInstance();//放入到beanPostProcessorListbeanPostProcessorList.add(beanPostProcessor);continue;}//先得到beanName//1. 得到Component注解Component componentAnnotation =clazz.getDeclaredAnnotation(Component.class);//2. 的配置value值String beanName = componentAnnotation.value();if ("".equals(beanName)) {//如果沒有寫value//將該類的類名首字母小寫作為beanNamebeanName = StringUtils.uncapitalize(className);}//3.將Bean的信息封裝到BeanDefinition對象->放入到BeanDefinitionMapBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClazz(clazz);//4. 獲取Scope值if (clazz.isAnnotationPresent(Scope.class)) {//如果配置了Scope, 獲取他配置的值Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);//scopeAnnotation= @com.campus.spring.annotation.Scope("prototype")//System.out.println("scopeAnnotation= "+scopeAnnotation);beanDefinition.setScope(scopeAnnotation.value());//scopeAnnotation value = prototype// System.out.println("scopeAnnotation value = "+scopeAnnotation.value());} else {//如果沒有配置Scope, 就默認的值singletonbeanDefinition.setScope("singleton");}//蔣beanDefinition 對象放入到MapbeanDefinitionMap.put(beanName, beanDefinition);} else {System.out.println("不是一個Spring bean= " + clazz + " 類名= " + className);}} catch (Exception e) {e.printStackTrace();}}}}}private Object createBean(String beanName, BeanDefinition beanDefinition) {//得到Bean的clazz對象Class clazz = beanDefinition.getClazz();try {//使用反射得到實例Object instance = clazz.getDeclaredConstructor().newInstance();for (Field declaredField : clazz.getDeclaredFields()) {//2. 判斷這個字段是否有@Autowiredif (declaredField.isAnnotationPresent(Autowired.class)) {//提示一下//處理@Autowired 的required ,很簡單//Autowired annotation = declaredField.getAnnotation(Autowired.class)//annotation.required()=> 然后根據true, 是false 進行其它處理..//3. 得到這個字段名字String name = declaredField.getName();//4. 通過getBean方法來獲取要組裝對象Object bean = getBean(name);//5. 進行組裝declaredField.setAccessible(true);//因為屬性是pirvate, 需要暴破declaredField.set(instance, bean);}}System.out.println("-------------------------------------------------------------------------------");System.out.println("===創建好bean實例===="+ instance);System.out.println("-------------------------------------------------------------------------------");//我們在Bean的初始化方法前,調用后置處理器的before方法for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在后置處理器的before方法,可以對容器的bean實例進行處理//然后返回處理后的bean實例, 相當于做一個前置處理Object current =beanPostProcessor.postProcessBeforeInitialization(instance, beanName);if (current != null) {instance = current;}}//這里判斷是否要執行Bean初始化方法//1. 判斷當前創建的Bean對象是否實現了InitializingBean//2. instanceof  表判斷某個對象的運行類型是不是 某個類型 或者 某個類型的子類型//3. 這里就使用到接口編程if (instance instanceof InitializingBean) {//3.將instance轉成InitializingBean類型try {((InitializingBean) instance).afterPropertiesSet();} catch (Exception e) {e.printStackTrace();}}//我們在Bean的初始化方法后,調用后置處理器的after方法for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {//在后置處理器的after方法,可以對容器的bean實例進行處理//然后返回處理后的bean實例, 相當于做一個后置處理Object current =beanPostProcessor.postProcessAfterInitialization(instance, beanName);if(current != null) {instance = current;}}return instance;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} //如何反射創建對象失敗return null;}//編寫方法getBean(String name),編寫方法返回對容器中對象public Object getBean(String name) {//判斷傳入的beanName是否在beanDefinitionMap中存在..if (beanDefinitionMap.containsKey(name)) {//如果存在BeanDefinition beanDefinition = beanDefinitionMap.get(name);//得到beanDefinition的scope, 分別進行處理if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {//說明是單例配置, 就直接從單例池獲取return singletonObjects.get(name);} else {//如果不是單例的,我就調用createBean, 反射一個對象return createBean(name, beanDefinition);}} else {//如果不存在//拋出一個空指針異常-我們可以自定義-Java基礎異常throw new NullPointerException("沒有該bean");}}
}

AppMain.java

package com.campus.spring;import com.campus.spring.component.MonsterDao;
import com.campus.spring.component.MonsterService;
import com.campus.spring.ioc.SmartAnimalable;
import com.campus.spring.ioc.SpringApplicationContext;
import com.campus.spring.ioc.SpringConfig;public class AppMain {public static void main(String[] args) throws ClassNotFoundException {//創建自己的容器SpringApplicationContext SpringApplicationContext = new SpringApplicationContext(SpringConfig.class);System.out.println("Hello World");//測試一下依賴注入的功能MonsterService monsterService =(MonsterService)SpringApplicationContext.getBean("monsterService");monsterService.m1();
//
//        MonsterService monsterService =
//                (MonsterService)SpringApplicationContext.getBean("monsterService");
//        MonsterService monsterService2 =
//                (MonsterService)SpringApplicationContext.getBean("monsterService");
//
//        System.out.println("monsterService=" + monsterService);
//        System.out.println("monsterService2=" + monsterService2);
//
//        MonsterDao monsterDao =
//                (MonsterDao)SpringApplicationContext.getBean("monsterDao");
//        MonsterDao monsterDao2 =
//                (MonsterDao)SpringApplicationContext.getBean("monsterDao");
//
//        System.out.println("monsterDao=" + monsterDao);
//        System.out.println("monsterDao2=" + monsterDao2);//        這里我們測試一下AOP機制是否生效了SmartAnimalable smartDog = (SmartAnimalable)SpringApplicationContext.getBean("smartDog");System.out.println("smartDog=" + smartDog.getClass());smartDog.getSum(10, 2);smartDog.getSub(10,2);System.out.println("ok");}
}

運行結果(主要代碼結果):

smartDog=class jdk.proxy2.$Proxy4
method=getSum
前置通知..
SmartDog-getSum-res=12.0
返回通知..
method=getSub
SmartDog-getSub-res=8.0
ok

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/96757.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/96757.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/96757.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

5G NR-NTN協議學習系列:NR-NTN介紹(2)

NTN網絡作為依賴衛星的通信方式&#xff0c;需要面對的通信距離&#xff0c;通信雙方的移動速度都和之前TN網絡存在巨大差異。在距離方面相比蜂窩地面網絡Terrestrial Network通信距離從最小幾百米到最大幾十km的情況&#xff0c;NTN非地面網絡的通信距離即使是近地軌道的LEO衛…

線掃相機采集圖像起始位置不正確原因總結

1、幀觸發開始時間問題 問題描述: 由于幀觸發決定了線掃相機的開始采集圖像位置,比如正確的位置是A點開始采集,結果你從B點開始觸發幀信號,這樣出來的圖像起始位置就不對 解決手段: 軟件需要記錄幀觸發時軸的位置 1)控制卡控制軸 一般使用位置比較觸發,我們可以通過監…

校園管理系統練習項目源碼-前后端分離-【node版】

今天給大家分享一個校園管理系統&#xff0c;前后端分離項目。這是最近在練習前端編程&#xff0c;結合 node 寫的一個完整的項目。 使用的技術&#xff1a; Node.js&#xff1a;版本要求16.20以上。 后端框架&#xff1a;Express框架。 數據庫&#xff1a; MySQL 8.0。 Vue2&a…

【項目】 :C++ - 仿mudou庫one thread one loop式并發服務器實現(模塊劃分)

【項目】 &#xff1a;C - 仿mudou庫one thread one loop式并發服務器實現一、HTTP 服務器與 Reactor 模型1.1、HTTP 服務器概念實現步驟難點1.2、Reactor 模型概念分類1. 單 Reactor 單線程2. 單 Reactor 多線程3. 多 Reactor 多線程目標定位總結二、功能模塊劃分2.1、SERVER …

浴室柜市占率第一,九牧重構數智衛浴新生態

作者 | 曾響鈴文 | 響鈴說2025年上半年&#xff0c;家居市場在政策的推動下展現出獨特的發展態勢。國家出臺的一系列鼓勵家居消費的政策&#xff0c;如“以舊換新”國補政策帶動超6000萬件廚衛產品煥新&#xff0c;以及我國超2.7億套房齡超20年的住宅進入改造周期&#xff0c;都…

源碼分析之Leaflet中TileLayer

概述 TileLayer 是 Layer 的子類&#xff0c;繼承自GridLayer基類&#xff0c;用于加載和顯示瓦片地圖。它提供了加載和顯示瓦片地圖的功能&#xff0c;支持自定義瓦片的 URL 格式和參數。 源碼分析 源碼實現 TileLayer的源碼實現如下&#xff1a; export var TileLayer GridL…

php學習(第二天)

一.網站基本概念-服務器 1.什么是服務器? 1.1定義 服務器&#xff08;server&#xff09;,也稱伺服器&#xff0c;是提供計算服務的設備。 供計算服務的設備” 這里的“設備”不僅指物理機器&#xff08;如一臺配有 CPU、內存、硬盤的計算機&#xff09;&#xff0c;也可以指…

C++(友元和運算符重載)

目錄 友元&#xff1a; 友元函數&#xff1a; 示例&#xff1a; 友元類&#xff1a; 示例&#xff1a; 優點&#xff1a; 注意事項&#xff1a; 運算符重載&#xff1a; 注意&#xff1a; 示例&#xff1a; 友元&#xff1a; C中如果想要外部函數或者類對一個類的pr…

和平精英風格射擊游戲開發指南

本教程將完整講解如何開發一款和平精英風格的HTML射擊游戲&#xff0c;涵蓋核心設計理念、代碼架構與關鍵實現細節。 核心設計架構 游戲機制系統 角色控制系統&#xff1a;通過鍵盤實現玩家移動戰斗系統&#xff1a;子彈發射與碰撞檢測道具系統&#xff1a;武器、彈藥和醫療包收…

21.1 《24GB顯存搞定LLaMA2-7B指令微調:QLoRA+Flash Attention2.0全流程實戰》

24GB顯存搞定LLaMA2-7B指令微調:QLoRA+Flash Attention2.0全流程實戰 實戰 LLaMA2-7B 指令微調 一、指令微調技術背景 指令微調(Instruction Tuning)是大模型訓練中的關鍵技術突破點。與傳統全量微調(Full Fine-Tuning)相比,指令微調通過特定格式的指令-響應數據訓練,…

周志華《機器學習導論》第10章 降維與度量學習

https://www.lamda.nju.edu.cn/aml24fall/slides/Chap10.pptx 目錄 1.MDS (Multiple Dimensional Scaling) 多維縮放方法 2. 主成分分析 (Principal Component Analysis, PCA) 2.1 凸優化證明 2.2 人臉識別降維應用 3. 核化PCA 4. 流行學習 4.1 LLE 局部線性嵌入&#…

Kubernetes 彈性伸縮:深入講解 HPA 和 VPA

1. 介紹 Kubernetes 提供了多種資源管理方式&#xff0c;其中 彈性伸縮&#xff08;Auto-scaling&#xff09;是最重要的特性之一。彈性伸縮可以根據應用的負載變化自動調整 Pod 的數量和資源&#xff0c;以確保在高負載下應用能夠正常運行&#xff0c;而在低負載時節省資源。在…

大數據畢業設計選題推薦-基于大數據的家庭能源消耗數據分析與可視化系統-Hadoop-Spark-數據可視化-BigData

?作者主頁&#xff1a;IT畢設夢工廠? 個人簡介&#xff1a;曾從事計算機專業培訓教學&#xff0c;擅長Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等項目實戰。接項目定制開發、代碼講解、答辯教學、文檔編寫、降重等。 ?文末獲取源碼? 精彩專欄推薦?…

【Spring】原理解析:Spring Boot 自動配置的核心機制與實戰剖析

一、引言在當今的 Java 開發領域&#xff0c;Spring Boot 憑借其快速搭建項目、簡化配置等優勢&#xff0c;成為了眾多開發者的首選框架。而 Spring Boot 自動配置作為其核心特性之一&#xff0c;極大地提升了開發效率&#xff0c;讓開發者能夠更專注于業務邏輯的實現。本文將深…

Java forEach中不能用i++的原因以及代替方案

因為在 Lambda 表達式內部訪問的外部局部變量必須是 final 或 effectively final&#xff08;事實最終變量&#xff09;&#xff0c;而 i 操作試圖改變這個變量的值&#xff0c;違反了這一規定。下面我們來詳細拆解這個問題&#xff0c;讓你徹底明白。1. 一個具體的例子我們先看…

第十四屆藍橋杯青少組C++選拔賽[2023.1.15]第二部分編程題(2 、尋寶石)

參考程序&#xff1a;#include <bits/stdc.h> using namespace std;int main() {int N;cin >> N; // 讀入盒子數vector<int> a(N);for (int i 0; i < N; i) cin >> a[i]; // 讀入每個盒子的寶石數// N > 3&#xff08;題目保證&#x…

9120 部 TMDb 高分電影數據集 | 7 列全維度指標 (評分 / 熱度 / 劇情)+API 權威源 | 電影趨勢分析 / 推薦系統 / NLP 建模用

一、引言在影視行業分析與數據科學實踐中&#xff0c;高分電影數據的深度挖掘已成為平臺優化內容推薦、制片方研判市場趨勢、影迷發現優質作品的核心支撐 —— 通過上映年份與評分的關聯可捕捉電影質量演變、依托熱度與投票數能定位爆款潛質、結合劇情概述可開展情感與主題分析…

Tomcat PUT方法任意寫文件漏洞學習

1 PUT請求 PUT請求是一種在HTTP協議中常見的請求方法 1.1 基本原理 PUT請求是一種用于向指定資源位置上傳新的實體數據的請求方法&#xff0c;與其他請求方法的區別在于&#xff0c;PUT請求用于創建或者更新只當資源位置的實體數據。它與GET請求不同&#xff0c;PUT請求會替換掉…

【C++基礎】初識模板——一起步入泛型編程的大門

引言在 C 世界里&#xff0c;模板&#xff08;Template&#xff09;就像一把萬能鑰匙。它允許你編寫通用的代碼&#xff0c;讓編譯器在需要的時候為具體類型生成對應的函數或類。換句話說&#xff0c;模板是 C 泛型編程&#xff08;Generic Programming&#xff09; 的基石。 如…

項目管理框架如何影響團隊協作

在項目執行過程中&#xff0c;項目管理框架不僅是一套工具和流程&#xff0c;更是團隊協作方式的基礎。不同的項目管理框架會深刻影響團隊溝通效率、任務分配、決策方式和整體協同效果。 傳統框架通常強調層級與計劃&#xff0c;帶來高度規范化的協作&#xff1b;敏捷框架則強調…