功能:在不改变原有类的前提下对被代理类进行动态扩展
分类:
静态代理较为简单,使用代理对象将目标对象包裹起来,实际执行时相当于还是调用了目标对象的方法,只是在调用目标方法前或后添加了其他的代码,以达到扩展目的。
/***
* 被代理对象所实现的接口
*/
public interface TargetInterface {
void method01();
void method02();
}
/**
* 被代理类
*/
public class Target implements TargetInterface{
@Override
public void method01() {
System.out.println("目标对象 方法1 执行");
}
@Override
public void method02() {
System.out.println("目标对象 方法2 执行");
}
}
/**
* 目标对象代理对象
*/
public class TargetProxy implements TargetInterface {
private TargetInterface target;
public TargetProxy(TargetInterface target) {
this.target = target;
}
@Override
public void method01() {
beforeMethod();
target.method01();
}
@Override
public void method02() {
target.method02();
afterMethod();
}
private void beforeMethod(){
System.out.println("代理对象方法 before");
}
private void afterMethod(){
System.out.println("代理对象方法 after");
}
}
public class Client {
public static void main(String[] args) {
TargetInterface target = new Target();
TargetInterface proxy = new TargetProxy(target);
proxy.method01();
System.out.println("-----------");
proxy.method02();
}
}
结果输出:
代理对象方法 before
目标对象 方法1 执行
-----------
目标对象 方法2 执行
代理对象方法 after
静态代理的缺点十分明显:目标对象和代理对象都需要实现相同的接口,并且代理对象编码过程中必须处理该接口中的全部方法。
实际使用代理模式的时候,并不是所有的类都会实现接口,又或者目标对象被封装的起来,无法得知想要扩展的方法来自哪个接口。也有些时候,仅想针对一个方法进行扩展,但是接口中有非常多的方法,这个时候使用静态代理就会产生特别多无用工作。
JDK动态代理为原生代理实现,要求扩展的方法同样来自目标对象实现的某个接口,但是不需要知道具体来自哪个接口。
JDK代理使用比较简单,不附类图,想要了解原理查看扩展学习内容
代码:
Target 和 TargetInterface 代码同上,不再重复
代理对象写法1:
/**
* 目标对象代理对象
*/
public class TargetProxy1 implements InvocationHandler {
private TargetInterface target;
public TargetInterface getProxyInstance(TargetInterface target){
this.target = target;
Class<? extends TargetInterface> clazz = target.getClass();
return (TargetInterface)Proxy.newProxyInstance(
clazz.getClassLoader(), //目标对象类加载器
clazz.getInterfaces(), //目标对象实现的接口
this // 当前代理对象
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue;
// 此处可对方法进行筛选,否则全部方法都会被扩展
if("method01".equals(method.getName())){
beforeMethod();
returnValue = method.invoke(this.target, args);
afterMethod();
}else{
returnValue = method.invoke(this.target, args);
}
return returnValue;
}
private void beforeMethod(){
System.out.println("代理对象方法 before");
}
private void afterMethod(){
System.out.println("代理对象方法 after");
}
}
业务不太复杂时可以使用更简单的写法
代理对象写法2:
/**
* 目标对象代理对象
*/
public class TargetProxy2{
public TargetInterface getProxyInstance(TargetInterface target){
Class<? extends TargetInterface> clazz = target.getClass();
return (TargetInterface)Proxy.newProxyInstance(
clazz.getClassLoader(), clazz.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue;
// 此处可对方法进行筛选,否则全部方法都会被扩展
if("method1".equals(method.getName())){
beforeMethod();
returnValue = method.invoke(target, args);
afterMethod();
}else{
returnValue = method.invoke(target, args);
}
return returnValue;
}
private void beforeMethod(){
System.out.println("代理对象方法 before");
}
private void afterMethod(){
System.out.println("代理对象方法 after");
}
});
}
}
Client:
更简单的写法可以直接在Client中使用匿名内部类
public class Client {
public static void main(String[] args) {
TargetInterface target = new Target();
TargetInterface proxy1 = new TargetProxy1().getProxyInstance(target);
TargetInterface proxy2 = new TargetProxy2().getProxyInstance(target);
// 业务简单时,更简洁的形式,不需要单独写代理类
// TargetInterface prox = (TargetInterface)Proxy.newProxyInstance(
// target.getClass().getClassLoader(),
// target.getClass().getInterfaces(),
// (Object proxy, Method method, Object[] params)->{
// System.out.println("代理对象方法 before");
// Object returnValue = method.invoke(target, params);
// System.out.println("代理对象方法 after");
// return returnValue;
// });
proxy1.method01();
System.out.println("-------------------");
proxy1.method02();
// proxy2.method01();
// System.out.println("-------------------");
// proxy2.method02();
}
}
输出结果:
代理对象方法 before
目标对象 方法1 执行
代理对象方法 after
-------------------
目标对象 方法2 执行
实际开发中,存在目标对象没有实现任何接口的情况,这时候想要进行动态代理就需要使用CGLib来实现。
使用CGLib代理需要引入CGLib相关的jar包
Spring核心包中已经包含CGLib功能,所以maven工程直接引入spring-core.jar即可,非maven工程需要引入 cglib-xxx.jar,asm-xxx.jar,xxx为版本,暂时没遇见过版本问题。
代码:
Target 代码同上,不再重复
/**
* 目标对象代理对象
*/
public class TargetProxy implements MethodInterceptor {
private Object target;
public TargetProxy(Object target) {
this.target = target;
}
public Object getProxyInstance(){
Enhancer en = new Enhancer();
en.setSuperclass(target.getClass());
en.setCallback(this);
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeMethod();
Object returnValue = method.invoke(target);
afterMethod();
return returnValue;
}
private void beforeMethod(){
System.out.println("代理对象方法 before");
}
private void afterMethod(){
System.out.println("代理对象方法 after");
}
}
public class Client {
public static void main(String[] args) {
Target target = new Target();
Target proxy = (Target)new TargetProxy(target).getProxyInstance();
proxy.method01();
}
}
结果输出:
代理对象方法 before
目标对象 方法1 执行
代理对象方法 after
JDK动态代理实现原理详解
CGLib动态代理实现原理详解
代理模式和装饰者模式有何区别?
其他设计模式