杂文之反射、装饰者模式、静动态代理(一)


什么是反射?

如下图所示:

反射图片
文字描述:

在运行状态下,

对于任意的一个类或对象,可以获取它所有的属性和方法;

这种动态获取信息以及动态调用对象的功能就叫做java的反射机制。

从上面那张图中可以看出,我们需要得到这个类的字节码对象, (以下统称为类对象)才能实现反射;

一些话

第一点:
既然我们得到的是类.class文件在内存中表现形式字节码class类,那么我们可以查看这个Class类的api:
如下:
Class类
其中方法接近有60个,数了数50多个。。。。
有时间,补上这些方法,下面是一些常用方法;

在上面那张图中我们得知,获取到类对象,有三种方式,如下:

类名.class
对象.getClass()
Class.forName("类的全限定名")

其实还有一种方式,因为其类的加载,就是依靠类加载 ClassLoader 类
如下:
 ClassLoader cl=this.getClass().getClassLoader();
 Class clazz=cl.loadClass("类全限定名");

ps:全限定名:就是类的全名:
例如类Hero,全名为:com.web.reflect.Hero
这儿讨论使用的方法都是Class的静态方法,即Class.forName(“全限定名”)

准备条件

创建三个类;
一个类是Hero英雄类

package com.web.reflect;

public class Hero {
    public String name;
    public float hp;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getHp() {
        return hp;
    }

    public void setHp(float hp) {
        this.hp = hp;
    }

    public void introduce(){
    	System.out.println("我是所有英雄的父类");
    }
}

一个是继承英雄美女英雄类BeautyHero1:

package com.web.reflect;

public class BeautyHero1 extends Hero{
    public void introduce(){
        System.out.println("我是妲己");
    }
}

另一个也是继承英雄美女英雄类BeautyHero2:

package com.web.reflect;

public class BeautyHero2 extends Hero {
    public void introduce(){
        System.out.println("我是貂蝉");
    }
}

反射创建对象

代码如下:

  public static void main(String[] args) throws Exception {
        Hero hero=new Hero();
        Class<?> clazz = Class.forName("com.web.reflect.Hero");
      //        方式一
       /* Constructor<?> ct = clazz.getConstructor();
        Hero h = (Hero) ct.newInstance();*/
        
//        方式二
        Hero h = (Hero) clazz.newInstance();
        System.out.println("传统方式创建的对象: "+hero+"\r\n"+
                " 反射创建的对象: "+h);
    }

输出结果如下:
在这里插入图片描述

反射获取属性和方法(公有的)

获取属性和方法

