静态代理设计模式
代理设计模式最本质的特质:一个真实业务主题只完成核心操作,而所有与之辅助的功能都由代理类来完成。
例如,在进行数据库更新的过程之中,事务处理必须起作用,所以此时就可以编写代理设计模式来完成。
范例:结合传统的代理设计模式以及以购物车CartDao为例来编写代理设计模式
package so.strong.mall.proxy; import java.util.List; public interface CartDao {boolean insert(Cart cart) throws Exception;List<Cart> findAll() throws Exception; }
以上CartDao接口定义的方法,更行插入一定需要事务控制,对于查询操作,不需要事务控制。
定义CartDao真实实现
package so.strong.mall.proxy; import java.util.List; public class CartDAOImpl implements CartDao{@Overridepublic boolean insert(Cart cart) throws Exception {System.out.println("=====执行数据增加操作=====");return false;}@Overridepublic List<Cart> findAll() throws Exception {System.out.println("=====执行数据列表操作=====");return null;} }
定义代理主题类
package so.strong.mall.proxy; import java.util.List; public class CartDAOProxy implements CartDao {private CartDao cartDao;public CartDAOProxy() {}public void setCartDao(CartDao cartDao) {this.cartDao = cartDao;}public void prepare() {System.out.println("=====取消掉jdbc的自动提交");}public void commit() {System.out.println("=====手工提交事务");}public void rollback() {System.out.println("=====出现错误,事务回滚");}@Overridepublic boolean insert(Cart cart) throws Exception {try {this.prepare();boolean flag = this.cartDao.insert(cart);this.commit();return flag;} catch (Exception e) {this.rollback();throw e;}}@Overridepublic List<Cart> findAll() throws Exception {return this.cartDao.findAll();} }
业务层现在并不关心到底是代理类还是真实主题类,它只关心一点,只要取得了CartDao接口对象就可以,那么这一操作可以通过工厂类来隐藏。
package so.strong.mall.proxy; public class DAOFactory {public static CartDao getCartDaoInstance() {CartDAOProxy proxy = new CartDAOProxy();proxy.setCartDao(new CartDAOImpl());return proxy;} }
此时业务层暂时不需要继续进行,只需要通过客户端模拟业务层调用即可。
public class TestDemo { public static void main(String[] args) throws Exception{CartDao dao = DAOFactory.getCartDaoInstance();dao.insert(new Cart());} } //=====取消掉jdbc的自动提交 //=====执行数据增加操作===== //=====手工提交事务
因为事务和处理本身与核心业务有关的功能,但是它不是核心,那么用代理解决是最合适的方式。
动态代理设计模式
上面给出的代理设计模式的确可以完成代理要求,但是有一个问题:如果说现在项目里面有200张数据表,那么至少也需要200个左右的DAO接口,如果用上面的代理设计模式,那么意味着除了编写200个的DAO接口实现,还要编写200个代理类,并且有意思的是,这些代理类实现几乎相同。
以上的代理设计模式属于静态代理设计模式,只能够作为代理模式的雏形出现,并不能购作为代码使用的设计模式,为此专门引入了动态代理设计模式的概念。
即:利用一个代理类可以实现所有被代理的操作。
如果要想实现动态设计模式,那么必须首先观察一个接口:java.lang.reflect.InvocatonHandler. 它里面有一个方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
这个方法就属于代理中调用真实主题类的操作方法,这个方法里面的参数意义如下:
- Object proxy:表示代理类的对象;
- Method method:表示现在正在调用的方法;
- Object[] args:表示方法里面的参数。
但是这个方法没有所对应的真实对象,所以需要在创建这个类对象的时候设置好真实代理对象。
如果要想找到代理对象则要使用java.lang.reflect.Proxy类来动态创建,此类主要使用以下方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException
此方法参数定义如下:
- ClassLoader loader :指的是取得对象的加载器;
- Class<?>[] interfaces: 代理设计模式的核心是围绕接口进行的,所以此处必须取出全部的接口;
- InvocationHandler h:代理的实现类。
范例:使用动态代理实现上面的代理
CartDao不变,修改CartDAOProxy代理类
package so.strong.mall.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class CartDAOProxy implements InvocationHandler {private Object obj; //这个是真实对象主题/*** 将要操作的真实主题对象绑定到代理之中,而后返回一个代理类对象* @param obj 真实对象主题* @return 代理类对象*/public Object bind(Object obj) {this.obj = obj;return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), this);}public void prepare() {System.out.println("=====取消掉jdbc的自动提交");}public void commit() {System.out.println("=====手工提交事务");}public void rollback() {System.out.println("=====出现错误,事务回滚");}//只要执行了操作方法,那么就一定会触发invoke @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object object = null;//接收返回值if (method.getName().contains("insert")) { //更新插入类操作this.prepare();try {object = method.invoke(this.obj, args); //反射调用方法this.commit();} catch (Exception e) {this.rollback();}} else {object = method.invoke(this.obj, args);//查询操作不需要事务支持 }return object;} }
//修改工厂 package so.strong.mall.proxy; public class DAOFactory {public static Object getCartDaoInstance(Object realObject) {return new CartDAOProxy().bind(realObject);} }
//修改调用 package so.strong.mall.proxy; public class TestDemo {public static void main(String[] args) throws Exception{CartDao dao =(CartDao) DAOFactory.getCartDaoInstance(new CartDAOImpl());dao.insert(new Cart());} }
CGLIB实现动态代理设计模式
动态代理模式的确好用,而且也解决了代理类重复的问题,但是不管是传统静态代理或动态代理都有个设计缺陷,以动态代理为例:
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); //传入真实主题类,返回代理主题类
代理设计模式有一个硬性要求,就是类必须要有接口,所以业界很多人认为应该在没有接口的环境下也能使用代理设计模式。
所以在此时在开源社区里面提供了一个组件包——CGLIB,利用此包可以在没有接口的情况下也能够使用动态代理设计模式,它是模拟的类。
如果要想使用CGLIB,那么必须首先搞清楚对应关系:
- Proxy:net.sf.cglib.proxy.Enhancer
- InvocationHandler:net.sf.cglib.proxy.MethodInterceptor
- 真实主题调用:net.sf.cglib.proxy.MethodProxy
老师课上使用的是引入CGLIB的jar包,我去mvn仓库找了一下,找到了一个cglib,放到pom里面发现也可以。
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version> </dependency>
范例:使用CGLIB实现动态代理设计模式
package so.strong.mall.proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method;class ItemDAOImpl {public void insert(Item item) {System.out.println("=====增加操作=====");} }class MyProxy implements MethodInterceptor {private Object target; //真实操作主题public MyProxy(Object target) {this.target = target;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {Object object = null;this.prepare();object = method.invoke(this.target, args);this.commit();return object;}public void prepare() {System.out.println("=====取消掉jdbc的自动提交=====");}public void commit() {System.out.println("=====手工提交事务=====");} }public class TestCGLIB {public static void main(String[] args) {ItemDAOImpl itemDAO = new ItemDAOImpl(); //真实主题对象//代理设计模式之中必须要有公共的集合点,例如:接口,而CGLIB没有接口Enhancer enhancer = new Enhancer(); //创建一个代理工具类enhancer.setSuperclass(ItemDAOImpl.class); //设置一个虚拟的父类enhancer.setCallback(new MyProxy(itemDAO)); //设置代理的回调操作ItemDAOImpl proxyDao = (ItemDAOImpl) enhancer.create();proxyDao.insert(new Item());} }
可以发现此时没有了对接口的依赖,也可以实现动态代理设计,但是需要模拟代理的父类对象。