第一阶段.模块二Spring自定义注解实现IOC容器笔记

前言

文章内容输出来源:拉勾教育Java高薪训练营。P7课程
本篇文章是学习课程中的一部分课后笔记。

一、作业

自定义@Service、@Autowired、@Transactional注解类, 完成基于注解的IOC容器(Bean对象创建及依赖注入维护)和声明式事务控制,写到转账工程中,并且可以实现转账成功和转账异常时事务回滚。

注意考虑以下情况:
1.注解有无value属性值【@service(value="") @Repository(value="")】
2.service层是否实现接口的情况【如果实现接口则用jdk代理,如果没有实现接口则用cglib代理】
这里因为自定义的功能很简单,在自定义里面service注解和repository注解要起到的作用一个样,所有这里我就只建了一个service注解,统一都用service注解去做处理。

二、做题思路,实现步骤。

1.扫描包下所有的@service,通过反射完成对象实例化
2.根据@Autowired完成注解的依赖关系
3.针对@Transactional,修改对象为对应的代理对象
4.对外提供获取实例对象的接口(根据id获取)

三、代码

1.首先对注解类进行开发

package com.lagou.edu.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 聂建明
 */

/*
关键字@interface,在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。
元注解(专门修饰注解的注解):
@Target注解,用来限定某个自定义注解能够被应用在哪些Java元素上面的。
@Retention注解,用来修饰自定义注解的生命周期,使用了RetentionPolicy枚举类型定义了三个阶段
注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。
 */
@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {

    /*
    注解类型元素:
    1.访问修饰符必须为public,不写默认为public;
    2.只能是基本数据类型(不包含对应的封装类型)、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及这些类型的数组类型;
    3.该元素的名称一般定义为名词,如果注解中只有一个元素,尽量把名字起为value(后面使用会带来便利操作);
    4.()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;
    5.default代表默认值,值必须和定义的类型一致;
    6.如果没有默认值,代表后续使用注解时必须给该类型元素赋值;

    注解类型元素的语法非常奇怪,即有属性的特征(可以赋值),又有方法的特征(打上了一对括号),
    但是这么设计是有道理的,使用的时候操作元素类型像在操作属性,解析的时候操作元素类型像在操作方法。
     */
    String value() default "";
    
}

2.编写初始化容器功能

package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * @author 聂建明
 * <p>
 * 工厂类,生产对象(使用反射技术)
 */
public class BeanFactory {

    /**
     * 1.扫描包下所有的@service,通过反射完成对象实例化
     * 2.根据@Autowired完成注解的依赖关系
     * 3.针对@Transactional,修改对象为对应的代理对象
     * 4.对外提供获取实例对象的接口(根据id获取)
     */

    private static Map<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); // 存储对象


    //声明静态方法块,一开始就加载
    //扫描包通过反射技术实例化对象并且存储待用(map集合)
    static {
        try {
            //扫描包获取反射对象集合
        /*
        Reflections通过扫描classpath,索引元数据,允许在运行时查询这些元数据,也可以保存收集项目中多个模块的元数据信息。
        使用 Reflections 可以查询以下元数据信息:
        1.获取某个类型的全部子类;
        2.只要类型、构造器、方法,字段上带有特定注解,便能获取带有这个注解的全部信息(类型、构造器、方法,字段);
        3.获取所有能匹配某个正则表达式的资源;
        4.获取所有带有特定签名的方法,包括参数,参数注解,返回类型;
        5.获取所有方法的名字;
        6.获取代码里所有字段、方法名、构造器的使用;
         */
            Reflections reflections = new Reflections("com.lagou.edu");
            //获取带有Service注解类型的类
            Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(Service.class);
            for (Class<?> resourceAsStream : typesAnnotatedWith) {
                //通过反射技术实例化对象
                Object beanObejct = resourceAsStream.getDeclaredConstructor().newInstance();
                //获取注解信息
                Service annotation = resourceAsStream.getAnnotation(Service.class);

                //该实例service注解有用value时用value的值,没有时用类名
                if (StringUtils.isEmpty(annotation.value())) {
                    //由于getName获取的是全限定类名,所以要分割去掉前面包名部分
                    Class<?>[] interfaces = resourceAsStream.getInterfaces();
                    for (Class<?> interfaceClass : interfaces) {
                        String[] names = interfaceClass.getName().split("\\.");
                        map.put(names[names.length - 1], beanObejct);
                    }
                } else {
                    map.put(annotation.value(), beanObejct);
                }
            }

            //实例化对象后,开始维护对象的依赖关系Autowired,检查哪些对象需要传值进入
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                Object bean = entry.getValue(); //获取bean
                Class<?> beanClass = bean.getClass(); //获取对应的class
                //获取所有变量
                Field[] declaredFields = beanClass.getDeclaredFields();
                //遍历变量,如果持有Autowired注解则注入
                for (Field field : declaredFields) {
                    //判断这个变量是否使用了注解
                    if (field.isAnnotationPresent(Autowired.class)) {
                        //获取变量的类型名(这里基本等于获取类名)
                        String[] names = field.getType().getName().split("\\.");
                        String name = names[names.length - 1];

                        //用到Autowired地方得有set方法,方便beanClass.getDeclaredMethods()获取
                        Method[] declaredMethods = beanClass.getDeclaredMethods();
                        for (int i = 0; i < declaredMethods.length; i++) {
                            Method declaredMethod = declaredMethods[i];
                            if (declaredMethod.getName().equalsIgnoreCase("set" + name)) {
                                declaredMethod.invoke(bean, map.get(name));
                            }
                        }
                    }
                }
            }

            //维护事务,进行aop切面增强
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                Object bean = entry.getValue(); //获取bean
                Class<?> beanClass = bean.getClass(); //获取对应的class
                //判断对象类是否有Transactional注解,如果有则修改对象为代理对象
                if (beanClass.isAnnotationPresent(Transactional.class)) {
                    //获取代理工厂
                    ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
                    Class<?>[] interfaces = beanClass.getInterfaces(); //获取bean实现的所有接口
                    //判断对象是否实现接口
                    if (null == interfaces || interfaces.length > 0) {
                        //没实现使用CGLIB代理
                        bean = proxyFactory.getCglibProxy(bean);
                    } else {
                        //实现使用JDK代理
                        bean = proxyFactory.getJdkProxy(bean);
                    }

                    //把处理好的bean重新放入map中
                    map.put(entry.getKey(), bean);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    // 对外提供获取实例对象的接口(根据id获取)
    public static Object getBean(String id) {
        return map.get(id);
    }

}

三、解说视频和完整的代码地址

代码地址(里面有视频地址):spring自定义注解实现IOC容器

四、IOC/AOP介绍与源码刨析

笔记地址:Spring介绍与核心思想(IOC/AOP)

笔记地址:IOC介绍与源码剖析

笔记地址:AOP介绍与源码剖析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值