小白---反射(spring底层原理)

反射

1.反射机制

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

1.1如何理解反射

首先反射是具有动态性的
1.反射是Java中特有的一种技术
2.Java中自省特性的一种实现(对象运行时动态发现对象成员,属性,方法)
3.反射是实现Java动态编程的基石(例如:AOP)

1.2反射的应用起点

反射应用的起点,也可以理解为反射应用的入口,在Java中这个入口是字节码对象(Class对象).
获取方式如下:
1.类名.class
2.Class.forName(“包名.类名”) 这种事最常用的
3.实例对象.getClass()
总结: 任意一个类在同一个JVM内部,类对象是唯一的,此类对象会在第一次类加载时创建

1.3反射的应用场景

1.框架中对象的构建:
例如:mybatis中的resultType,resultMap,spring中的Bean
如下代码:之前学习过的分页查询 xml方式
mybatis中的resultType="cn.mry.mybatis.pojo.User"映射的结果封装到这个对象中,
利用反射方式获取User对象
${pageSize}同样也是利用反射原理获取类中的属性,底层是将参数封装了Map集合中

<select id="getUserList2" resultType="User">
        select * from user
        <where>
            <if test="query != null and query !=''"> username like "%"#{query}"%"</if>
        </where>
        limlt ${(pageNum - 1) * pageSize},#{pageSize}
    </select>

spring中的bean注解,spring管理的对象都叫做bean对象,并且这个对象的名字一般与方法名相同,也可以在注解中给对象起名字.可以直接从spring的容器Beans中获取
2.框架中的方法的调用:
例如:对象的set,get方法,spring MVC 控制层方法
Bean对象,也是根据反射创建对象的

 @Bean//对象名默认为方法名
//    @Bean("bCryptPasswordEncoder")bean对象名字为bCryptPasswordEncoder
    public BCryptPasswordEncoder passwordEncoder (){
        return new BCryptPasswordEncoder();
    }

总之: 反射不能遇见未来,但是可以驾驭未来,通过反射可以更好构建一些编程框架,以实现通用变成,从而打到简单代码编写

2.反射起点创建对象

2.1验证:任意一个类在同一个JVM内部,类对象是唯一的,此类对象会在第一次类加载时创建

1.首先准备一个Configuration类.
2.以下为三种通过反射创建对象的方式:
3.用==判断对象是否为同一个对象
框架用的最多的就是这种方式
通过包路径+类名找到这个类,进而可以执行对象的创建,属性的赋值,方法的调用
Class<?> cls3 = Class.forName(“com.mry.reflect.Configuration”);

//首先准备一个类
class Configuration{
   }
   //测试
public class TestReflect01 {
    public static void main(String[] args) throws Exception {
       doClass01();
    }
}
//1.反射起点
    private static void doClass01() throws ClassNotFoundException{
        //获取反射起点对象(字节码对象): 一个JVM最多有一份 类对象只有一份
        //方式1
        Configuration c1 = new Configuration();
        Class<?> cls1 = c1.getClass();
        //方式2
        Class<?> cls2 = Configuration.class;
        //方式3   框架中用的最多的就是这种方式
        //框架用的最多的就是这种方式
        //resultType = "com.mry.reflect.Configuration"
        Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
        System.out.println(cls1 == cls2);
        System.out.println(cls2 == cls3);
        //说明: 此对象在类加载时即可创建
    }

2.2基于类对象创建无参实例对象

  1. 首先利用Class<?> cls3 = Class.forName(“com.mry.reflect.Configuration”);方式拿到这个对象,在通过这个对象构建类的实例对象
  2. 系统底层通过cls3.getDeclaredConstructor();方式获取无参构造函数,通过 con.newInstance();无参构造函数对象,构建类的无参实例对象
  3. 调用doInstance01(),就可以获取到实例对象了.但是这个前提是Configuration类中必须要有无参构造函数,如果没有无参构造函数的话会报错:com.mry.reflect.Configuration.()
