代理模式就是给一个对象提供一个代理对象,让该代理对象来控制对原对象的引用,比如买车去4s店,而不是去汽车厂,4s就起到一个代理的作用。
1.静态代理
代理类实现一个接口,之后实现该接口的其他类就可在该代理类中被增强,具有通用性。 如下:
/*
抽象用户操作定义
*/
public interface UserDao {
void saveUser();
}
/*
普通用户实现类
*/
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("普通用户实现");
}
}
/*
静态代理类
*/
public class StaticProxy implements UserDao{
UserDao userDao;
public StaticProxy(UserDao userDao) {
this.userDao = userDao;
}
@Override
public 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 就是动态获取的真正要执行的方法
*/
@Override
public 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方式比较合适。