package com.web.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestReflect7 {
    public static void main(String[] args) {
        method1();
        method2();
    }
    public static void method1(){
        /*
        * 反射获取公有属性
        * */
        try {
//            获取到类对象
            Class<?> clazz = Class.forName("com.web.reflect.BeautyHero1");
//            通过类对象获取到实例化对象
            Hero h = (Hero) clazz.newInstance();
//            通过类对象获取到属性,参数为属性名
            Field name = clazz.getField("name");
            Field hp = clazz.getField("hp");
            /*设置属性值*/
//            设置哪个对象的哪个属性的哪个值,参数1是对象名,参数2是属性值
            name.set(h,"妲己");
            hp.set(h,800);
            System.out.println("设置的属性name是: "+h.name+"\r\n"+"设置的属性hp是: "+h.hp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void method2(){
        /*
         * 反射获取公有方法
         * */
        try {
//            获取到类对象
            Class<?> clazz = Class.forName("com.web.reflect.BeautyHero1");
//            通过类对象获取到实例化对象
            Hero h = (Hero) clazz.newInstance();
//            通过类对象获取到方法,第一个参数是方法名introduce,第二个是方法参数类型,为字节码.class类型
            Method m = clazz.getMethod("introduce", null);
//            调用哪个对象的哪个方法的哪些参数,第一个参数是对象名,第二个是参数
            m.invoke(h,null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:
在这里插入图片描述

注意

a.这里getField();使用的是子类的全限定名,却能获取到父类的属性;

获取所有的属性和所有的方法

ps:在第一个美女英雄类里面加上一个公有的成员属性:

package com.web.reflect;

public class BeautyHero1 extends Hero{
    public String skill;
    
    public void introduce(){
        System.out.println("我是妲己");
    }
}

获取所有的属性:

/*
     * 反射获取父类和子类中的公有成员变量
     * */
    public static void method3(){
        try {
//            获取到类对象
            Class<?> clazz = Class.forName("com.web.reflect.BeautyHero1");
            Field[] fields = clazz.getFields();
            for (Field field : fields) {
                System.out.println("属性为: "+field+"\r\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

执行结果:
在这里插入图片描述

注意

b.这里getFields();使用的是子类的全限定名,却能获取到父类的属性;

获取所有的方法:

 /*
     * 反射获取父类和子类中的公有成员方法和构造方法
     * */
    public static void method4(){
        try {
//            获取到类对象
            Class<?> clazz = Class.forName("com.web.reflect.BeautyHero1");
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                System.out.println("方法为: "+method+"\r\n");
            }

            System.out.println("==========================================================");

            Constructor<?>[] cs = clazz.getConstructors();
            for (Constructor<?> c : cs) {
                System.out.println("构造方法为: "+c+"\r\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

执行结果为:在这里插入图片描述

在这里插入图片描述

除了父类中的方法之外,还获取到了Object超类中的方法;
从这里可以观察到其父类的introduce方法并没有获取到,并且父类的构造方法也没有获取到;

猜测是因为子类中重写了父类的introduce方法,所以父类中的方法就没有获取到;为了验证猜测:

我们在父类中加上一个方法a,如下:

package com.web.reflect;

public class Hero {
    public String name;
    public float hp;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getHp() {
        return hp;
    }

    public void setHp(float hp) {
        this.hp = hp;
    }

    public void introduce(){
        System.out.println("我是所有英雄的父类");
    }
    public void a(){
        System.out.println("我是方法a");
    }
}

然后再运行获取所有方法的代码,如下:
在这里插入图片描述
猜测对了,父类中的方法重写之后的确没有获取到;

注意

1.这里getMethods();使用的是子类的全限定名,却能获取到父类的方法,不能获取到父类的构造方法;
2.使用getMethods()方法也不能获取已经在子类中重写了的父类方法;这符合Java设计。。。不然你重写父类的方法干嘛。。。。。
c.使用getMethods();使用的是子类的全限定名,能获取到父类除构造方法和未重写之外的方法

反射获取属性和方法(私有的)

这个就不一一举例了。。。太麻烦了;其调用的方法是一样的,只差一个declared单词,这个单词的意思是:公开的,宣明的;
这里列举调用私有属性

在Hero和BeautyHero1中分别加入一个私有属性,如下:
Hero类:

package com.web.reflect;

public class Hero {
    public String name;
    private String priName;
    public float hp;

    public String getPriName() {
        return priName;
    }

    public void setPriName(String priName) {
        this.priName = priName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getHp() {
        return hp;
    }

    public void setHp(float hp) {
        this.hp = hp;
    }

    public void introduce(){
        System.out.println("我是所有英雄的父类");
    }
    public void a(){
        System.out.println("我是方法a");
    }
}

BeautyHero1类:

package com.web.reflect;

public class BeautyHero1 extends Hero{
    public String skill;
    private String priSkill;
    public void introduce(){
        System.out.println("我是妲己");
    }
}



获取私有属性

先获取父类中的私有属性,代码如下:

 /*
     * 反射获取父类私有属性
     * */
    public static void method5(){
        try {
//            获取到类对象
            Class<?> clazz = Class.forName("com.web.reflect.BeautyHero1");
//            通过类对象获取到实例化对象
            Hero h = (Hero) clazz.newInstance();
//            通过类对象获取到属性,参数为属性名
            Field name = clazz.getDeclaredField("priName");
            System.out.println("设置的属性name是: "+h.name);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

执行结果如下:
在这里插入图片描述
报错信息说:没有这样的属性。。。。。

那么很明显,父类的私有属性获取不了。。。。。。。抱着疑问,查找一下api,这个方法的解释如下:
在这里插入图片描述
既然属性无法获取父类的,查看下方法和构造方法看下,带declared的方法,
在这里插入图片描述
在这里插入图片描述
父类的都不能获取。。。。。。

注意

d.Class类中带declared单词的方法,无法获取父类属性,成员方法和构造方法。。。。

那么再获取本类的私有属性,在BeautyHero1中添加私有属性的公有get和set方法
为了验证是否获取到值,设置其值,代码如下:

 /*
     * 反射获取本类私有属性
     * */
    public static void method5(){
        try {
//            获取到类对象
            Class<?> clazz = Class.forName("com.web.reflect.BeautyHero1");
//            通过类对象获取到实例化对象
            BeautyHero1 bh = (BeautyHero1) clazz.newInstance();
//            通过类对象获取到属性,参数为属性名
            Field name = clazz.getDeclaredField("priSkill");
            /*设置属性值*/
//            设置哪个对象的哪个属性的哪个值,参数1是对象名,参数2是属性值
            name.set(bh,"二技能冲击波");
            System.out.println("设置的属性name是: "+bh.getPriSkill());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

执行结果如下:
在这里插入图片描述
报错。。。。。。。
是一个权限报错,不能接受修改私有的。。。。
其是在于要修改一个java的语言访问检查,也就是修饰符检查,其不同的修饰符有对应的权限,需要修改其是否检查;
修改代码如下:

  /*
     * 反射获取本类私有属性
     * */
    public static void method5(){
        try {
//            获取到类对象
            Class<?> clazz = Class.forName("com.web.reflect.BeautyHero1");
//            通过类对象获取到实例化对象
            BeautyHero1 bh = (BeautyHero1) clazz.newInstance();
//            通过类对象获取到属性,参数为属性名
            Field name = clazz.getDeclaredField("priSkill");
//            设置取消检查语言访问检查
            name.setAccessible(true);
            /*设置属性值*/
//            设置哪个对象的哪个属性的哪个值,参数1是对象名,参数2是属性值
            name.set(bh,"二技能冲击波");
            System.out.println("设置的属性name是: "+bh.getPriSkill());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

执行结果如下:
在这里插入图片描述
这样就执行成功了

既然带着declared的属性Filed,需要设置取消检查语言访问检查,那么其它的勒,方法和构造方法勒?
也是一样的。。。。。
查找了一下api,其setAccessible(true);方法来自于AccessibleObject

如下:
在这里插入图片描述
很明显了

注意

e.Class类中带declared单词的方法,要想实现修改私有属性,构造方法,方法那么就要使用setAccessible(true);方法取消java语言检查;

反射获取父类的类对象

其用到了Class的getSuperclass()方法
代码如下:

  /*
    * 反射获取父类的字节码对象
    * */
    public static void method6(){
        try {
//            获取到类对象
            Class<?> clazz = Class.forName("com.web.reflect.BeautyHero1");
//            通过类对象获取父类的字节码对象
            Class<?> superClass = clazz.getSuperclass();
            Hero hero = (Hero) superClass.newInstance();
            Field priName = superClass.getDeclaredField("priName");
            priName.setAccessible(true);
            priName.set(hero,"父类私有属性");
            System.out.println("设置的属性name是: "+hero.getPriName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

执行结果如下:
在这里插入图片描述

反射总结

其注意点总结如下:

1.Class类中不带declared的方法,可以获取

公有的父类的本类的
属性
方法
构造方法


2.Class类中带declared的方法,可以获取

私有的本类的
属性
方法
构造方法
并且需要,添加setAccessible(true);方法


3.要想获取私有的父类的属性方法构造方法,那么就使用getSuperclass()方法;获取到父类的字节码对象

反射的用途

引用文章如下:

(1)反编译:.class到.java
(2)通过反射机制访问java对象的属性,方法,构造方法等
(3)IED中自动显示对象或类的方法列表,方便快速查阅
(4)反射机制最重要的用途就是通用框架的开发,框架写好后,方便使用时根据项目需求的不同来动态加载需要的对象

其spring的控制反转,依赖注入可以很明显的感知到使用了反射

反射的优缺点

引用文章如下:

(1)反射的优点:
使用反射,可以在运行时获取类的各种内容,进行反编译(从.class文件到.java文件的操作),对于java这种先编译后运行的语言,能够让我们创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的连接,更加容易实现面向对象;
(2)反射的缺点:
<1>反射会消耗一定的系统资源,因此,如果不需要动态创建一个对象,那么就不要用反射;
<2>反射调用方法是可以忽略权限检查,因此会破坏封装性而导致安全问题;

参考的文章:
反射的作用:通过反射获取属性和方法

java基础总结(面试高频问题)三:反射机制,Class.forName和classloader的区别


装饰者模式

参考文章:
菜鸟教程

设计模式之装饰者模式

含义

查了些网上的定义,基本上如下定义:

动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

讲真的,说这些完全不好理解。。。。。。
咱得接地气好嘛。。。。
说的那么高大上。。。。
完全听不懂。。。。

之后自己看了些网上文章举例,很经典的咖啡举例,看是看得懂。。。但是感觉跟继承没有啥两样。。。。之后自己打了下代码,才渐渐明白过来。

例子

形状的分类有很多,有三角形正方形圆形等等,这些都是可以统称为形状的,这是一个超类的统称;就像种群的划分,鸡鸭牛羊这些种群都是动物一样,我们都可以统称为动物;
这里用形状举例
设计一个形状接口Shape为超类,里面有一个方法,画画方法,画什么随便,如下:

package com.web.decorator;
/**
* 创建一个形状接口
* */
public interface Shape {
    void draw();
}

然后我们画一个正方形和圆形,这些都是属于形状,所以继承了形状这个超类,如下:
正方形类:

package com.web.decorator;

/**
 * 创建了一个实现了形状接口的实体类
 * 正方形类
 * */
public class Rectangle implements Shape{
    public void draw() {
        System.out.println("我是一个正方形的形状");
    }
}

圆形类:

package com.web.decorator;

/**
 * 创建了一个实现了形状接口的实体类
 * 三角形类
 * */
public class Circle implements Shape {
    public void draw() {
        System.out.println("我是一个圆形的形状");
    }
}

然后现在有一个需求,假如客户想要画一个有颜色的圆形或正方形,颜色没想好。。。。

按照常规来说,我们可以有两种方式解决这个需求:
a.是在圆形和正方形的类中添加一个颜色方法;
b.是写两个类分别继承圆形和正方形,然后两个类中都有一个颜色方法;

很明显,我们不会选择第二种,因为这样会让类的数量过多,不利于维护;那么否决第二种,那就是选择第一种,在圆形和正方形的类中都添加一个颜色方法

当客户要红色的时候,我们就把颜色方法中的颜色改为红色,当客户要绿色的时候,我们就要把颜色改为绿色。。。。

如此频繁的修改原代码,代码量小还好,要是多个对象勒,比如客户希望多种形状圆形啊,菱形啊,长方形啊等等,每一个类中的颜色方法都不一样,那我们就得改多个对象方法。。。这样维护成本就很高了。。。而且还是一个颜色功能,要是还需要额外的功能,那不是又得在每一个对象中添加额外功能的代码。。。。。。。

这就很不符合java的开放封闭原则(一个软件实体应当对扩展开放(open),其修改尽量关闭);
当然,要解决这个问题,肯定还有其它方法,这里不过多陈述;主角装饰者模式出场了;

说明点

在使用之前,得说明两点:
1.装饰者模式,增强内容是固定的,被增强对象是动态的;
2.装饰者模式,只能在一定程度上减少类的继承;并不能避免继承!

我们创建一个装饰类,这个类是一个抽象类,实现了Shape接口,如下:
package com.web.decorator;
/**
 * 装饰者
 * 创建一个实现了Shape接口的抽象装饰者类
 * */
public abstract class ShapeWrapper implements Shape{//第一点
    protected Shape shape;//第二点
    /*也就是本装饰类实例化的时候传递进来一个Shape的对象参数
    * 将这个Shape对象的参数赋予本类的实例化对象
    * */
    public ShapeWrapper(Shape shape) {//第三点
        this.shape = shape;
    }

    /*
    * 实现接口的方法还是调用了接口中的方法
    * 也就是具体的实现还是要有实现了该抽象类的类去实现
    * */
    public void draw() {
        shape.draw();
    }
}

其具体实现步骤如下:
第一点:继承或者实现某个基类;
第二点:在装饰者类中(ShapeWrapper )创建一个被装饰者的也就是基类(Shape类)的成员变量;
第三点:创建一个带有基类(Shape)参数类型的构造方法,然后将这个参数值赋予当前装饰者类(ShapeWrapper)的基类成员变量;

这样也就意味着,每一次实例化,都需要传递给装饰者一个基类或者基类的实现类对象;

然后我们再创建一个装饰者类真正实现功能的子类,如下:

package com.web.decorator;
/**
 *
 * 创建了一个继承了形状装饰类的实体装饰类
 * */
public class RedShapeWrapper extends ShapeWrapper{
    public RedShapeWrapper(Shape shape) {
        super(shape);
    }

    /*
    * 重写了父类的方法,也就是说
    * 继承了抽象装饰类之后
    * 还加强了装饰类的能力
    * */
    @Override
    public void draw() {
        super.draw();
        setRedBorder();
    }

    private void setRedBorder(){
        System.out.println("我形状的颜色是红色");
    }
}

这个子类实现的功能是,添加红色的颜色;

我们测试一下,代码如下:

package com.web.decorator;

public class TestWrapper {
    public static void main(String[] args) {
//        实例化实现了抽象装饰类的类
        RedShapeWrapper redCircle = new RedShapeWrapper(new Circle());
        RedShapeWrapper redRectangle = new RedShapeWrapper(new Rectangle());

        System.out.println("我是咋样的?:");
        redCircle.draw();

        System.out.println("我是咋样的?:");
        redRectangle.draw();
    }
}

执行结果如下:
在这里插入图片描述
如果要一个大的红色的正方形和圆形,该咋样?
那我们就添加一个功能类继承装饰者类,如下:

package com.web.decorator;

public class BigShapeWrapper extends ShapeWrapper {
    public BigShapeWrapper(Shape shape) {
        super(shape);
    }

    @Override
    public void draw() {
        super.draw();
        setSizeBorder();
    }

     private void setSizeBorder(){
        System.out.println("我的大小是大的形状");
    }
}

测试如下:

package com.web.decorator;

public class TestWrapper {
    public static void main(String[] args) {
//        实例化实现了抽象装饰类的类
        RedShapeWrapper redCircle = new RedShapeWrapper(new BigShapeWrapper(new Circle()));
        RedShapeWrapper redRectangle = new RedShapeWrapper(new BigShapeWrapper(new Rectangle()));

        System.out.println("我是咋样的?:");
        redCircle.draw();

        System.out.println("我是咋样的?:");
        redRectangle.draw();
    }
}

执行结果如下:
在这里插入图片描述

分析:

从上面的例子来看,也验证了之前说的,装饰者模式其增强的内容是固定的(每一个继承了ShapeWrapper的类,增强内容是写死的),但是被增强的对象是动态的(可以在原有继承Shape类中任意组合一个类)

装饰者模式总结

1.三部曲:
a.继承或者实现基类,
b.创建基类对象类型成员变量,
c.创建基类对象为参数的构造方法;

2.装饰者模式增强对象是动态的,可扩展的,但增强内容和被增强对象是固定的;

装饰者模式的优缺点

优点:
1.符合开放闭合原则,低耦合;
2.扩展类功能的时候在一定程度上减少了子类的数量;
3.因为动态扩展功能的原因,所以其类功能更丰富;
4.降低了代码维护成本;

缺点:
1.多层装饰者类,会使代码变复杂;
2.虽然一定程度上减少了子类,但是随着功能的扩展,会让子类越来越多;也降低了维护性;


静态代理

静态代理的本质就是装饰者模式。。。只是不同的是,这个是在装饰者类中调用的方法前和调用方法后添加一些功能方法,就没了。实现步骤跟装饰者模式一样,还是那三部曲;

这里就不赘述了。.。。。




动态代理

学习动态代理,主要目的是更好的理解spring的aop,那么aop是啥?
AOP 即 Aspect Oriented Program 面向切面编程
aop是面向切面编程;
这个面向切面编程,我理解为,面向增强内容所在类编程;

那么aop的底层实现就是动态代理技术

之前说的装饰者,增强内容是固定的,增强对象是动态的,被增强对象固定;
而这个动态代理,增强内容是动态的,增强对象也是动态的,连被增强对象也是动态的;这个是真的灵活!

动态代理目前晓得的方式是两种(哪位大佬晓得更多的!欢迎告知!):
a.基于接口的,jdk动态代理;
b.基于父类的,cglib动态代理

jdk动态代理技术

需要用到一个类,Proxy类;这个类是属于reflect类,好家伙!!反射中的东西!!!
查了下文档,如下:
在这里插入图片描述
在这里插入图片描述
文档显示,有两种方式创建代理类的实例:
第一种方式是通过getProxyClass()方法实现的;
第二种是静态方法newProxyInstance()方法实现的;
很明显勒,选择第二种,第一种看起来都复杂些,也看了些网上的文章,大家也都是用的第二种方式;

(不过,留一个坑,第二种方式以后要试试;看了看跟第二种差别不大,只是少了invocationHandler参数)

既然使用第二种方式,那么就得看看这个newProxyInstance静态方法需要的参数是啥?
查文档如下:
在这里插入图片描述
这个类需要的参数就出来了,如下:

* 三大参数:
        * 1.Classloader 类加载器
        * 2.Class[] interfaces  要实现的接口
        * 3.invocationHandler   调用处理器

那么话不多说,写一个ProxyTest类,类中有两个接口A,B,都有一个introduc方法;代码如下:

package com.web.dynamicproxy.proxyinstance;

import org.junit.Test;

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

public class TestProxyInstance {
    @Test
    public void Test(){
//        第一个参数:获取当前对象的Classloader对象,其实勒,只要是类加载器就可以了
        ClassLoader loader=this.getClass().getClassLoader();

//        ClassLoader loader = new D().getClass().getClassLoader();

//        第二个参数:要实现的接口
        Class[] classes=new Class[]{A.class,B.class};
//        第三个参数:调用处理器
        InvocationHandler h=new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("hello,实现的方法在这里");
                return null;
            }
        };
//        这个方法使用三大参数创建一个类的对象
        /*
        * 这是一个怎样的对象?
        * 实现给定接口的对象,称之为代理对象
        * */
        Object o = Proxy.newProxyInstance(loader, classes, h);
        A a= (A) o;
        B b= (B) o;
        a.introduce();//执行结果为: hello,实现的方法在这里
        b.introduce();//执行结果为: hello,实现的方法在这里
    }
}
interface A{
    public void introduce();
}
interface B{
    public void introduce();
}

执行结果如下:
在这里插入图片描述

注意点:

1.生成的代理对象,是一个实现了给定接口实现类的代理对象;

2.代理对象的实现方法是invocationHandler即调用处理器中的invoke方法,也就是说调用给定接口的几乎所有方法(有些方法不是,native修饰的Object类除hashcode外都不是。。。),都是在调用调用处理器invoke方法;

3.需要的类加载器,任意一个对象都可以!!!!!并不是只有当前对象的!



小案例

晓得了代理类之后,写一个小案例,大致模仿aop,注意只是大致,真正的aop是用代理工厂实现的;

创建一个被增强的接口,就是目标接口,如下:

package com.web.dynamicproxy.jdk;
/**
 * 创建目标接口
* */
public interface TargetInterface {
    void save();
}

再创建一个实现了目标接口的目标对象,就是要被增强的对象,如下:

package com.web.dynamicproxy.jdk;
/**
 * 创建目标类
 * */
public class Target implements TargetInterface {
    public void save() {
        System.out.println("我是目标接口的实现类,目标对象");
    }
}

之后创建一个通知类,也就是增强类,如下:

package com.web.dynamicproxy.jdk;
/**
 * 创建增强类
 * */
public class Advice {
    public void before(){
        System.out.println("前置增强。。。。。");
    }
    public void afterReturning(){
        System.out.println("后置增强。。。。。");
    }
}

然后写一个生成代理对象的类,如下:

package com.web.dynamicproxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 创建jdk的代理对象
 * 基于接口
 * */
public class ProxyTest {
    public static void main(String[] args) {
//        创建目标对象
        final Target target=new Target();
//        创建增强对象
        final Advice advice=new Advice();


        /*
        * Proxy.newProxyInstance()这个方法
        * 返回了代理对象实现的所有接口
        * */

//        使用Proxy类创建代理类的实例化对象
        TargetInterface proxy= (TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),//获取到代理对象的类加载器,也就是目标对象的;
                target.getClass().getInterfaces(),//获取代理对象实现的一组接口数组(实现多个接口),也是目标对象的;

//                调用代理处理器,是一个接口,需要我们自己实现
                /*
                * 表示代理对象的所有方法都会调用invocationHandle()的invoke方法
                * 个别不执行。。。。
                * */
                new InvocationHandler(){
//                    第一个是代理对象,第二个目标对象接口方法的Method实例,第三个是传递的参数
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        advice.before();
//                        使用反射中的Method类型方法,调用哪个对象的哪个方法的哪些参数
                        /*
                        * 第一个参数是:调用的是哪个对象
                        * 第二个参数是:方法的参数
                        * */
                        Object invoke = method.invoke(target, args);
                        advice.afterReturning();
                        return invoke;
                    }
                }
        );
//        调用代理对象的方法
        proxy.save();
    }
}

执行结果如下:
在这里插入图片描述
这样就实现了,aop的大致效果!

未完待续....

cglib动态代理技术

cglib的动态代理技术,无需基于接口实现动态代理的技术;基于父类实现动态代理技术;

其底层,如下:

底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

小案例

这里只演示一个怎么使用的小案例,具体的一些方法以及是如何实现的,留下坑;等以后自己来填;

创建一个目标类,如下:

package com.web.dynamicproxy.cglib;
/**
 * 创建目标对象
 * 属于cglib的,无需实现目标接口
 * */
public class Target {
    public void introduce(){
        System.out.println("我是cglib的目标对象");
    }
}

创建一个增强类,如下:

package com.web.dynamicproxy.cglib;

/**
 * 创建增强对象
 * */
public class Advice {
    public void before(){
        System.out.println("前置增强。。。。。");
    }
    public void afterReturning(){
        System.out.println("后置增强。。。。。");
    }
}

因为cglib是基于继承,也就是将目标对象作为父类,产生子类代理类实现的,所以,不需要创建一个接口,直接创建一个生成代理对象的类,如下:

package com.web.dynamicproxy.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 创建cglib的代理对象
 * 基于父类
 * */
public class ProxyTest {
    public static void main(String[] args) {
//        创建目标对象
        final Target target= new Target();
//        创建增强对象
        final Advice advice=new Advice();

//        1.使用在spring包中的cglib的增强器
        Enhancer enhancer=new Enhancer();
//        2.设置父类(目标对象)
        enhancer.setSuperclass(Target.class);
//        3.设置回调
        enhancer.setCallback(new MethodInterceptor() {
            /*
            * proxy:代理对象
            * method:目标对象的方法
            * args:目标对象的方法参数
            * methodParoxy:代理方法
            * */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//                执行前置
                advice.before();
//
                /*
                * 如果使用methodProxy方法的话,
                * 那就要使用methodProxy.invokeSuper()方法
                * 调用父类,即目标方法的方法
                * */
                Object invoke = method.invoke(target, args);
//                Object invoke = methodProxy.invokeSuper(proxy, args);
//                执行后置
                advice.afterReturning();
                return invoke;
            }
        });
//        创建代理对象
        Target proxy = (Target) enhancer.create();
        proxy.introduce();
    }
}

执行结果如下:
在这里插入图片描述

注意点:

跟jdk通过反射实现对目标方法的调用不一样,这个methodProxy.invokeSuper(proxy, args);不是通过反射实现的;使用FastClass机制实现的;

这个机制的实现,参考文章:
CGLib动态代理

沉木的cglib

jdk和cglib的不同点

参考文章:
JDK和CGLib动态代理实现和区别

Java动态代理 jdk和cglib的实现比较

这个地方也是留了坑!!!

未完待续



内省

预知后事如何,且听下回分解。。。。。。。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值