1.代理是什么?
代理(Proxy)是一种设计模式。提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。
有一个类我们无法更改,但我们希望在原有类上加上我们自己的逻辑(增强),这时就可以使用代理,使用代理的优点是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
用户和目标对象通过代理对象交互,就像是商家和明星之间通过经纪人交流沟通。
2. 静态代理
使用静态代理时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同的父类。
代码:
接口:TargetInteface.java
public interface TargetInteface {
void method1();
void method2();
int method3(Integer i);
}
目标对象类:Target.java 实现了接口的方法
import com.heaboy.aopdemo.jdkproxy.TargetInteface;
public class Target implements TargetInteface {
@Override
public void method1() {
System.out.println("method1 running ...");
}
@Override
public void method2() {
System.out.println("method2 running ...");
}
@Override
public int method3(Integer i) {
System.out.println("method3 running ...");
return i;
}
}
代理对象类:TargetProxy.java 实现了接口的方法
public class TargetProxy implements TargetInteface {
@Override
public void method1() {
System.out.println("执行方法前...");
new Target().method1();
System.out.println("执行方法后...");
}
@Override
public void method2() {
System.out.println("执行方法前...");
new Target().method2();
System.out.println("执行方法后...");
}
@Override
public int method3(Integer i) {
System.out.println("执行方法前...");
int method3 = new Target().method3(i);
System.out.println("执行方法后...");
return method3;
}
}
测试类:TargetUser.java
public class TargetUser {
public static void main(String[] args) {
TargetInteface target = new TargetProxy();
target.method1();
System.out.println("-----------------------------");
target.method2();
System.out.println("-----------------------------");
System.out.println(target.method3(3));
}
}
运行结果如下:
总结:
1.在不修改目标对象的前提下,对目标功能进行扩展。
2.缺点:因为代理对象需要和目标对象实现一样的接口,所以会有很多代理类,导致类太多;同时一旦接口增加方法,目标对象与代理对象都需要增加维护,使用动态代理可避免。
3.动态代理
动态代理也叫作JDK代理,接口代理。使用动态代理,代理对象不需要实现接口;代理对象的生成是利用JDK的API,动态的在内存中创建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)。
创建代理对象可以使用newProxyInstance方法,该方法需要接收三个参数:
static Object newProxyInstance(
ClassLoader loader,//指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,//目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h )//事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
代码:
接口类:TargetInteface.java
public interface TargetInteface {
void method1();
void method2();
int method3(Integer i);
}
目标对象类:Target.java 实现了接口的方法
public class Target implements TargetInteface {
@Override
public void method1() {
System.out.println("method1 running ...");
}
@Override
public void method2() {
System.out.println("method2 running ...");
}
@Override
public int method3(Integer i) {
System.out.println("method3 running ...");
return i;
}
}
代理对象类:TargetProxy.java
没有实现接口,但是使用newProxyInstance方法动态创建了一个代理对象。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TargetProxy {
public static <T> Object getTarget(T t) {
return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
//比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
System.out.println("执行方法前...");
Object invoke = method.invoke(t, args);
System.out.println("执行方法后...");
return invoke;
}
});
}
}
测试类:TargetUser.java
public class TargetUser {
public static void main(String[] args) {
TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
target.method1();
System.out.println("-----------------------------");
target.method2();
System.out.println("-----------------------------");
System.out.println(target.method3(3));
}
}
执行结果如下:
总结:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。
4.Cglib代理
前面说的静态代理和动态代理模式都是要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式来实现代理,这种代理方法称为Cglib代理。
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
1.JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。
2. CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
Cglib代理实现方法:
1.使用cglib需要引入cglib的jar包,如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。
cglib的Maven坐标:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
2.引入功能包后,就可以在内存中动态构建子类。
3.代理的类不能为final,否则报错。
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。
代码:
目标对象类:Target.java
public class Target {
public void method1() {
System.out.println("method1 running ...");
}
public void method2() {
System.out.println("method2 running ...");
}
public int method3(Integer i) {
System.out.println("method3 running ...");
return i;
}
}
代理对象类:TargetProxy.java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TargetProxy {
static <T> Object getProxy(T t) {
Enhancer en = new Enhancer(); //帮我们生成代理对象
en.setSuperclass(t.getClass());//设置要代理的目标类
en.setCallback(new MethodInterceptor() {//代理要做什么
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("执行方法前。。。");
//调用原有方法
Object invoke = methodProxy.invokeSuper(object, args);
// Object invoke = method.invoke(t, args);// 作用等同与上面。
System.out.println("执行方法后。。。");
return invoke;
}
});
return en.create();
}
}
测试类:TargetUser.java
public class TargetUser {
public static void main(String[] args) {
Target target = (Target) TargetProxy.getProxy(new Target());
target.method1();
}
}
执行结果:
总结:
在Spring的AOP编程中,如果加入容器的目标对象有实现接口,用JDK代理,如果目标对象没有实现接口,用Cglib代理。