代理模式实现
一、概念:代理模式属于结构型设计模式。客户端不能直接访问一个对象,可以通过代理的第三者来间接访问该对象,代理对象控制着对于原对象的访问,并允许在客户端访问对象的前后进行一些扩展和处理;这种设置模式称为代理模式;
2个点:
由代理对象去控制对原对象的访问(方法调用),客户端不直接对原对象进行访问,比如数据库的访问,不由Controller去控制;
在访问原对象时,可以有代理对象对原对象的功能(方法)进行扩展,比如访问数据库时,可以在代理对象里面进行开启事务和提交事务或回滚事务;
二、UML类图:
三、实现
1、静态代理实现
/**
* Subject
*/
public interface IUserMapperInterface {
void save(String userName, String userNumber);
}
/**
* RealSubject
*/
public class UserMapperInterfaceImpl implements IUserMapperInterface {
@Override
public void save(String userName, String userNumber) {
System.out.println("执行insert语句,保存数据,并返回成功!");
}
}
/**
* 代理类
*/
public class UserMapperInterfaceProxy implements IUserMapperInterface {
private IUserMapperInterface userMapperInterface;
public UserMapperInterfaceProxy(IUserMapperInterface userMapperInterface) {
this.userMapperInterface = userMapperInterface;
}
@Override
public void save(String userName, String userNumber) {
System.out.println("开启事务~~~");
userMapperInterface.save(userName, userNumber);
System.out.println("提交事务");
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
String userName = "user name";
String userNumber = "user number";
UserMapperInterfaceProxy proxy = new UserMapperInterfaceProxy(new UserMapperInterfaceImpl());
proxy.save(userName, userNumber);
}
}
优缺点:
优点:可以在不修改目标类的前提下,对目标类进行扩展
缺点:
代码冗余,代理对象实现了和目标对象一致的接口,产生了一些冗余接口;
一旦目标接口中增加了新的方法,那么代理对象和真实接口实现都需要修改代码才行;
2、动态代理的实现:
动态代理与静态代理的区别:
静态代理在类加载完经已经加载JVM中,并且如有其他对象需要加载,需要修改代理对象;
动态代理只有在运行的时候才会去加载目标类, 当需要加载其他对象的时候,不需要修改,也就是动态代理可以代理任何对象Object;
动态代理有两种实现方式:JDK动态代理,和CGLib动态代理
- JDK的动态代理,通过动态的生成一个接口对象的实现实例来实现对接口对象实例的功能扩展,在JDK的动态代理实现中,目标对象必须实现一个接口;
- 通过反射包下的Proxy.newProxyInstance()方法实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理的工厂类
*/
public class ProxyFactory {
//需要代理的目标类
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getTargetInstance() {
//Proxy是反射包下(java.lang.reflect)的一个代理类,通过这个类的newProxyInstance(新建代理对象实例)来动态的加载一个代理目标对象,并返回
//newProxyInstance方法需要传递三个参数,1、目标对象的类加载器, 2、目标对象实现的接口(如果有实现就有,没有实现就没有),3、目标对象需要执行的方法,相当于扩展静态代理中的那个save方法
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
//,目标对象被代理后需要执行的扩展方法,也有三个参数,1、目标对象,2、目标对象需要执行的方法实例,3、方法实例需要传递的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启一个事务~!");
//执行方法,执行的时候把目标对象和方法参数传递过去
method.invoke(target, args);
System.out.println("提交事务");
//返回值是指目标执行的方法的返回值,如果没有就返回null,在静态代理中,目标方法save的返回值是void,因此这里返回null
return null;
}
});
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
String userName = "user name";
String userNumber = "user number";
//使用静态代理的模式实现代理,此时在类加载的时候就会创建该对象
UserMapperInterfaceProxy proxy = new UserMapperInterfaceProxy(new UserMapperInterfaceImpl());
proxy.save(userName, userNumber);
System.out.println("静态代理实现的类名:" + proxy.getClass().getName());
//使用动态代理的模式实现代理功能,在类加载的时候不会创建对象,在运行过程中才会去创建对象并调用对象 的方法
ProxyFactory proxyFactory = new ProxyFactory(new UserMapperInterfaceImpl());
IUserMapperInterface targetInstance = (IUserMapperInterface) proxyFactory.getTargetInstance();
targetInstance.save(userName, userNumber);
System.out.println("动态代理实现的类名:" + targetInstance.getClass().getName());
}
}
- CGLib动态代理的实现
CGLib是一个第三方的代码生成类库,运行时在内存中生成一个子类继承目标对象,从而实现对目标对象的扩展;SpringAOP和Hibernate都是基于CGLib动态代理实现的,而CGlib又是通过封装ASM工具包去操作字节码的;
引入包:如果是Spring项目,则不需要单独引入,SpringCore里面已经包含该包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
目标类:
/**
* CGLib方式实现代理的目标类
*/
public class CGLibTargetClass {
public void targetMethod(String userName, String userNumber) {
System.out.println("CGLibTargetClass targetMethod");
System.out.println("保存用户信息:username=" + userName + ",usernumber=" + userNumber);
}
}
代理类:实现MethodInterceptor接口,并重写interceptor方法,
public class CGLibProxyIntercept implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("保存用户信息前开启事务");
System.out.println(objects.toString());
System.out.println(o.getClass().getName());
methodProxy.invokeSuper(o, objects);
System.out.println("保存用户信息后提交事务");
return o;
}
}
代理模式的总结:
由于CGlib采用的是继承目标类的方式去进行增强的,因此对于final类或方法是不能被继承的,无法使用CGlib的方式进行代理;
在jdk1.8后,实际上JDK动态代理比CGLib动态代理效率高,但JDK动态代理需要目标类实现一个接口,因此如果对象有实现接口,用JDK动态代理,没实现 用CGLib动态代理;
有缺点:
在客户端与目标对象之间起到中介作用,保护目标对象
对目标对象进行扩展,将客户端与目标对象分离,解耦
增加系统复杂度