反射与泛型、动态代理

泛型

在Java中的泛型简单来说就是:在创建对象或调⽤⽅法的时候才明确下具体的类型

好处是:代码更加简洁(不再需要强制转换),程序更加健壮(在编译期间没有警告,在运⾏期就不会出现ClassCastException异常), 类型限制提高程序的类型安全(在开发阶段增加了类型检查,类型错误会有警告

集合中常用,IDEA也不建议使用原生集合,也就是没有添加泛型的

另外就是基础组件了,为了通用行会用到泛型

--java3y《对线面试官》

背景

原生集合或者Object类型数组等可以存储任意元素,然而在使用过程中经常需要做类型转换,程序员需要知道每个元素是什么类型,不然很容易发生类型转换异常ClassCastException这种问题在编译期间还不会报错,在运行期间才会报错
 
    @Test
    public void methodNameTest() {
        List list = new ArrayList<>();
        list.add(new Object());
        System.out.println((String)list.get(0));
    }

 

目的

编译阶段提供类型检查,避免运行期间发生ClassCastException

减少强制类型转换的使用

应用

https://juejin.cn/post/6844903925666021389

泛型类

    @Test
    public void methodNameTest() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        System.out.println(list.get(0));
    }

 

 

 

泛型方法

 

泛型接口

 

什么事泛型擦除

编译阶段使用泛型,运行阶段取消泛型,就是泛型擦除

反射

反射就是Java可以给我们在运⾏时获取类的信息

反射就允许程序在执行期借助于API的方法来进行获取任何类的内部数据信息,并能进行直接的操作任意对象的内部属性和方法

