封装表现:
1、方法就是一个最基本封装体。
2、类其实也是一个封装体。
从以上两点得出结论,封装的好处:
1、提高了代码的复用性。
2、隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。这是核心之一,也可以理解为就是封装的概念。
3、提高了安全性。
发表于: 2020-10-17 23:39:04
1 1462
三:实现动态代理
3.1:静态代理的不足:
通过看静态代理可以动态扩展我们的对象,但是有个问题,在我们进行方法扩展的时候,比如我们的日志功能:每个前面都得写第一步、第二步。如果我们要再一些其他的东西,比如权限校验、代码说明,一个两个方法还好,万一方法成百个呢,那我们岂不是要累死。这就是动态代理要解决的问题,只需要写一次就可以,究竟是怎么实现的呢,接下里我们来一探究竟吧。
3.2:动态代理的准备:
动态代理需要用到JDk的Proxy类(代理),通过它的newProxyInstance()方法可以生成一个代理类,我们来通过jdk看一下具体的说明,如何使用它:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:
Proxy.getProxyClass(loader, interfaces).
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与 Proxy.getProxyClass 相同。
参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
抛出:
IllegalArgumentException - 如果违反传递到 getProxyClass 的参数上的任何限制
NullPointerException - 如果 interfaces 数组参数或其任何元素为 null,或如果调用处理程序 h 为 null
从中可以看出它有三个参数,分别是classlcoder、interface、InvocationHandler.只要我们把这三个参数传递给他,它就可以 返回给我们一个代理对象,访问这个代理对象就可以实现对原对象的扩展。接下来,我们用代码来实现它。
3.3:代码场景
我们来做这样一个场景,我们实现一个计算器,计算器里面有加减乘除方法,然后我们实现这个计算的接口,有具体的类和被代理的类,我们通过动态代理来生成代理类,而不用自己去建了,好了,看接下来的代码:
3.4:动态代理的代码实现
3.4.1:首先我们新建一个接口,命名为Calculator ,声明四个方法:
package DynamicProxyTest;
public interface Calculator {
int add(int i, int j);//加
int sub(int i, int j);//减
int mul(int i, int j);//乘
double div(int i, int j);//除
}
3.4.2:新建一个实现类,命名为CalculatorImpl ,也就是被代理类
package DynamicProxyTest;
public class Calculatorlmpl implements Calculator{
@Override
public int add(int i, int j) {
return i+j;
}
@Override
public int sub(int i, int j) {
return i-j;
}
@Override
public int mul(int i, int j) {
return i*j;
}
@Override
public double div(int i, int j) {
return (i/j);
}
}
3.4.3:新建一个类,命名为CalCulatorDynamicProxy,也就是我们的代理类,用来对上面的类进行代理:
package DynamicProxyTest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class CalCulatorDynamicProxy { //动态代理类
private Calculator calculator;//要代理的对象
public CalCulatorDynamicProxy(Calculator calculator) {
this.calculator = calculator;
}
public Calculator getCalculator() {
Calculator proxy = null;
ClassLoader loader = calculator.getClass().getClassLoader();//获取类加载器
Class[] interfaces = new Class[] {Calculator.class};//代理对象的类型
InvocationHandler h = new InvocationHandler() {//调用处理器
//proxy:正在返回的代理对象
//method:被调用的方法
//args:传入的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---日志记录开始---");
String name = method.getName();//获取方法的名字
System.out.println("方法"+name+"()开始执行了");
System.out.println("方法中的参数是:"+Arrays.asList(args));
Object result = method.invoke(calculator, args);
System.out.println("方法执行后的结果是"+result);
return result;
}
};
proxy=(Calculator)Proxy.newProxyInstance(loader, interfaces, h);//代理对象
return proxy;
}
}
这里要特别强调的问题就是:invoke()方法,注意其中的参数,分别是被代理对象、方法、和对象参数,这里的原理是反射,通过获取原对象的class对象,然后进行处理,我们可以通过method对象拿到被代理对象的方法,也是add()、mul()、sub()、div()方法,也可以通过args对象数组取得传入的参数,比如我们具体传入的数值,再通过method.invoke()方法进行调用,就进行了被代理对象的方法的执行,然后就是返回的结果(如果方法前为void,返回的就是null)
3.4.4:我们来做具体的测试
package DynamicProxyTest;
public class Test {
public static void main(String[] args) {
Calculator cal = new Calculatorlmpl();
Calculator proxy = new CalCulatorDynamicProxy(cal).getCalculator();
int add = proxy.add(29, 1);
int sub = proxy.sub(9, 2);
int mul = proxy.mul(3, 7);
double div = proxy.div(6,8);
}
}
具体的测试结果:
可以看出动态代理模式轻松完成了对被代理对象的日志记录功能,并且只用写一次,这样即便有成百上千的方法我们也不怕,这就是动态代理领先于静态代理之处,虽然实现起来有点麻烦,但是其方便,动态的给被代理对象添加功能。我们所写的重复代码更少,做的事情更少。
四:总结
本篇博客介绍了动态代理和静态代理的概念,并对其进行了代码实现,在实际的工作中,我们会经常遇到需要代理模式的地方,希望能多多思考,促进我们形成一定的思维模式。并且动态代理作为SpringAop的实现原理,封装了动态代理,让我们实现起来更加方便,对于这部分内容可以只做了解,理解其背后的运行机制即可,并不需要具体实现,如果需要实现,直接使用spring的Aop功能即可。
对toString()不太理解,去了解了一下toString(),发现toString()是java.lang.Object类下的一个方法顺便了解了一下Object类
也了解了一下实体类,原来实体类就是Javabean
不知道Map在这里的意思,去了解了一下Map
最后了解了一下反射
编写一个person类
public class Person {
private String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(String name) {
this.name = name;
}
public Person() {
}
public void show(){
System.out.println("你好,我是况博凯");
}
private String showNation(String nation){
System.out.println("我的国籍是:" + nation);
return nation;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
编写一个测试类
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
//反射之前,对于Person的操作
@Test
public void test1() {
//1.创建person类的对象
Person p1 = new Person("Tom", 12);
//2.通过对象,调用其内部的属性,方法
p1.age = 10;
System.out.println(p1.toString());
p1.show();
//在Person类外部,不可以通过Person类的对象调用其内部私有结构
//比如name,showNation()以及私有(private)的构造器
}
//反射之后,对于Person的操作
@Test
public void test2() throws Exception {
Class clazz = Person.class;
//1.通过反射,创建Person类的对象
Constructor cons = clazz.getConstructor(String.class, int.class);
Object obj = cons.newInstance("Tom", 12);
Person p = (Person) obj;
System.out.println(p.toString());
//2.通过反射,调用对象指定的属性、方法
//调用属性
Field age = clazz.getDeclaredField("age");
age.set(p, 10);
System.out.println(p.toString());
//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
System.out.println("*******************************");
//通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
//调用私有的构造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("MEKBK");
System.out.println(p1);
//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1, "魏思静");
System.out.println(p1);
//调用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p1, "中国");//相当于String nation = p1.showNation("中国")
System.out.println(nation);
}
}
没有反射之前,通过对象来无法调用Person类的其内部私有结构
知道反射可以调用person的私有结构,也就是封装(private),顺便了解一下封装
封装表现:
1、方法就是一个最基本封装体。
2、类其实也是一个封装体。
从以上两点得出结论,封装的好处:
1、提高了代码的复用性。
2、隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。这是核心之一,也可以理解为就是封装的概念。
3、提高了安全性。
明天计划的事情:
做完Mapper代理,然后学习Debug模式,练习调试,学会查看单步执行时的变量值
收获:以上
评论