浅撸一下spring源码---大致内容实现

news2024/11/28 16:51:31

手写spring-模拟spring

1,普及一个小知识

UserService.class
@Component
public class UserService{

public void test(){
System.out.println("test")
}

}
Test.class
    public class Test{
        public static void main(String[] args){
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
            UserService userService = (UserService)context.getBean("userService");
        }
    }

上述userService bean是什么时候创建得呢?

这时候要查看UserService类上是否含有@Lazy懒加载这个注解(作用是在需要使用该bean的时候就创建)

非懒加载的单例bean,bean就会在spring启动的时候,就会将非懒加载的单例bean全部创建出来,然后直接通过getBean直接去获取bean

类似下面

//非懒加载的单例bean   
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
            UserService userService = (UserService)context.getBean("userService");

还有一种原型bean

UserService.class
@Component
@Scope("prototype")
public class UserService{

public void test(){
System.out.println("test")
}

}

原型bean在每一次getBean的时候会创建,每一次getBean时都会创建一个新对象出现

原型bean就是多例,每一次去get去拿都是一个新对象


2,开始创建一个普通maven工程,开始模拟spring准备工作

这个是项目的初始目录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZV2cAueW-1670871183206)(C:\Users\靓仔在此\AppData\Roaming\Typora\typora-user-images\image-20221211144823782.png)]

写在spring包底下的东西就模拟是spring内部的东西

llb包下的东西就是使用spring的方式

//spring包下的Component
    /*
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
那怎么来选择合适的注解生命周期呢?
首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。
 */


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)//该注解可以用在类上
//用来给bean创建一个名字
public @interface Component {
    String value() default "";
}

//spring包下的componentScan
/*
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
那怎么来选择合适的注解生命周期呢?
首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)//该注解可以用在类上
//用来扫描bean
public @interface ComponentScan {
    String value() default "";
}
//spring包下的LLBApplicationContext
public class LLBApplicationContext {

    //为了实现TestLLBApplicationContext()里面接收参数
    private Class configClass;//定义一个配置类参数

    public LLBApplicationContext(Class configClass) {
        this.configClass = configClass;
    }

    //为了实现context.getBean   在容器中获取bean对象
    public Object getBean(String userService) {
        return null;
    }
}

接下来是llb包下的service里的UserService

@Component("userService")//给bean取一个名字userService
public class UserService {
    public void test(){
        System.out.println("test");
    }
}
//llb包下的AppConfig
@ComponentScan("com.hhxy.llb.service")//扫描com.hhxy.llb.service下面的内容
public class AppConfig {

}
//llb包下的test
public class Test {
    public static void main(String[] args) {
        //模仿
        /**
         * //非懒加载的单例bean
         * AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
         * UserService userService = (UserService)context.getBean("userService");
         * 写法
         */
        LLBApplicationContext context = new LLBApplicationContext(AppConfig.class);
        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }
}

3,开始模拟spring

前提我们先忽略懒加载

首先我们要进行扫描

假设llb下service包下还存在一个orderService

public class OrderService {
    public void test(){
        System.out.println("test");
    }
}

图上APPConfig,我们会让AppConfig先去扫描com.hhxy.llb.service包下面到底哪些类是单例bean

所以在创建单例bean之前需要进行扫描

//创建单例bean    
LLBApplicationContext context = new LLBApplicationContext(AppConfig.class);

这些操作要在LLBApplicationContext的构造方法中实现 怎么让他扫描呢

LLBApplicationContext中构造方法改造成如下
    
  public LLBApplicationContext(Class configClass) {
        this.configClass = configClass;
        if (configClass.isAnnotationPresent(ComponentScan.class)){//先判断该类上是否定义了ComponentScan注解
            //定义了就接收ComponentScan的信息
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //在从ComponentScan中获取扫描路径
            String path = componentScanAnnotation.value();
            System.out.println(path);
        }
    }

现在我们运行一下写的程序看看是否能获取到扫描路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-85Q1DMAt-1670871183207)(C:\Users\靓仔在此\AppData\Roaming\Typora\typora-user-images\image-20221211153232533.png)]

控制台输出

com.hhxy.llb.service   //这个就是扫描路径
//下面这个是报错,因为我们Test的getBean方法内部是return null
Exception in thread "main" java.lang.NullPointerException
	at com.hhxy.llb.Test.main(Test.java:17)

这里的扫描路径是我们的写的源文件是.java文件

我们应该取的是编译后文件.class文件(target下的)

应该是根据com.hhxy.llb.service 找到

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lFohZzAF-1670871183208)(C:\Users\靓仔在此\AppData\Roaming\Typora\typora-user-images\image-20221211153913108.png)]

找到这个service下所有的.class文件把他们解析看看谁的类上含有注解

所以我们应该如何找到这个class下的扫描路径呢

运行Java程序可以注意到

"C:\Program Files\Java\jdk1.8.0_201\bin\java.exe" "-javaagent:D:\软件\IntelliJ IDEA 2020.2.3\IntelliJ IDEA 2020.2.3\lib\idea_rt.jar=12760:D:\软件\IntelliJ IDEA 2020.2.3\IntelliJ IDEA 2020.2.3\bin" -Dfile.encoding=UTF-8 

    
-classpath "C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;C:\hhxy\spring_demo\target\classes" com.hhxy.llb.Test
com.hhxy.llb.service

倒数第一、二行C:\hhxy\spring_demo\target\classes" com.hhxy.llb.Test
com.hhxy.llb.service与我们class下的类相似 为什么有什么原理么

与类加载器有关

那些目录是它们加载的呢

BootStrap ClassLoader ----------->jre/lib

ExtensionClassLoader ------------>jre/ext/bin

AppClassLoader ------------->是上述classpath后面指定的(差不多就是管理target目录)

//在spring下添加Scope类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)//该注解可以用在类上
//用来给bean创建一个名字
public @interface Scope {
    String value() default "";
}
//改造com.hhxy.llb.service
@Component("userService")
@Scope("singleton")
public class UserService {
    public void test(){
        System.out.println("test");
    }
}
//改造LLBApplicationContext
public class LLBApplicationContext {

    //为了实现TestLLBApplicationContext()里面接收参数
    private Class configClass;//定义一个配置类参数

