• 1. 第 三 章 Spring AOP
  • 2. 什么是AOP为了理解AOP,请思考如下需求: 角色:上帝,人(每个人都有大脑) 现在上帝希望知道每个人的想法,请用面向对象的方法解决 2
  • 3. 设计登场角色上帝,只有一个public class God { /** * 上帝的读心术 * @param mind 某个人的思想 */ public void read(String mind) { log.debug("上帝知道了,某个人正在想:“" + mind + "”"); } }3
  • 4. 设计登场角色人,好几十亿public class Person { private Brain brain; public void look(String thing) { if(thing.equals("美女")){ log.debug( this.brain.thinking("上帝真伟大,造出来这么漂亮的人")); } else if (thing.equals("丑八怪")) { log.debug(this.brain.thinking("上帝真XX...")); } else { log.debug(this.brain.thinking("")); } } }4
  • 5. 设计登场角色大脑,重要器官,每个人有自己的大脑public class Brain { public String thinking(String thing){ if(thing == null || thing.trim().length() == 0) { return ("发呆中..."); } else { return ("正在思考“" + thing + "”的问题"); } } }5
  • 6. 解决问题为了要知道人们的思想,上帝决定用换脑的手段来达到目的 由于人在出生时并不知道自己的大脑是怎么来的(由Spring注入的),因此,可以在他出生时安装另一个大脑6
  • 7. 换脑的两种方法用继承方式 用接口方式 7
  • 8. 使用继承的方式public class BrainExt extends Brain { private God god; public void setGod(God god) { this.god = god; } @Override public String thinking(String thing) { // 告诉上帝 god.read(thing); return super.think(thing); } }8
  • 9. 使用接口的方式实际上,只要能“思考”,人类不在意究竟给他的脑袋里装一个大脑还是一个石头(假如石头能思考的话) “大脑”只是一种能够思考的象征 因此,重新审视我们的设计,将大脑变成接口,变成一个抽象的概念9
  • 10. 使用接口的方式public class BrainProxy implements Brain{ private God god; private Brain brainNatural; public void setBrainNatural(Brain brainNatural) { this.brainNatural = brainNatural; } public String thinking(String thing) { god.read(thing); return brainNatural.thinking(thing); } public void setGod(God god) { this.god = god; } }10
  • 11. 优先选择接口而非继承当需要进行动态代理时,接口比继承更容易实现 接口具有更好的扩展性11
  • 12. 问题上帝不想创建过多的代理对象,仅仅希望实现具体操作12
  • 13. 使用Spring提供的办法刚才的例子里,无论是使用继承还是接口进行换脑,在设计模式里都称为代理模式 请大家区分代理与被代理对象分别是谁 Spring为代理提供了更方便的控制13
  • 14. 正常操作Spring中AOPAOP即面向切面的编程 在AOP的概念中,将刚才的场景中的角色划分的更为细致切面里执行 附加操作Pointcut正常操作之前正常操作之后14
  • 15. 上例中的上帝,称之为Advice(被通知对象),它知道该做什么 关于上帝何时在什么时间、地点被通知,称之为Pointcut(切入点) 对于真实的大脑,称之为Target(目标对象) 对于假的大脑,不管是真实大脑的子类还是它们实现了共同的接口,都称之为Proxy(代理对象) 15
  • 16. Spring中的Advice和Pointcut常见的Advice和Pointcut的类型 MethodBeforeAdvice AfterReturningAdvice ThrowsAdvice MethodInterceptor 16
  • 17. 配置文件相应的改动 god 17
  • 18. public class God implements MethodBeforeAdvice{ public void read(String mind) { System.out.println("上帝知道了,某个人正在想:“" + mind + "”"); } public void before(Method method, Object[] args, Object target) throws Throwable { read(args[0].toString()); } }在方法执行前通知告知你执行的方法、方法的参数s 还有目标(被代理的)对象18
  • 19. 注意事项Advice仅用于通知,如果需要拦截方法的调用需要使用Interceptor 无论是MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice它们都不能改变方法的执行(本身的行为) 而Interceptor具有改变方法行为的能力 19
  • 20. MethodInterceptor需要实现的方法注意,一旦实现了MethodInterceptor,那些Advice(通知)就不管用了public Object invoke(MethodInvocation mi) throws Throwable { if(mi.getArguments()[0].toString().indexOf("XX") > 0){ return ("凡人,别做无谓的思考了..."); //throw new RuntimeException("现在大脑一片混乱,无法思考"); } else { return mi.proceed(); } }改变方法的行为, 只在一念之间Proceed让方法 继续执行20
  • 21. 讨论使用Spring提供的ProxyFactoryBean提供代理的好处 不必自己创建整个代理对象了,代理对象由ProxyFactoryBean产生,程序员只需要关注实现具体功能 使用ProxyFactoryBean可以创建几乎任何对象的代理,减少了程序中代理类的数量 21
  • 22. 问题但是现在上帝很郁闷,现在它必须戴上一大堆身份,弄得身上脏兮兮的 现在的结果是使得上帝依赖于Spring提供的接口,上帝离了Spring就活不了了 怎么办22
  • 23. Spring2.0里的AOP使用AspectJ23
  • 24. 使用AspectJ达到相同的效果在AspectJ的表达中上帝是一个POJO,不需要实现特定的接口就可进行AOP操作 这个例子里,上帝在AspectJ中被成为Aspect(即切面),它的目的是执行正常逻辑之外的附加操作 与之前的Spring1.x中的AOP相比,可以把Advisor(通知)看作是知道了切入地点的Aspect24
  • 25. 使用AspectAOP的步骤将BeanFactory换成ApplicationContext ApplicationContext是BeanFactory的子接口 ApplicationContext提供了更多的功能 如果将BeanFactory比作一个小作坊,那么ApplicationContext就是一个生产车间 25
  • 26. 使用AspectAOP的步骤使用spring2.0中提供的schema样式的配置文件 26
  • 27. God类的变化public class God { private static Log log = LogFactory.getLog(God.class); /** * 上帝的读心术 * @param mind 某个人的思想 */ public void read(String thing) { log.debug("上帝知道了,某个人正在想:“" + thing + "”"); } }God不需要再实现 那些Advice接口27
  • 28. 配置文件的变化 28
  • 29. 它们如何对应public class God { public void read(String thing) { log.debug("上帝知道了,某个人正在想:“" + thing + "”"); } } package org.yihang.aop; public interface Brain { public String thinking(String thing); }29
  • 30. 其他类型的JoinPoint -- 表示已经定义好连接点的切面30
  • 31. Pointcut表达式 execution表示执行 public表示公共方法 第一个*表示任意返回类型 第二个*表示任意方法名 (..)表示任意参数execution(public * *(..)) 31
  • 32. 更多的例子execution(* set*(..)) execution(* transfer()) execution(* transfer(*)) execution(* transfer(*,String)) execution(* com.xyz.service.AccountService.*(..)) execution(* com.xyz.service.*.*(..)) execution(* com.xyz.service..*.*(..)) 32
  • 33. 问题如何配合切入点表达式,向切面类传递参数33
  • 34. 使用新语法的好处减少了代理类 在切入点的控制上更为细腻34