反射与泛型、动态代理
泛型
在Java中的泛型简单来说就是:在创建对象或调⽤⽅法的时候才明确下具体的类型
好处是:代码更加简洁(不再需要强制转换),程序更加健壮(在编译期间没有警告,在运⾏期就不会出现ClassCastException异常), 类型限制提高程序的类型安全(在开发阶段增加了类型检查,类型错误会有警告)
集合中常用,IDEA也不建议使用原生集合,也就是没有添加泛型的
另外就是基础组件了,为了通用行会用到泛型
--java3y《对线面试官》
背景
@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动态代理
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(); } }
作者: deity-night
出处: https://www.cnblogs.com/deity-night/
关于作者:码农
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可邮件(***@163.com)咨询.