    public LLBApplicationContext(Class configClass) {
        this.configClass = configClass;
        if (configClass.isAnnotationPresent(ComponentScan.class)){//先判断该类上是否定义了ComponentScan注解
            //定义了就接收ComponentScan的信息
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //在从ComponentScan中获取扫描路径
            String path = componentScanAnnotation.value();
            String replacePath = path.replace(".", "/");//com/hhxy/llb/service  相对路径
//            System.out.println(replacePath);
//            System.out.println("----------------------------------------------------------------");
            ClassLoader classLoader = LLBApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(replacePath);//通过传入这个相对路径就可以找到target下classes下com/hhxy/llb/service
//            System.out.println(resource);
//            System.out.println("----------------------------------------------------------------");
            File file = new File(resource.getFile());//看看拿到target下classes下com/hhxy/llb/service下的文件或目录
            if (file.isDirectory()){//判断拿到的对象file是不是目录
                for (File f : file.listFiles()) {//是一个目录的话就遍历出目录下所有文件
                    String absolutePath = f.getAbsolutePath();
//                    System.out.println(absolutePath);
//                    System.out.println("----------------------------------------------------------------");
                    //需要判断这两个.class文件是否有注解@Component
                    //最简单的方式是用classLoader把这俩个class文件加载再判断
//                    Class<?> aClass = classLoader.loadClass();
//                    if (aClass.isAnnotationPresent(Component.class)) {
//                    }
//spring内部用的是其他方式   ASM技术
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));//截取com开头,.class结尾的路径
                    absolutePath = absolutePath.replace("\\", ".");//把/转换成.
                    System.out.println(absolutePath);
                    try {
                        Class<?> aClass = classLoader.loadClass(absolutePath);//target下classes下com/hhxy/llb/service下的文件使用classLoader加载进来
                        if (aClass.isAnnotationPresent(Component.class)){//判断classloader加载的类是否含有Component
                            //Bean
                            System.out.println(aClass);//能输出的都是bean也就是带了@Component的UserService
                            //但是我们此时是需要一个单例bean
                            if (aClass.isAnnotationPresent(Scope.class)) {//判断classloader加载的类是否含有Scope
                                Scope scopeAnnotation = aClass.getAnnotation(Scope.class);//拿到Scope信息
                                String scopeValue = scopeAnnotation.value();//拿到Scope注解中的值
                                //此时是直接创建bean?
                                //接着看下面的getBean
                            }else {
                                //单例
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }


                }
            }
        }
    }

    //为了实现context.getBean   在容器中获取bean对象
    public Object getBean(String beanName) {
        //beanName---->UserService.class---->再去判断当前类是否含有@Scope注解,注解中内容是什么
        //这样通过beanName来查找UserService.class的话查找注解@Scope内容等重复一遍   较为繁琐
        //所以此时我们应该引入一个BeanDefinition
        return null;
    }
}
//com.hhxy.spring下 BeanDefinition
public class BeanDefinition {
    private Class type;//bean的类型
    private String scope;//单例或原型
    private boolean isLazy;//是否是懒加载的
}

添加了BeanDefinition后,我们在修改一下前面的逻辑

public class LLBApplicationContext {

    //为了实现TestLLBApplicationContext()里面接收参数
    private Class configClass;//定义一个配置类参数
    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();

    public LLBApplicationContext(Class configClass) {
        this.configClass = configClass;
        scan(configClass);//扫描方法
    }



    //为了实现context.getBean   在容器中获取bean对象
    public Object getBean(String beanName) {
        //beanName---->UserService.class---->再去判断当前类是否含有@Scope注解,注解中内容是什么
        //这样通过beanName来查找UserService.class的话查找注解@Scope内容等重复一遍   较为繁琐
        return null;
    }
    private void scan(Class configClass) {
        if (configClass.isAnnotationPresent(ComponentScan.class)){//先判断该类上是否定义了ComponentScan注解
            //定义了就接收ComponentScan的信息
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //在从ComponentScan中获取扫描路径
            String path = componentScanAnnotation.value();
            String replacePath = path.replace(".", "/");//com/hhxy/llb/service  相对路径
//            System.out.println(replacePath);
//            System.out.println("----------------------------------------------------------------");
            ClassLoader classLoader = LLBApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(replacePath);//通过传入这个相对路径就可以找到target下classes下com/hhxy/llb/service
//            System.out.println(resource);
//            System.out.println("----------------------------------------------------------------");
            File file = new File(resource.getFile());//看看拿到target下classes下com/hhxy/llb/service下的文件或目录
            if (file.isDirectory()){//判断拿到的对象file是不是目录
                for (File f : file.listFiles()) {//是一个目录的话就遍历出目录下所有文件
                    String absolutePath = f.getAbsolutePath();
//                    System.out.println(absolutePath);
//                    System.out.println("----------------------------------------------------------------");
                    //需要判断这两个.class文件是否有注解@Component
                    //最简单的方式是用classLoader把这俩个class文件加载再判断
//                    Class<?> aClass = classLoader.loadClass();
//                    if (aClass.isAnnotationPresent(Component.class)) {
//                    }
//spring内部用的是其他方式   ASM技术
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));//截取com开头,.class结尾的路径
                    absolutePath = absolutePath.replace("\\", ".");//把/转换成.
                    System.out.println(absolutePath);
                    try {
                        Class<?> aClass = classLoader.loadClass(absolutePath);//target下classes下com/hhxy/llb/service下的文件使用classLoader加载进来
                        if (aClass.isAnnotationPresent(Component.class)){//判断classloader加载的类是否含有Component
                            //因为我们的bean上有Component注解,里面可以自定义名字,或者默认首字母小写
                            Component componentAnnotation = aClass.getAnnotation(Component.class);//获取Component所有信息
                            String beanName = componentAnnotation.value();//拿到component中的值
                            //Bean
//                            System.out.println(aClass);//能输出的都是bean也就是带了@Component的UserService
                            //但是我们此时是需要一个单例bean
                            BeanDefinition beanDefinition = new BeanDefinition();//因为存在component注解,所以就可以为它创建一个BeanDefinition描述信息
                            beanDefinition.setType(aClass);//定义bean的类型
                            if (aClass.isAnnotationPresent(Scope.class)) {//判断classloader加载的类是否含有Scope
                                Scope scopeAnnotation = aClass.getAnnotation(Scope.class);//拿到Scope信息
                                String scopeValue = scopeAnnotation.value();//拿到Scope注解中的值
                                beanDefinition.setScope(scopeValue);//把Scope的值设置到BeanDefinition bean的描述中 的Scope中
                            }else {
                                //单例
                                beanDefinition.setScope("singleton");
                            }
                            //把信息存入到map里  键为beanName,值为beanDefinition对象
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }


                }
            }
        }
    }
}

该scan扫描方法做了哪些事情

首先是解析传入进来的类----》得到扫描路径----》遍历路径每一个.class文件----》加载每一个.class文件得到一个class对象----》判断是否携带Component注解----》然后去解析bean的名字beanName----》解析Scope注解----》得到beanDefinition对象----》存到beanDefinitionMap中

