代理(Proxy)是一种设计模式,提供了对目标对象其他的访问方式,也就是通过代理对象来访问目标对象。这种设计可以在不修改目标对象的基础上,增强它的功能。
代理模式有三种实现方式:
- 静态代理
- jdk代理
- cglib代理
静态代理
静态代理需要定义接口或者父类,代理对象与目标对象要实现相同的接口或者继承同一父类。
我们以dao保存数据为例(目标对象),使用代理对象进行开启和提交事务。
统一接口:1
2
3
4
5
6
7
8
9package com.xiaopeng.proxy;
public interface IBookDao {
/**
* 保存数据
*/
void save();
}
目标对象:BookDao1
2
3
4
5
6
7
8package com.xiaopeng.proxy;
public class BookDao implements IBookDao {
public void save() {
System.out.println("----保存书籍信息----");
}
}
代理对象:BookDaoProxy
代理对象也要实现IBookDao接口,负责在保存前开启事务,保存完毕后提交事务。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.xiaopeng.proxy;
public class BookDaoProxy implements IBookDao {
private IBookDao target;
public BookDaoProxy(IBookDao target) {
this.target = target;
}
public void save() {
System.out.println("----开启事务----");
target.save();
System.out.println("----提交事务----");
}
}
测试类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package com.xiaopeng.proxy;
public class Client {
public static void main(String[] args)
{
//目标对象
IBookDao target = new BookDao();
//代理对象
IBookDao proxy = new BookDaoProxy(target);
proxy.save();
}
}
优缺点:
优点:可以在不修改目标对象的前提下实现业务逻辑的扩展
缺点:目标对象与代理对象需要实现相同的接口,一旦接口改动会导致许多代码改动
jdk代理
使用java.lang.reflect.Proxy类的功能进行代理1
Static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
三个参数依次为:
- ClassLoader loader:当前目标对象使用的类加载器,可以通过target.getClass().getClassLoader()获取
- Class<?>[] interfaces:目标对象实现的接口类型
- InvocationHandler h:事件处理器,执行目标对象的方法时,会触发事件处理器的方法,并把目标对象的方法作为参数参入。使用反射调用目标对象的方法。
代理工厂:ProxyFactory1
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
34package com.xiaopeng.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
//内部维护一个目标对象的引用
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), //目标对象的类加载器
target.getClass().getInterfaces(), //目标对象实现的接口
new InvocationHandler() { //事件处理器
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
//------------代理信息
System.out.println("proxy:" + proxy.getClass());
System.out.println("method:" + method.getName());
System.out.println("----开启事务----");
Object returnVal = method.invoke(target,args);
System.out.println("----提交事务----");
return returnVal;
}
});
}
}
测试类:ProxyClient1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.xiaopeng.proxy;
public class ProxyClient {
public static void main(String[] args){
IBookDao target = new BookDao();
System.out.println("目标对象类型:" + target.getClass());
//创建代理对象
IBookDao proxy = (IBookDao) new ProxyFactory(target).getProxyInstance();
System.out.println("代理对象的类型:" + proxy.getClass());
//调用保存过程
proxy.save();
}
}
输出:
jdk代理输出结果
总结:
- jdk代理要求目标对象必须实现接口,否则不能使用。
Cglib代理
无论是静态代理还是jdk代理,都要求实现接口,但是有时目标对象只是一个单独的类,没有实现任何接口。可以使用Cglib代理处理类似的情况。
Cglib代理也叫做子类代理,在内存中构建一个子类对象来实现对目标对象功能的扩展
实现方法及注意点
- 引入Cglib的jar文件,spring-core-3.2.5.RELEASE.jar
- 在内存中动态构建子类
- 目标对象的方法为final/static时,不会被代理
目标对象:UserDao1
2
3
4
5
6
7
8
9
10package com.xiaopeng.cglib.proxy;
public class UserDao {
public void save()
{
System.out.println("保存信息");
}
}
Cglib代理工厂:ProxyFactory1
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
39
40
41
42
43
44
45package com.xiaopeng.cglib.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 ProxyFactory implements MethodInterceptor{
//内部维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 创建代理对象
* @return
*/
public Object getProxyInstance()
{
//1. Enhancer
Enhancer en = new Enhancer();
//2. 设置父类
en.setSuperclass(target.getClass());
//3. 设置回调函数
en.setCallback(this);
//4. 返回代理对象
return en.create();
}
public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
System.out.println("开启事务");
Object returnVal = method.invoke(target,params); //调用目标对象方法
System.out.println("提交事务");
return returnVal;
}
}
测试类:CglibClient1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.xiaopeng.cglib.proxy;
public class CglibClient {
public static void main(String[] args)
{
//获取目标对象
UserDao target = new UserDao();
//获取代理对象
UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
//调用目标对象方法
proxy.save();
}
}
输出:
Cglib输出结果
Cglib代理大量运用的Spring的AOP编程中。
如果目标对象有实现接口,使用jdk代理;如果目标对象没有实现接口,使用Cglib代理;
[———–以下为20190113更———–]
- UserDao类不能为final类,否则会报错「final类不能被继承」
1 | Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class class com.xiaopeng.proxy.UserDao |
- UserDao中的save()方法若被final/static修饰,那么Cglib代理时不能不能被代理拦截,除了原本的功能之外不能扩展业务逻辑
1 | class com.xiaopeng.proxy.UserDao |
本文链接: http://www.xiaopeng.pro/articles/7b510e10.html
版权声明: 本原创文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!