Java 代理模式:静态代理、JDK 动态代理和 Cglib 动态代理的区别
在接触代理模式之前觉得使用代理很麻烦,一个类的方法为什么不直接调用,还要通过增加的代理类,其实,当你只有一个类要实现一些增加的功能时,直接在这个类里增加方法是很简单,当你有几十个,几百个类要增加相同功能时,一个一个的添加就非常繁琐。使用代理:
-
一是增加功能很方便,直接写在代理类里就可以了;
-
二是降低了代码的耦合度,更加便于调整和维护,要调整时,只要调整代理类就可以了,其他类的代码都不用动。
静态代理
-
接口
public interface Animal {
public void eat();
} -
实现类(被代理的类)
public class Dog implements Animal{
-
静态代理类
public class StaticProxy implements Animal{
//被代理对象
private Animal dog = new Dog();
-
测试
public class Test {
public static void main(String[] args) {
Animal dog = new StaticProxy();
dog.eat();
}
}
// 运行结果
StaticProxy执行前
狗吃肉骨头
StaticProxy执行后
JDK 动态代理
接口和实现类同上
// JDK 动态代理,实现 InvocationHandler 接口
public class DynamicProxy implements InvocationHandler{
private Object obj;//被代理对象
//传入被代理对象
public Object getProxy(Object objA){
this.obj = objA;
//创建代理对象,并关联被代理对象
Object objProxy = Proxy.newProxyInstance(objA.getClass().getClassLoader(),
objA.getClass().getInterfaces(), this);
return objProxy;
}
//使用反射技术,协助调用被代理对象的方法
-
测试
public class Test {
public static void main(String[] args) {
//JDK 动态代理
Animal dog = new Dog();
DynamicProxy dp = new DynamicProxy();
Animal dogProxy = (Animal) dp.getProxy(dog);
dogProxy.eat();
}
}
// 运行结果
DynamicProxy方法执行前
狗吃肉骨头
DynamicProxy方法执行后
Cglib 动态代理
接口和实现类同上
//cglib 动态代理,实现 MethodInterceptor 接口
public class CglibProxy implements MethodInterceptor{
//被代理的对象
private Object obj;
//传入目标对象
public CglibProxy(Object objA){
this.obj = objA;
}
public Object getProxy(){
//创建目标对象的子对象,通过反射对父对象的内容拦截和处理
Enhancer e = new Enhancer();
e.setSuperclass(obj.getClass());
e.setCallback(this);
//返回代理对象
return e.create();
}
-
测试
public class Test {
public static void main(String[] args) {
//cglib代理
Animal dog = new Dog();
CglibProxy proxy = new CglibProxy(dog);
Animal dogProxy = (Animal) proxy.getProxy();
dogProxy.eat();
}
}
AOP代理
AOP代理可替代目标对象,AOP代理对象包含了目标对象的全部方法,但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。
思路:创建Dog对象和他的GunDog实现类,创建InvocationHandler的实现类MyInvocationHandler, 在invoke方法中调用目标对象的方法,并且在方法前后加入自定义的Utils类的方法, 创建代理对象工厂类MyProxyFactory,用于生产代理对象。
-
接口
public interface Dog {
void info();
void run();
}
-
实现接口(被代理类)
public class GunDog implements Dog {
-
类aop的前置后置执行方法
public class Util {
public void method1(){
System.out.println("----模拟第一个通用方法----");
}
public void method2(){
System.out.println("----模拟第二个通用方法----");
}
}
-
代理类
public class MyInvocationHandler implements InvocationHandler {
//被代理的对象;
private Object target;
public void setTarget(Object object) {
this.target = object;
}
-
创建代理及利用jdk代理执行类
public class MyProxyFactory {
public static Object getProxy(Object target){
//创建一个MyInvocationHandler对象
MyInvocationHandler handler=new MyInvocationHandler();
//设置指定的要生成动态代理的target对象
handler.setTarget(target);
//创建并返回一个动态代理对象。
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);
}
}
-
测试
public class MainTest {
public static void main(String args[]) throws Exception {
GunDog dog=new GunDog();
Dog proxyDog= (Dog) MyProxyFactory.getProxy(dog);
proxyDog.info();
proxyDog.run();
}
}
// 结果
----模拟第一个通用方法----
我是一只猎狗
----模拟第二个通用方法----
----模拟第一个通用方法----
我奔跑迅捷
----模拟第二个通用方法----
四者的特点和区别
-
静态代理,要把被代理的对象写在类里面,只能处理一个类,执行效率高,代码的耦合度很高,复用性很差。
-
JDK 动态代理,是代理类要实现 InvocationHandler 接口,接口里面有个(method.invoke(对象,参数) 方法,它是利用反射执行被代理对象的方法;Java 动态代理通过 Proxy.newProxyInstance() 方法动态的获得代理对象,这个方法有三个参数:(类加载器、接口,InvocationHandler 接口的子类实例);其中有个参数是接口,也就是说,Java 动态代理只能代理实现了接口的类,被代理的类如果没有实现任何接口,则不能实现 JDK 动态代理。
-
Cglib 动态代理,和 JDK 动态代理通过接口实现不同, Cglib 动态代理通过继承实现,通过生成子类字节码,重写被代理类的方法,在重写的方法中增强功能;因为 Cglib 动态代理要继承被代理的类,所以,被 final 修饰的类或方法不能实现 Cglib 动态代理。
-
AOP动态代理,AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。类似JDK的动态代理,但是它代理的是IOC容器内的方法,不能执行代理容器外的方法
Spring AOP不起作用原因
一、直接在切面类定义切点;
-
AOP切面类里面的方法全部不支持触发切面,否则一个切面函数把自己当做切点就会导致递归层层调用。
-
AOP切面类发出函数调用一律不触发切面,避免两个切面类相互调用迭代请求的情况。
二、被代理对象两个切点方法内部调用;
被Spring的AOP增强的类,在同一个类的内部方法调用时,其被调用方法上的增强通知将不起作用,即Spring的事务传播策略在内部方法调用时将不起作用,不管你希望某个方法需要单独事务,是RequiresNew,还是要嵌套事务,要Nested,等等,统统不起作用。