参考博文[https://www.jianshu.com/p/471c80a7e831]
代理分为3种
- jdk静态代理
- jdk动态代理
- cglib动态代理
jdk静态代理
实现过程:
- 创建一个接口
public Interface HelloInterface(){
void sayHello();
}
2.创建被代理的类实现该接口并实现接口中的抽象方法
public class Hello implements HelloInterface{
@Override
public void sayHello(){
System.out.println("Hello Eakon");
}
}
3.创建一个代理类,同时也实现这个接口,在代理类中持有一个被代理对象的引用,之后在代理类方法中调用该对象的方法。
public class HelloProxy implements HelloInterface{
private HelloInterface interface=new Hello();
@Override
public void sayHello(){
System.out.println("Before invoke sayHello");
interface.sayHello();
System.out.println("After invoke sayHello");
}
}
测试代码:
public class Client {
public static void main(String[] args){
HelloProxy helloProxy=new HelloProxy();
helloProxy.sayHello();
}
}
运行结果:
Before invoke sayHello
Hello Eakon
After invoke sayHello
代理类HelloProxy为被代理类Hello预处理过滤,之后将消息转发给被代理类,之后还能进行消息的后置处理。被代理类和代理类存在依赖关系。代理类本身不实现服务,而是通过调用被代理类的方法来提供服务。
缺点:静态代理只能为一个类服务,如果需要为多个类服务(需要代理的类很多),需要编写大量的代理类,比较繁琐。
jdk动态代理
代理类
public class HelloProxy implements InvocationHandler{
private Object subject;
public HelloProxy(Object subject) {
super();
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke"+method.getName());
method.invoke(subject, args);
System.out.println("After invoke"+method.getName());
return null;
}
}
测试代码:
public class Client {
public static void main(String[] args) {
Hello hello=new Hello();
InvocationHandler handler=new HelloProxy(hello);
HelloInterface helloInterface=(HelloInterface) Proxy.newProxyInstance(
handler.getClass().getClassLoader(), hello.getClass().getInterfaces(),
handler);
helloInterface.sayHello();
}
}
也是基于基本接口实现的。通过接口指向实现类实例的多态方式,可以将具体实现与调用解耦,便于后期的维护。
jdk动态代理和静态代理区别:
相同点:都要创建代理类,代理类都要实现接口
不同点:静态代理中,需要对接口,被代理类创建代理类,在编译前就需要代理类实现和被代理类相同的接口,并且直接在实现的方法中调用被代理类相应的方法;但是,动态代理中,我们不知道对哪个接口哪个被代理类创建代理类,因为代理类的创建是利用反射在运行时。
spring中用到是jdk动态代理和cglib动态代理。动态代理天生就是为aop而生,如果没有动态代理,需要写一些重复的逻辑。有了动态代理,结合反射,可以很轻松的实现接口级别的aop 。
如果,没有接口,想要实现代理,使用cglib动态代理。
cglib动态代理
首先引入cglib依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
被代理类
public class HelloImpl {
void sayHello() {
System.out.println("hello cglib");
}
}
代理类实现MethodInterceptor接口
public class HelloMethodInterceptor implements MethodInterceptor{
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("Before"+arg1.getName());
Object object=arg3.invokeSuper(arg0, arg2);
System.out.println("After"+arg1.getName());
return object;
}
}
测试代码:
public class App
{
public static void main( String[] args )
{
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(HelloImpl.class);//继承被代理类
enhancer.setCallback(new HelloMethodInterceptor());//设置回调
HelloImpl helloService=(HelloImpl) enhancer.create();//生成代理类对象
helloService.sayHello();//调用该方法被我们实现的方法拦截器拦截
}
}
jdk代理要求被代理的类必须实现接口,但cglib动态代理没这样的强制要求,它会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理和后置处理)。
代理类对象是由Enhancer(cglib的字节码增强器,可以方便的对类进行扩展)创建的。
缺点:不能为final的类或方法进行代理。因为final的类或方法不能继承。
代理方式 | 实现 | 优点 | 缺点 | 特点 |
---|---|---|---|---|
jdk静态代理 | 代理类和被代理类实现同一接口,并且在代理类中需要持有被代理对象的引用 | 实现容易 | 实际使用中,需要声明硬编码接口,浪费存储空间且效率低 | 无 |
jdk动态代理 | 代理类和被代理类实现同一接口,代理类实现InvocationHanlder接口,重写invoke方法,在invoke中进行增强处理 | 不需要编码接口,代码重用率高 | 只能代理实现了接口的被代理类 | 使用反射进行方法的调用 |
cglib动态代理 | 代理类将被代理类作为自己的父类,并为其中的非final方法创建两个方法,一个是与被代理类方法签名相同的方法,它在方法中通过super调用被代理的方法;另一个是代理类独有的方法,它会判断是否实现了MethodInterceptor接口的对象,若存在调用intercept方法进行代理 | 可以对类和接口进行代理,并且被代理类无须实现接口 | 不能对final的类或方法进行代理 | 将全部方法存入一个数组中,通过数组索引直接进行方法调用。 |