Java代理模式,就是通过代理对象来访问目标对象,扩展目标对象的功能,比如:当你的项目以及写完了,但是突然要求新增一个功能,在每个业务操作前去判断权限,如果直接修改源代码的话,就会很麻烦,甚至破坏业务结构,这时候使用代理来扩展功能就很合适。
静态代理
静态代理就是写一个接口或者父类,让代理对象和目标对象都实现这个接口或者继承这个父类,然后调用相同的方法,这里其实就是对Java多态思想的应用。
//公用的接口
public interface StaticBiz {
public void say();
}
//真实的业务方法去实现业务接口
public class StaticBizImpl implements StaticBiz{
@Override
public void say() {
System.out.println("这是原生的say方法");
}
}
//代理对象也实现业务接口,同时保存需要代理的对象
public class StaticBizImplProxy implements StaticBiz{
//保存需要代理的对象(父类的引用指向子类对象)
private StaticBiz statizBiz;
public StaticBizImplProxy(StaticBiz statizBiz) {
this.statizBiz = statizBiz;
}
@Override
public void say() {
//调用需要扩展的方法,追加日志或者管理权限等等
System.out.println("这是我的扩展");
//调用正在的业务方法
this.statizBiz.say();
}
}
//测试类
public class StaticProxyTest {
public static void main(String[] args) {
StaticBiz staticBiz = new StaticBizImpl();
StaticBizImplProxy proxy = new StaticBizImplProxy(staticBiz);
proxy.say();
}
}
这里有一个有意思的问题,就是我的代理类StaticBizImplProxy就算不实现StaticBiz接口,也可以实现一样的功能,为什么还要实现这个接口呢?
这里其实就是在真实的开发中,我们使用的是面向接口的开发方式,也就是说,在实际的开发中,我们的代码是这样的:
StaticBiz staticBiz = new StaticBizImpl();
//调用业务方法
staticBiz.say();
当我们使用代理时,代码是这个的:
StaticBiz staticBiz = new StaticBizImplProxy();
//调用代理对象的方法,增加功能
staticBiz.say();
我们发现这样的话,我们的代理对象可以直接替换目标对象,在项目中直接使用,但是如果代理对象不实现StaticBiz 接口,则不能使用这种方法,那我们就还是要修改源代码,没有真正起到代理对象的作用。
静态代理模式的优点:实现很简单,也不需要知道目标对象的实际功能,只需要专注于我们要扩展的功能。
缺点:
1、每个代理对象都需要实现接口,那如果接口发生改变,不仅仅要改变目标对象的方法,还要修改代理对象的方法,使得维护变得麻烦。
2、一个代理对象只能对应一种类型的目标对象,如果项目中目标对象有很多种,那么就得有很多种代理对象,比如当例子中除了StaticBiz,还有StaticBiz2、StaticBiz3…StaticBizN的话,我们就得写N种代理对象。
动态代理
在上面的例子中,我们针对个业务实现类都要创建一个代理类来扩充,主要是因为在静态代理中,代理对象是在编译期就已经写好了,不能做很好的扩展,我们就想,能不能有一种办法,使得我们在运行期根据不同的实现类来自动的产生对应的代理类呢?这就是动态代理。
动态代理,也就是在运行期间,jvm根据我们需要添加代理的类动态生成代理类,有如下特点:
1、目标类必须实现接口
2、代理类不需要实现目标类的接口,但必须实现InvocationHandler接口
//业务方法的接口
public interface UserBiz {
public void say(String name,String age);
}
//业务方法的实现类
public class UserBizImpl implements UserBiz{
@Override
public void say(String name, String age) {
System.out.println("我叫:"+name+",今年:"+age+"岁");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//动态代理的代理类需要实现InvocationHandler 接口
public class Handler implements InvocationHandler {
//与静态代理类似,动态代理也需要保存代理对象,但是因为编译时不知道代理对象的类型,所以用object
private Object targetObject;
//绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public Object newProxyInstance(Object targetObject) {
this.targetObject=targetObject;
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
//根据传入的目标返回一个代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
@Override
//proxy表示代理,method表示原对象被调用的方法,args表示方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这是自己添加的扩展方法
System.out.println("需要扩展的方法");
//调用原始的业务方法,是用java反射实现
Object ret=method.invoke(targetObject, args);
return ret;
}
}
//测试
public class HandlerProxyTest {
public static void main(String[] args) {
Handler handler = new Handler();
UserBiz user = (UserBiz) handler.newProxyInstance(new UserBizImpl());
user.say("张三", "22");
}
}
Cglib代理
我们是用静态代理或者动态代理时发现,我们都需要使目标对象继承类或者实现接口,但是如果我们的目标对象没有实现任何接口或者继承任何类,那我们该怎么办?
这时候我们可以是用Cglib代理,Cglib底层是构建一个目标类的子类来实现扩展,所以这就表示我们的目标类不能是final(不能被继承)的,并且需要扩展的方法不能是final或者static修饰(不能被执行代理)
Cglib不是JDK提供的代理方法,所以使用前需要导入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
//业务类
public class UserService {
public void say() {
System.out.println("这是业务方法");
}
}
//代理类必须实现MethodInterceptor
public class Cglib implements MethodInterceptor {
//需要保存的目标对象
private Object target;
//给目标对象创建一个代理对象
public Object getProxyInstance(Object target) {
this.target=target;
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(this.target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类代理对象
return en.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//你需要扩展的方法
System.out.println("这是扩展方法");
//调用真实的业务方法
Object obj = method.invoke(target);
return obj;
}
}
//测试
public class CglibTest {
public static void main(String[] args) {
Cglib cglib = new Cglib();
UserService user = (UserService) cglib.getProxyInstance(new UserService());
user.say();
}
}