运行时:在编译器写的代码是 .java ⽂件,经过javac 编译会变成 .class ⽂件,class ⽂件会被JVM装载运⾏(这⾥就是真正运⾏着我们所写的代码(虽然是被编译过的),也就所谓的运⾏时

在运⾏时获取类的信息,其实就是为了让我们所写的代码更具有「通⽤性」和「灵活性」一般用在工具上

像SpringMVC 你在⽅法上写上对象,传⼊的参数就会帮你封装到对象上

Mybatis可以让我们只写接⼝,不写实现类,就可以执⾏SQL

你在类上加上@Component注解,Spring就帮你创建对象

都有反射的身影:约定⼤于配置,配置⼤于硬编码

通过”约定“使⽤姿势,使⽤反射在运⾏时获取相应的信息(毕竟作为⼀个”⼯具“是真的不知道你是怎么⽤的),实现代码功能的「通⽤性」和「灵活性」

泛型是会擦出的,为什么反射能获取到泛型的信息

(泛型的信息只存在编译阶段,在class字节码就看不到泛型的信息了)

泛型擦除是有范围的,定义在类上的泛型信息是不会被擦除的。Java 编译器仍在 class ⽂件以 Signature 属性的⽅式保留了泛型信息

 

简单使用

 一般是通过Class、Method、Field类提供的方法

 

 

 

 

 

 

 访问私有属性需要开启访问权限

Class user = user.getClass();

Field name = user.getDelcaredField("name");

name.setAccessible(true);

// 获取对象的name属性值

name.get(user);

 

 

 

 

 

动态代理

动态代理其实就是代理模式的⼀种,代理模式是设计模式之⼀

代理模型有静态代理和动态代理。静态代理需要⾃⼰写代理类,实现对应的接⼝,⽐较麻烦

在Java中,动态代理常⻅的⼜有两种实现⽅式:JDK动态代理和CGLIB代理

JDK动态代理其实就是运⽤了反射的机制,⽽CGLIB代理则⽤的是利⽤ASM框架,通过修改其字节码⽣成⼦类来处理

JDK动态代理会帮我们实现接⼝的⽅法,通过invokeHandler对所需要的⽅法进⾏增强

 JDK动态代理

代理目标类实现的接口

package testJdkProxy;
//jdk动态代理必须使用接口
public interface JDKProxy {
public void sayHelloWorld();
}

目标类

package testJdkProxy;
//接口实现,真实主题实现类
public class ImplementClass implements JDKProxy {

    @Override
    public void sayHelloWorld() {
        // TODO Auto-generated method stub
        System.out.println("hello world");
    }

}

 

手写代理类

package testJdkProxy;

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

//代理,代理操作类
public class DynamicProxy implements InvocationHandler{
//真实对象     (通过实现类实现)
private Object target=null;
//建立代理对象和真实对象之间的关系
/**
 * 
 * @param target 真实对象
 * @return  代理对象 
 */
 //接受真实主题实现类(目标对象),把代理对象下挂在接口下 生成下挂后的代理对象来实例化接口
public Object bind(Object target) {
    //保存真实对象,invoke里面会用到    实例化的真实主题实现类 对象——>指向被代理的真实主题
    this.target=target;
    return Proxy.newProxyInstance(
            //实现类的类加载器      实现类所实现的接口          代理对象     ——>把代理对象下挂在接口下
            target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//代理逻辑
/**
 * @param proxy 代理对象
 * @param method 当前调度方法
 * @param args 当前方法参数
 * @return 代理结果返回
 * @throws Throwable 异常
 */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // TODO Auto-generated method stub
    System.out.println("进入代理逻辑方法");
    System.out.println("在调度真实对象之前的服务--------");
    //------------------------------------------------------------------执行前业务逻辑
    Object object=method.invoke(target, args); //相当于调用sayhelloworld方法
    //---------------------------------------------------------------------执行后业务逻辑
    System.out.println("在调度真实对象之后的服务");
    //invoke的返回值
    return object;
}

}

测试类

package testJdkProxy;

public class ToTest {
public static void main(String[] args) {
    new ToTest().testJdkProxy();
}
public void testJdkProxy() {
    //创建代理
        DynamicProxy jdk=new DynamicProxy();
    //绑定关系,实例化接口,下挂代理JDKProxy接口下,通过代理实例化接口对象 
    JDKProxy  pro=(JDKProxy)jdk.bind(new ImplementClass());
    //此时JDKProxy对象已经是一个代理对象,调用方法的时候,他会自动进入代理的逻辑方法invoke里 重复的业务逻辑不用额外手动调用
    pro.sayHelloWorld();
}
}

 

CGLIB动态代理

第三方包 要用到 cglib-nodep-2.2.jar 包
代理要implements MethodInterceptor
原理 Cglib代理可以对任何类生成代理,代理的原理是对目标对象进行继承代理
注意 如果目标对象,被final修饰了,那么该类似无法被cglib代理的
目标类
package testCGLIBOproxy;

public class ProxyedClass {
    public void sayHello() {
        System.out.println("hellloworld");
    }
}

 

代理类

package testCGLIBOproxy;

import java.lang.reflect.Method;

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

//CGLIB不需要提供接口,只需要一个非抽象类就能实现动态代理
public class CglibProxyExample implements MethodInterceptor {
    /**
     * 生成代理对象
     * @param cla——Class类
     * @return Class类的CGLIB代理对象
     */
    
    
    public Object getProxy(Class cla) {
        //CGLIB enhancer 增强类对象      加强者
        Enhancer enhancer=new Enhancer();
        //设置代理谁  setSuperclass设置超类  要继承的目标对象
        enhancer.setSuperclass(cla);
        //setCallback设置代理类    定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
        enhancer.setCallback(this);
        //生成并返回代理对象
        return enhancer.create();
    }
    /**
     * 代理逻辑方法
     * @param arg0代理对象
     * @param arg1方法
     * @param arg2方法参数
     * @param arg3方法代理
     * @return 代理逻辑返回
     * 
     */
    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("调用真实对象前");
        //反射调用真实对象方法 传参数给它
        Object result=arg3.invokeSuper(arg0,arg2);
        //调用真实对象后
        System.out.println("调用真实对象后");
        return result;
    }
    
    
}

 

测试类

package testCGLIBOproxy;

public class TestClass {
public static void main(String[] args) {
    new TestClass().testCGLIB();
}
public void testCGLIB() {
    //创建代理对象
    CglibProxyExample cep=new CglibProxyExample();
    //通过代理实例化被代理对象类
    ProxyedClass obj=(ProxyedClass)cep.getProxy(ProxyedClass.class);
    //与代理关联后,调用方法时,自动进入代理逻辑intercept方法
    obj.sayHello();
}
}

 

 
 
 
posted on 2023-03-04 10:38  or追梦者  阅读(34)  评论(0编辑  收藏  举报