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

手写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就有值了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值