说起代理模式,相信很多人早已经很理解,但对于我这个菜鸟,理解的可能还是不到位,写一次博客加深印象。
什么是代理模式呢?代理模式是常用的Java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类和委托类之间通常会存在关联关系,
一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不是真正实现服务,而是通过调用委托类对象的相关
的方法来提供特定的服务。
按照代理的创建时期,一般分为两类:
静态代理:由程序员或特定工具自动生成的源代码,再对其编译,在程序运行之前,代理的类编译生成的.class文件就已经存在了
动态代理:在程序运行时,通过反射机制动态创建而成。
重点:
动态代理主要实现使用的是聚合,这种方法更有利于继承
作用:
(1)方法前后加日志,记录时间(方法运行的时间)
(2)方法前后加事务,事务的提交或者回滚(AOP)
(3)方法前后加判断调用者是否拥有权限,用于权限的控制
1、静态代理:
由程序员创建或工具生成代理类的源码,再编译代理类。所谓的静态代理,就是在程序运行前就已经存在的代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
代码如下:
接口Moveable.java
package designMode.proxy;
/**
*
* @Description
* @author CCQ
* @date 2017年6月22日 上午11:19:30
*
*/
public interface Moveable {
void move();
}
Tank.java
package designMode.proxy;
import java.util.Random;
/**
*
* @Description
* @author CCQ
* @date 2017年6月22日 上午11:19:52
*
*/
public class Tank implements Moveable{
@Override
public void move() {
System.out.println("Tank Moving...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package designMode.proxy;
public class TankLogProxy implements Moveable {
public TankLogProxy(Moveable t) {
super();
this.t = t;
}
Moveable t;
@Override
public void move() {
System.out.println("log Start ...");
t.move();
System.out.println("log end ...");
}
}
package designMode.proxy;
public class TankTimeProxy implements Moveable {
public TankTimeProxy(Moveable t) {
super();
this.t = t;
}
Moveable t;
@Override
public void move() {
long start = System.currentTimeMillis();
t.move();
long end = System.currentTimeMillis();
System.out.println("运行时间:"+(end-start)/1000+"秒");
}
}
package designMode.proxy;
public class TestProxy {
public static void main(String[] args) {
Tank t = new Tank();
TankTimeProxy ttp = new TankTimeProxy(t);
TankLogProxy tlp = new TankLogProxy(ttp);
Moveable m = tlp;
m.move();
}
}
2、动态代理
Spring主要的两大思想,一个是IOC,一个是AOP,对于IOC,就是依赖注入,而对于AOP来说,我们不但要知道怎
么通过AOP来满足我们的功能,更需要知道底层是怎样的原理,而AOP的原理就是Java的动态代理机制。
jdk动态代理和cglib动态代理。两种方法的存在,各有各自的优势。jdk动态代理是由java内部的反射机制来实现的,
cglib动态代理底层则是借助asm来实现。
总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm
生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标
类基于同一的接口。如果没有上述的前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,
cglib这种第三方类库实现的代理应用更加的广泛,且在效率更有优势。
(1)jdk动态代理
在java的动态代理机制中,有两个重要的接口和类。一个是接口InvoactionHandler,一个是类Proxy,这一个类和一个
接口是实现动态代理所必须用到的。
每一个动态代理类都必须实现InvocationHandler这个接口,并且每一个代理类的实例都关联到一个Handler,当我们
通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来调用。
观察InvocationHandler接口的唯一一个方法。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
proxy:代理的真实对象
method:代理的真实对象的某个方法的Method对象
args:调用真实方法对象传递的参数
源码模拟实现:
UserDao.java 接口
package designMode.proxy.proxyJDK.dao;
import java.util.Date;
/**
*
* @Description
* @author CCQ
* @date 2017年6月22日 上午9:28:42
*
*/
public interface UserDao {
void save();
void delete(int id);
void update(int id,String name,String pwd,Date date);
}
package designMode.proxy.proxyJDK.dao.impl;
import java.util.Date;
import designMode.proxy.proxyJDK.dao.UserDao;
/**
*
* @Description
* @author CCQ
* @date 2017年6月22日 下午1:16:41
*
*/
public class UserDaoMysqlImpl implements UserDao {
@Override
public void save() {
System.out.println("Mysql执行保存...");
}
@Override
public void delete(int id) {
System.out.println("Mysql执行删除..."+id);
}
@Override
public void update(int id, String name, String pwd, Date date) {
System.out.println("Mysql执行修改...id:"+id+"name:"+name+"pwd:"+pwd+"date:"+date);
}
}
package designMode.proxy.proxyJDK.dao.impl;
import java.util.Date;
import designMode.proxy.proxyJDK.dao.UserDao;
public class UserDaoOracleImpl implements UserDao {
@Override
public void save() {
System.out.println("Oracle执行保存...");
}
@Override
public void delete(int id) {
System.out.println("Oracle执行删除..."+id);
}
@Override
public void update(int id, String name, String pwd, Date date) {
System.out.println("Oracle执行修改...id:"+id+"name:"+name+"pwd:"+pwd+"date:"+date);
}
}
分别有增加事务(getTransactionProxyInstance)和日志(getLogProxyInstance)的代理:
package designMode.proxy.proxyJDK;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
* @Description 创键代理对象工厂
* @author CCQ
* @date 2017年6月22日 上午9:54:44
*
*/
public class ProxyFactory {
private Object obj;
public ProxyFactory(Object obj) {
super();
this.obj = obj;
}
public Object getTransactionProxyInstance(){
Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
/**
* 三个参数:1、代理对象,2、目标对象的方法,3、目标对象的参数值列表
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务..."); //执行核心业务之前执行的内容
method.invoke(obj, args); //执行目标对象方法,即核心业务
System.out.println("关闭事务..."); //执行核心业务之后执行的内容
return proxy;
}
});
return proxy;
}
public Object getLogProxyInstace(){
Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
/**
* 三个参数:1、代理对象,2、目标对象的方法,3、目标对象的参数值列表
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启日志..."); //执行核心业务之前执行的内容
method.invoke(obj, args); //执行目标对象方法,即核心业务
System.out.println("关闭日志..."); //执行核心业务之后执行的内容
return proxy;
}
});
return proxy;
}
}
LogHander.java,增加日志handler
package designMode.proxy.proxyJDK;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class LogHandler implements InvocationHandler {
Object target = null;
public LogHandler(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启日志..."); //执行核心业务之前执行的内容
method.invoke(target, args); //执行目标对象方法,即核心业务
System.out.println("关闭日志..."); //执行核心业务之后执行的内容
return proxy;
}
public Object getInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces() , this);
}
}
package designMode.proxy.proxyJDK;
import java.util.Date;
import org.junit.Test;
import designMode.proxy.proxyJDK.dao.UserDao;
import designMode.proxy.proxyJDK.dao.impl.UserDaoMysqlImpl;
import designMode.proxy.proxyJDK.dao.impl.UserDaoOracleImpl;
/**
*
* @Description
* @author CCQ
* @date 2017年6月22日 上午10:02:45
*
*/
public class TestJDKProxy {
@Test
public void test1(){
UserDao userDao = new UserDaoMysqlImpl();
UserDao userDaoProxy = (UserDao) new ProxyFactory(userDao).getTransactionProxyInstance();
userDaoProxy.save();
userDaoProxy.update(1, "张三", "123456", new Date());
System.out.println("目标对象类型:"+userDao.getClass());
System.out.println("代理对象类型:"+userDaoProxy.getClass());
}
@Test
public void test2(){
UserDao userDao = new UserDaoOracleImpl();
UserDao userDaoProxy = (UserDao) new LogHandler(userDao).getInstance();
userDaoProxy.save();
System.out.println("目标对象类型:"+userDao.getClass());
System.out.println("代理对象类型:"+userDaoProxy.getClass());
}
}
(2)cglib代理
JDK动态代理机制只能代理实现接口的类,一般没有实现接口的类不能进行代理。cglib就是针对类来实现代理的,它
的原理是对指定目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进
行代理。
使用cglib实现动态代理,完全不受代理类必须实现接口的限制,而且cglib底层采用ASM字节码生成框架,使用字节码
技术生成代理类,比使用java反射效率要高。
需要引入两个jar包:cglib.jar,asm.jar
定义了一个拦截器,在调用目标方法之前,cglib回调MethodInterceptor接口方法拦截,来实现自己的业务逻辑,类似
于JDK中的InvocationHandler接口。
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable ;
method:为上文中实体类所调用的被代理的方法调用
args:为method参数数值列表
methodProxy:为生成代理类对方法的代理引用
返回:从代理实例方法调用返回的值
其中,methodProxy.invokeSuper(obj,arg):
调用代理类实例上的proxy方法的父类方法
UserDaoImpl.java
package designMode.proxy.proxyCglib;
/**
*
* @Description
* @author CCQ
* @date 2017年6月22日 下午1:16:41
*
*/
public class UserDaoImpl {
public void save() {
System.out.println("Mysql执行保存...");
}
}
CglibProxyFactory.java
package designMode.proxy.proxyCglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
*
* @Description
* @author CCQ
* @date 2017年6月22日 下午1:50:09
*
*/
public class CglibProxyFactory {
private Object obj;
public CglibProxyFactory(Object obj) {
super();
this.obj = obj;
}
public Object getProxyFactory(){
//Enhancer类是cglib中的一个字节码增强器,它可以方便的为你所要处理的类进行扩展
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());//将目标对象所在的类作为Enhaner类的父类
enhancer.setCallback(new MethodInterceptor() {
//通过实现MethodInterceptor实现方法回调
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("事务开启...");
method.invoke(obj, args);
System.out.println("事务结束...");
return proxy;
}
});
return enhancer.create();//生成目标对象并返回
}
}
测试类
package designMode.proxy.proxyCglib;
import org.junit.Test;
/**
*
* @Description
* @author CCQ
* @date 2017年6月22日 上午10:02:45
*
*/
public class TestCglibProxy {
@Test
public void test1(){
UserDaoImpl userDao = new UserDaoImpl();
UserDaoImpl userDaoProxy = (UserDaoImpl) new CglibProxyFactory(userDao).getProxyFactory();
userDaoProxy.save();
System.out.println("目标对象类型:"+userDao.getClass());
System.out.println("代理对象类型:"+userDaoProxy.getClass());
}
}