摘要:建议先看“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 {
@Autowired
private 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
@Autowired
private UserDao userDao;
public void m1() {
userDao.hi();
}
//这里我们需要指定init() 是初始化方法
@PostConstruct
public 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);
//测试 AOP
SmartAnimalable 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) 判断该类是否有 @Component
Class<?> 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) 判断该类是否有 @Component
Class<?> 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
//将该类的类名首字母小写作为beanName
beanName = StringUtils.uncapitalize(className);
}
//3.将Bean的信息封装到BeanDefinition对象->放入到BeanDefinitionMap
BeanDefinition 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, 就默认的值singleton
beanDefinition.setScope("singleton");
}
//蒋beanDefinition 对象放入到Map
beanDefinitionMap.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) 判断该类是否有 @Component
Class<?> 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
//将该类的类名首字母小写作为beanName
beanName = StringUtils.uncapitalize(className);
}
//3.将Bean的信息封装到BeanDefinition对象->放入到BeanDefinitionMap
BeanDefinition 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, 就默认的值singleton
beanDefinition.setScope("singleton");
}
//蒋beanDefinition 对象放入到Map
beanDefinitionMap.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()){
//得到beanName
String beanName = keys.nextElement();
//通过beanName 得到对应的beanDefinition对象
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判断该bean是singleton还是prototype
if ("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 {
@Autowired
private MonsterDao monsterDao;
public void m1() {
monsterDao.hi();
}
}
实现代码:
for (Field declaredField : clazz.getDeclaredFields()) {
//2. 判断这个字段是否有@Autowired
if (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()) {
//得到beanName
String beanName = keys.nextElement();
//通过beanName 得到对应的beanDefinition对象
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判断该bean是singleton还是prototype
if ("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) 判断该类是否有 @Component
Class<?> 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
//将该类的类名首字母小写作为beanName
beanName = StringUtils.uncapitalize(className);
}
//3.将Bean的信息封装到BeanDefinition对象->放入到BeanDefinitionMap
BeanDefinition 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, 就默认的值singleton
beanDefinition.setScope("singleton");
}
//蒋beanDefinition 对象放入到Map
beanDefinitionMap.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. 判断这个字段是否有@Autowired
if (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()) {
//得到beanName
String beanName = keys.nextElement();
//通过beanName 得到对应的beanDefinition对象
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判断该bean是singleton还是prototype
if ("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) 判断该类是否有 @Component
Class<?> 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
//将该类的类名首字母小写作为beanName
beanName = StringUtils.uncapitalize(className);
}
//3.将Bean的信息封装到BeanDefinition对象->放入到BeanDefinitionMap
BeanDefinition 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, 就默认的值singleton
beanDefinition.setScope("singleton");
}
//蒋beanDefinition 对象放入到Map
beanDefinitionMap.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. 判断这个字段是否有@Autowired
if (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 {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("后置处理器HspBeanPostProcessor Before调用 bean类型="
+ bean.getClass() + " bean的名字=" + beanName);
return bean;
}
@Override
public 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()) {
//得到beanName
String beanName = keys.nextElement();
//通过beanName 得到对应的beanDefinition对象
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判断该bean是singleton还是prototype
if ("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) 判断该类是否有 @Component
Class<?> 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();
//放入到beanPostProcessorList
beanPostProcessorList.add(beanPostProcessor);
continue;
}
//先得到beanName
//1. 得到Component注解
Component componentAnnotation =
clazz.getDeclaredAnnotation(Component.class);
//2. 的配置value值
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {//如果没有写value
//将该类的类名首字母小写作为beanName
beanName = StringUtils.uncapitalize(className);
}
//3.将Bean的信息封装到BeanDefinition对象->放入到BeanDefinitionMap
BeanDefinition 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, 就默认的值singleton
beanDefinition.setScope("singleton");
}
//蒋beanDefinition 对象放入到Map
beanDefinitionMap.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. 判断这个字段是否有@Autowired
if (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 {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("后置处理器HspBeanPostProcessor Before调用 bean类型="
+ bean.getClass() + " bean的名字=" + beanName);
return bean;
}
@Override
public 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() {
@Override
public 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 proxyInstance
return proxyInstance;
}
//如果不需要AOP, 返回 bean
return 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()) {
//得到beanName
String beanName = keys.nextElement();
//通过beanName 得到对应的beanDefinition对象
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判断该bean是singleton还是prototype
if ("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) 判断该类是否有 @Component
Class<?> 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();
//放入到beanPostProcessorList
beanPostProcessorList.add(beanPostProcessor);
continue;
}
//先得到beanName
//1. 得到Component注解
Component componentAnnotation =
clazz.getDeclaredAnnotation(Component.class);
//2. 的配置value值
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {//如果没有写value
//将该类的类名首字母小写作为beanName
beanName = StringUtils.uncapitalize(className);
}
//3.将Bean的信息封装到BeanDefinition对象->放入到BeanDefinitionMap
BeanDefinition 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, 就默认的值singleton
beanDefinition.setScope("singleton");
}
//蒋beanDefinition 对象放入到Map
beanDefinitionMap.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. 判断这个字段是否有@Autowired
if (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