摘要:建議先看“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 關系
- AOP 實現 Spring 可以通過給一個類,加入注解 @EnableAspectJAutoProxy ?來指定, 比如?
package com.campus.spring.aop;import org.springframework.context.annotation.EnableAspectJAutoProxy;@EnableAspectJAutoProxy
public class Test {
}
- 我們來追一下@EnableAspectJAutoProxy
- 看一下 AnnotationAwareAspectJAutoProxyCreator 的類圖
- AOP 底層是基于 BeanPostProcessor 機制的.
- 即在 Bean 創建好后,根據是否需要 AOP 處理,決定返回代理對象,還是原生 Bean
- 在返回代理對象時,就可以根據要代理的類和方法來返回
- 其實這個機制并不難,本質就是在 BeanPostProcessor 機制 + 動態代理技術
- 下面我們就準備來實現 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;}
}
注:接口有兩個方法postProcessBeforeInitialization 和 postProcessAfterInitialization,這兩個方法,會對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