代理
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
静态代理
特点
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
示例
dao接口类
public interface UserDao {
public void setUser();
public void getUser();
}
接口实现类
public class UserDaoImpl implements UserDao {
@Override
public void setUser() {
System.out.println("设置user");
}
@Override
public void getUser() {
System.out.println("获取user");
}
}
静态代理类
public class MyStaticProxy implements UserDao {
private UserDao userDao;
public MyStaticProxy(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void setUser() {
System.out.println("开启事务");
userDao.setUser();
System.out.println("提交事务");
}
@Override
public void getUser() {
System.out.println("开启事务");
userDao.getUser();
System.out.println("提交事务");
}
}
测试类
public class MyStaticProxyTest {
public static void main(String[] args) {
UserDaoImpl userDaoImpl = new UserDaoImpl();
UserDao myStaticProxy = new MyStaticProxy(userDaoImpl);
myStaticProxy.setUser();
myStaticProxy.getUser();
}
}
分析
- 跟适配器,装饰器模式类似,在代理类(MyStaticProxy)中传入被代理类对象(UserDaoImpl),然后实现被代理类接口(UserDao),然后在实现的接口方法中调用被代理类的相应方法,当然在调用方法的前后可以做一些其他操作(类似上面的事务操作),从而保证在原有逻辑不变的情况下插入逻辑;
动态代理
特点
动态代理类:在程序运行时,运用反射机制动态创建而成。
JDK自带Proxy
JDK的动态代理必须具备四个条件:1、目标接口 2、目标类 3、拦截器 4、代理类
目标接口跟目标类沿用静态代理的UserDao跟UserDaoImpl
示例
事务类
public class Transaction {
public void beginTransaction(){
System.out.println("开启事务");
}
public void endTransaction(){
System.out.println("关闭事务");
}
public void rollback(){
System.out.println("回滚事务");
}
}
拦截类
public class MyProxyHandler implements InvocationHandler {
private Transaction transaction;
private UserDao userDao;
public MyProxyHandler(Transaction tansTransaction,UserDao userDao) {
this.transaction = tansTransaction;
this.userDao = userDao;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object object = null;
try {
transaction.beginTransaction();
object = method.invoke(this.userDao, args);
transaction.endTransaction();
} catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}
return object;
}
}
jdk自带代理测试类
public class MyJdkProxyTest {
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
MyProxyHandler myProxyHandler = new MyProxyHandler(new Transaction(), userDao);
userDao = (UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(), myProxyHandler);
userDao.setUser();
userDao.getUser();
}
}
分析
- Proxy类利用newProxyInstance方法传入被目标类接口跟类加载器构造一个代理对象,然后代理对象调用目标接口中的方法时会通过MyProxyHandler 对象的invoke方法调用目标实现类的相应方法,而MyProxyHandler 中的invoke方法只需要我们自己编写即可,可以理解成策略模式,依赖倒置原则;
cglib动态代理
jar包下载
示例
设置拦截器
public class MyInterceptor implements MethodInterceptor {
private UserDao userDao;
private Transaction transaction;
public MyInterceptor(UserDao userDao,Transaction transaction) {
this.userDao = userDao;
this.transaction = transaction;
}
public Object create(){
Enhancer enhancer = new Enhancer();//该类用于生成代理对象
enhancer.setCallback(this);// 参数为拦截器
enhancer.setSuperclass(userDao.getClass());// 设置父类
return enhancer.create();// 创建代理对象
}
/**
* @param obj 目标对象代理类的实例
* @param method 代理实例上 调用父类方法的Method实例
* @param args 传入到代理实例上方法参数值的对象数组
* @param methodProxy 使用它调用父类的方法
* @return
* @throws Throwable
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Object object = null;
try {
this.transaction.beginTransaction();
object = method.invoke(userDao,args);
this.transaction.endTransaction();
} catch (Exception e) {
this.transaction.rollback();
e.printStackTrace();
}
return object;
}
}
测试类
public class MyCglibProxyTest {
public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
Transaction transaction = new Transaction();
MyInterceptor myInterceptor = new MyInterceptor(userDao, transaction);
userDao = (UserDaoImpl)myInterceptor.create();
userDao.setUser();
userDao.getUser();
}
}
可以在方法中调用生成字节码的方法查看生成的代理类class字节码文件
/**
* 把代理类的字节码写到硬盘上
* @param path 保存路径
*/
public static void writeProxyClassToHardDisk(String path) {
// 第一种方法
//System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
// 第二种方法
// 获取代理类的字节码
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", UserDaoImpl.class.getInterfaces());
FileOutputStream out = null;
try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
最终生成的代理类class
public final class $Proxy1 extends Proxy
implements UserDao
{
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m0;
private static Method m2;
public $Proxy1(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void getUser()
throws
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void setUser()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m4 = Class.forName("com.huzd.study.proxy.staticproxy.UserDao").getMethod("getUser", new Class[0]);
m3 = Class.forName("com.huzd.study.proxy.staticproxy.UserDao").getMethod("setUser", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
区别
- JDK:
- 目标类和代理类实现了共同的接口
- 拦截器必须实现InvocationHandler接口,而这个接口中invoke方法体的内容就是代理对象方法体的内容
- CGLIB:
- 目标类 是代理类的父类
- 拦截器必须实现MethodInterceptor接口,而接口中的intercept方法就是代理类的方法体,使用字节码增强机制创建代理对象的.
springAop代理机制
- 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
- 优点:因为有接口,所以使系统更加松耦合
- 缺点:为每一个目标类创建接口
- 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
- 优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
- 缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。