//改造LLBApplicationContext中getBean()方法
   //为了实现context.getBean   在容器中获取bean对象
    public Object getBean(String beanName) {
        //beanName---->UserService.class---->再去判断当前类是否含有@Scope注解,注解中内容是什么
        //这样通过beanName来查找UserService.class的话查找注解@Scope内容等重复一遍   较为繁琐

        if(!beanDefinitionMap.containsKey(beanName)) {
            //bean的名称不存在
            throw new NullPointerException();
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("singleton")){
            //单例   按照名字从单例池中搜索单例bean
            Object singletonBean = singletonObjects.get(beanName);
            return singletonBean;
        }else {
            //原型bean的实现 每一次get都会重新创建一个bean
            Object prototypeBean = createBean(beanName, beanDefinition);
            return prototypeBean;
        }
    }
//改造LLBApplicationContext中scan()方法
 private void scan(Class configClass) {
        if (configClass.isAnnotationPresent(ComponentScan.class)){//先判断该类上是否定义了ComponentScan注解
            //定义了就接收ComponentScan的信息
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //在从ComponentScan中获取扫描路径
            String path = componentScanAnnotation.value();
            String replacePath = path.replace(".", "/");//com/hhxy/llb/service  相对路径
//            System.out.println(replacePath);
//            System.out.println("----------------------------------------------------------------");
            ClassLoader classLoader = LLBApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(replacePath);//通过传入这个相对路径就可以找到target下classes下com/hhxy/llb/service
//            System.out.println(resource);
//            System.out.println("----------------------------------------------------------------");
            File file = new File(resource.getFile());//看看拿到target下classes下com/hhxy/llb/service下的文件或目录
            if (file.isDirectory()){//判断拿到的对象file是不是目录
                for (File f : file.listFiles()) {//是一个目录的话就遍历出目录下所有文件
                    String absolutePath = f.getAbsolutePath();
//                    System.out.println(absolutePath);
//                    System.out.println("----------------------------------------------------------------");
                    //需要判断这两个.class文件是否有注解@Component
                    //最简单的方式是用classLoader把这俩个class文件加载再判断
//                    Class<?> aClass = classLoader.loadClass();
//                    if (aClass.isAnnotationPresent(Component.class)) {
//                    }
//spring内部用的是其他方式   ASM技术
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));//截取com开头,.class结尾的路径
                    absolutePath = absolutePath.replace("\\", ".");//把/转换成.
//                    System.out.println(absolutePath);
                    try {
                        Class<?> aClass = classLoader.loadClass(absolutePath);//target下classes下com/hhxy/llb/service下的文件使用classLoader加载进来
                        if (aClass.isAnnotationPresent(Component.class)){//判断classloader加载的类是否含有Component
                            //因为我们的bean上有Component注解,里面可以自定义名字,或者默认首字母小写
                            Component componentAnnotation = aClass.getAnnotation(Component.class);//获取Component所有信息
                            String beanName = componentAnnotation.value();//拿到component中的值
                            //Bean
//                            System.out.println(aClass);//能输出的都是bean也就是带了@Component的UserService
                            //但是我们此时是需要一个单例bean
                            BeanDefinition beanDefinition = new BeanDefinition();//因为存在component注解,所以就可以为它创建一个BeanDefinition描述信息
                            beanDefinition.setType(aClass);//定义bean的类型
                            if (aClass.isAnnotationPresent(Scope.class)) {//判断classloader加载的类是否含有Scope
                                Scope scopeAnnotation = aClass.getAnnotation(Scope.class);//拿到Scope信息
                                String scopeValue = scopeAnnotation.value();//拿到Scope注解中的值
                                beanDefinition.setScope(scopeValue);//把Scope的值设置到BeanDefinition bean的描述中 的Scope中
                            }else {
                                //单例
                                beanDefinition.setScope("singleton");
                            }
                            //把信息存入到map里  键为beanName,值为beanDefinition对象
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }


                }
            }
        }
    }
//改造LLBApplicationContext中createBean()方法
 private Object createBean(String beanName,BeanDefinition beanDefinition){
        //如何创建bean
        //肯定是根据bean的类型创建
        Class type = beanDefinition.getType();
        Object instance=null;
        try {
            //通过调用类的无参构造方法实例化得到对象instance
            instance = type.getConstructor().newInstance();

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return instance;
    }
//改造LLBApplicationContext中变量
//为了实现TestLLBApplicationContext()里面接收参数
    private Class configClass;//定义一个配置类参数
    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();//bean详细信息
    private Map<String,Object> singletonObjects = new HashMap<String, Object>();//单例池

    public LLBApplicationContext(Class configClass) {
        this.configClass = configClass;
        scan(configClass);//扫描方法
        //创建单例bean
        //遍历map中所有信息  map中存储的是entrySet  entrySet-entry组成
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();//获取beanName
            BeanDefinition beanValue = beanDefinitionEntry.getValue();//BeanDefinitionbean的描述依赖信息
            if (beanValue.getScope().equals("singleton")){ //判断scope是否有值值是否等于singleton
                //找到单例bean   这时候应该创建bean
                Object bean = createBean(beanName, beanValue);
                //把创建的单例bean,存放到单例池中
                singletonObjects.put(beanName,bean);
            }
        }
    }
//完整的LLBApplicationContext类
public class LLBApplicationContext {

    //为了实现TestLLBApplicationContext()里面接收参数
    private Class configClass;//定义一个配置类参数
    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();//bean详细信息
    private Map<String,Object> singletonObjects = new HashMap<String, Object>();//单例池

