简述
代理模式是提供对目标对象另外一种访问方式的模式;
优点:可以在目标对象实现的基础上,添加额外的功能,即扩展目标对象的功能;
思想:在添加对目标对象新的操作时,不用修改别人的代码,只需要通过代码对象,去扩展目标对象新的功能;
三种代理模式
静态代理
定义一个接口或者父类, 目标对象和代理对象同时继承或者实现该接口;
优点: 不用修改目标对象的情况下,对目标对象进行新的扩展
缺点:产生过多的代理对象,造成代码冗余;并且当共同实现或继承同一个父类或接口,当父类或接口改变时,目标对象与代理对象都难以维护;
代码示例:
/**
* common interface
*/
public interface UserDao{
void save();
}
/**
* target object
*/
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("-----save the data ------");
}
}
/**
* proxy object
*/
public class UserDaoProxy implements UserDao {
private UserDao userDao; //use to receive target object who like UserDaoImpl
public UserDaoProxy(UserDao userDao){
this.userDao = userDao;
}
public void save(){
System.out.println("-----add new function----");
userDao.save();
System.out.println("------add new function----");
}
}
/**
* test method
*/
public class test {
public static void main(String[] args) {
UserDao targetObject = new UserDaoImpl(); //new a target object
//bulid relationships bethween tagert object and proxy object
UserDaoProxy proxyObject = new UserDaoProxy(targetObject);
proxyObject.save();//execute proxy method
}
}
动态代理
特点:代理对象不用实现接口;代理对象的生成,是通过JDK提供的API,动态帮我们在内存中构建代理对象;(需要我们指定目标对象的接口的类型)
JDK中动态生成代理对象的API:
包所在:java.lang.reflect.Proxy
实现方法:
/**
* loader 指定目标对象的类加载器
* interface 目标对象实现的接口的类型
* h 执行目标对象的方法时,会触发事件处理器的方法,将当前执行目标对象的发放作为参数传入
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h )
代码示例:
/**
* create proxy object factory
*/
public class ProxyFactory() {
//targer object
private Object target;
public ProxyFactory(Obkect target) {
this.target = target;
}
//return a proxy object
public Object getProxyInstance() {
/**
* InvocationHandler接口只定义了一个invoke方法,因此对于这样的接口,我们不用单独去定义一个类来实现该接口,
* 而是直接使用一个匿名内部类来实现该接口,new InvocationHandler() {}就是针对InvocationHandler接口的匿名实现类
*
* 在invoke方法编码指定返回的代理对象干的工作
* proxy : 把代理对象自己传递进来
* method:把代理对象当前调用的方法传递进来
* args:把方法参数传递进来
*
* 当调用代理对象的person.sing("冰雨");或者 person.dance("江南style");方法时,
* 实际上执行的都是invoke方法里面的代码,
* 因此我们可以在invoke方法中使用method.getName()就可以知道当前调用的是代理对象的哪个方法
*/
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getClassInterface(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy,Method method,Object[] arge) {
if (method.getName().equals("save")) {
System.out.println("--execute your new method in here--");
//execute target object method in here
Object returnValue = method.invoke(target, args);
System.out.println("--execute your new method in here--");
}
return returnValue;
}
}
);
}
}
/**
* test
*/
public class test {
public static void main(String[] args) {
UserDao targetObject = new UserDaoImpl();
//create a proxy object
UserDao proxyObject = (UserDao) new ProxyFactory(target).getProxyIntence();
//use proxy object execute target method
proxyObject.save();
}
}
总结:动态代理对象不需要实现接口,但目标对象一定要实现接口,否则JDK无法生成目标对象的代理对象;
Cglib代理
静态代理和动态代理都的目标对象都是需要实现一个接口的,但是有时候一些对象就是一个独立的个体,并没有实现接口,这时候就有了Cglib代理的出现;它是在内存中,构建一个目标对象的子类,从而实现对目标对象的扩展;
代码示例:
/**
* target object
*/
public class UserDao {
public void save() {
System.out.println("----save one data ---");
}
}
/**
* Cglib procy factory
*/
public class ProxyFactory implements MethodInterceptor{
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//create proxy object
public Object getProxyInstance() {
//tool object
Enhancer en = new Enhancer();
//set target object's class
en.setSuperClass(target.getClass());
//set callback
en.setCallback(this);
//create child class(proxy object)
return en.cteate();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
System.out.println("-- your function --");
Object returnValue = method.invoke(target, args);
System.out.println(" -- your function --");
return returnValue;
}
}
/**
* test method
*/
public class test {
public static void main(String[] args) {
UserDao userDao = new UserDao();
UserDao proxyDao = (UserDao)new ProxyFactory(userDao).getProxyInstance();
proxyDao.save();
}
}
在Spring的AOP中
如果加入容器的目标对象有实现接口,则用动态代理,
如果目标对象没有实现接口,则用Cglic代理;