代理模式就是给一个对象提供一个代理对象,让该代理对象来控制对原对象的引用,比如买车去4s店,而不是去汽车厂,4s就起到一个代理的作用。
1.静态代理
代理类实现一个接口,之后实现该接口的其他类就可在该代理类中被增强,具有通用性。 如下:
/*抽象用户操作定义*/
public interface UserDao {void saveUser();}
/*普通用户实现类*/
public class UserDaoImpl implements UserDao {@Overridepublic void saveUser() {System.out.println("普通用户实现");}}
/*静态代理类*/
public class StaticProxy implements UserDao{UserDao userDao;public StaticProxy(UserDao userDao) {this.userDao = userDao;}@Overridepublic void saveUser() {System.out.println("保存日志");try {userDao.saveUser();}catch (Exception e){System.out.println("保存日志");}finally {System.out.println("保存日志");}System.out.println("保存日志");}}
public class Test {public static void main(String[] args) {//具体对象UserDao userDao = new UserDaoImpl();StaticProxy staticProxy = new StaticProxy(userDao);staticProxy.saveUser();}
}
创建静态代理类的对象,将我们创建的普通对象作为参数传入,之后调用代理类中的saveUser()方法,就起到了增强的作用,缺点就是一个代理类只能代理一个接口,而且接口如果有所修改,代理类也要同步修改。
2.动态代理
2.1JDK代理
JDK代理是通过反射来获取代理对象的,通过reflect.Proxy来实现,通过固定的规则生成。如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*动态代理类代理类不需要实现与目标类相同的接口,这样就可以代理任意的目标类但是是有要求的,目标类必需实现接口,此种方式是动态代理的实现方式之一: jdk代理 是一种纯反射机制实现(动态获取目标类接口方法)*/
public class DynamicProxy implements InvocationHandler {Object object;//真实对象,接收任何的目标类对象public DynamicProxy(Object object) {this.object = object;}/*在代理类中调用目标类中的具体方法,动态的将代理动态对象,目标类中要调用的方法,及方法中的参数传递过来Method method 就是动态获取的真正要执行的方法*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("之前开启事务");method.invoke(object);System.out.println("之后提交事务");return proxy;}//真正意义上,运行时生成代理对象的方法,需要被代理类的类加载器,类实现的接口public Object getProxy(){return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);}}
public class Test {public static void main(String[] args) {UserDaoImpl userDao = new UserDaoImpl();DynamicProxy dtproxy = new DynamicProxy(userDao);//自己创建的代理类对象//这才是真正的创建动态代理对象UserDao userDao = (UserDao)dtproxy.getProxy();userDao.saveUser();//使用代理对象调用接口中的方法,获取当前调用的方法,最终调用invoke方法}
}
JDK代理的致命缺陷就是被代理的目标类必须实现接口。
2.2Cglib代理
Cglib代理很好的解决了目标类无接口实现的情况,Cglib底层采用了字节码的方式给目标类创建了一个子类,在子类中采用方法拦截的方式拦截父类的方法调用,然后使用横切的思想去增强。
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/** 动态代理类*/
public class CGLibProxy implements MethodInterceptor {private Enhancer enhancer = new Enhancer();public Object getProxy(Class<?> clazz){ enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } /** 拦截所有目标类方法的调用 * 参数: * obj 目标实例对象 * method 目标方法的反射对象 * args 方法的参数 * proxy 代理类的实例 */ public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {//代理类调用父类的方法 System.out.println("开始事务"); Object obj1 = proxy.invokeSuper(obj, args); System.out.println("关闭事务"); return obj1; }
}
public class Test {public static void main(String[] args) {CGLibProxy proxy = new CGLibProxy(); UserDaoImpl userDaoImpl = (UserDaoImpl)proxy.getProxy(UserDaoImpl.class);userDaoImpl.save();}
}
需要注意的是,如果使用Cglib代理,那么目标类不能被final修饰,目标类中的方法不能被final或static修饰,虽然Cglib方式创建的代理对象性能功能比JDK方式创建的代理对象性能好,但是相应的花费的时间也多,所以对于单例的,Cglib方式比较合适。