class Configuration{
   // private String path;
    public Configuration(){ }
   // public Configuration(String path){
        this.path = path;
    }
}
//测试
public class TestReflect01 {
    public static void main(String[] args) throws Exception {
       doInstance01();
    }
}
//2.基于类对象创建实例 无参
private static void doInstance01() throws Exception{
        //利用这种方式,构建类的实例对象
        //通过反射构建对象时,需要类里有无参构造函数
        //如果类中没有无参构造函数的话,会报错com.mry.reflect.Configuration.<init>()
        Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
        //获取无参构造方法的对象   系统底层通过这种方式获取,这是默认的无参构造函数
        Constructor<?> con = cls3.getDeclaredConstructor();
        //获取类中的私有方法  也就是暴力反射
        con.setAccessible(true);
        //构建类的实例对象  获取的对象都是Object类型的
        Object obj = con.newInstance();
        System.out.println(obj);
    }

2.3基于类对象创建含参实例对象

  1. 利用cls3.getDeclaredConstructor(String.class)获取含参的构造函数
  2. 创建对象时,就需要传参数Object obj = con.newInstance(“classpath:cfg.xml”);
class Configuration{
    private String path;
    public Configuration(){ }
    public Configuration(String path){
        this.path = path;
    }
}
//测试
public class TestReflect01 {
    public static void main(String[] args) throws Exception {
       doInstance02();
    }
}
//3.基于类对象创建实例 含参
private static void doInstance02() throws Exception{
        //利用这种方式,构建类的实例对象
        Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
        //获取含参构造方法的对象   系统底层通过这种方式获取对象
        Constructor<?> con = cls3.getDeclaredConstructor(String.class);
        //获取类中的私有方法  也就是暴力反射
        con.setAccessible(true);
        //构建类的实例对象  获取的对象都是Object类型的
        Object obj = con.newInstance("classpath:cfg.xml");
        System.out.println(obj);
    }

2.4基于类对象创建实例 包含@Component注解时创建对象 调用无参/含参

如果创建对象时需要判断是否有注解修饰时,有注解修饰就创建对象,没有注解修饰就不创建这个对象

  1. 自定义注解Component,该注解可以描述类,并且在运行时有效
  2. 首先判断类是否被Component注解修饰boolean flag = cls3.isAnnotationPresent(Component.class);
  3. 假如需要把对象注入到容器中,可以给对象起个名字,也可以默认(默认名是类的首字母小写)
  4. 以config作为key,实例对象作为value,存储到Map集合中
    利用con.newInstance(“classpath:cfg.xml”)获取实例对象也就是Map中的value,利用cls3.getAnnotation(Component.class)方式,拿到注解.利用comp.value()获取属性,也就是Map中的key
  5. map.put(beanName, obj); beanName=config obj=实例对象
    这就是模拟spring中获取对象的原理
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.TYPE)//可以描述的类型   类中有效
@interface Component{
	String value() default "";
}//注解组件

//有这个注解才允许构建对象
@Component("config")
class Configuration{
    private String path;
    public Configuration(){ }
    public Configuration(String path){
        this.path = path;
    }
    public void setPath(String path) {
        this.path = path;
    }
    public String getPath() {
        return path;
    }
    @Override
    public String toString() {
        return "Configuration{" +
                "path='" + path + '\'' +
                '}';
    }
}
//测试
public class TestReflect01 {
    public static void main(String[] args) throws Exception {
       doInstance03();
    }
}
//4.基于类对象创建实例 包含@Component注解时创建对象  调用无参/含参
    private static void doInstance03() throws Exception{
        //利用这种方式,构建类的实例对象
        // @Component修饰这个类,spring扫描时会将带有注解的扫描进内存
        Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
        //判断是否有Component这个注解    方法同理
        boolean flag = cls3.isAnnotationPresent(Component.class);
        if (!flag)return;
        //获取含参构造方法的对象   系统底层通过这种方式获取对象
        Constructor<?> con = cls3.getDeclaredConstructor(String.class);
        //获取类中的私有方法  也就是暴力反射
        con.setAccessible(true);

        //  Value       构建类的实例对象  获取的对象都是Object类型的
        Object obj = con.newInstance("classpath:cfg.xml");
        //将对象以K,V方式存储到Spring容器中
        Map<String,Object> map = new HashMap<>();

        //拿到注解
        Component comp = cls3.getAnnotation(Component.class);
        //拿到注解后   通过注解.value()获取属性 作为key
        String beanName = comp.value();
        //模拟spring中的原理  将对象存到map容器中
        map.put(beanName, obj);
        //spring中就是以K-V方式存储对象
        System.out.println(map);
    }

