学习java深度学习,提升编程思维,适合掌握基础知识的工作者学习
- 1.反射和代理
- 1.1 概念介绍
- 1.2应用场景
- 1.3 反射-reflect
- 1.3.1 获得类-Class
- 1.3.2 获得类的字段-Field
- 1.3.3 动态访问和修改对象实例的字段
- 1.3.4 获得类方法-Method
- 1.3.5 调用方法.invoke
- 1.3.6 类实例化-构造函数Constructor
- 1.3.7 instanceof
- 1.3.8 利用反射来解析spring配置
- 1.4 代理-proxy
- 1.4.1 代理模式
- 1.4.2 准备工作
- 1.4.3 静态代理
- 1.5 动态代理
- 1.5.1 InvocationHandler介绍
- 1.5.2 Proxy类
- 1.5.3动态代理类的实现
- 1.5.4动态实现接口
- 1.5.4替换某个方法
- 1.5 动态代理框架CGLIB
- 1.5.1 使用介绍
- 1.5.2 cglib说明
- 1.6 总结
- 2. java小干货
- 2.1循环遍历
- 2.2可变参数
- 2.3 list和数组转化
- 2.4 地址引用
- 2.5集合
- 2.6文件流
- 2.7java代码块、内部类和匿名类
- 2.8 java泛型及通配符
- 2.9 日期类LocalDate
- 2.10枚举
- 2.11 java常见数据结构
- 3. java注解
- 4. lambda语言
- 5.http网络
- 6.java线程
1.反射和代理
1.1 概念介绍
在Java编程中,反射和代理是两个强大的特性,能够在运行时动态地操作和扩展类的行为。通过反射,我们可以在不知道类的具体信息的情况下操作类的属性和方法;而代理则允许我们创建一个代理类来拦截并增强目标对象的行为
1.2应用场景
像咱们平时大部分时候都是在写业务代码,很少会接触到直接使用反射机制的场景。
但是,这并不代表反射没有用。相反,正是因为反射,你才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制,掌握反射和代理机制,有利于我们了解这些框架的内核原理,提升编程思维
- xml的bean配置的注入
- AOP拦截
- 数据库事务
- springMVC
- Java注解
- 开发工具如IDEA,提供一个类的属性方法展示给我们智能快捷选择
这些内核都是反射和代理机制在其作用
1.3 反射-reflect
由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射(Reflection)。
我们正常得到一个对象实例是通过new方法创建对象;反射就是通过class来实现,
1.获得类class
2.获得类的字段属性和方法
3.获得类的实例对象
4.获得对方法的调用
准备工作
Food.java
public class Food {private Integer id;private String name;// ....省略get set 等
}
1.3.1 获得类-Class
获得类有三种方式,看代码演示
类.class
对象.getClass();
Class.forName(“类名”)
/**** @Description: 得到Class*/@Testpublic void t1() throws ClassNotFoundException {Class cls=null;//1.通过类名直接获得cls= Food.class;//2.通过实例获得cls=new Food(1, "火锅").getClass();//3.通过路径+类名的字符串获得cls=Class.forName("com.jsoft.reflection.Food");printClassInfo(cls);}/**** @Description: 读取类的信息*/static void printClassInfo(Class cls) {System.out.println("Class name: " + cls.getName());System.out.println("Simple name: " + cls.getSimpleName());if (cls.getPackage() != null) {System.out.println("Package name: " + cls.getPackage().getName());}System.out.println("is interface: " + cls.isInterface());System.out.println("is enum: " + cls.isEnum());System.out.println("is array: " + cls.isArray());System.out.println("is primitive: " + cls.isPrimitive());}
打印效果
Class name: com.jsoft.reflection.Food
Simple name: Food
Package name: com.jsoft.reflection
is interface: false
is enum: false
is array: false
is primitive: false
1.3.2 获得类的字段-Field
演示代码
public class Father extends Man{private Integer age;public String job;}public class Man{private Integer id;private String name;public String address;}
对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。
我们先看看如何通过Class实例获取字段信息。Class类提供了以下几个方法来获取字段:
方法 | 说明 |
---|---|
Field getField(name) | 根据字段名获取某个public的field(包括父类) |
Field[] getFields() | 获取所有public的field(包括父类) |
Field getDeclaredField(name) | 根据字段名获取当前类的某个field(不包括父类) |
Field[] getDeclaredFields() | :获取当前类的所有field(不包括父类) |
注意:
Declared修饰就只管当前类字段,和private、public无关
否则就包含父类的public
测试代码
@Testpublic void t2() throws NoSuchFieldException {Class cls= Father.class;//1.得到当前类的所有字段,不包含父类Field[] fs=cls.getDeclaredFields();System.out.println("1.得到当前类的所有字段,不包含父类");for (Field f : fs) {printFeild(f);}//2.得到当前类某个字段System.out.println("2.得到当前类某个字段,不包含父类");Field f=cls.getDeclaredField("job");printFeild(f);//3.得到当前类及父类的所有public字段System.out.println("3.得到当前类及父类的所有public字段");fs=cls.getFields();for (Field f1 : fs) {printFeild(f);}//4.得到当前类及父类的public字段System.out.println("4.得到当前类及父类的public字段,如果是private,则要报错");f=cls.getField("address");printFeild(f);}/**** @Description: 打印字段信息*/public void printFeild( Field f){System.out.println("//------------字段信息 start");System.out.println("字段名称:"+f.getName());System.out.println("字段类型:"+f.getType().getName());int m = f.getModifiers();System.out.println("field is final:"+Modifier.isFinal(m));; // trueSystem.out.println("field is Public:"+Modifier.isPublic(m));; // trueSystem.out.println("field is Protected:"+Modifier.isProtected(m));; // trueSystem.out.println("field is Private:"+Modifier.isPrivate(m));; // trueSystem.out.println("field is Static:"+Modifier.isStatic(m));; // trueSystem.out.println("//------------字段信息 end");}
1.3.3 动态访问和修改对象实例的字段
我们可以通过一个对象对应的class去动态访问或修改对象的字段
Field.set(vo对象,修改值) //修改字段值
Feild.get(vo对象) //获得字段值
Feild.setAccessible(true); //私有字段,需要设置这个允许访问,否则报异常
@Testpublic void t3() throws NoSuchFieldException, IllegalAccessException {Food food=new Food(1, "火锅");System.out.println("food.getName:"+food.getName());Class cls=food.getClass();Field f=cls.getDeclaredField("name");f.setAccessible(true); //不设置这个private 字段要抛出异常//通过field获得值Object name= f.get(food);System.out.println("name:"+name);//通过field设置值f.set(food, "西北风");System.out.println("name:"+food.getName());}
1.3.4 获得类方法-Method
方法 | 说明 |
---|---|
Method getMethod(name, Class…) | 获取某个public的Method(包括父类) |
Method[] getMethods() | 获取所有public的Method(包括父类) |
Method getDeclaredMethod(name, Class…) | 获取当前类的某个Method(不包括父类) |
Method[] getDeclaredMethods() | :获取当前类的所有Method(不包括父类) |
示例代码
@Data
public class Father extends Man{private Integer age;public String job;public String play(int type){System.out.println("param value:"+type);return type+"";}public String sleep(String type, Date date){System.out.println("param value type:"+type+";date:"+date);return type+"";}private void self(){System.out.println("this is private method");}public void dd(){System.out.println("没有参数方法()");}}@Data
public class Man{private Integer id;private String name;public String address;@Overridepublic Food eat() {return new Food(1,"火锅");}
}
测试代码
@Testpublic void t4() throws IllegalAccessException, NoSuchMethodException {Class cls= Father.class;System.out.println("1. 获取所有public的Method(包括父类)");Method[] methods=cls.getMethods();for (Method method : methods) {printMethod(method);}System.out.println("2. 获取某个public的Method(包括父类)");//第一个参数为方法名,第二参数是可变参数,传递方法的参数类型,如果无参数则不传递Method m= cls.getMethod("getName");printMethod(m);//方法有1个参数//注意int foat double这些基本类型对用的类是int.class,不是Integer.classm=cls.getMethod("play", int.class);printMethod(m);//方法有多个参数m=cls.getMethod("sleep", String.class, Date.class);printMethod(m);System.out.println("3. 获取当前类的的Method(不包括父类)");m= cls.getDeclaredMethod("getJob");printMethod(m);//私有方法也可以m= cls.getDeclaredMethod("self");//父类方法不可以,要报java.lang.NoSuchMethodExceptionm= cls.getDeclaredMethod("eat");printMethod(m);printMethod(m);}public void printMethod(Method addMethod){System.out.println("---------方法名称: 【" + addMethod.getName()+"】---相关属性------------");System.out.println("修饰符: " + Modifier.toString(addMethod.getModifiers())); //public private等System.out.println("返回值: " + addMethod.getReturnType()); //返回值类型的class数组Class[] paramsCls= addMethod.getParameterTypes(); //参数值类型对应的class数组}
1.3.5 调用方法.invoke
我们可以用Method.invoke(vo,参数)来调用方法
代码
@Testpublic void t5() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Father father=new Father();Class cls= father.getClass();Method m=null;//无参无返回方法m=cls.getMethod("dd");m.invoke(father);//有参数,有返回值m=cls.getMethod("sleep", String.class,Date.class);String ret=(String)m.invoke(father, "1",new Date());System.out.println("返回值:"+ret);//private方法调用m=cls.getDeclaredMethod("self");//私有方法必须设置为m.setAccessible(true);m.setAccessible(true);m.invoke(father);}
jdk内置对象的方法调用的另外一种写法
@Testpublic void t6() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {String s="abcde";String sub=s.substring(0,2);System.out.println(sub);//通过类加载Class cls=String.class;Method method=cls.getMethod("substring",int.class,int.class);sub=(String) method.invoke(s, 0,2);System.out.println(sub);}
Method m =Father.class.getMethod(“dd”);
m.invoke(new Father());
其实就相当于
Father f=new Father();
f.dd();
1.3.6 类实例化-构造函数Constructor
实例化对象有3中方法
1.我们都知道的new 对象Father father=new Father();
2.通过类class:newInstance
3.构造方法:Constructor
准备类
public class Const {private Integer id;private String name;public Const() {System.out.println("无参数构造方法");}public Const(Integer id, String name) {System.out.println("有参数构造方法");this.id = id;this.name = name;}
}
在很多框架里,我们为了足够的扩展,都通过配置来实现类的实例化
Class.newInstance()
@Testpublic void t7() throws ClassNotFoundException, InstantiationException, IllegalAccessException {Class cls =Class.forName("com.jsoft.reflection.Const");Const aConst = (Const) cls.newInstance();}
Class.newInstance()最大的问题是只能实例化无参的构造方法,如果我们把无参构造方法屏蔽掉,代码会出错,所以我们需要使用Constructor类来实现有参的构造方法
这里是引用一共有4种方法,全部都在Class类中:
- getConstructors():获取类中的公共方法
- getConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型
- getDeclaredConstructors(): 获取类中所有的构造方法(public、protected、default、private)
- getDeclaredConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型
代码示例
@Testpublic void t8() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {Class cls =Class.forName("com.jsoft.reflection.Const");//得到所有构造函数Constructor[] constructors= cls.getConstructors();for (Constructor constructor : constructors) {System.out.println("修饰符: " + Modifier.toString(constructor.getModifiers()));System.out.println("构造函数名: " + constructor.getName());System.out.println("参数列表: " );Class[] cs= constructor.getParameterTypes();for (Class c : cs) {System.out.println(c.getName());}}//获得具体一个(无参)Constructor cst=cls.getConstructor();//newInstance实例化Const const1=(Const) cst.newInstance();//获得具体一个(有参)cst=cls.getConstructor(Integer.class,String.class);//传入构造函数的参数的类型//newInstance实例化const1=(Const) cst.newInstance(1,"蒋增奎");}
1.3.7 instanceof
instanceof是判断一个实例对象是不是一个类的实例,是则返回true
对象 instanceof 类
class Person{
}class Teacher extends Person{
}class Student extends Person{
}
测试
public class test01 {public static void main(String[] args) {Object obj = new Student(); // 主要看这个对象是什么类型与实例化的类名System.out.println(obj instanceof Student); // trueSystem.out.println(obj instanceof Person); // trueSystem.out.println(obj instanceof Object); // trueSystem.out.println(obj instanceof String); // falseSystem.out.println(obj instanceof Teacher); // false 无关系System.out.println("========================");Person person = new Student();System.out.println(person instanceof Person); // trueSystem.out.println(person instanceof Object); // true// System.out.println(person instanceof String); // 编译错误System.out.println(person instanceof Teacher); // false 无关系}
}
1.3.8 利用反射来解析spring配置
<bean id="employee" class="com.jsoft.po.Employee"><property name="id" value="1" /><property name="name" value="蒋增奎" /><property name="deptId" value="001" />
</bean>
解析思路
1.用dom4j解析xml得到xml数据
2.创建类:Class cls=Class.forName(“Employee”);
3.创建对象:Employee obj=(Employee)cls.newInstance();
4.调用修改字段或者方法给字段注入值
1.4 代理-proxy
1.4.1 代理模式
代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
代理模式作用:
代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一>些自定义的操作。
代理模式价值
代理模式的价值在于不修改代理类代码,增加其他功能,我们理解成我们通常所说的赋能
(1)比如日常生活中的品牌加盟,我们忽悠一个饭店加盟一个品牌连锁,我们忽悠饭店,你只需要开店,营销、菜品、供应链我们全部帮你搞定。
(2)比如老板要求我们所有方法都要加入日志,优雅的方法,不是修改以前代码,而是增加一个代理,把在代理类去处理日志,这就是spring框架的日志、事务原理
举个例子:
boy向girl求婚
(1)现代:boy和girl直接发生通信,girl迫不及待的同意了
(2)古代:boy不能直接和girl发生关系,需要通过媒婆,boy向媒婆发起请求,媒婆转告girl,gilr同意,媒婆觉得女孩如果同意,婚前应该收彩礼10万,婚后不能家暴,媒婆就是代理,在原始的反馈上增加了自己的要求,提升了框架的健壮性。
代理分为静态代理和动态代理两种类型
1.4.2 准备工作
public interface Love {/**** @Description: 结婚请求*/public void marray();/**** @Description: 睡觉请求*/public void sleep();public String like(int type);public int saveMoney(int money);
}/**
接口实现类
**/
public class LoveImpl implements Love {@Overridepublic void marray() {System.out.println("我同意");}public void sleep() {System.out.println("我同意");}public String like(int type){if(type==1)return "69";return "96";}@Overridepublic int saveMoney(int money) {return money*10;}
}
1.4.3 静态代理
静态代理实现步骤:
1.定义一个接口及其实现类;
2.创建一个代理类同样实现这个接口
3.将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。
代码:
//代理类要实现接口
public class LoveProxy implements Love {private Love target;//代理的接口/**** @Description: 构造方法注入代理的接口*/public LoveProxy(Love love){target=love;}@Overridepublic void marray(){System.out.println("============代理marray方法");System.out.println("要彩礼10万");target.marray();System.out.println("不能家暴");}public void sleep(){System.out.println("============代理sleep方法");System.out.println("安全措施");target.sleep();}public String like(int type){System.out.println("============代理like方法");System.out.println("注意身体");return target.like(type);}public static void main(String[] args) {//实际应用:代理的应用Love love=new LoveImpl();LoveProxy proxy=new LoveProxy(love);proxy.marray();proxy.sleep();System.out.println( proxy.like(1));}}
效果:在以前的需求上扩展了功能
============代理marray方法
要彩礼10万
我同意
不能家暴
============代理sleep方法
安全措施
我同意
============代理like方法
注意身体
69
总结:代理模式的实现
1.给一个要代理的类升级一个接口
2.代理目标类和代理类都实现这个接口
3.把目标类传入代理类。代理类是方法里面去封装目标类的实现
1.5 动态代理
静态代理:通过手动编写代理类来实现,需要为每个目标对象编写一个代理类。
动态代理:通过Java提供的相关接口和类,在运行时动态生成代理类,无需手动编写代理类。
我们仍然先定义了接口,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。
动态代理三个重要的java类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
1.5.1 InvocationHandler介绍
代理实例的调用处理器需要实现InvocationHandler接口,并且每个代理实例都有一个关联的调用处理器。当一个方法在代理实例上被调用时,这个方法调用将被编码并分派到其调用处理器的invoke方法上。
接口里最重要的方法invoke
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
其有三个参数,分别为:
proxy:是调用该方法的代理实例,是java在编译环节自动生成的,对开发者没有意义,如:jdk.proxy1.$Proxy0,注意不是代理的目标类,也不是接口。
method:是在代理实例上调用的接口方法对应的Method实例。
args:一个对象数组,其中包含在代理实例上的方法调用中传递的参数值,如果接口方法不带任何参数,则为 null。基元类型的参数包装在相应基元包装类的实例中,例如 java.lang.Integer 或 java.lang.Boolean
返回值:调用代理实例上的方法的返回值。
如果接口方法声明的返回类型是基元类型,那么该方法返回的值必须是对应基元包装类的实例;
否则,它必须是可分配给声明的返回类型的类型。如果此方法返回的值为 null,并且接口方法的返回类型为基元,则代理实例上的方法调用将引发 NullPointerException。如果此方法返回的值与接口方法的声明返回类型不兼容(如上所述),则代理实例上的方法调用将引发 ClassCastException
这里最重要的参数是method,可以通过moth.invote调用目标对象的方法
1.5.2 Proxy类
Proxy类提供了创建动态代理类及其实例的静态方法,该类也是动态代理类的超类,其最主要的方法是一个newProxyInstance的静态方法
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){......}
参数说明:
ClassLoader :
对应代理目标类对应的类加载器
Class<?>[] interfaces
目标代理类的接口类对应的CLASS,代理目标类的接口可以是多个
InvocationHandler h
要调用的处理器
返回对象:
返回实例,Object可以转化成对应的目标代理类的接口
1.5.3动态代理类的实现
准备代码:为了增加效果,我们在增加一个类和接口
public interface LifeService {public String eat(String name);public String drink(String name);public int like(int type);}public class Life implements LifeService {public String eat(String name){String ret="I like eat:"+name;System.out.println(ret);return ret;}public String drink(String name){String ret="I like drink:"+name;System.out.println(ret);return ret;}public int like(int type){String ret="mylike:"+type;System.out.println(ret);return type;}}
自己做一个调度类,继承InvocationHandler
//自己编写处理器
public class MyHandler implements InvocationHandler {private Object target;//代理的目标类//通过构造方法注入代理的目标public MyHandler(Object target){this.target=target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//给所有方法加上日志System.out.println("给大家增加一个log日志...........");String mName=method.getName();//给以save开头的方法名添加事务if(mName.length()>=4 && "save".equals(mName.substring(0,4))){System.out.println("开启事务...............");}//方法名是sleep的增加安全措施if("sleep".equals(mName)){System.out.println("注意安全措施...............");}//调用目标代理类的方法并返回方法返回值return method.invoke(target,args);}}
封装一个调度工厂
public class ProxyFactory {/**** @Description: 动态代理工厂* @Create:2023/12/24 12:43* @Param: [target:代理的对象实例* @Return: java.lang.Object 代理接口对象*/public static Object creatProxy(Object target){//动态调用代理return Proxy.newProxyInstance(target.getClass().getClassLoader(), //目标的加载target.getClass().getInterfaces(), //目标类的接口的class,可以是多个,数组,如果指定,则要使用new Class[...]new MyHandler(target));}public static void main(String[] args) {//应用1Love love=(Love)creatProxy(new LoveImpl() );love.marray();love.sleep();int money=love.saveMoney(10);System.out.println("存钱:"+money);//应用2LifeService lifeService=(LifeService)creatProxy(new Life());lifeService.eat("火锅");lifeService.drink("可乐");}
}
执行效果:
给大家增加一个log日志...........
我同意
给大家增加一个log日志...........
注意安全措施...............
我同意
给大家增加一个log日志...........
开启事务...............
存钱:100
给大家增加一个log日志...........
I like eat:火锅
给大家增加一个log日志...........
I like drink:可乐
1.5.4动态实现接口
有时候,我们不想为接口单独做一个实现类,我们可以通过动态代理来实现,这个在springboot里面非常常见的编程思想
public class DynamicProxy {public static void main(String[] args) {//定义一个InvocationHandler对象,但不传入实现类,相当于InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getName().equals("marray")) { //这个方法去实现,和做一个实现类原理一致System.out.println("我同意个锤子");}return null;}};Love love = (Love) Proxy.newProxyInstance(Love.class.getClassLoader(), // 传入ClassLoadernew Class[] { Love.class }, // 传入要实现的接口handler); // 传入处理调用方法的InvocationHandlerSystem.out.println("3333");love.marray();}
}
1.5.4替换某个方法
在大量的java框架中,某种功能有默认实现,我们想替换掉,自己定义实现,其他方法不变,我们也可以使用动态代理技术实现。
public class ProxyReplcae {public static void main(String[] args) {LoveImpl loveImp=new LoveImpl();//默认实现,在spring里一般采用注入//定义一个InvocationHandler对象InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//这个对象不想使用以前实现,自定义实现if (method.getName().equals("marray")) {System.out.println("不同意结婚");return null;}elsereturn method.invoke(loveImp,args); //其他的依然使用默认实现}};Love love = (Love) Proxy.newProxyInstance(Love.class.getClassLoader(), // 传入ClassLoadernew Class[] { Love.class }, // 传入要实现的接口handler); // 传入处理调用方法的InvocationHandlerlove.marray(); //已经使用自定义love.sleep();//依然是默认实现}}
1.5 动态代理框架CGLIB
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。
为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都使用到了CGLIB, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。
1.5.1 使用介绍
1.引入框架
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
2.准备好一个普通类
public class MyDay {public void work(int hour){System.out.println(hour+"点开始工作");}public void sleep(int hour){System.out.println(hour+"点开始睡觉");}
}
代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;/*** @class: com.jsoft.reflection.CglibProxyFacory* @description:* @author: jiangzengkui* @company: 教育家* @create: 2023-12-24 15:29*/
public class CglibProxyFacory {/***clazz:代理目标类的class*/public static Object getProxy(Class<?> clazz) {//自定义一个拦截器MethodInterceptor me=new MethodInterceptor(){/*** @param o 被代理的对象(需要增强的对象)* @param method 被拦截的方法(需要增强的方法)* @param args 方法入参* @param methodProxy 用于调用原始方法*/@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)throws Throwable {System.out.println("before method " + method.getName());Object object = methodProxy.invokeSuper(o, args);//调用方法之后,我们同样可以添加自己的操作System.out.println("after method " + method.getName());return object; // 方法的返回值,如果void则为null}};// 创建动态代理增强类Enhancer enhancer = new Enhancer();// 设置类加载器enhancer.setClassLoader(clazz.getClassLoader());// 设置被代理类enhancer.setSuperclass(clazz);// 设置方法拦截器enhancer.setCallback(me);// 创建代理类return enhancer.create();}public static void main(String[] args) {MyDay myDay= (MyDay) CglibProxyFacory.getProxy(MyDay.class);myDay.work("早上8");myDay.sleep("晚上23");}}
效果:
before method work
早上8点开始工作
after method work
before method sleep
晚上23点开始睡觉
after method sleep
1.5.2 cglib说明
主要要用到的3个类
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
net.sf.cglib.proxy.MethodInterceptor
这个就是拦截器处理接口,其重要接口方法
/*** @param obj 被代理的对象(需要增强的对象)* @param method 被拦截的方法(需要增强的方法)* @param args 方法入参* @param proxy 用于调用原始方法* renturn 方法的返回值,如果void则为null*/public Object intercept(Object obj, java.lang.reflect.Method method,Object[] args,MethodProxy proxy) throws Throwable;
net.sf.cglib.proxy.MethodProxy
这个方法为调用代理目标类的原始方法,其调用函数:MethodProxy.invokeSuperr(Object obj, Object[] args) ,其自动为代理目标类创建一个接口
/**
Object obj:传入代理目标类对象实例Object[] args:代理目标类的方法参数**/
public Object invokeSuper(Object obj, Object[] args) throws Throwable {try {init();FastClassInfo fci = fastClassInfo;return fci.f2.invoke(fci.i2, obj, args);} catch (InvocationTargetException e) {throw e.getTargetException();}}
net.sf.cglib.proxy.Enhancer
这个类用于调用拦截器,创建增强的代理目标类
// 创建动态代理增强类Enhancer enhancer = new Enhancer();// 设置类加载器enhancer.setClassLoader(clazz.getClassLoader());// 设置被代理类enhancer.setSuperclass(clazz);// 设置方法拦截器enhancer.setCallback(me);// 创建代理类-增强后的目标代理类Object obj=enhancer.create();
1.6 总结
反射机制:
- 根据类的名称或者class本身,可以进行实例化对象、构造方法、获得和修改字段、获得方法名和调用方法
代理机制:
- 代理机制就是代理本身目标类,增强其功能
- 静态代理,就是继承目标类对应的接口,实现所有的接口方法,通过接口的封装,增强目标类
- 动态代理:就是创造一个拦截器,拦截器里去增强目标类的方法,前提:必须有接口类
- Cglib:增对目标类没有接口这种情况,自动创建一个接口,实现没有接口类也可以做代理