java--反射

反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射就是把java类中的各种成分映射成一个个的Java对象,在运行时可以知道任意一个类的属性和方法

Class类介绍

作用:
一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
获取Class对象的三种方式:

  • 类名.class
  • 对象.getClass()
  • Class.forName(类全名)

获取属性:

  • getField
  • getFields
  • getDeclaredFields
  • getDeclaredField

获取方法:

  • getMethod
  • getMethods
  • getDeclareMethod
  • getDeclareMethods
  • invoke

获取构造方法:

  • getConstructor
  • getConstructors
  • getDeclaredConstructor
  • getDeclaredConstructors

创建对象:

  • newInstance
  • Constructors.newInstance

获取父类:
接口

  • getAnnotatedInterfaces—AnnotatedType[]
  • getInterfaces—Class[]
    父类
  • getSuperclass
  • getInterfaces

Modifier: 修饰符

内省

内省是指计算机程序在运行时(Run time)检查对象(Object)类型的一种能力,通常也可以称作运行时类型检查。
不应该将内省和反射混淆。相对于内省,反射更进一步,是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。
在这里插入图片描述

javaBean

JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。

获取Beaninfo

BeanInfo这个类就可以获取到类中的方法和属性。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性
方法:

  • introspector.getBeanInfo(Class A)—获取BeanInfo
  • beaninfo.getPropertyDescriptors();
  • getReadMethod
  • getWriteMethod

动态代理

需加入asm-9.2.jar,cglib-3.3.0.jar这两个包。

JDK动态代理

底层实现:

  • 通过实现 InvocationHandler 接口创建自己的调用处理器;
  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

代码:

package com;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyProxy {
    public static void main(String[] args) {
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        HelloInterface hello = new Hello();
        
        InvocationHandler handler = new ProxyHandler(hello);

        HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler);

        proxyHello.sayHello();
    }
}

interface HelloInterface {
    void sayHello();
}

class Hello implements HelloInterface{
    @Override
    public void sayHello() {
        System.out.println("Hello World!");
    }
}
class ProxyHandler implements InvocationHandler{
    private Object object;
    public ProxyHandler(Object object){
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invoke "  + method.getName());
        method.invoke(object, args);
        System.out.println("After invoke " + method.getName());
        return null;
    }
}

CGLIB代理(第三方)

cglib是一个java字节码的生成工具,它动态生成一个被代理类的子类,子类重写被代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

代码:

package com;

import java.lang.reflect.Method;

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGProxy {
	public static void main(String[] args) {
		Enhancer enhancer = new Enhancer();
		// 继承被代理类
		enhancer.setSuperclass(HelloServiceImpl.class);
		// 设置回调
		enhancer.setCallback(new HelloMethodInterceptor());
		// 设置代理类对象
		HelloServiceImpl helloService = (HelloServiceImpl) enhancer.create();
		// 在调用代理类中方法时会被我们实现的方法拦截器进行拦截
		helloService.sayBey();
	}
}

class HelloServiceImpl {
	public void sayHello() {
		System.out.println("Hello Zhanghao");
	}

	public void sayBey() {
		System.out.println("Bye Zhanghao");
	}
}

class HelloMethodInterceptor implements MethodInterceptor {
	@Override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		System.out.println("Before: " + method.getName());
		Object object = methodProxy.invokeSuper(o, objects);
		System.out.println("After: " + method.getName());
		return object;
	}
}

总结

  • 静态代理比较容易理解, 需要被代理的类和代理类实现自同一个接口, 然后在代理类中调用真正实现类, 并且静态代理的关系在编译期间就已经确定了。而动态代理的关系是在运行期间确定的。静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性。
  • JDK 动态代理所用到的代理类在程序调用到代理类对象时才由 JVM 真正创建,JVM 根据传进来的业务实现类对象以及方法名 ,动态地创建了一个代理类的 class 文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。
  • 静态代理和动态代理都是基于接口实现的, 而对于那些没有提供接口只是提供了实现类的而言, 就只能选择 CGLIB 动态代理了

