当前位置: 首页 > news >正文

实现AOP机制 + Spring总结

文章目录

    • 1.目录
    • 2.SmartAnimal.java 接口(JDK代理必须有接口)
    • 3.SmartDog.java
    • 4.SmartAnimalAspect.java
    • 5.SunSpringApplicationContext.java
        • 1.在Bean的后置处理器之后使用动态代理
        • 2.完整代码
    • 6.测试
        • 1.AppMain.java 调用被代理的类的方法
        • 2.结果
    • 7.Spring底层机制总结
          • 1.扫描包,存储Bean定义信息,初始化单例池,依赖注入
          • 2.后置处理器的Before方法对Bean进行处理
          • 3.实现InitializingBean接口或者有@PostConstruct注解的初始化方法被调用
          • 4.后置处理器的After方法对Bean进行处理
          • 5.如果发现某个类被AOP了,则在后置处理器之后就会返回代理对象,当目标对象使用代理对象调用方法时,就可以得到目标对象和调用的方法的信息

1.目录

CleanShot 2024-08-06 at 14.19.09@2x

2.SmartAnimal.java 接口(JDK代理必须有接口)

package com.sunxiansheng.sunspring.compent;/*** Description: SmartAnimal* @Author sun* @Create 2024/8/4 14:51* @Version 1.0*/
public interface SmartAnimal {public float getSum(float a, float b);public float getSub(float a, float b);}

3.SmartDog.java

package com.sunxiansheng.sunspring.compent;import com.sunxiansheng.sunspring.annotation.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** Description: SmartDog* @Author sun* @Create 2024/8/4 14:51* @Version 1.0*/
@Component // 交给Spring容器管理
public class SmartDog implements SmartAnimal {private static final Logger log = LoggerFactory.getLogger(SmartDog.class);@Overridepublic float getSum(float a, float b) {log.info("SmartDog...getSum...res=" + (a + b));return a + b;}@Overridepublic float getSub(float a, float b) {log.info("SmartDog...getSub...res=" + (a - b));return a - b;}}

4.SmartAnimalAspect.java

package com.sunxiansheng.sunspring.compent;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** Description: 切面类* @Author sun* @Create 2024/8/6 13:50* @Version 1.0*/
public class SmartAnimalAspect {private static final Logger log = LoggerFactory.getLogger(SmartAnimalAspect.class);public static void showBeginLog() {log.info("AOP:前置通知-方法执行开始");}public static void showEndLog() {log.info("AOP:后置通知-方法执行结束");}}

5.SunSpringApplicationContext.java

1.在Bean的后置处理器之后使用动态代理

CleanShot 2024-08-06 at 14.21.38@2x

