代理模式,就是我们使用代理对象来代替真实对象的访问,这样在不改变真实对象的前提下,就可以控制或者增加一些操作,达到扩展真实对象的目的。代理模式的本质就是控制或者增强对象的访问。在实际的开发中,代理模式分为静态代理和动态代理,而动态代理又分为JDK动态代理和Cglib动态代理两种方式。下面对这三种实现方式进行一个总结。
顾名思义,静态代理就是手动的对代理对象编写代理类,在被代理的对象发生变化的时候,代理对象不能动态的改变,需要我们手动的去修改。在静态代理中,实现思路是代理对象和真实的对象实现同样的接口,实现和被代理对象同样的方法,并在实现中增加了对应的业务逻辑,然后代理对象持有一个真实的对象,在使用的过程中,我们实际上使用的是代理对象,由代理对象控制是否访问真实的对象。静态代理需要手动的实现被代理对象的每一个方法,即使需要增加的逻辑是相同的,也不能做到统一去实现,这也是动态代理和静态代理最本质上的区别。
public interface Subject {
String request();
}
public class RealSubject implements Subject {
@Override
public String request() {
System.out.println("this is RealSubject:request()");
return "RealSubject:request()";
}
}
public class StaticProxy implements Subject{
private Subject subject;
public StaticProxy(Subject subject){
this.subject = subject;
}
@Override
public String request() {
beforeRequest();
String result = this.subject.request();
afterRequest();
return result;
}
private void afterRequest() {
System.out.println("after request!");
}
private void beforeRequest() {
System.out.println("before request!");
}
}
public class Client {
@Test
public void testStaticProxy() {
// 创建代理对象,用来代理真正的目标对象
Subject subject = new StaticProxy(new RealSubject());
String result = subject.request();
System.out.println("-----------------------------------");
System.out.println("result= " + result);
}
}
before request!
this is RealSubject:request()
after request!
-----------------------------------
result= RealSubject:request()
JDK动态代理是面向接口的代理,需要被代理的对象实现一个接口,jdk会将这个接口中的所有方法进行统一的加强,加强的逻辑就是代理类,这个过程中,代理类不再要求和被代理对象实现相同的接口,而是要实现jdk的InvocationHandler接口,在invoke方法中统一的实现加强的逻辑。jdk的动态代理实现原理就是jdk给我们生成了一个新的class对象,这个对象实现了和被代理对象相同的接口(这就是为什么要求要被代理对象要有一个接口的原因),在新创建的对象中,jdk将我们实现的增强代码和原来的代码放在了一起,生成了一个新的方法,这样就做到了对被代理对象的增强。
public interface JdkSubject {
String request();
}
public class JdkRealSubject implements JdkSubject {
private String name;
public JdkRealSubject() {
}
public JdkRealSubject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String request() {
System.out.println("this is JdkRealSubject:request()");
return "JdkRealSubject:request():" + (null == name ? "" : name);
}
}
public class JdkProxy implements InvocationHandler {
private JdkSubject subject;
public JdkProxy(JdkSubject subject) {
this.subject = subject;
}
public Object getProxyInstance() throws Exception {
return Proxy.newProxyInstance(this.subject.getClass().getClassLoader(), this.subject.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeRequest();
Object invoke = method.invoke(this.subject, args);
afterRequest();
return invoke;
}
private void afterRequest() {
System.out.println("after request!");
}
private void beforeRequest() {
System.out.println("before request!");
}
}
public class JdkClient {
@Test
public void testJdkProxy() throws Exception {
// 创建代理对象,用来代理真正的目标对象
JdkSubject subject = (JdkSubject) (new JdkProxy(new JdkRealSubject())).getProxyInstance();
String result = subject.request();
System.out.println("-----------------------------------");
System.out.println("result= " + result);
}
}
before request!
this is JdkRealSubject:request()
after request!
-----------------------------------
result= JdkRealSubject:request():
在JDK的动态代理中,如果真实的对象有含参的构造函数或者getter和setter方法怎么弄呢?
要回答这个问题,我们需要从两个方面来看待:
@Test
public void testJdkProxyHasConstructArgus() throws Exception {
// 创建代理对象,用来代理真正的目标对象
JdkSubject subject = (JdkSubject) (new JdkProxy(new JdkRealSubject("name"))).getProxyInstance();
String result = subject.request();
System.out.println("-----------------------------------");
System.out.println("result= " + result);
}
before request!
this is JdkRealSubject:request()
after request!
-----------------------------------
result= JdkRealSubject:request():name
Cglib动态代理不强制要求被代理对象实现任何的接口,只要是一个正常的类就可以,其实现原理是cglib会生成一个代理类,这个类继承了需要被代理的类,然后把增强逻辑和被代理的对象的方法都写入到新的代理类中,新生成的代理类会重写除了私有和final的所有方法。
public class CglibSubjectClass {
private String name;
public CglibSubjectClass() {
}
public CglibSubjectClass(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String request() {
System.out.println("this is JdkRealSubjectCglibSubjectClass:request()");
return "CglibSubjectClass:request():" + (null == name ? "" : name);
}
}
public class CglibProxy implements MethodInterceptor {
/**
* 没有构造函数参数的构建代理方式
*
* @return :
*/
public static CglibSubjectClass getSubjectClassProxy() {
// 创建Enhancer对象,用来生成代理类
Enhancer enhancer = new Enhancer();
// 设置需要继承的类
enhancer.setSuperclass(CglibSubjectClass.class);
// 设置代理的回调方法
enhancer.setCallback(new CglibProxy());
return (CglibSubjectClass) enhancer.create();
}
/**
* 有构造函数参数的创建代理方式
*
* @param argumentTypes :
* @param arguments :
* @return :
*/
public static CglibSubjectClass getSubjectClassProxy(Class[] argumentTypes,
Object[] arguments) {
// 创建Enhancer对象,用来生成代理类
Enhancer enhancer = new Enhancer();
// 设置需要继承的类
enhancer.setSuperclass(CglibSubjectClass.class);
// 设置代理的回调方法
enhancer.setCallback(new CglibProxy());
return (CglibSubjectClass) enhancer.create(argumentTypes, arguments);
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeRequest();
// 这里需要调用invokeSuper方法才能调用到真正的对象
Object invoke = methodProxy.invokeSuper(o, objects);
afterRequest();
return invoke;
}
private void afterRequest() {
System.out.println("after request!");
}
private void beforeRequest() {
System.out.println("before request!");
}
}
public class CglibClient {
/**
* 没有构造函数的方式
*
* @throws Exception :
*/
@Test
public void testCglibProxyNoConstructArguments() throws Exception {
// 创建代理对象,用来代理真正的目标对象
CglibSubjectClass subject = CglibProxy.getSubjectClassProxy();
String result = subject.request();
System.out.println("-----------------------------------");
System.out.println("result= " + result);
}
}
before request!
this is JdkRealSubjectCglibSubjectClass:request()
after request!
-----------------------------------
result= CglibSubjectClass:request():
同样的道理,cglib对于真实对象如果有构造方法和getter和setter方法是如何操作的呢?
我们可以从一下两个方面进行分析
/**
* 有构造函数参数的创建代理方式
*
* @param argumentTypes :
* @param arguments :
* @return :
*/
public static CglibSubjectClass getSubjectClassProxy(Class[] argumentTypes,
Object[] arguments) {
// 创建Enhancer对象,用来生成代理类
Enhancer enhancer = new Enhancer();
// 设置需要继承的类
enhancer.setSuperclass(CglibSubjectClass.class);
// 设置代理的回调方法
enhancer.setCallback(new CglibProxy());
return (CglibSubjectClass) enhancer.create(argumentTypes, arguments);
}
在CglibClient中编写一个测试函数
/**
* 有构造函数参数的调用方式
*
* @throws Exception :
*/
@Test
public void testCglibProxyHasConstructArguments() throws Exception {
// 创建代理对象,用来代理真正的目标对象
String name = "name";
CglibSubjectClass subject = CglibProxy.getSubjectClassProxy(new Class[] {name.getClass()}, new Object[] {name});
String result = subject.request();
System.out.println("-----------------------------------");
System.out.println("result= " + result);
}
执行结果如下:
before request!
this is JdkRealSubjectCglibSubjectClass:request()
after request!
-----------------------------------
result= CglibSubjectClass:request():name
/**
* 用setter和getter的方式设置值
*
* @throws Exception :
*/
@Test
public void testCglibProxyHasGetterAndSetter() throws Exception {
// 创建代理对象,用来代理真正的目标对象
CglibSubjectClass subject = CglibProxy.getSubjectClassProxy();
String result = subject.request();
System.out.println("-----------------------------------");
System.out.println("result= " + result);
System.out.println("---------执行setter方法---------");
subject.setName("name");
System.out.println("---------执行getter方法---------");
System.out.println("name=" + subject.getName());
}
执行结果如下:
before request!
this is JdkRealSubjectCglibSubjectClass:request()
after request!
-----------------------------------
result= CglibSubjectClass:request():
---------执行setter方法---------
before request!
after request!
---------执行getter方法---------
before request!
after request!
name=name
从结果中我们可以清楚的看到,不论是setter还是getter方法,cglib都给我们进行了增强,所以我们应该明白Spring为什么不推荐用setter方法来注入对象,而是推荐使用构造方法的方式了吧,因为spring的aop默认就是用的cglib的实现方式,所以如果采用setter方法注入方式,会写很多多余的二进制代码。
后记
个人总结,欢迎转载、评论、批评指正