JDK动态代理与CGLIB动态代理的区别(面试题

  • JDK 动态代理基于 Java 反射机制实现, 必须要实现了接口的业务类才能用这种方法生成代理对象。
  • CGLIB 动态代理基于 ASM 框架通过生成业务类的子类来实现。
  • JDK 动态代理的优势是最小化依赖关系,减少依赖意味着简化开发和维护并且有 JDK 自身支持。还可以平滑进行 JDK 版本升级,代码实现简单。
  • 基于 CGLIB 框架的优势是无须实现接口,达到代理类无侵入,我们只需操作我们关系的类,不必为其它相关类增加工作量,性能比较高。

描述代理的几种实现方式?分别说出优缺点

  • 代理可以分为 “静态代理” 和 “动态代理”,动态代理又分为 “JDK 动态代理” 和 “CGLIB 动态代理” 实现。
  • 静态代理:代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object.
    优点: 可以很好的保护实际对象的业务逻辑对外暴露,从而提高安全性。
    缺点: 不同的接口要有不同的代理类实现,会很冗余。
  • JDK 动态代理:
    为了解决静态代理中,生成大量的代理类造成的冗余;
    JDK 动态代理只需要实现 InvocationHandler 接口,重写 invoke 方法便可以完成代理的实现。
  • jdk 的代理是利用反射生成代理类 Proxyxx.class 代理类字节码,并生成对象。
  • jdk 动态代理之所以只能代理接口是因为代理类本身已经 extends 了 Proxy,而 java 是不允许多重继承的,但是允许实现多个接口
    优点:解决了静态代理中冗余的代理实现类问题。
    缺点:JDK 动态代理是基于接口设计实现的,如果没有接口,会抛异常。
  • CGLIB 代理:
    由于 JDK 动态代理限制了只能基于接口设计,而对于没有接口的情况,JDK 方式解决不了;
  • CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。
  • CGLib 在创建代理对象时所花费的时间却比 JDK 多得多,所以对于单例的对象,因为无需频繁创建对象,用 CGLib 合适,反之,使用 JDK 方式要更为合适一些。
  • 由于 CGLib 由于是采用动态创建子类的方法,对于 final 方法,无法进行代理。
    优点:没有接口也能实现动态代理,而且采用字节码增强技术,性能也不错。
    缺点:技术实现相对难理解些。

Lombok

安装:

作用:
帮使用者提高编码效率减少重复与冗余的代码
原理:
ASM 动态修改class文件

java bean相关

@Setter
@Getter

  • @Getter(lazy = true) 懒加载属性(懒汉)
  • 这个与上面@Getter不同,那个是修饰在类上的,也可以修饰在属性上。如果有lazy=true只能修饰在属性,并且还要是private final修饰,限制很大.

@ToString
@EqualsAndHashCode:生成equals方法与hashCode方法
@NoAragsConstructor

  • 添加一个无参构造函数
  • 这个注解在没有其它有参构造函数的情况下使用意义不大,因为在这种情况下java默认会添加一个无参构造函数

@AllArgsConstructor:添加一个所有参数的构造函数
@RequiredArgsConstructor

  • 生成一个包含必填参数的构造函数
  • 要与@NonNull 搭配使用,该注解修饰的属性就是必填参数

@Date: 这是一个综合注解了,等于同时使用@Getter, @Setter, @ToString,@EqualsAndHashCode,@RequiredArgsConstructor
@Value: 相当于不可变类的@Date,他会默认给属性加上final
@Accessors

  • 这个注解要搭配@Getter与@Setter使用,用来修改默认的setter与getter方法的形式
  • @Accessors有三个参数可以使用,chain 链式的形式;fluent 流式的形式(getter和setter方法名和属性名一致)prefix 生成指定前缀的属性的getter与setter方法,并且生成的getter与setter方法时会去除前缀(@Data不起作用)

@Synchroized:给方法加一个同步锁
@NonNull: 设置不能为空的参数或属性
@SneakyThrows:忽略异常(throws)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值