JAVA 代理模式
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
接口类
/**
*被代理到目标对象
*/
public interface IUserDao {
/**
* 目标对象提供操作方法
*/
void save();
}
被代理对象(目标对象)
/**
* 目标对象的实现类
*/
public class UserDaoImpl implements IUserDao {
@Override
public void save() {
System.out.println("save something ...");
}
}
public class UserAccountDaoImpl implements IUserDao {
@Override
public void save() {
System.out.println("用户账户DAO do something。。。");
}
}
代理对象(和目标对象一样,实现相同的接口)
public class UserDaoProxy implements IUserDao {
//注入被代理的对象
private IUserDao userDao;
public UserDaoProxy(IUserDao userDao) {
this.userDao = userDao;
}
/**
* 实现被代理对象提供的方法
*/
@Override
public void save() {
System.out.println("加入方法执行前的业务逻辑。。。。");
userDao.save();
System.out.println("加入方法执行后的业务逻辑。。。。");
}
}
测试
public static void main( String[] args ){
/**
* 静态代理
*/
//被代理的对象
IUserDao userDao = new UserDaoImpl();
//代理类
UserDaoProxy proxy = new UserDaoProxy(userDao);
//代理类向外提供被代理类的方法
proxy.save();
}
动态代理
静态代理代码块在程序运行前,代理类的class文件就已经存在了。动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码
JDK动态代理
JDK动态代理也是
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理类可以不用实现被代理对象的接口方法
*/
public class DynamicUserDaoProxy<T> {
//同样的,注入被代理的对象
private T target;
public DynamicUserDaoProxy(T target) {
this.target = target;
}
//给目标对象动态生成代理对象
/**
* 核心API
* java.lang.reflect.Proxy
* static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
* InvocationHandler 代理对象对外提供访问目标对象的具体实现逻辑
* @return
*/
public T getProxyInstance(){
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("加入方法执行前的业务逻辑。。。。");
Object invoke = method.invoke(target, args);//执行被代理对象方法
System.out.println("加入方法执行后的业务逻辑。。。。");
return invoke;
}
});
}
}
测试类
public class App {
public static void main( String[] args ){
/**
* JDK 动态代理
*/
IUserDao userDao = new UserDaoImpl();
IUserDao userAccountDao = new UserAccountDaoImpl();
//创建动态代理对象,注入被代理对象
IUserDao userDaoProxyInstance = new DynamicUserDaoProxy<>(userDao).getProxyInstance();
IUserDao userAccoutproxyInstance = new DynamicUserDaoProxy<>(userAccountDao).getProxyInstance();
//调用代理对象对外提供的方法
userDaoProxyInstance.save();
userAccoutproxyInstance.save();
}
CGLIB动态代理
对于没有实现任何接口的类,或者没有实现方法的接口类。
Cglib代理,也叫作子类代理,它是在内存中构建一个被代理对象的子类对象从而实现对目标对象功能的扩展.
-
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
-
Cglib是一个强大的高性能的代码生成包,它可以在运行期**扩展java类与实现java接口**.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
-
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
-
依赖jar
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.0</version> </dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* cglib既可代理接口又可以代理实现类
* @param <T>
*/
public class DynamicUserDaoCglibProxy<T> implements MethodInterceptor {
/**
* 这里可以注入被代理的 接口对象或者实现类对象
*/
private Class<T> target;
public DynamicUserDaoCglibProxy(Class<T> target) {
this.target = target;
}
public T getProxyInstance(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target);
enhancer.setCallback(this);
return (T) enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("加入方法执行前的业务逻辑。。。");
//执行目标对象的方法
//建议不使用 method.invoke(target,args) 方法 避免造成栈溢出
//具体原因自行搜素
//如果注释掉 则可以实现对 接口的代理
Object invoke = methodProxy.invokeSuper(object, args);
System.out.println("加入方法执行后的业务逻辑。。。");
return invoke;
}
}
测试类
public class App {
public static void main( String[] args ){
//对实现类代理
UserDaoImpl proxyInstance = new DynamicUserDaoCglibProxy<>(UserDaoImpl.class).getProxyInstance();
proxyInstance.save();
//对接口的代理,在方法拦截中,注意注释掉 methodProxy.invokeSuper(object, args);
// IUserDao proxyInterfaceInstance = new DynamicUserDaoCglibProxy<>(IUserDao.class).getProxyInstance();
// proxyInterfaceInstance.save();
}
}
总结
以上只是对java的几种代理模式的简单demo,具体的实现细节需要研究源码,后续如果可能的话再补上。over