    public LLBApplicationContext(Class configClass) {
        this.configClass = configClass;
        scan(configClass);//扫描方法
        //创建单例bean
        //遍历map中所有信息  map中存储的是entrySet  entrySet-entry组成
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();//获取beanName
            BeanDefinition beanValue = beanDefinitionEntry.getValue();//BeanDefinitionbean的描述依赖信息
            if (beanValue.getScope().equals("singleton")){ //判断scope是否有值值是否等于singleton
                //找到单例bean   这时候应该创建bean
                Object bean = createBean(beanName, beanValue);
                //把创建的单例bean,存放到单例池中
                singletonObjects.put(beanName,bean);
            }
        }
    }

    private Object createBean(String beanName,BeanDefinition beanDefinition){
        //如何创建bean
        //肯定是根据bean的类型创建
        Class type = beanDefinition.getType();
        Object instance=null;
        try {
            //通过调用类的无参构造方法实例化得到对象instance
            instance = type.getConstructor().newInstance();

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return instance;
    }



    //为了实现context.getBean   在容器中获取bean对象
    public Object getBean(String beanName) {
        //beanName---->UserService.class---->再去判断当前类是否含有@Scope注解,注解中内容是什么
        //这样通过beanName来查找UserService.class的话查找注解@Scope内容等重复一遍   较为繁琐

        if(!beanDefinitionMap.containsKey(beanName)) {
            //bean的名称不存在
            throw new NullPointerException();
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("singleton")){
            //单例   按照名字从单例池中搜索单例bean
            Object singletonBean = singletonObjects.get(beanName);
            return singletonBean;
        }else {
            //原型bean的实现 每一次get都会重新创建一个bean
            Object prototypeBean = createBean(beanName, beanDefinition);
            return prototypeBean;
        }
    }
    private void scan(Class configClass) {
        if (configClass.isAnnotationPresent(ComponentScan.class)){//先判断该类上是否定义了ComponentScan注解
            //定义了就接收ComponentScan的信息
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //在从ComponentScan中获取扫描路径
            String path = componentScanAnnotation.value();
            String replacePath = path.replace(".", "/");//com/hhxy/llb/service  相对路径
//            System.out.println(replacePath);
//            System.out.println("----------------------------------------------------------------");
            ClassLoader classLoader = LLBApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(replacePath);//通过传入这个相对路径就可以找到target下classes下com/hhxy/llb/service
//            System.out.println(resource);
//            System.out.println("----------------------------------------------------------------");
            File file = new File(resource.getFile());//看看拿到target下classes下com/hhxy/llb/service下的文件或目录
            if (file.isDirectory()){//判断拿到的对象file是不是目录
                for (File f : file.listFiles()) {//是一个目录的话就遍历出目录下所有文件
                    String absolutePath = f.getAbsolutePath();
//                    System.out.println(absolutePath);
//                    System.out.println("----------------------------------------------------------------");
                    //需要判断这两个.class文件是否有注解@Component
                    //最简单的方式是用classLoader把这俩个class文件加载再判断
//                    Class<?> aClass = classLoader.loadClass();
//                    if (aClass.isAnnotationPresent(Component.class)) {
//                    }
//spring内部用的是其他方式   ASM技术
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));//截取com开头,.class结尾的路径
                    absolutePath = absolutePath.replace("\\", ".");//把/转换成.
//                    System.out.println(absolutePath);
                    try {
                        Class<?> aClass = classLoader.loadClass(absolutePath);//target下classes下com/hhxy/llb/service下的文件使用classLoader加载进来
                        if (aClass.isAnnotationPresent(Component.class)){//判断classloader加载的类是否含有Component
                            //因为我们的bean上有Component注解,里面可以自定义名字,或者默认首字母小写
                            Component componentAnnotation = aClass.getAnnotation(Component.class);//获取Component所有信息
                            String beanName = componentAnnotation.value();//拿到component中的值
                            //Bean
//                            System.out.println(aClass);//能输出的都是bean也就是带了@Component的UserService
                            //但是我们此时是需要一个单例bean
                            BeanDefinition beanDefinition = new BeanDefinition();//因为存在component注解,所以就可以为它创建一个BeanDefinition描述信息
                            beanDefinition.setType(aClass);//定义bean的类型
                            if (aClass.isAnnotationPresent(Scope.class)) {//判断classloader加载的类是否含有Scope
                                Scope scopeAnnotation = aClass.getAnnotation(Scope.class);//拿到Scope信息
                                String scopeValue = scopeAnnotation.value();//拿到Scope注解中的值
                                beanDefinition.setScope(scopeValue);//把Scope的值设置到BeanDefinition bean的描述中 的Scope中
                            }else {
                                //单例
                                beanDefinition.setScope("singleton");
                            }
                            //把信息存入到map里  键为beanName,值为beanDefinition对象
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }


                }
            }
        }
    }
}

大致的spring完成 可以实现单例、原型

测试

//test类
public class Test {
    public static void main(String[] args) {
        //模仿
        /**
         * //非懒加载的单例bean
         * AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
         * UserService userService = (UserService)context.getBean("userService");
         * 写法
         */
        LLBApplicationContext context = new LLBApplicationContext(AppConfig.class);
//        UserService userService = (UserService) context.getBean("userService");
//        userService.test();
        //获取多个bean测试看UserService中Scope值为singleton 判断bean是否相同是否为单例bean
        System.out.println((UserService) context.getBean("userService"));
        System.out.println((UserService) context.getBean("userService"));
        System.out.println((UserService) context.getBean("userService"));
    }
}

执行效果如下,UserService是单例对象singleton

com.hhxy.llb.service.UserService@5e2de80c
com.hhxy.llb.service.UserService@5e2de80c
com.hhxy.llb.service.UserService@5e2de80c
//单例创建对象都是同一个

我们再来去掉UserService上注解@Scope(“singleton”) 再查看执行效果

com.hhxy.llb.service.UserService@60e53b93
com.hhxy.llb.service.UserService@60e53b93
com.hhxy.llb.service.UserService@60e53b93
//默认不写,单例创建对象都是同一个

接着修改UserService上注解为@Scope(“prototype”) 再查看执行效果

com.hhxy.llb.service.UserService@5e2de80c
com.hhxy.llb.service.UserService@1d44bcfa
com.hhxy.llb.service.UserService@266474c2
//原型就每一个对象不同

测试给OrderService加上Component注解

运行test发现

@Component
public class OrderService {
    public void test(){
        System.out.println("test");
    }
}
public class Test {
    public static void main(String[] args) {
        //模仿
        /**
         * //非懒加载的单例bean
         * AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
         * UserService userService = (UserService)context.getBean("userService");
         * 写法
         */
        LLBApplicationContext context = new LLBApplicationContext(AppConfig.class);
//        UserService userService = (UserService) context.getBean("userService");
//        userService.test();
        //获取多个bean测试看UserService中Scope值为singleton 判断bean是否相同是否为单例bean
        System.out.println((UserService) context.getBean("userService"));
        System.out.println((UserService) context.getBean("userService"));
        System.out.println((OrderService) context.getBean("orderService"));
    }
}

com.hhxy.llb.service.UserService@60e53b93
com.hhxy.llb.service.UserService@60e53b93
Exception in thread "main" java.lang.NullPointerException
	at com.hhxy.spring.LLBApplicationContext.getBean(LLBApplicationContext.java:67)
	at com.hhxy.llb.Test.main(Test.java:22)

因为没有给名字beanName默认就为空了

这个时候我们查看spring源码可以发现给某个类创建一个名字方法的代码其实很简单就简单的一句话

//Introspector.decapitalize(aClass.getSimpleName());  
//以下是我在LLBApplicationContext获取@Component注解值时加入的代码
if ("".equals(beanName)){
                                beanName = Introspector.decapitalize(aClass.getSimpleName());
                            }
