动态代理
动态代理看起来高大上,但是实现起来特别的简单。这种方式我们在各种框架的源码中见到的也是最多的。
那下面我们就一起讨论一下动态代理这种增强类方法的方式。
动态代理在使用时,通常会和工厂设计模式一起使用,即我们使用工厂这种方式去获取目标对象的代理对象。
既然讨论到工厂设计模式,那么接着讨论一下,工厂设计模式中两种获得代理对象的方式:一、通过工厂类的
静态方法,这种方式不需要我们去创建工厂类对象(推荐使用),二、通过工厂类的非静态方法,这种方式需要我们
先创建一个工厂类的实例对象,在通过调用对应的方法,获得代理对象。
下面我们就分别用这两种方式去实现以下动态代理。
一、静态方法
1.目标类的代码
package com.itcast.c_proxy;
public class UserServiceImpl implements UserService
public void save(){
System.out.println("UserService:保存了。。。。");
}
}
现在我们要做的就是获取它的一个代理对象,当我们执行save方法时,在save方法执行前输出一句话,在执行后再输出一句话。下面我们创建我们的工厂类:
package com.itcast.c_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class UserServiceFactory2 {
public static Object getProxy(final Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke ;
if("save".equalsIgnoreCase(method.getName())){
System.out.println("save方法执行之前");
invoke= method.invoke(target, args);
System.out.println("save方法执行之后");
}else{
invoke= method.invoke(target, args);
}
return invoke;
}
});
}
}
在上面的代码中,下面这段代码是我们更为关心的代码
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
//新建一个匿名内部类,实现InvocationHandler接口中的invoke方法
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke
if("save".equalsIgnoreCase(method.getName())){
System.out.println("save方法执行之前")
invoke= method.invoke(target, args)
System.out.println("save方法执行之后")
}else{
invoke= method.invoke(target, args)
}
return invoke
}
})
在这段代码中,我们通过传入的target目标对象,获取一个他的代理对象,使用的newProxyInstance方法中包含三个参数,下面我们就对这三个参数做进一步的说明。第一个参数就是目标对象的类加载器,这个是固定的写法,第二个参数是目标对象实现的接口(重点来了,因为这里用到了目标对象的实现接口,所以和装饰设计模式一样,要想使用动态代理来创建代理对象,目标对象必须实现接口),这个也是固定写法,关键的是第三个参数,这个参数要求是Invocationhandler这个接口的一个实现类,这里我们采取了匿名内部类的方式,当然你也可以在外部创建一个类实现这个接口,然后在这里直接new对象就可以了,这不是我们要讨论的重点,我们关心的是实现这个接口中的方法invoke。这个方法中包含三个参数,我们来分别介绍一下,第一个就是我们要创建的代理对象,这么说可能不太对,我们还没有创建代理对象,但是这里却出现了这个代理对象的参数,可能是我的经验太少,对这个参数我从来没使用过,但是如果你想使用,我先提醒你的是你的程序会进入死循环,导致内存溢出。第二个参数是我们要执行的方法,第三个是这个方法要执行用到的参数。我们最经常使用的是第二个参数,因为我们要对目标对象某一个或几个方法进行处理,而不是全部,这就要求我们在处理之前先通过方法名来判断这个方法是不是我们要增强的方法,然后再操作。
下面是我们的测试代码
package com.itcast.c_proxy;
import org.junit.Test;
public class TestDemo {
@Test
public void test1(){
try {
UserServiceImpl service = new UserServiceImpl();
UserService proxy =(UserService)UserServiceFactory2.getProxy(service);
proxy.save();
} catch (Exception e) {
e.printStackTrace();
}
}
}
好了动态代理基本的使用我么就讲完了,如果只说使用,他没有听起来的那个高大上,但是他的内部实现还是很高大上的,在这里我们就先不讨论了,以后有机会再讨论。通过上面的介绍,我们可以看出他的底层肯定用到的是反射,我们都知道反射虽然好用,但是还是很消耗资源的,所以动态代理的效率一般,远不及我们前面讲的装设设计模式。所以说这两种方式都有优点和缺点,在具体使用时,还请各位自己权衡。
下面是用工厂设计的非静态方法实现的代码,拿走不谢。
package com.itcast.c_proxy
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
public class UserServiceFactory {
private UserService target
public UserServiceFactory(UserService target) {
this.target = target
}
//提供非静态方法返回目标对象的代理对象
public Object getProxy(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
Object invoke
if("save".equalsIgnoreCase(method.getName())){
System.out.println("save方法执行之前")
invoke= method.invoke(target, args)
System.out.println("save方法执行之后")
}else{
invoke= method.invoke(target, args)
}
return invoke
})
}
}
下面是测试类
package com.itcast.c_proxy;
import org.junit.Test;
public class TestDemo {
@Test
public void test1(){
try {
UserServiceImpl service = new UserServiceImpl();
UserServiceFactory factory = new UserServiceFactory(service);
UserService proxy =(UserService)factory.getProxy();
proxy.save();
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后我们总结一下动态代理,上面我们提到了,要想使用动态代理去获取代理对象来增强目标对象的某个方法的前提条件是目标对象必须实现了接口,有接口才能使用,这和装饰设计模式一样。那么问题来了,如果目标对象没有实现接口那该怎么办呢?这就是我们要讲的最后一种方式cglib方式。