2.完整代码
package com.sunxiansheng.sunspring.ioc;import com.sunxiansheng.sunspring.annotation.*;
import com.sunxiansheng.sunspring.annotation.myenum.MyScope;
import com.sunxiansheng.sunspring.compent.SmartAnimalAspect;
import com.sunxiansheng.sunspring.processor.BeanPostProcessor;
import com.sunxiansheng.sunspring.processor.InitializingBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;/*** Description: 自定义Spring容器* @Author sun* @Create 2024/8/4 16:35* @Version 1.0*/
public class SunSpringApplicationContext {private static final Logger log = LoggerFactory.getLogger(SunSpringApplicationContext.class);/*** bean定义的map*/private ConcurrentHashMap<String, BeanDefintion> beanDefintionMap = new ConcurrentHashMap<>();/*** 单例池*/private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();/*** bean的后置处理器*/private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();// 构造器,接收配置类的class对象public SunSpringApplicationContext(Class<?> configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {// 完成bean的扫描,将bean的信息记录到beanDefintionMap中beanDefinitionByScan(configClass);// 初始化单例池initSingletonObjects();}/*** 给某个bean对象完成依赖注入*/private void populateBeans(Object bean) {// 扫描beanDefintionMap中的bean信息,对bean对象中的属性进行依赖注入// 获取Class对象Class<?> clazz = bean.getClass();// 获取所有字段Field[] fields = clazz.getDeclaredFields();// 判断字段上是否有@Resource注解for (Field field : fields) {if (field.isAnnotationPresent(Resource.class)) {// 获取字段名String fieldName = field.getName();// 根据字段名获取bean对象Object beanObject = null;// 从beanDefintionMap中获取bean对象BeanDefintion beanDefintion = beanDefintionMap.get(fieldName);try {// 根据bean的定义信息创建bean对象beanObject = getBean(fieldName);} catch (Exception e) {throw new RuntimeException(e);}// 设置字段可访问field.setAccessible(true);try {// 依赖注入field.set(bean, beanObject);log.info("依赖注入成功:{} => {}.{}", beanObject.getClass(), clazz, fieldName);} catch (IllegalAccessException e) {e.printStackTrace();}}}}/*** 初始化单例池*/private void initSingletonObjects() {// 将beanDefintionMap中的bean信息创建成bean对象放到单例池中beanDefintionMap.forEach((beanName, beanDefintion) -> {try {// 根据bean的定义信息创建bean对象Object bean = createBean(beanDefintion);if (bean != null) {// 将bean对象放到单例池中singletonObjects.put(beanName, bean);}} catch (Exception e) {e.printStackTrace();}});// 打印单例池中的bean对象log.info("根据bean定义信息初始化单例池:{}", singletonObjects);}// 返回容器中的对象public Object getBean(String name) throws Exception {BeanDefintion beanDefintion = beanDefintionMap.get(name);if (beanDefintion == null) {throw new NullPointerException("在bean定义中没有找到bean对象");}// 根据单例和多例来获取bean对象MyScope scope = beanDefintion.getScope();Object bean = null;if (scope == MyScope.SINGLETON) {// 单例就直接从单例池中获取对象bean = singletonObjects.get(name);} else {// 多例就创建一个新的对象bean = createProtoTypeBean(beanDefintion);}// 给bean对象完成依赖注入populateBeans(bean);// 记录当前对象,因为后置处理器可能会返回一个新的对象Object current = bean;// 初始化方法之前调用后置处理器 postProcessBeforeInitializationfor (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {Object bean1 = beanPostProcessor.postProcessBeforeInitialization(bean, name);// 如果beanPostProcessor返回的对象为空,则使用原来的对象if (bean1 != null) {current = bean1;}}// 初始化beaninit(current);// 初始化方法之后调用后置处理器 postProcessAfterInitializationfor (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {bean = beanPostProcessor.postProcessAfterInitialization(current, name);// 如果beanPostProcessor返回的对象为空,则使用原来的对象if (bean == null) {bean = current;}}// 这里直接写死,如果是SmartDog类,则使用动态代理if (name.equals("smartDog")) {final Object proxyObject = current;// 返回代理对象/*** 参数1:被代理对象的类加载器,参数2:被代理对象的接口,参数3:代理对象*/bean = Proxy.newProxyInstance(current.getClass().getClassLoader(), current.getClass().getInterfaces(), (proxy, method, args) -> {// 前置通知SmartAnimalAspect.showBeginLog();// 调用目标方法Object invoke = method.invoke(proxyObject, args);// 后置通知SmartAnimalAspect.showEndLog();return invoke;});log.info("SmartDog返回代理对象:{}", bean.getClass());return bean;}log.info("getBean:{}", bean.getClass());return bean;}/*** 初始化bean* @param bean*/public void init(Object bean) {if (bean instanceof InitializingBean) {((InitializingBean) bean).afterPropertiesSet();}}/*** 根据bean的定义信息创建bean对象(单例bean)* @param beanDefintion* @return* @throws Exception*/private Object createBean(BeanDefintion beanDefintion) throws Exception {// 得到bean的类型Class<?> clazz = beanDefintion.getClazz();// 根据bean的作用域创建bean对象,多例就不创建了,单例就创建if (beanDefintion.getScope() == MyScope.PROTOTYPE) {return null;}Object bean = clazz.getDeclaredConstructor().newInstance();return bean;}/*** 创建多例bean* @param beanDefintion* @return* @throws InstantiationException* @throws IllegalAccessException* @throws InvocationTargetException* @throws NoSuchMethodException*/private static Object createProtoTypeBean(BeanDefintion beanDefintion) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {// 多例就创建一个新的对象Class<?> clazz = beanDefintion.getClazz();Object bean = clazz.getDeclaredConstructor().newInstance();return bean;}/*** 完成bean的扫描,将bean的信息记录到beanDefintionMap中* @param configClass* @throws ClassNotFoundException*/private void beanDefinitionByScan(Class<?> configClass) {// 传进来一个配置类的Class对象// 一、获取要扫描的包// 1.首先反射获取类的注解信息ComponentScan componentScan = configClass.getDeclaredAnnotation(ComponentScan.class);// 2.通过注解来获取要扫描的包的路径String path = componentScan.packagePath();log.info("扫描的包路径:{}", path);// 二、得到要扫描包的.class文件对象,从而得到全路径进行反射// 1.获取App类加载器ClassLoader classLoader = SunSpringApplicationContext.class.getClassLoader();// 2.获取要扫描包的真实路径,默认刚开始在根目录下path = path.replace(".", "/");URL resource = classLoader.getResource(path);// 3.由该路径创建一个文件对象,可使用resource.getFile()将URL类型转化为String类型File file = new File(resource.getFile());// 4.遍历该文件夹下的所有.class文件对象if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {// 反射注入容器// 1.获取所有文件的绝对路径String absolutePath = f.getAbsolutePath();// 只处理class文件if (absolutePath.endsWith(".class")) {// 2.分割出类名String className = extractClassName(absolutePath);// 3.得到全路径String fullPath = path.replace("/", ".") + "." + className;// 4.判断是否需要注入容器,查看有没有自定义的注解ComponentClass<?> aClass = null;try {aClass = classLoader.loadClass(fullPath);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}// 如果该类使用了注解Component则说明是一个spring beanif (aClass.isAnnotationPresent(Component.class)) {log.info("扫描到Spring Bean:{}", aClass);// 将Bean的后置处理器加入到beanPostProcessorList中// 判断Class对象是否实现了BeanPostProcessor接口if (BeanPostProcessor.class.isAssignableFrom(aClass)) {Object o = null;try {o = aClass.getDeclaredConstructor().newInstance();} catch (Exception e) {log.info("BeanPostProcessor实例化失败:{}", e);}if (o instanceof BeanPostProcessor) {beanPostProcessorList.add((BeanPostProcessor) o);}log.info("BeanPostProcessor实例化成功:{}", o);// 直接跳过,不需要将BeanPostProcessor加入到beanDefintionMap中continue;}// 将bean的信息记录到beanDefintionMap中BeanDefintion beanDefintion = new BeanDefintion();// 1.获取Scope注解的value值if (aClass.isAnnotationPresent(Scope.class)) {Scope scope = aClass.getDeclaredAnnotation(Scope.class);MyScope value = scope.value();// 放到beanDefintion中beanDefintion.setScope(value);} else {// 如果没有指定作用域,则默认为单例beanDefintion.setScope(MyScope.SINGLETON);}beanDefintion.setClazz(aClass);// 2.获取Component注解的value值Component component = aClass.getDeclaredAnnotation(Component.class);String beanName = component.value();if ("".equals(beanName)) {// 如果没有指定value属性,则使用类名首字母小写作为bean的idbeanName = className.substring(0, 1).toLowerCase() + className.substring(1);}// 3.将bean的id和bean的信息放到beanDefintionMap中beanDefintionMap.put(beanName, beanDefintion);} else {log.info("这不是一个Spring Bean={}", aClass);}}}}// 打印beanDefintionMap中的bean信息log.info("将bean定义信息放到beanDefintionMap:{}", beanDefintionMap);}/*** 分割出类名* 类似于 com/sunxiansheng/sunspring/compent/MonsterService.class 的类名* @param filePath* @return*/private String extractClassName(String filePath) {// 获取最后一个 '/' 的位置int lastSlashIndex = filePath.lastIndexOf('/');// 获取最后一个 '.' 的位置int lastDotIndex = filePath.lastIndexOf('.');// 提取两者之间的字符串作为类名return filePath.substring(lastSlashIndex + 1, lastDotIndex);}}

6.测试

1.AppMain.java 调用被代理的类的方法

CleanShot 2024-08-06 at 14.22.58@2x

2.结果

CleanShot 2024-08-06 at 14.23.22@2x

7.Spring底层机制总结

1.扫描包,存储Bean定义信息,初始化单例池,依赖注入
2.后置处理器的Before方法对Bean进行处理
3.实现InitializingBean接口或者有@PostConstruct注解的初始化方法被调用
4.后置处理器的After方法对Bean进行处理
5.如果发现某个类被AOP了,则在后置处理器之后就会返回代理对象,当目标对象使用代理对象调用方法时,就可以得到目标对象和调用的方法的信息

http://www.mrgr.cn/news/5235.html

相关文章:

