代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展。
比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing()
1 public class Singer{
2 public void sing(){
3 System.out.println("唱一首歌");
4 }
5 }
假如你希望对目标对象Singer的sing方法进行功能扩展,例如在唱歌前后向观众问好和答谢,类似这样:
1 public void sing(){
2 System.out.println("向观众问好");
3 System.out.println("唱一首歌");
4 System.out.println("谢谢大家");
5 }
但是又不能直接对源代码进行修改,甚至有可能你都不知道要对哪个目标对象进行扩展。这时就需要用到java的代理模式了。
静态代理要求原始类有实现某个接口。
需要创建一个代理类,实现和原始类相同的接口,并在需要增强的方法里,调用原始类的该方法,调用前后加上我们需要添加的代码。
使用的时候,直接创建一个代理类实例,调用目标方法即可。
共同接口
public interface Action {
public void doSomething();
}
原始类
public class RealObject implements Action{
public void doSomething() {
System.out.println("do something");
}
}
代理类
public class Proxy implements Action {
private Action realObject;
public Proxy(Action realObject) {
this.realObject = realObject;
}
public void doSomething() {
System.out.println("proxy do");
realObject.doSomething();
}
}
使用
Proxy proxy = new Proxy(new RealObject());
proxy.doSomething();
public interface ISinger {
void sing();
}
/**
* 目标对象实现了某一接口
*/
public class Singer implements ISinger{
public void sing(){
System.out.println("唱一首歌");
}
}
/**
* 代理对象和目标对象实现相同的接口
*/
public class SingerProxy implements ISinger{
// 接收目标对象,以便调用sing方法
private ISinger target;
public UserDaoProxy(ISinger target){
this.target=target;
}
// 对目标对象的sing方法进行功能扩展
public void sing() {
System.out.println("向观众问好");
target.sing();
System.out.println("谢谢大家");
}
}
测试类:
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
//目标对象
ISinger target = new Singer();
//代理对象
ISinger proxy = new SingerProxy(target);
//执行的是代理的方法
proxy.sing();
}
}
总结:这里做的事情无非就是,创建一个代理类SingerProxy,继承原始类的ISinger接口,实现其中的方法,并在实现中调用目标对象的方法。这里的关键是“调用目标对象方法”,如果直接重写就不叫代理了。
优点:扩展原功能,不侵入原代码。
缺点:
①冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
②不易维护。代理对象必须提前写出,一旦接口发生了变化,代理对象的代码也要进行维护。
代理类在程序运行时运用反射机制创建的代理方式被成为动态代理。
也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
同样要求原始类实现某个接口,但不用手动创建代理类,而是创建中介类。中介类实现InvocationHandler接口。
调用Proxy类中的newProxyInstance(ClassLoader loader,Class>[]
interfaces,InvocationHandler h)方法以创建一个动态代理对象,其中第三个参数为我们创建的实现InvocationHandler接口的类(中介类),前两个参数可通过目标类.getclass().getxxx获取。
需实现InvocationHandler接口,包含一个Object类型的对象,并利用其编写中介类的有参构造函数。重写的方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
里,proxy表示代理类对象, method标识了我们具体调用的代理类的方法,args为这个方法的参数。
public interface ISinger {
void sing();
}
/**
* 目标对象实现了某一接口
*/
public class Singer implements ISinger{
public void sing(){
System.out.println("唱一首歌");
}
}
-------------------------
public class Test{
public static void main(String[] args) {
Singer target = new Singer();//这行要自己写
ISinger proxy = (ISinger) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("向观众问好");
//目标对象方法前后编写需要扩展的代码
Object returnValue = method.invoke(target, args);
System.out.println("谢谢大家");
return returnValue;
}
});
proxy.sing();
}
}
public static void main(String[] args) throws InterruptedException {
EnHello enHello=new EnHello();
Hello hello=(Hello)Proxy.newProxyInstance(enHello.getClass().getClassLoader(),enHello.getClass().getInterfaces(), new MyInvocationHandler(enHello));
hello.sayHello("Tom");
}
interface Hello{
String sayHello(String username);
}
static class EnHello implements Hello{
@Override
public String sayHello(String username) {
System.out.println("Hello, "+username);
return "finished";
}
}
static class MyInvocationHandler implements InvocationHandler{
private Object object;
public MyInvocationHandler(Object object){
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
System.out.println("before say hello");
if("sayHello".equals(method.getName())){
result=method.invoke(object,args);
}
System.out.println("before say hello");
return result;
}
}
还可以只为指定方法动态代理,在invoke方法加上以下判断:
String methodName = method.getName();
if("eating".equals(methodName))
method.invoke(obj,args);
优点一:可以隐藏委托类的实现;
优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理
JDK动态代理和cglib动态代理有什么区别?
使用JDK动态代理的对象必须实现一个或多个接口
使用cglib代理的对象则无需实现接口。
cglib可以对任意类生成代理对象,它的原理是对目标对象进行继承代理,所以如果目标对象被final修饰,那么该类无法被cglib代理。
导包-创建MethodInterceptor实现类
使用cglib需要引入cglib的jar包,如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。
目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
/**
* 目标对象,没有实现任何接口
*/
public class Singer{
public void sing() {
System.out.println("唱一首歌");
}
}
----------------------
/**
* Cglib子类代理工厂
*/
public class ProxyFactory implements MethodInterceptor{
// 维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
/*
使用时只有intercept方法中,代码行 method.invoke前后的代码需要修改,其他的代码直接使用
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("向观众问好");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("谢谢大家");
return returnValue;
}
}
-----------------------
/**
* 测试类
*/
public class Test{
public static void main(String[] args){
//目标对象
Singer target = new Singer();
//代理对象
Singer proxy = (Singer)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.sing();
}
}
public class TestCglib implements MethodInterceptor {
Object target;
//动态生成一个新的类,使用父类的无参构造方法创建一个指定了特定回调的代理实例
public Object getProxyObject(Object object) {
this.target = object;
//增强器,动态代码生成器
Enhancer enhancer=new Enhancer();
//回调方法
enhancer.setCallback(this);
//设置生成类的父类类型
enhancer.setSuperclass(target.getClass());
//动态生成字节码并返回代理对象
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy
methodProxy) throws Throwable {
System.out.println("----------before");
// 调用方法
Object result = methodProxy.invoke(target, objects);
System.out.println("----------after");
return null;
}
}
//使用
public static void main(String[] args) {
Boss boss=(Boss) new TestCglib().getProxyObject(new Boss());
boss.eating();
boss.sleeping();
}