//测试代码
public class Test {
    public static void main(String[] args) {
        //模仿
        /**
         * //非懒加载的单例bean
         * AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
         * UserService userService = (UserService)context.getBean("userService");
         * 写法
         */
        LLBApplicationContext context = new LLBApplicationContext(AppConfig.class);
//        UserService userService = (UserService) context.getBean("userService");
//        userService.test();
        //获取多个bean测试看UserService中Scope值为singleton 判断bean是否相同是否为单例bean
        System.out.println((UserService) context.getBean("userService"));
        System.out.println((UserService) context.getBean("userService"));
        System.out.println((OrderService) context.getBean("orderService"));
    }
}

//测试结果   这个是OrderService上@Component注解内没有值
com.hhxy.llb.service.UserService@60e53b93
com.hhxy.llb.service.UserService@60e53b93
com.hhxy.llb.service.OrderService@5e2de80c

这时候我们考虑添加一个@Autowired注解 依赖注入


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//该注解可以用在字段属性上
//用来给bean创建一个名字
public @interface Autowired {
}

接着在UserService添加上一段

@Autowired
private OrderService orderService;

再编写Test类改造

        LLBApplicationContext context = new LLBApplicationContext(AppConfig.class);
//        UserService userService = (UserService) context.getBean("userService");
//        userService.test();
        //获取多个bean测试看UserService中Scope值为singleton 判断bean是否相同是否为单例bean
        UserService userService = (UserService) context.getBean("userService");
        userService.test();

这个时候请问userService里的test方法中的orderService是否有值呢

答案是没有为null。。。

要如何实现依赖注入 —在bean的创建时createBean