  • 甲烷传感器的应用领域:从油气行业到环境监测的全面覆盖
  • 【机器学习】联邦学习技术
  • React 入门第三天:深入理解Hooks的强大功能
  • Python:Django 和 Tornado 的关系
  • LongWriter——从长文本语言模型中释放出10,000+字的生成能力
  • Docker私人学习笔记
  • git 目录提交代码
  • 浪潮服务器NVME 硬盘通过 Intel VROC 做RAID
  • C 作用域规则
  • 51 无显式主键时 mysql 增加的 DB_ROW_ID
  • 分形比特币(Fractal Bitcoin)
  • CentsOS 7 “Could not resolve host: mirrorlist.centos.org; 未知的错误”问题解决
  • day32(学习playbook-roles+脚本创建数据库和表+mycat读写分离))
  • 【论文阅读】A Closer Look at Parameter-Efficient Tuning in Diffusion Models
  • ant design pro 中用户的表单如何控制多个角色
  • ~构造类型~
  • Pytorch添加自定义算子之(12)-开闭原则设计tensorrt和onnxruntime推理语义分割模型
  • 74. 搜索二维矩阵
  • 【Spring Boot进阶】掌握Spring Boot框架核心注解:从入门到精通(实战指南)
  • 网络硬盘录像机NVR程序源码NVR全套运用方案