使用JDK创建代理有一个限制,即它只能为接口创建代理,这一点我们从Proxy的接口方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)就看得很清楚,第三个入参interfaces就是为代理实例指定的实现接口。虽然,面向接口的编程被很多很有影响力人(包括Rod Johnson)的推崇,但在实际开发中,开发者也遇到了很多困惑:难道对一个简单业务表的操作真的需要创建5个类(领域对象类、Dao接口,Dao实现类,Service接口和Service实现类)吗?对于这一问题,我们还是留待大家进一步讨论。现在的问题是:对于没有通过接口定义业务方法的类,如何动态创建代理实例呢?JDK的代理技术显然已经黔驴技穷,CGLib作为一个替代者,填补了这个空缺
下面得演示还是居于上面一直在使用得例子
1.新建CglibProxy代理类
2.修改MethodAdvice类的invoke方法,添加private MethodProxy porxy;这样一个属性,及其set方法
3.修改Test类,改成使用CglibProxy代理
- /**
- *
- */
- package com.advice;
- import java.lang.reflect.Method;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- /**
- * @author zjb
- *
- */
- public class CglibProxy implements MethodInterceptor {
- private MethodAdvice methodAdvice;
- private Enhancer enhancer = new Enhancer();
- public Object getBean(String className){
- try {
- enhancer.setSuperclass(Class.forName(className));
- enhancer.setCallback(this);
- return enhancer.create();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- public MethodAdvice getMethodAdvice() {
- return methodAdvice;
- }
- public void setMethodAdvice(MethodAdvice methodAdvice) {
- this.methodAdvice = methodAdvice;
- }
- @Override
- public Object intercept(Object object, Method method, Object[] args,
- MethodProxy porxy) throws Throwable {
- methodAdvice.setArgs(args);
- methodAdvice.setMethod(method);
- methodAdvice.setPos(-1);
- methodAdvice.setDone(false);
- methodAdvice.setTarget(object);
- methodAdvice.setPorxy(porxy);
- return methodAdvice.proceed();
- }
- }
- private Object invoke(){
- done=true;
- try {
- if(porxy!=null){
- return porxy.invokeSuper(target, args);
- }else{
- return method.invoke(target, args);
- }
- } catch (Exception e) {
- e.printStackTrace();
- } catch (Throwable e) {
- e.printStackTrace();
- }
- return null;
- }
- package com.myadvice.test;
- import com.advice.CglibProxy;
- import com.advice.DynaProxy;
- import com.advice.MethodAdvice;
- import com.test.BookBiz;
- import com.test.BookBizImpl;
- public class Test {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // DynaProxy proxy= new DynaProxy();
- CglibProxy proxy= new CglibProxy();
- MethodAdvice methodAdvice= new MethodAdvice();
- methodAdvice.AddAdvice(new Object[]{new MyAdvice()});
- proxy.setMethodAdvice(methodAdvice);
- // BookBiz bookBiz=(BookBiz)proxy.getBean(BookBizImpl.class.getName());
- BookBizImpl bookBiz=(BookBizImpl)proxy.getBean(BookBizImpl.class.getName());
- bookBiz.buy("张三", "Spring深入潜出", 50);
- bookBiz.comment("李四", "《恐怖世界》一点都不恐怖,很好看!");
- bookBiz.comment("张三", "《Spring深入潜出》还是写得不错的!");
- }
- }
下面是运行结果:
[环绕通知]
前通知,调用的方法:buy,参数:[张三, Spring深入潜出, 50.0]
业务方法buy开始执行
·张三购买图书:Spring深入潜出
·张三增加积分:5
·向物流系统下发货单
业务方法buy结束
后通知,调用的方法:buy,参数:[张三, Spring深入潜出, 50.0]
[环绕通知]
屏蔽李四所有的评论
[环绕通知]
前通知,调用的方法:comment,参数:[张三, 《Spring深入潜出》还是写得不错的!]
业务方法comment开始执行
·张三发表书评《Spring深入潜出》还是写得不错的!
业务方法comment结束
后通知,调用的方法:comment,参数:[张三, 《Spring深入潜出》还是写得不错的!]
通过学习AOP,俺们现在回去想想Spring的数据库事务自动管理是怎么实现的,就比较清晰了,应该使用了环绕通知,在proceed之前开始一个事务,然后在提交事务,就这么简单而已。(^_^,大功告成,关于aop打完收工)