代理含义: 使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原方法。
一、静态代理
直接上代码如下
1、接口
public interface ClothFactory {
void produceCloth();
}
2、静态代理类
public class ProxyClothFactory implements ClothFactory{
private ClothFactory clothFactory; // 就拿被代理对象进行实例化
public ProxyClothFactory(ClothFactory clothFactory) {
this.clothFactory = clothFactory;
}
@Override
public void produceCloth() {
System.out.println("代理工厂做一些准备工作");
clothFactory.produceCloth();
System.out.println("代理工厂做一些后续的收尾工作");
}
}
3、被代理对象
public class SupClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("sup生产衣服、。。。。。。。。");
}
}
4、静态代理的使用
public class StaticProxyTest {
public static void main(String[] args) {
// 创建被代理对象
SupClothFactory supClothFactory = new SupClothFactory();
// 创建代理类对象
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(supClothFactory);
proxyClothFactory.produceCloth();
}
}
总结
所所谓静态代理即
1、创建一个实现了接口的代理类。
2、通过这个代理类 去创建 被代理的类,并对代理类要执行的方法前后进行增强。
二、动态代理
使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原方法。
- 想要实现动态代理,需要解决的问题:
- 如何根据加载到内存中的被代理类,动态创建一个代理类及其对象
- 当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
动态代理常用的两种方式
- java自带的代理方式
-
1、javaProxy
(1)接口
public interface Human {
String getBelief();
void eat(String food);
}
(2)动态代理工厂
```java public class MyInvocationHandler implements InvocationHandler {
private Object obj;// 需要使用被代理类的对象进行赋值
public void bind(Object o) { this.obj = o; }
// 当我们通过代理类的对象,调用方法A时,就会自动的调用如下方法:invoke(0 // 将被代理类要执行的方法a的功能,声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在原方法执行执行之前加入方法 System.out.println(“method.invoke(obj, args),执行之前”);
// 代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 // obj:被代理对象 Object invoke = method.invoke(obj, args);
// 在原方法执行执行之后加入方法 System.out.println(“method.invoke(obj, args),执行之后”);
// 上诉方法的返回值就作为当前类中的invoke()的返回值 return invoke; } }
public class ProxyFactory { // 调用此方法,放回一个被代理类对象,被代理类的对象 public static Object getProxyInstance(Object o) { MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(o); // 被代理类的 类加载器、接口、 return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), handler); } }
<a name="Dsskl"></a>
##### (3)被代理对象
```java
public class Superman implements Human {
@Override
public String getBelief() {
return "I believe I can fly";
}
@Override
public void eat(String food) {
System.out.println("I like eating " + food);
}
}
(5)动态代理的使用
/**
* @description: 动态代理
* 想要实现动态代理,需要解决的问题:
* 1、如何根据加载到内存中的被代理类,动态创建一个代理类及其对象
* 2、当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
* @Author: wangchao
* @Date: 2021/7/18
*/
public class DynamicProxyTest {
public static void main(String[] args) {
// 被代理类对象
Superman superman = new Superman();
// 注意:这里的human不是superman,因为我们是使用superman在这里是被代理类,
// 通过ProxyFactory.getProxyInstance(superman)生成了superman的代理对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superman);
System.out.println(proxyInstance.getBelief());
proxyInstance.eat("fish");
System.out.println("\n----------------------\n");
// 之前的静态代理,我们也可以通过动态代理来创建SupClothFactory的代理对象
SupClothFactory supClothFactory = new SupClothFactory();
ClothFactory clothFactory = (ClothFactory)ProxyFactory.getProxyInstance(supClothFactory);
clothFactory.produceCloth();
}
}
2、Cglib
CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。 CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解。
(1)创建Cglib代理工厂
public class CreatureCglibFactory {
public static <T>T getInstanceCglib(T t) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(t.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Hi creature,come on!");
Object invoke = methodProxy.invoke(t, objects);
System.out.println("Oh,shit!");
return invoke;
}
});
return (T)enhancer.create();
}
}
(2)测试
public class CreatureCglibFactoryTest {
public static void main(String[] args) {
Dog dog = CreatureCglibFactory.getInstanceCglib(new Dog());
dog.eat();
}
}
输出:
Hi creature,come on!
哈哈哈哈,真好吃
Oh,shit!
三、总结
javaProxy
1、如何根据加载到内存中的被代理类,动态创建一个代理类及其对象
Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), handler);
2、当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
实现InvocationHandler方法,并使用method.invoke(obj,args);
如第二章:https://www.yuque.com/wangchao-volk4/fdw9ek/pc33e3#C31kr
Cglib
我们通过一个 Enhancer 和一个 MethodInterceptor 来实现对方法的拦截。
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
两者区别
1、java自带的proxy只能对接口进行代理,而cglib可以对所有类进行代理。
2、java动态代理使用java原生反射API(java.lang.reflect)进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效。