前言
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】的最后一篇具体实现了任务阶段 6- AOP 机制
个人主页:尘觉主页
个人简介:大家好,我是尘觉,希望我的文章可以帮助到大家,您的满意是我的动力
在csdn获奖荣誉: csdn城市之星2名
Java全栈群星计划top前5
端午大礼包获得者
欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,感谢大家的观看
如果文章有什么需要改进的地方还请大佬不吝赐教 先在次感谢啦
// Scope 可以指定Bean的作用范围[singleton, prototype]
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
//通过value可以指定singleton,prototype
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
//通过value可以指定要扫描的包
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
//通过value可以给注入的bean/对象指定名字
String value() default "";
}
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
//这里属性,同学可以参考思路完成,还是比较简单
//boolean required() default true;
}
@Component
public class Car implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Car的初始化方法..");
}
}
@Component(value = "monsterDao")
//@Scope(value = "prototype")
public class MonsterDao implements InitializingBean {
public void hi() {
System.out.println("MonsterDao-hi()");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MonsterDao 初始化方法被调用...");
}
}
package com.wyxdu.spring.component;
import com.wyxdu.spring.annotation.Autowired;
import com.wyxdu.spring.annotation.Component;
import com.wyxdu.spring.annotation.Scope;
import com.wyxdu.spring.processor.InitializingBean;
/**
* 说明MonsterService 是一个Service
* 1. 如果指定了value,那么在注入spring容器时,以你指定为准
* 2. 如果没有指定value ,则使用类名首字母小写名字
*/
@Component//(value = "monsterService") //把MonsterService注入我们自己的spring容器中
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {
//这里我们使用自己的@Autowired来修饰属性
//表示该属性,是通过容器完成依赖注入
//说明: 我们实现按照名字来进行组装即可
@Autowired
private MonsterDao monsterDao;
public void m1() {
monsterDao.hi();
}
/**
* 1. afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
* 2 即就是初始化方法
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MonsterService 初始化方法被调用 程序员在这里加入初始化的业务..");
}
}
public interface SmartAnimalable {
float getSum(float i, float j);
float getSub(float i, float j);
}
当做一个切面类来使用
@Aspect //我们的注解
@Component //这是实现了
public class SmartAnimalAspect {
@Before(value = "execution com.wyxdu.spring.aop.aspectj.SmartDog getSum")
public static void showBeginLog() {
System.out.println("前置通知..");
}
@AfterReturning(value = "execution com.wyxdu.spring.aop.aspectj.SmartDog getSum")
public static void showSuccessLog() {
System.out.println("返回通知..");
}
}
@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;
}
}
\1. 这是我们自己的一个后置处理器
\2. 实现了BeanPostProcessor
\3. 我们可以重写before和after方法
\4. 在Spring容器中,仍然把WyxBeanPostProcessor当做一个Bean对象, 要在注入到容器
\5. @Component 标识
\6. 我们要让WyxBeanPostProcessor成为真正的后置处理器, 需要在容器中加入业务代码
\7. 还要考虑多个后置处理器对象注入到容器问题
package com.wyxdu.spring.component;
import com.wyxdu.spring.annotation.Component;
import com.wyxdu.spring.processor.BeanPostProcessor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@Component
public class WyxBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
//一定要体会到,后置处理器是会容器的创建的bean生效
//,相当于是可以对多个对象编程, 切面编程
//日志,权限,身份, 事务.......
if (bean instanceof Car) {
System.out.println("这是一个Car对象, 我可以处理");
//((Car)bean)
}
System.out.println("后置处理器WyxBeanPostProcessor Before调用 bean类型="
+ bean.getClass() + " bean的名字=" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("后置处理器WyxBeanPostProcessor After调用 bean类型="
+ bean.getClass() + " bean的名字=" + beanName);
//实现AOP, 返回代理对象, 即对Bean进行包装
//1. 先死后活-> 后面我们可以通过注解就可以更加灵活
if ("smartDog".equals(beanName)) {
//使用Jdk的动态代理,返回返回bean的代理对象
//如果没有印象的,回去看的动态代理的博客
Object proxyInstance = Proxy.newProxyInstance(WyxBeanPostProcessor.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;
}
}
用于封装/记录Bean的信息
package com.wyxdu.spring.ioc;
/**
* BeanDefinition 用于封装/记录Bean的信息[
* 1. scope
* 2 Bean对应的Class对象, 反射可以生对应的对象]
*/
public class BeanDefinition {
private String scope;
private Class clazz;
//可以根据需求,进行扩展
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "BeanDefinition{" +
"scope='" + scope + '\'' +
", clazz=" + clazz +
'}';
}
}
作用类似Spring原生ioc容器
package com.wyxdu.spring.ioc;
import com.wyxdu.spring.annotation.Autowired;
import com.wyxdu.spring.annotation.Component;
import com.wyxdu.spring.annotation.ComponentScan;
import com.wyxdu.spring.annotation.Scope;
import com.wyxdu.spring.processor.BeanPostProcessor;
import com.wyxdu.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;
/**
* WyxSpringApplicationContext 类的作用类似Spring原生ioc容器
*/
public class WyxSpringApplicationContext {
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 WyxSpringApplicationContext(Class configClass) {
//完成扫描指定包
beanDefinitionsByScan(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("singletonObjects 单例池=" + singletonObjects);
//System.out.println("beanDefinitionMap=" + beanDefinitionMap);
}
//该方法完成对指定包的扫描,并将Bean信息封装到BeanDefinition对象,在放入到Map
public void beanDefinitionsByScan(Class configClass) {
this.configClass = configClass;
//获取要扫描的包
//1. 先得到WyxSpringConfig配置的的@ComponentScan(value = "com.wyxedu.spring.component")
ComponentScan componentScan =
(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2. 通过componentScan的value=> 即要扫描的包
String path = componentScan.value();
System.out.println("要扫描的包= " + path);
//得到要扫描的包下的所有资源(类 .class)
//1.得到类的加载器->APP 类加载器
ClassLoader classLoader =
WyxSpringApplicationContext.class.getClassLoader();
//2. 通过类的加载器获取到要扫描的包的资源 url=》类似一个路径
path = path.replace(".", "/");//一定要把. 替换成 /
URL resource =
classLoader.getResource(path);
System.out.println("resource=" + resource);
//3. 将要加载的资源(.class) 路径下的文件进行遍历=>io
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());
String fileAbsolutePath = f.getAbsolutePath();
//这里我们只处理.class文件
if (fileAbsolutePath.endsWith(".class")) {
//1. 获取到类名
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//2. 获取类的完整的路径(全类名)
//解读 path.replace("/",".") => com.wyxedu.spring.component.
String classFullName = path.replace("/", ".") + "." + className;
//3. 判断该类是不是需要注入容器, 就看该类是不是有注解 @Component @Service..
try {
Class<?> clazz = classLoader.loadClass(classFullName);
if (clazz.isAnnotationPresent(Component.class)) {
//如果该类使用了@Component, 说明是Spring bean
System.out.println("是一个Spring bean =" + clazz + " 类名=" + className);
//说明
//1. 为了方便,这里将后置处理器放入到一个ArrayList
//2. 如果发现是一个后置处理器, 放入到 beanPostProcessorList
//3. 在原生的Spring容器中, 对后置处理器还是走的getBean, createBean
// , 但是需要我们在singletonObjects 加入相应的业务逻辑
//4. 因为这里我们是为了讲解后置处理去的机制,我就简化
//5. 如果,仍然走以前的逻辑,也可以,就是要麻烦一点
//判断当前的这个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);
beanDefinition.setScope(scopeAnnotation.value());
} else {
//如果没有配置Scope, 就默认的值singleton
beanDefinition.setScope("singleton");
}
//蒋beanDefinition 对象放入到Map
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果该类没有使用了@Component, 说明不是Spring bean
System.out.println("不是一个Spring bean =" + clazz + " 类名=" + className);
}
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("===============================");
}
}
}
private Object createBean(String beanName, BeanDefinition beanDefinition) {
//得到Bean的clazz对象
Class clazz = beanDefinition.getClazz();
try {
//使用反射得到实例
Object instance = clazz.getDeclaredConstructor().newInstance();
//1. 遍历当前要创建的对象的所有字段
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("=====创建好实例====" + instance);
//我们在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 java基础中讲 表判断某个对象的运行类型是不是某个类型或者
// 某个类型的子类型
//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实例, 相当于做一个后置处理
//原生Spring容器,比我们这个还要复杂
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),编写方法返回对容器中对象
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");
}
}
}
package com.wyxdu.spring.ioc;
import com.wyxdu.spring.annotation.ComponentScan;
/**
* 这是一个配置类, 作用类似我们原生spring的 beans.xml 容器配置文件
*/
@ComponentScan(value = "com.wyxdu.spring.component")
public class WyxSpringConfig {
}
参考原生Spring容器定义一个接口BeanPostProcessor
package com.wyxdu.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;
}
}
这个方法就是初始化方法
package com.wyxdu.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;
}
package com.wyxdu.spring;
import com.wyxdu.spring.component.MonsterService;
import com.wyxdu.spring.component.SmartAnimalable;
import com.wyxdu.spring.ioc.WyxSpringApplicationContext;
import com.wyxdu.spring.ioc.WyxSpringConfig;
public class AppMain {
public static void main(String[] args) {
//创建自己的容器
WyxSpringApplicationContext wyxSpringApplicationContext =
new WyxSpringApplicationContext(WyxSpringConfig.class);
//测试一下依赖注入的功能
MonsterService monsterService =
(MonsterService) wyxSpringApplicationContext.getBean("monsterService");
monsterService.m1();
//MonsterService monsterService =
// (MonsterService)wyxSpringApplicationContext.getBean("monsterService");
//MonsterService monsterService2 =
// (MonsterService)wyxSpringApplicationContext.getBean("monsterService");
//
//System.out.println("monsterService=" + monsterService);
//System.out.println("monsterService2=" + monsterService2);
//
//MonsterDao monsterDao =
// (MonsterDao)wyxSpringApplicationContext.getBean("monsterDao");
//MonsterDao monsterDao2 =
// (MonsterDao)wyxSpringApplicationContext.getBean("monsterDao");
//
//System.out.println("monsterDao=" + monsterDao);
//System.out.println("monsterDao2=" + monsterDao2);
//这里我们测试一下AOP机制是否生效了
SmartAnimalable smartDog = (SmartAnimalable) wyxSpringApplicationContext.getBean("smartDog");
//System.out.println("smartDog=" + smartDog.getClass());
smartDog.getSum(10, 2);
smartDog.getSub(10,2);
System.out.println("ok");
}
}
前面我们使用的硬编码,不灵活, 但是已经把 AOP 核心机制说清楚了
到此我们已经全部完成了
Spring的初始化 -IOC容器±依赖注入-+BeanPostProcessor 机制-+AOP实现
.
.
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】系列
手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【1】
手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【2】
手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器,实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】实现任务阶段 3- 初始化 bean 单例池 和 实现任务阶段 4- 完成依赖注入
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】之实现任务阶段 5- bean 后置处理器
.
.
热门专栏推荐
想学习vue的可以看看这个
java基础合集
数据库合集
redis合集
nginx合集
linux合集
等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持
欢迎大家加入我的社区 尘觉社区
文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力