java-代理

java常用的代理模式


代理模式是对目标对象提供的另一种访问形式,即通过代理对象访问目标对象,这样就可以在不修改目标对象的情况下来扩展目标对象的功能

常用的代理分为静态,动态

静态:程序员创建,在程序运行前已被编译成.class文件
动态:JDK动态代理,Cglib代理,在程序运行期间通过反射生成代理类

1:静态代理

静态代理需要定义接口或者父类,代理对象和目标对象同时实现或者继承
1
2
3
4
5
package com.java.proxy;
//接口
public interface UserService {
public String getName(String name);
}
1
2
3
4
5
6
7
8
package com.java.proxy;
//实现类
public class UserServiceImpl implements UserService {
public String getName(String name) {
System.out.println("helloWorld");
return name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.java.proxy;
//目标代理类
public class ProxyUserServiceImpl implements UserService {

private UserService userService;
//默认构造方法把目标类传入
ProxyUserServiceImpl(UserService userService){
this.userService = userService;
}

public String getName(String name) {
System.out.println("代理前执行。。。");
try {
//代理类中调用目标类方法
return userService.getName("hello");
} finally {
System.out.println("代理后执行。。。");
}
}
}
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
UserServiceImpl userServiceImpl = new UserServiceImpl();

ProxyUserServiceImpl proxyUserService = new ProxyUserServiceImpl(userServiceImpl);
proxyUserService.getName("");
}
//运行结果
代理前执行。。。
helloWorld
代理后执行。。。

优点:在不修改目标类(UserServiceImpl)的情况下扩展目标类
缺点:每个代理类要实现与目标类相同的接口,代码重复太多,如果接口修改,目标类和代理类都要修改,代码入侵性较大

2:动态代理–JDK动态代理

JDK动态代理不需要实现接口,通过我们指定目标类或者代理对象的接口类型JDK动态创建代理对象
主要类:java.lang.reflect.Proxyh中的newProxyInstance()方法
        java.lang.reflect.InvocationHandler 实现接口

Proxy.newProxyInstance():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* ClassLoader loader: 类加载器
* Class<?>[] interfaces: 代理的目标类
* InvocationHandler h: 得到InvocationHandler接口实例,执行目标方法时,会触发invoke方法
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
```
```java
/**
* Object proxy:被代理的对象
* Method method:要调用的方法
* Object[] args:方法调用时所需要参数
*/
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

在上个例子基础上创建动态代理,目标类还是(UserServiceImpl),这里通过一个动态代理工厂来创建代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.java.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

private Object object;

ProxyFactory(Object o){
this.object = o;
}

public Object getProxyInstance() {
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("动态代理前....");
try {
return method.invoke(object,args);
} finally {
System.out.println("动态代理后....");
}
});
}
}
1
2
3
4
5
6
7
8
9
    public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserService proxyUserService = (UserService) new ProxyFactory(userService).getProxyInstance();
proxyUserService.getName("");
}
//运行结果:
动态代理前....
helloWorld
动态代理后....

Proxy.newProxyInstance()源码中,主要代码如下:
Class<?> cl = getProxyClass0(loader, intfs);

1
2
3
4
5
6
7
8
9
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//通过接口加载代理实现类,如果代理实现类存在,直接返回,不存在,通过proxyClassFactory创建代理类
//private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); 缓存生成的代理类
return proxyClassCache.get(loader, interfaces);
}
//获取代理类参数为InvocationHandler的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams)
//生成代理类,并返回
return cons.newInstance(new Object[]{h})
大致步骤为:1根据传入的interfaces动态生成代理类,它实现interfaces接口
                    2用ClassLoader把上步生成的代理类加载到jvm中
                    3调用InvocationHandler构造函数把代理类实例化

总结:代理对象不需要实现接口,但是目标对象一定要实现接

3:动态代理–Cglib代理

上边两种代理方式都是通过实现与目标类相同的接口来生成代理对象,但有种情况我们只有类,没有接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做Cglib代理。
Cglib代理也叫子类代理,通过动态构建一个子类,继承目标类来实现扩展目标类

java运行顺序:.java文件 --> 编译成.class文件 --> classloader加载实例化成具体的类
Cglib包的底层是通过使用字节码处理框架ASM来转换字节码(.class文件)并生成新的代理类。(spring AOP)
1
2
3
4
5
6
7
8
9
package com.java.proxy;
//代理对象 目标类
public class UserController {

public String getName(String name) {
System.out.println("user constroller");
return "";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.java.proxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
//代理工厂类
public class CglibProcxyFactory implements MethodInterceptor {

private Object object;

CglibProcxyFactory(Object o){
this.object = o;
}


public Object getCglibProxyFactory(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(object.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("cglib代理前。。。");
try {
return method.invoke(object,args);
} finally {
System.out.println("cglib代理后。。。");
}
}
}
1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
UserController userController = new UserController();
UserController cglibProxyFactory = (UserController) new CglibProcxyFactory(userController).getCglibProxyFactory();
cglibProxyFactory.getName("");
}
//运行结果:
cglib代理前。。。
user constroller
cglib代理后。。。

总结:

  • cglib代理的对象不能实现,
  • 需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core的jar包即可
  • 代理类不能为finl
  • 目标对象不能为static或者final,那么就不会被拦截,即不会执行目标对象额外的业务方法。

Spring AOP 如果代理类实现接口,就采用JDK动态代理,如果没有实现接口,用Cglib代理