2.5基于类对象创建实例 访问属性,给属性赋值

  1. 想要获取属性,首先还是要获取实例对象Class.forName(“com.mry.reflect.Configuration”);
  2. 可以利用cls3.getDeclaredField(“path”);获取属性
  3. 想为属性赋值,首先还是要先创建对象的con.newInstance();
  4. 获取到的属性中有set方法可以为属性赋值f1.set(obj, “classpath:cfg.xml”);
  5. 但是需要注意一点,属性是私有的,所以需要暴力反射,设置属性可以访问f1.setAccessible(true);
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.TYPE)//可以描述的类型   类中有效
@interface Component{   //想要把对象存储到容器中,可以给对象起个名字,系统底层也可以是类名首字母小写
    String value() default "";//定义
}//注解组件

//类对象
@Component("config")
class Configuration{
    private String path;
    public Configuration(){ }
    public Configuration(String path){
        this.path = path;
    }
    public void setPath(String path) {
        this.path = path;
    }
    public String getPath() {
        return path;
    }
    @Override
    public String toString() {
        return "Configuration{" +
                "path='" + path + '\'' +
                '}';
    }
}

//测试
public class TestReflect01 {
    public static void main(String[] args) throws Exception {
       doField01();
    }
}
public static void doField01() throws Exception{
        //在框架中不知道写的是什么类,所以通过这种方式获取对象
        Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
        //获取对象中的属性
        Field f1 = cls3.getDeclaredField("path");
        //想为属性赋值一定是要有对象的情况下才能给对象的属性赋值
        System.out.println(f1);
        //获取无参构造方法的对象   系统底层通过这种方式获取对象
        Constructor<?> con = cls3.getDeclaredConstructor();
        //获取类中的私有方法  也就是暴力反射
        con.setAccessible(true);
        //构建类的实例对象  获取的对象都是Object类型的
        Object obj = con.newInstance();
        //获取类中的私有属性  暴力反射
        f1.setAccessible(true);
        //属性是私有的  要给目标对象赋值,不能直接赋值  需要加con.setAccessible(true)
//        Class com.mry.reflect.TestReflect01 can not access a member of class com.mry.reflect.Configuration with modifiers "private"
        f1.set(obj, "classpath:cfg.xml");
    }

2.6基于类对象创建实例 调用类中某个对象的方法

  1. 对于框架来说,框架不知道属性具体的类型,所以不能直接通过obj直接调用,所以只能通过放射方式调用
  2. 与属性类似,利用反射cls3.getDeclaredMethod(“setPath”, String.class);调用set方法给属性赋值
  3. 调用到set方法后利用method.invoke(obj,“classpath:cfg.xml”);给属性赋值
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.TYPE)//可以描述的类型   类中有效
@interface Component{   //想要把对象存储到容器中,可以给对象起个名字,系统底层也可以是类名首字母小写
    String value() default "";//定义
}//注解组件

//类对象
@Component("config")
class Configuration{
    private String path;
    public Configuration(){ }
    public Configuration(String path){
        this.path = path;
    }
    public void setPath(String path) {
        this.path = path;
    }
    public String getPath() {
        return path;
    }
    @Override
    public String toString() {
        return "Configuration{" +
                "path='" + path + '\'' +
                '}';
    }
}

//测试
public class TestReflect01 {
    public static void main(String[] args) throws Exception {
       doMethod01();
    }
}
public static void doMethod01() throws Exception{
        //在框架中不知道写的是什么类,所以通过这种方式获取对象
        Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
        //获取无参构造方法的对象   系统底层通过这种方式获取对象
        Constructor<?> con = cls3.getDeclaredConstructor();
        //获取类中的私有方法  也就是暴力反射
        con.setAccessible(true);
        //构建类的实例对象  获取的对象都是Object类型的
        Object obj = con.newInstance();
        //调用对象的方法
        Method method = cls3.getDeclaredMethod("setPath", String.class);
        //通过反射执行方法
        method.invoke(obj,"classpath:cfg.xml");
        System.out.println(obj);
        Method method1 = cls3.getDeclaredMethod("getPath");
        method1.invoke(obj);
        method.invoke(obj);
        System.out.println(obj);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我想睡到自然醒y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值