阶段5:
// 1.编写自己的Spring容器,实现扫描包,得到bean的class对象.
// 2.扫描将 bean 信息封装到 BeanDefinition对象,并放入到Map.
// 3.初始化单例池并完成getBean() createBean()方法
// 4.完成依赖注入(如果创建某个Bean对象,存在依赖注入,需要进行bean组装操作)
// 5.bean后置处理器实现,如果存在BeanPostProcessor扩展机制,就进行处理.
// 先完成原生Spring使用Bean后置处理器案例
// 再实现自己的bean的后置处理器
6.实现AOP机制
思路:
先完成原生的Spring使用AOP机制,明白Aop机制需要:
后置处理器机制+动态代理机制
具体实现
1.在component包下写
(1)martAnimalable接口
package com.elf.spring.component;
/**
* @author 45
* @version 1.0
*/
public interface SmartAnimalable {
float getSum(float i, float j);
float getSub(float i, float j);
}
(2)SmartDog类
package com.elf.spring.component;
import com.elf.spring.annotation.Component;
/**
* @author 45
* @version 1.0
*/
@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;
}
}
(3) 切面类
package com.elf.spring.component;
import com.elf.spring.annotation.AfterReturning;
import com.elf.spring.annotation.Aspect;
import com.elf.spring.annotation.Before;
import com.elf.spring.annotation.Component;
/**
* @author 45
* @version 1.0
* 说明:SmartAnimalAspect当做一个切面类来使用
* ,后面再分析如何做的更加灵活
*/
@Aspect //我们的注解
@Component //这是实现了
public class SmartAnimalAspect {
@Before(value = "execution com.elf.spring.aop.aspectj.SmartDog getSum")
public static void showBeginLog() {
System.out.println("前置通知..");
}
@AfterReturning(value = "execution com.elf.spring.aop.aspectj.SmartDog getSum")
public static void showSuccessLog() {
System.out.println("返回通知..");
}
}
2.annotation包下
<1> Aspect注解
package com.elf.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 45
* @version 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Aspect {
String value() default "";
}
<2>Before注解
package com.elf.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 45
* @version 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Before {
String value();
String argNames() default "";
}
<3>After注解
package com.elf.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 45
* @version 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface After {
String value();
String argNames() default "";
}
<4>AfterReturning注解
package com.elf.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 45
* @version 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AfterReturning {
String value() default "";
String pointcut() default "";
String returning() default "";
String argNames() default "";
}
3.Spring包下写测试类 ElfTest
package com.elf.spring;
import com.elf.spring.annotation.AfterReturning;
import com.elf.spring.annotation.Before;
import com.elf.spring.component.SmartAnimalAspect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author 45
* @version 1.0
*/
public class ElfTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//1. 获取SmartAnimalAspect的class对象
Class<SmartAnimalAspect> smartAnimalAspectClass = SmartAnimalAspect.class;
//2. 遍历该类的所有方法(因为如果要拿到切面类的切入方法的名称,以及注解肯定要一个一个遍历)
for (Method declaredMethod : smartAnimalAspectClass.getDeclaredMethods()) {
//如果切面类的方法有Before注解
if (declaredMethod.isAnnotationPresent(Before.class)) {
//得到切面类的切入方法名
System.out.println("m:= " + declaredMethod.getName());
//得到Before(value="xxxx")
//得到Before注解
Before annotation = declaredMethod.getAnnotation(Before.class);
//得到Before注解的value
System.out.println("value:= " + annotation.value());
//得到切入要执行的方法.[反射基础]
Method declaredMethod1 = smartAnimalAspectClass.getDeclaredMethod(declaredMethod.getName());
//调用切入方法[通过反射调用]
declaredMethod1.invoke(smartAnimalAspectClass.newInstance(), null);
} else if (declaredMethod.isAnnotationPresent(AfterReturning.class)) {
//如果发现切面类有AfterReturning注解,同样可以进行处理..
System.out.println("m:= " + declaredMethod.getName());
AfterReturning annotation = declaredMethod.getAnnotation(AfterReturning.class);
System.out.println("value:= " + annotation.value());
//得到切入要执行的方法.
Method declaredMethod1 = smartAnimalAspectClass.getDeclaredMethod(declaredMethod.getName());
//调用切入方法[反射调用]
declaredMethod1.invoke(smartAnimalAspectClass.newInstance(), null);
}
}
}
}
4.容器文件
package com.elf.spring.ioc;
import com.elf.spring.annotation.*;
import com.elf.spring.processor.BeanPostProcessor;
import com.elf.spring.processor.InitializingBean;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author 45~
* @version 1.0
*/
public class ElfSpringApplicationContext {
//第一步,扫描包,得到bean的class对象,排除包下不是bean的,因此还没有放到容器中
//因为现在写的spring容器比原先的基于注解的,要更加完善,所以不会直接把它放在ConcurrentHashMap
private Class configClass;
//定义属性BeanDefinitionMap -> 存放BeanDefinition对象
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
new ConcurrentHashMap<>();
//定义属性SingletonObjects -> 存放单例对象 (跟原生容器的名字保持一致)
//因为将来存放单例池的时候肯定要指定单例对象是对应哪个Bean的,所以k用String来充当
//存放单例对象的类型是不确定的,可能是Dog,Cat,或者其他的对象,所以用Object
private ConcurrentHashMap<String, Object> singletonObjects =
new ConcurrentHashMap<>();
//定义一个属性ArrayList,存放后置处理器[]
private List<BeanPostProcessor> beanPostProcessorList =
new ArrayList<>();
//构造器
public ElfSpringApplicationContext(Class configClass) {
//完成扫描指定包
beanDefinitionsByScan(configClass);
//通过beanDefinitionMap,初始化singletonObjects单例池
//封装成方法
//遍历所有的beanDefinition,用到集合和枚举的知识
Enumeration<String> keys = beanDefinitionMap.keys();//把所有bean的名字拿到
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("singletonObjects 单例池=" + singletonObjects);
// System.out.println("beanDefinitionMap=" + beanDefinitionMap);
}//构造器结束
//该方法完成对指定包的扫描,并将Bean信息封装到BeanDefinition对象,在放入到Map
public void beanDefinitionsByScan(Class configClass) {
this.configClass = configClass;
/**获取要扫描的包:
1.先得到ElfSpringConfig配置的 @ComponentScan(value= "com.elf.spring.component")
2.通过 @ComponentScan的value => 即要扫描的包 **/
ComponentScan componentScan =
(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value();
// System.out.println("要扫描的包path=" + path);
/**
* 得到要扫描包下的所有资源(类.class)
* 1.得到类的加载器 -> APP类加载器是可以拿到 target目录下的classes所有文件的
* 2.通过类的加载器获取到要扫描的包的资源url => 类似一个路径
* 3.将要加载的资源(.class)路径下的文件进行遍历 => io
*/
ClassLoader classLoader = ElfSpringApplicationContext.class.getClassLoader();
path = path.replace(".", "/"); // 把.替换成 /
URL resource = classLoader.getResource(path);
// System.out.println("resource=" + resource);
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) { //把所有的文件都取出来
// System.out.println("============================");
// System.out.println("f.getAbsolutePath()=" + f.getAbsolutePath());
String fileAbsolutePath = f.getAbsolutePath();//到target的classes目录下了
//这里只处理.class文件
if (fileAbsolutePath.endsWith(".class")) {
//1.获取类名
String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
fileAbsolutePath.indexOf(".class"));
//2.获取类的完整路径(全类名)
String classFullName = path.replace("/", ".") + "." + className;
//System.out.println("classFullName=" + classFullName);
//3.判断该类是否需要注入,就看是不是有注解@Component @Service @Contoller @Re....
try {
Class<?> cla = classLoader.loadClass(classFullName);
if (cla.isAnnotationPresent(Component.class) ||
cla.isAnnotationPresent(Controller.class) ||
cla.isAnnotationPresent(Service.class) ||
cla.isAnnotationPresent(Repository.class)) {
//演示机制
//如果该类使用了@Component注解,说明是一个Spring bean
System.out.println("这是一个Spring bean=" + cla + " 类名=" + className);
//1.真实的还是走下面是代码,为了方便,这里将后置处理器放到一个ArrayList
//2.如果发现是一个后置处理器,就将其放入到beanPostProcessorList
//3. 在原生的Spring容器中, 对后置处理器还是走的getBean, createBean
// , 但是需要我们在singletonObjects 加入相应的业务逻辑
//4. 因为这里我们是为了讲解后置处理去的机制,我就简化
//5. 如果小伙伴们,仍然走以前的逻辑,也可以,就是要麻烦一点
//判断当前的这个cla有没有实现BeanPostProcessor
//说明, 这里我们不能使用 instanceof 来判断cla是否实现了BeanPostProcessor
//原因: cla不是一个实例对象,而是一个类对象/cla, 使用isAssignableFrom
//小伙伴将其当做一个语法理解
if (BeanPostProcessor.class.isAssignableFrom(cla)) {
BeanPostProcessor beanPostProcessor =
(BeanPostProcessor) cla.newInstance();
//放入到beanPostProcessorList
beanPostProcessorList.add(beanPostProcessor);
continue;//把后置处理器放好以后就不在往下走,让它去调用create方法
}
//先得到beanName(有可能通过经典4注解,例如Component注解的value值来指定)
//1.得到类上的Component注解,此时的clazz已经是当前bean的class对象,通过类加载器得到的 反射知识
Component componentAnnotation = cla.getDeclaredAnnotation(Component.class);
//2.得到配置的value
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {//如果没有写value,空串
//将该类的类名首字母小写作为beanName
//StringUtils其实是在springframework的框架下面的类,而这里本身我就是要自己写所以不用
beanName = StringUtils.uncapitalize(className);
}
//3.将Bean的信息封装到BeanDefinition对象中,然后将其放入到BeanDefinitionMap集合中
BeanDefinition beanDefinition = new BeanDefinition();
//!!!多看看这里多理解
beanDefinition.setClazz(cla);//由类加载器通过反射得到对象,Class<?> cla = classLoader.loadClass(classFullName);
//4.获取Scope值
if (cla.isAnnotationPresent(Scope.class)) {
//如果配置了Scope,就获取它配置的值
Scope scopeAnnotation = cla.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else {
//如果没有配置Scope,就以默认的值singleton
beanDefinition.setScope("singleton");
}
//将beanDefinitionMap对象放入Map
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果该类没有使用了@Component注解,说明是一个Spring bean
System.out.println("这不是一个Spring bean" + cla + " 类名=" + className);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
}//遍历文件结束
}
}
//完成createBean(BeanDefinition beanDefinition)方法
private Object createBean(String beanName,BeanDefinition beanDefinition) {
//得到Bean的Clazz对象
Class clazz = beanDefinition.getClazz();
try {
//使用反射得到实例
Object instance = clazz.getDeclaredConstructor().newInstance();
//这里加入依赖注入的业务逻辑!!!
//1.遍历当前要创建的对象的所有字段( 比如:要创建MonsterService,需要把所有的字段都扫描一遍,看看字段上有没有@Autowired的注解)
for(Field declaredField : clazz.getDeclaredFields()){
//2.判断这个字段是否有@Autwired的修饰
if(declaredField.isAnnotationPresent(Autowired.class)){
//提示:这里处理@Autowired的required
// Autowired annotation = declaredField.getAnnotation(Autowired.class);
// annotation.required()=>进行下一步其他处理
//3.得到这个字段的名字
String name = declaredField.getName();
//4.通过getBean()方法来获取要组装的对象
Object bean = getBean(name);
//5.进行组装
declaredField.setAccessible(true);//因为反射里属性是私有的,须进行暴破.
declaredField.set(instance,bean);
}
}
System.out.println("========创建好实例=========" + instance);
//我们在bean的初始化方法前,调用后置处理器的before方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在后置处理器的before方法,可以对容器生成的bean实例进行处理
//然后返回处理后的bean实例,相当于做了一个前置处理
Object current = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
if(current != null){//判断后置处理器的before方法,返回来的对象是不是一个空,
//如果不是空,再去改变
//如果是一个空,就不用去该说明还是以前的instance
//原生SPringl容器比这个复杂
instance = current;
}
}
//这里判断是否要执行Bean的初始化方法
//1.判断当前创建的Bean对象是否实现了InitializingBean接口
//2.instanceof是比较操作符,基础 PPT363,
// 表示判断某个对象的运行类型,是不是某个类型或者某个类型的子类型
//如果某个对象instance 实现了这个接口InitializingBean,那它可以认为是这个接口的子类型,那就是true
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;
}
}
System.out.println("-------------------------------------------");
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)方法,返回容器中的对象.
// 既然是getBean()那么返回类型是Object,因为不管是单例池还是创建的对象也好,类型是不确定的
public Object getBean(String name) {
//加一个判断,严谨. 传入的beanName是否在beanDefinitonMap中存在..
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 { //如果不存在,就抛出一个空指针异常,也可自定义
throw new NullPointerException("没有该bean");
}
}
}
运行结果
PPT89%的部分有扩展思考…
自我梳理: Spring容器是怎么工作的?
== 1.首先去扫描bean.xml文件或者配置文件(通过注解来获取),以拿到Conponent包.
2.拿到过后先进行扫描,得到bean里的class对象,因为这个包下的有些bean是不需要进行注入的,所以需要排除不是bean的class.
3.将扫描到的bean信息先封装到definition对象去,再放入到Map中,放好以后在这个过程中,还要完成很多任务:
eg: 初始化单例池,也就是说如果bean是单例的,就实例化并放入到单例池中,供你使用.但若为多例,最终是把bean的信息放在了beanDefinitionMap中(其里面放到是key-value,k指beanName,value指BeanDefinition对象[对象里最核有2点: class对象, scope表明它是单例还是多例],如果是多实例的对动态地返回对象,如果是单例的直接从单例池中返回即可)
4.要获取的时候执行getBean()方法:思路是首先根据传入的名字到BeanDefinitionMap里面去拿BeanDefinition对象,拿到后看是一个单例的还是一个多例的,如果是单例的直接从单例池获取,如果是protorype,通过到BeanDefinitionMap中得到Bean的class对象,使用反射,创建bean对象返回.这就是ioc的一个机制.
5.Spring的扩展功能:
(1)依赖注入,就是在createBean的时候判断是否需要依赖注入,通过@Autowired来进行匹配,还是基于BeanDefinitionMap来处理的,进行装配.
(2)Bean的后置处理机制BeanPostProcessor
(3)AOP的机制是基于Bean的后置处理机制和动态代理的,因为是在后置处理器的after方法里面通过一个处理返回了一个动态代理对象交给使用者,使用者通过调用这个方法,比如getSum()实际上是走的动态代理对象的invoke()方法来实现这样一个动态调用的,在这个过程可以进行前置通知,返回通知,异常通知,最终通知来进行切入.==