摘要:建议先看“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;敏捷框架则强调…