//createBean中属性赋值
  private Object createBean(String beanName,BeanDefinition beanDefinition){
        //如何创建bean
        //肯定是根据bean的类型创建
        Class type = beanDefinition.getType();
        Object instance=null;
        try {
            //通过调用类的无参构造方法实例化得到对象instance
            instance = type.getConstructor().newInstance();
            //这边要是存在一个有参狗方法使用需要传参,参数值是需要通过类型或者名字来获取
            //依赖注入
            for (Field field : type.getDeclaredFields()) {//遍历出该对象所有属性
                if (field.isAnnotationPresent(Autowired.class)) {//判断哪些属性上存在@Autowired注解
                    field.setAccessible(true);//反射
                    //属性设置值,一个对象  一个根据对象类型去找
                    //field.getType()//定义一个新的map,key为Class,value为beanDefinition   在扫描scan的时候把每一个加载的class与beanDefinition存在map里面去
                    //field.set(instance,????);
                    //给这个属性值赋值,通过这个instance(UserService)对象的字段属性名(field)去赋值
                    //赋什么值,通过这个字段的名字去调用getBean方法,得到一个bean对象,然后将这个bean赋值给这个字段
                    field.set(instance,getBean(field.getName()));//可能存在循环依赖问题
                }
            }

//如果首先创建得bean是UserService,在属性赋值时通过orderService名字去找bean,发现bean没有该怎么办呢?
//在getBean方法中返回单例对象
public Object getBean(String beanName) {
        //beanName---->UserService.class---->再去判断当前类是否含有@Scope注解,注解中内容是什么
        //这样通过beanName来查找UserService.class的话查找注解@Scope内容等重复一遍   较为繁琐

        if(!beanDefinitionMap.containsKey(beanName)) {
            //bean的名称不存在
            throw new NullPointerException();
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("singleton")){
            //单例   按照名字从单例池中搜索单例bean
            Object singletonBean = singletonObjects.get(beanName);
            if (singletonBean==null){//万一UserService先创建,给属性赋值,而OrderService并没有创建  那就只能再创建一个值
                singletonBean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
            return singletonBean;
        }else {
            //原型bean的实现 每一次get都会重新创建一个bean
            Object prototypeBean = createBean(beanName, beanDefinition);
            return prototypeBean;
        }
    }

接着再次测试Test.class

        LLBApplicationContext context = new LLBApplicationContext(AppConfig.class);
//        UserService userService = (UserService) context.getBean("userService");
//        userService.test();
        //获取多个bean测试看UserService中Scope值为singleton 判断bean是否相同是否为单例bean
        UserService userService = (UserService) context.getBean("userService");
        userService.test();

console输出

com.hhxy.llb.service.OrderService@6f94fa3e

接下来模仿初始化操作 spring里有接口InitializingBean

//接口InitializingBean
//内部方法 spring内源码方法
void afterPropertiesSet(); 
//UserService实现这个接口 重写该方法
@Component("userService")
//@Scope("singleton")
//@Scope("prototype")
public class UserService implements InitializingBean {

    //给这个OrderService赋值,最开始可以按照类型来找  从单例池里去寻找  但是orderService可能在单例池没有找到,因为orderService对象还没有创建,这个时候就可以依赖BeanDefinition中属性了Class 根据某一个类型来找到该类型bean到底有几个有哪些
    //根据类型去寻找bean如果找到多个就可以根据名字去找,根据名字就可以看我们写的LLBApplicationContext这个类两个map都存放的是beanName,既可以去单例池中去找,也可以去BeanDefinition中去找
    //这就类似byType,byName
    @Autowired
    private OrderService orderService;

    public void test(){
        System.out.println(orderService);
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化");
    }
}

这个重写的方法暂时现在还调用不到,还没有完善

应该在创建某一个bean的过程中间 依赖注入过后,spring支持这个功能

//创建bean  ---createBean
 private Object createBean(String beanName,BeanDefinition beanDefinition){
        //如何创建bean
        //肯定是根据bean的类型创建
        Class type = beanDefinition.getType();
        Object instance=null;
        try {
            //通过调用类的无参构造方法实例化得到对象instance
            instance = type.getConstructor().newInstance();
            //这边要是存在一个有参狗方法使用需要传参,参数值是需要通过类型或者名字来获取
            //依赖注入
            for (Field field : type.getDeclaredFields()) {//遍历出该对象所有属性
                if (field.isAnnotationPresent(Autowired.class)) {//判断哪些属性上存在@Autowired注解
                    field.setAccessible(true);//反射
                    //属性设置值,一个对象  一个根据对象类型去找
                    //field.getType()//定义一个新的map,key为Class,value为beanDefinition   在扫描scan的时候把每一个加载的class与beanDefinition存在map里面去
                    //field.set(instance,????);
                    //给这个属性值赋值,通过这个instance(UserService)对象的字段属性名(field)去赋值
                    //赋什么值,通过这个字段的名字去调用getBean方法,得到一个bean对象,然后将这个bean赋值给这个字段
                    field.set(instance,getBean(field.getName()));//可能存在循环依赖问题
                }
            }
            //在任意的bean中实现这个接口,spring就会自动帮我们调用这个方法
            if (instance instanceof InitializingBean){//判断这个对象是否实现了InitializingBean这个接口
                ((InitializingBean)instance).afterPropertiesSet();//然后把这个对象转换成InitializingBean对象调用这个方法
            }

运行Test.class test没有做修改,结果如下

初始化
com.hhxy.llb.service.OrderService@6f94fa3e

重点讲解BeanPostProcesor 初始化前后都跟这个接口息息相关

查看spring源码BeanPostProcesor 可以发现两个方法

@NotNull
default Object postProcessBeforeInterInitialization(Object bean,String beanName) throw BeansException{
    return bean;
}

@NotNull
default Object postProcessAfterInterInitialization(Object bean,String beanName) throw BeansException{
    return bean;
}

定义好接口BeanPostProcesor

public interface BeanPostProcesor {
    default Object postProcessBeforeInterInitialization(Object bean,String beanName) {
        return bean;
    }

    default Object postProcessAfterInterInitialization(Object bean,String beanName){
      return bean;
    }
}

再定义一个类用来实现该方法可以再方法内部写任意逻辑


public class LLBBeanPostProcessor implements BeanPostProcesor {
    @Override
    public Object postProcessBeforeInterInitialization(Object bean, String beanName) {
        return null;
    }

    @Override
    public Object postProcessAfterInterInitialization(Object bean, String beanName) {
        return null;
    }
}

接着我们可以在创建bean初始化时那个地方进行初始化前,初始化后等操作

以下代码是我个人思路见解


    private Object createBean(String beanName,BeanDefinition beanDefinition){
        //如何创建bean
        //肯定是根据bean的类型创建
        Class type = beanDefinition.getType();
        Object instance=null;
        try {
            //通过调用类的无参构造方法实例化得到对象instance
            instance = type.getConstructor().newInstance();
            //这边要是存在一个有参狗方法使用需要传参,参数值是需要通过类型或者名字来获取
            //依赖注入
            for (Field field : type.getDeclaredFields()) {//遍历出该对象所有属性
                if (field.isAnnotationPresent(Autowired.class)) {//判断哪些属性上存在@Autowired注解
                    field.setAccessible(true);//反射
                    //属性设置值,一个对象  一个根据对象类型去找
                    //field.getType()//定义一个新的map,key为Class,value为beanDefinition   在扫描scan的时候把每一个加载的class与beanDefinition存在map里面去
                    //field.set(instance,????);
                    //给这个属性值赋值,通过这个instance(UserService)对象的字段属性名(field)去赋值
                    //赋什么值,通过这个字段的名字去调用getBean方法,得到一个bean对象,然后将这个bean赋值给这个字段
                    field.set(instance,getBean(field.getName()));//可能存在循环依赖问题
                }
            }
            //初始化前
            LLBBeanPostProcessor llbBeanPostProcessor = new LLBBeanPostProcessor();
            llbBeanPostProcessor.postProcessBeforeInterInitialization(instance,beanName);
            //在任意的bean中实现这个接口,spring就会自动帮我们调用这个方法
            if (instance instanceof InitializingBean){//判断这个对象是否实现了InitializingBean这个接口
                ((InitializingBean)instance).afterPropertiesSet();//然后把这个对象转换成InitializingBean对象调用这个方法
            }
            //初始化后
            llbBeanPostProcessor.postProcessAfterInterInitialization(instance,beanName);
            //以上初始化前后就会调用LLBBeanPostProcessor里的两个初始化前后方法,可以进行业务逻辑编写

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return instance;
    }


可以发现如果直接实例化自己写的那个类,spring内部会这样写么,肯定不会,是使用接口获取实例

那么我们得先让spring知道这个类,所以我们可以在LLBBeanPostProcessor类上添加一个@Component注解

@Component
public class LLBBeanPostProcessor implements BeanPostProcesor {
    @Override
    public Object postProcessBeforeInterInitialization(Object bean, String beanName) {
        System.out.println(beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInterInitialization(Object bean, String beanName) {
        System.out.println(beanName);
        return bean;
    }

所以我们在扫描的时候根据加了@Component注解的类进行判断,判断是否实现了BeanPostProcesor接口

//创建了一个list用于存储BeanPostProcesor
 private List<BeanPostProcesor> beanPostProcesorsList = new ArrayList<BeanPostProcesor>();

if (aClass.isAnnotationPresent(Component.class)){//判断classloader加载的类是否含有Component
                            if (BeanPostProcesor.class.isAssignableFrom(aClass)) {//判断该类是否实现了BeanPostProcesor
                                //instanceof某一个对象类型,这里是某一个类
                                BeanPostProcesor instance = (BeanPostProcesor) aClass.getConstructor().newInstance();
                                //每拿到一个BeanPostProcesor对象就放到beanPostProcesorsList里
                                beanPostProcesorsList.add(instance);
                            }

再接着从创建bean时,初始化后面添加

//            //初始化后
//            llbBeanPostProcessor.postProcessAfterInterInitialization(instance,beanName);
            for (BeanPostProcesor beanPostProcesor : beanPostProcesorsList) {//遍历所有BeanPostProcesor
                //把实例对象传给你再调用该方法再把这个方法的方法返回值对象又赋值给它
                instance = beanPostProcesor.postProcessAfterInterInitialization(instance,beanName);//调用xx对象调用方法 一旦调用这个方法instance就变成返回的一个代理对象了  就会创建一个userService的代理对象
            }

这样可以完成aop

aop底层也是基于BeanPostProcesor来做的

//添加接口完善动态代理
public interface UserInterface {
    public void test();
}

在UserService里面去实现这个接口

@Component("userService")
//@Scope("singleton")
//@Scope("prototype")
public class UserService implements UserInterface ,InitializingBean {

    //给这个OrderService赋值,最开始可以按照类型来找  从单例池里去寻找  但是orderService可能在单例池没有找到,因为orderService对象还没有创建,这个时候就可以依赖BeanDefinition中属性了Class 根据某一个类型来找到该类型bean到底有几个有哪些
    //根据类型去寻找bean如果找到多个就可以根据名字去找,根据名字就可以看我们写的LLBApplicationContext这个类两个map都存放的是beanName,既可以去单例池中去找,也可以去BeanDefinition中去找
    //这就类似byType,byName
    @Autowired
    private OrderService orderService;
    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化");
    }

    public void test(){
        System.out.println(orderService);
    }


}

可以在LLBBeanPostProcessor的postProcessAfterInterInitialization定义自己写的东西 不过得先模仿出jdk动态代理

  @Override
    public Object postProcessAfterInterInitialization(Object bean, String beanName) {
        if (beanName.equals("userService")){
            //最好是使用cglib形式  实现aop
            //做一下简单实现
            //生成一个代理对象   模仿jdk动态代理
            Object proxyInstance = Proxy.newProxyInstance(LLBBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override            //代理对象
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //test类进行调用进入到这个方法中
                    //定义切面逻辑
                    System.out.println("切面逻辑");
                    return method.invoke(bean,args);//最终还是要执行原始的bean(userService)对象方法
                }
            });
            return proxyInstance;
        }
        return bean;
    }

这样当创建bean时调用下面这行代码时就会返回一个代理对象

instance = beanPostProcesor.postProcessAfterInterInitialization(instance,beanName);//调用xx对象调用方法 一旦调用这个方法instance就变成返回的一个代理对象了  就会创建一个userService的代理对象

这个代理对象里就定义了切面逻辑,然后最终还是得执行原始的bean(UserService)对象方法

Test进行测试

public class Test {
    public static void main(String[] args) {
        //模仿
        /**
         * //非懒加载的单例bean
         * AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
         * UserService userService = (UserService)context.getBean("userService");
         * 写法
         */
        LLBApplicationContext context = new LLBApplicationContext(AppConfig.class);
//        UserService userService = (UserService) context.getBean("userService");
//        userService.test();
        //获取多个bean测试看UserService中Scope值为singleton 判断bean是否相同是否为单例bean
        UserInterface userService = (UserInterface) context.getBean("userService");
        userService.test();
    }
}
//执行结果如下
初始化
切面逻辑
com.hhxy.llb.service.OrderService@63947c6b

这样大致的aop功能不久实现了么

基于BeanPostProcessor这个机制可以对bean做出很多种事情

现在假设我想实现一个功能,自定义一个注解@LLBValue 通过自定义注解把注解中的值写入到下面属性这个name里面

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface LLBValue {
    String value() default "";
}

以及改造UserService


@Component("userService")
//@Scope("singleton")
//@Scope("prototype")
public class UserService implements UserInterface ,InitializingBean {

    //给这个OrderService赋值,最开始可以按照类型来找  从单例池里去寻找  但是orderService可能在单例池没有找到,因为orderService对象还没有创建,这个时候就可以依赖BeanDefinition中属性了Class 根据某一个类型来找到该类型bean到底有几个有哪些
    //根据类型去寻找bean如果找到多个就可以根据名字去找,根据名字就可以看我们写的LLBApplicationContext这个类两个map都存放的是beanName,既可以去单例池中去找,也可以去BeanDefinition中去找
    //这就类似byType,byName
    @Autowired
    private OrderService orderService;

    @LLBValue("giao")//通过自定义注解把注解中的值写入到下面属性这个name里面
    private String name;

    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化");
    }

    public void test(){
        System.out.println(name);
        System.out.println(orderService);
    }


}

//在创建bean初始化之前添加
        for (BeanPostProcesor beanPostProcesor : beanPostProcesorsList) {//遍历所有BeanPostProcesor
                beanPostProcesor.postProcessBeforeInterInitialization(instance,beanName);//调用方法
            }
//内部方法
public class LLBBeanValuePostProcessor implements BeanPostProcesor {
    //aop底层就是基于BeanPostProcessor实现的
    @Override
    public Object postProcessBeforeInterInitialization(Object bean, String beanName) {
        System.out.println(beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInterInitialization(Object bean, String beanName) {
        for (Field f : bean.getClass().getDeclaredFields()) {
            if (f.isAnnotationPresent(LLBValue.class)) {
                f.setAccessible(true);
                try {
                    f.set(bean,f.getAnnotation(LLBValue.class).value());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }
}

spring中AWare

spring中BeanNameAWare接口

我想知道UserService中的name属性知道自己的bean名字


@Component("userService")
//@Scope("singleton")
//@Scope("prototype")
public class UserService implements UserInterface ,InitializingBean ,BeanNameAware{

    //给这个OrderService赋值,最开始可以按照类型来找  从单例池里去寻找  但是orderService可能在单例池没有找到,因为orderService对象还没有创建,这个时候就可以依赖BeanDefinition中属性了Class 根据某一个类型来找到该类型bean到底有几个有哪些
    //根据类型去寻找bean如果找到多个就可以根据名字去找,根据名字就可以看我们写的LLBApplicationContext这个类两个map都存放的是beanName,既可以去单例池中去找,也可以去BeanDefinition中去找
    //这就类似byType,byName
    @Autowired
    private OrderService orderService;

    @LLBValue("giao")
    private String giao;

    private String name;

    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化");
    }

    public void test(){
        System.out.println(giao);
        System.out.println(name);
        System.out.println(orderService);
    }


    @Override
    public void setBeanName(String name) {
        this.name = name;
    }
}

 //aware它是在依赖注入完之后,在这个阶段后执行的beanNameAware 再去执行初始化前
//在LLBApplicationContext时改造
          //aware它是在依赖注入完之后,在这个阶段后执行的beanNameAware 再去执行初始化前
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware)instance).setBeanName(beanName);
            }

这样打印的beanName就有值了

           }
        }
    }
    return bean;
}

}




spring中AWare

spring中BeanNameAWare接口

我想知道UserService中的name属性知道自己的bean名字

```java

@Component("userService")
//@Scope("singleton")
//@Scope("prototype")
public class UserService implements UserInterface ,InitializingBean ,BeanNameAware{

    //给这个OrderService赋值,最开始可以按照类型来找  从单例池里去寻找  但是orderService可能在单例池没有找到,因为orderService对象还没有创建,这个时候就可以依赖BeanDefinition中属性了Class 根据某一个类型来找到该类型bean到底有几个有哪些
    //根据类型去寻找bean如果找到多个就可以根据名字去找,根据名字就可以看我们写的LLBApplicationContext这个类两个map都存放的是beanName,既可以去单例池中去找,也可以去BeanDefinition中去找
    //这就类似byType,byName
    @Autowired
    private OrderService orderService;

    @LLBValue("giao")
    private String giao;

    private String name;

    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化");
    }

    public void test(){
        System.out.println(giao);
        System.out.println(name);
        System.out.println(orderService);
    }


    @Override
    public void setBeanName(String name) {
        this.name = name;
    }
}

 //aware它是在依赖注入完之后,在这个阶段后执行的beanNameAware 再去执行初始化前
//在LLBApplicationContext时改造
          //aware它是在依赖注入完之后,在这个阶段后执行的beanNameAware 再去执行初始化前
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware)instance).setBeanName(beanName);
            }

这样打印的beanName就有值了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么软件可以将录音转写为文字?这几款好用软件不容错过

录音实时转写软件有哪些 &#xff08;百&#xff09;录音实时转写软件有哪些&#xff1f;这些录音转文字软件推荐给你 &#xff08;搜&#xff09;音频转文字工具有哪些&#xff1f;推荐三个实用转写软件 &#xff08;企&#xff09;哪些软件可以把录音转写为文字&#xff1f;这…

微信小程序|系统配送员如何登陆抢单?同城配送、预约服务

系统中有多个功能有有配送端&#xff0c;最主要的是同城配送&#xff0c;和预约服务两个功能&#xff0c;首先介绍一下同城配送的配送员登录&#xff08;扩展的同城配送需要开启&#xff09; 第一步&#xff0c;设置模板消息&#xff0c;配送员通过微信模板消息接收配送信息&am…

离婚数据可视化,经济越发达的地区离婚率越高,但为何天津离婚率最高?

这几年我国的离婚率持续走高&#xff0c;2021年全国各个省份平均离婚率在2.1‰&#xff0c;离婚率高于和低于全国平均水平的省份各有15个。 离婚率高于平均水平2.1‰有9个是北方地区&#xff0c;分别是天津、北京、新疆和内蒙古以及黑吉辽三省&#xff1b;南方地区有6个省份高…

[附源码]Python计算机毕业设计电视设备租借系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

最全的Go资料汇总和最走心的学Go建议

推荐语 最用心的Go学习资料和学习建议&#xff0c;可能没有之一。怎么学Go快&#xff1f;有哪些教程建议看&#xff1f;学到什么程度能去找工作&#xff1f;我觉得这个问题的关键要看你的目标是什么&#xff1f;一定要结合你自己的情况去考虑这个事情。 前言 最近不少读者私…

智能学习硬件小程序开发,提升学习感受

随着科学技术的飞速发展&#xff0c;各种智能学习硬件层出不穷&#xff0c;日常生活中的智能电子产品也愈来愈多&#xff0c;但是大部分硬件产品都是单独使用的&#xff0c;并没有配套的app或者小程序&#xff0c;在使用过程中总是会出现一些小插曲&#xff0c;比如&#xff1a…

FL Studio21.0.0中文版发布更新下载支持简体中文版

FL Studio 21.0.0官方中文版重磅发布纯正简体中文支持&#xff0c;更快捷的音频剪辑及素材管理器&#xff0c;多样主题随心换&#xff01;Mac版新增对苹果M2/1家族芯片原生支持。 更新版本&#xff1a;21.0.0支持语言&#xff1a;简体中文/英语更新时间&#xff1a;2022.12.07…

[附源码]Python计算机毕业设计动物园动物饲养管理Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

[附源码]Python计算机毕业设计电影推荐系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

[附源码]Nodejs计算机毕业设计基于web的网上订餐系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

Seekbar和ProgressBar进度条样式

有时项目中需要实现水平圆角进度&#xff0c;如下两种&#xff0c;其实很简单 下面开始看代码&#xff0c;先从主界面布局开始看起: <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/…

计算机网络:应用层

应用层 应用层是计算机体系结构中的最顶层&#xff0c;是直接为应用进程提供服务的。其作用是在实现多个系统应用进程相互通信的同时,完成一系列业务处理所需的服务. 1. C&#xff08;客户端&#xff09;/S&#xff08;服务器&#xff09;和P&#xff08;Peer&#xff09;2P&a…

Java编码实现Activemq通讯

目录 一、IDEA建Maven工程 二、pom.xml 三、JMS编码总体架构 四、粗说目的地Destination——队列和主题 五、点对点消息传递域中&#xff0c;目的地被称为队列&#xff08;queue&#xff09; 六、在发布订阅消息传递域中&#xff0c;目的地被称为主题&#xff08;topic&a…

vue axios实战,请求天气预报接口

效果预览 创建流程 1. 创建项目目录 mkdir test22. 进入目录 cd test23. 引入vue 引入vue&#xff0c; 一路敲回车就行了 npm init vuelatest4. 启动项目 创建成功&#xff0c;启动项目 cd vue-project npm install npm run dev执行run 后的访问结果 > vue-project…

简单得令人发指,说不会一定是在骗领导:配准法在地图上叠加边缘透明旅游图演示

目录 1 前言 2 基本设置 3 配准设置 4 切图 5 更简单的切图方法 1 前言 居然还有人在抱怨&#xff0c;说在地图上叠加手绘图好麻烦啊好复杂&#xff0c;都干了一星期了又要推倒重来简直不想活了&#xff0c;眼睛都累哗啦了。。。嗯&#xff0c;我相信同学你没说假话&#…

网络工程师备考5章

5.1 考点分析 5.2 WLAN基础 注&#xff1a;例如上面图中蜂窝的A&#xff0c;周围全是其他的蜂窝&#xff0c;实现了相同频率的区域隔离&#xff0c;简单了解一下它发展的几个阶段。 注&#xff1a;简单了解即可。 WLAN、802.11Wifi 这三个有什么区别&#xff1f; WLAN是无线局…

链式二叉树(C语言实现)

文章目录&#xff1a;二叉树链式结构实现1.链式二叉树的结构2.遍历二叉树2.1前序遍历2.2中序遍历2.3后序遍历2.4层序遍历3.功能接口3.1二叉树节点个数3.2叶子节点个数3.3树的深度3.4第k层节点个数&#xff08;k>1&#xff09;3.5查找目标节点3.6判断是否为完全二叉树3.7构建…

AI-多模态-2022:TCL【triple contrastive learning】【三重对比学习的视觉-语言预训练模型】

论文&#xff1a;https://arxiv.org/pdf/2202.10401.pdf 代码&#xff1a;https://github.com/uta-smile/TCL 写在前面&#xff1a; CPC[1]这篇论文中&#xff0c;作者对互信息的公式进行了分析&#xff0c;得到互信息下界的相反数为InfoNCE loss&#xff0c;即最小化InfoNC…

IPv4地址和子网掩码

目录 1.ip的定义 A.官方定义 B.IP的表现形式 C.IP地址分类 2.网络地址和主机地址 3.子网掩码 4.应用场景 1.ip的定义 A.官方定义 IP地址是一种在Internet上的给主机编址的方式&#xff0c;也称为网际协议地址。IP地址是IP协议提供的一种统一的地址格式&#xff0c;它为…

three.js之组对象

文章目录简介例子查看组对象组对象相关方法addremove层级模型节点命名、查找、遍历模型命名例子遍历查找本地坐标与世界坐标例子本地坐标世界坐标缩放系数专栏目录请点击 简介 层级模型就是一个树的结构&#xff0c;他有一个组的概念&#xff0c;对于组我们可以进行旋转、平移…