什么是代理
代理对象一般用来对既有对象增加或者修改一些功能使用,是源对象的副本。一般拥有源对象的所有方法,java中一般都是继承原来的对象。可以不修改原来对象前提下,实现我们期待的行为。
在实际应用中,根据单一职责原则,代理对象一般只是负责代理,不负责其它处理逻辑,处理逻辑一般有handler完成
如何创建代理
- 静态代理:利用组合关系实现静态代理,组合关系注入源对象,然后可以在外层对象进行其它逻辑的注入。静态代理的问题是不同的逻辑注入需要不同的代理对象,一直增加代理对象显然不实际。
- 动态代理:在java中,可以直接利用java.lang.refect.Proxy类直接创建代理类或者代理对象。
静态代理
静态代理主要把握两点:
- 代理类和源对象必需实现同一个接口
- 通过组合,调用源对象方法,并进行自定义逻辑的注入
举例如下:
源对象:
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.addUser");
}
@Override
public void delUser(String userId) {
System.out.println("UserManagerImpl.delUser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser");
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
代理类(实现同一个接口,组合关系引用):
public class UserManagerImplProxy implements UserManager {
// 目标对象
private UserManager userManager;
// 通过构造方法传入目标对象
public UserManagerImplProxy(UserManager userManager){
this.userManager=userManager;
}
@Override
public void addUser(String userId, String userName) {
try{
//添加打印日志的功能
//开始添加用户
System.out.println("start-->addUser()");
userManager.addUser(userId, userName);
//添加用户成功
System.out.println("success-->addUser()");
}catch(Exception e){
//添加用户失败
System.out.println("error-->addUser()");
}
}
@Override
public void delUser(String userId) {
userManager.delUser(userId);
}
@Override
public String findUser(String userId) {
userManager.findUser(userId);
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
userManager.modifyUser(userId,userName);
}
}
客户端调用:
public class Client {
public static void main(String[] args){
//UserManager userManager=new UserManagerImpl();
UserManager userManager=new UserManagerImplProxy(new UserManagerImpl());
userManager.addUser("1111", "张三");
}
}
静态代理模式简单易用,但是有个问题,代理类必需实现源对象的接口,如果要代理多个接口,需要很多的代理对象,类的数量增加太多,有没有什么优化的方式呢?请看动态代理
动态代理
JDK动态代理
Jdk动态代理我们先利用jdk自带的Proxy类创建一个代理对象,代码如下:
package com.puhui.goosecard.bank.utils;
import com.puhui.goosecard.bank.service.AccountService;
import com.puhui.goosecard.bank.service.impl.AccountServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 2 * @Author: kerry
* 3 * @Date: 2018/8/24 19:48
* 4
*/
public class Test {
private static AccountService accountService = new AccountServiceImpl();
public static void main(String[] args) {
AccountService accountServiceProxy =
(AccountService) Proxy.newProxyInstance(AccountService.class.getClassLoader(), new Class[]{AccountService.class}, new AccountHandler());
accountServiceProxy.openAccount(null);
}
static class AccountHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
method.invoke(accountService, args);
System.out.println("after");
return null;
}
}
}
上述代码注意以下几点:
- 利用Proxy类的newProxyInstance方法创建代理对象,第一个参数是classloader,第二个是接口类的数组(jdk代理支持多个接口的关键所在),第三个是invocationhandler
- InvocationHander实现类中,invoke方法的入参,第一个是生成的代理对象,第二个是方法对象,第三个是参数,所以hadler中需要注入源对象,handler是没有源对象的引用的。
- jdk动态代理通过反射调用被代理对象的方法,cglib是通过fastclass的索引方式
如果接口类数组传入多个接口呢?这个毕竟是jdk动态代理要解决的问题,我们看代码:
package com.puhui.goosecard.bank.utils;
import com.puhui.goosecard.bank.service.AccountService;
import com.puhui.goosecard.bank.service.TradeService;
import com.puhui.goosecard.bank.service.impl.AccountServiceImpl;
import com.puhui.goosecard.bank.service.impl.TradeServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 2 * @Author: kerry
* 3 * @Date: 2018/8/24 19:48
* 4
*/
public class Test {
private static AccountService accountService = new AccountServiceImpl();
private static TradeService tradeService = new TradeServiceImpl();
public static void main(String[] args) {
Object proxyInstance =
Proxy.newProxyInstance(AccountService.class.getClassLoader(), new Class[]{AccountService.class, TradeService.class}, new AccountHandler());
// if (proxyInstance instanceof AccountService) {
// AccountService accountService = (AccountService) proxyInstance;
// accountService.openAccount(null);
// }
if (proxyInstance instanceof TradeService) {
TradeService tradeService = (TradeService) proxyInstance;
tradeService.processRefund(null, null);
}
}
static class AccountHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (proxy instanceof AccountService) {
System.out.println("before");
method.invoke(accountService, args);
System.out.println("after");
}
if (proxy instanceof TradeService) {
System.out.println("before");
method.invoke(tradeService, args);
System.out.println("after");
}
return null;
}
}
}
上面的代码注意以下几点:
- 接口数组传入了accouont service和trade service两个接口,返回的代理对象只能是object。处理时做instanceOf判断
- handler中第一个参数proxy在一个接口是并没有使用,但是在多个接口类时,proxy可以判断当前是哪个接口
- 第一个和第二条的if 都可以进入,也即是生成的代理对象实现了接口数组中的所有接口。
总结:
我们发现jdk动态代理解决了静态代理每个接口一个代理类的问题,但是关键点在于jdk代理是面向接口的,创建代理对象传入的是接口数组,如果没有接口怎么创建代理类,继续往下看
CGLib动态代理
在没有接口的情况下,如何生成代理对象,这就是CGlib解决的问题。CGLib对ASM字节码操作框架进行了封装,在运行时生成代理对象。很多开源框架都利用cglib生成代理对象,比如spring AOP,hibernate等。
先看一个例子:
定义一个service,注意没有接口,这也是利用cglib的原因
package com.puhui.goosecard.web.cglib;
public class UserServiceImpl {
public void add() {
System.out.println("This is add service");
}
public void delete(int id) {
System.out.println("This is delete service:delete " + id);
}
}
定义一个回调方法,也即是methodInterceptor,注入自定义的逻辑,也即是为什么用代理的原因。比如权限校验等。
package com.puhui.goosecard.web.cglib;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
System.out.println("Before:" + method);
Object object = proxy.invokeSuper(obj, arg);
System.out.println("After:" + method);
return object;
}
}
生成代理对象,调用进行测试
package com.puhui.goosecard.web.cglib;
import org.springframework.cglib.proxy.Enhancer;
/**
* 2 * @Author: kerry
* 3 * @Date: 2018/8/29 19:41
* 4
*/
public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new MyMethodInterceptor());
UserServiceImpl userService = (UserServiceImpl) enhancer.create();
userService.add();
}
}
通过以上代码,可以发现:
- 通过Enhancer类生成代理对象,其中包括设置代理类(setSuperClass),设置回调方法(setCallBack)
- 通过setSuperClass,可以发现代理类继承了被代理类,也就是UserServiceImpl,且不能处理被final关键字修饰的方法
- jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;