文章目录
什么是反射?
如下图所示:
文字描述:
在运行状态下,
对于任意的一个类或对象,可以获取它所有的属性和方法;
这种动态获取信息以及动态调用对象的功能就叫做java的反射机制。
从上面那张图中可以看出,我们需要得到这个类的字节码对象, (以下统称为类对象)才能实现反射;
一些话
第一点:
既然我们得到的是类.class文件在内存中表现形式字节码class类,那么我们可以查看这个Class类的api:
如下:
其中方法接近有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.装饰者模式,只能在一定程度上减少类的继承;并不能避免继承!
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是面向切面编程;
这个面向切面编程,我理解为,面向增强内容所在类编程;
之前说的装饰者,增强内容是固定的,增强对象是动态的,被增强对象固定;
而这个动态代理,增强内容是动态的,增强对象也是动态的,连被增强对象也是动态的;这个是真的灵活!
a.基于接口的,jdk动态代理;
b.基于父类的,cglib动态代理
jdk动态代理技术
需要用到一个类,Proxy类;这个类是属于reflect类,好家伙!!反射中的东西!!!
查了下文档,如下:
文档显示,有两种方式创建代理类的实例:
第一种方式是通过getProxyClass()方法实现的;
第二种是静态方法newProxyInstance()方法实现的;
很明显勒,选择第二种,第一种看起来都复杂些,也看了些网上的文章,大家也都是用的第二种方式;
既然使用第二种方式,那么就得看看这个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动态代理
jdk和cglib的不同点
参考文章:
JDK和CGLib动态代理实现和区别
这个地方也是留了坑!!!
未完待续