• 1. Spring-AOPLouis Young
  • 2. 功能模块化与面向过程最早的功能模块化是把某些重复的或功能独立日志子过程业务处理…过程日志子过程某业务处理过程(过程集成)
  • 3. IoC回顾IoC就是利用容器通过XML配置描述把各种不同功能的模块组合成一个完整的业务逻辑流程. 其核心是:容器与XML描述. 其实现是:通过Java的反射实例化对象,管理对象,访问对象(定位). 其作用是:降低模块间的耦合度,达到模块复用,业务逻辑流程动态化,灵活化,可定制化,可扩展化.
  • 4. IoC与模块化日志功能bean业务处理…功能bean日志功能beanBeanFactory(功能Bean集成)
  • 5. 对模块化的概念抽象-切面日志模块业务处理模块日志模块一个切面(方面)Aspect
  • 6. 对模块化的概念抽象-连接点连接点(调用日志模块的地方)连接点(JoinPoint) 程序运行过程中的某个阶段点。
  • 7. AOP在前面我们了解的的概念其实就是面向Aspect的.简称AOP. 面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。 因为OOP面向的是以数据为中心的对象. AOP面向的是以功能为中心的模块.AOP的实现还是以OOP为手段.
  • 8. AOP具体实现 AspectJ (TM): 创建于Xerox PARC. 有近十年历史,成熟 缺点:过于复杂;破坏封装;需要专门的Java编译器。 动态AOP: 使用JDK的动态代理API或字节码Bytecode处理技术。 Spring 采用的是动态AOP.
  • 9. Spring中的AOP实现Spring AOP用纯Java实现。它不需要专门的编译过程。Spring AOP不需要控制类装载器层次,因此它适用于J2EE web容器或应用服务器。 Spring实现AOP的方法跟其他的框架不同。Spring并不是要尝试提供最完整的AOP实现(尽管Spring AOP有这个能力), 相反的,它其实侧重于提供一种AOP实现和Spring IoC容器的整合,用于帮助解决在企业级开发中的常见问题。 Spring缺省使用J2SE 动态代理(dynamic proxies)来作为AOP的代理。这样任何接口都可以被代理。 Spring也支持使用CGLIB代理. 对于需要代理类而不是代理接口的时候CGLIB代理是很有必要的。
  • 10. AOP与IoCSpring IoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP. AOP提供强大的中间件解决方案,这使得Spring IoC容器更加完善。
  • 11. AOP概念(一/二) 切面(Aspect): 一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 连接点(Joinpoint): 在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的执行。 通知(Advice): 在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 切入点(Pointcut): 匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心
  • 12. AOP概念(二/二)引入(Introduction): (也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口到任何被代理的对象。 目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。 AOP代理(AOP Proxy): AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 织入(Weaving): 把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。
  • 13. AOP的目的提供功能分离框架(模块化) 主要代码上分离业务逻辑实现与非业务逻辑实现. 可以降低两种代码的耦合性 . 从而达到代码重用和易于维护的目的
  • 14. AOP与Java代理机制AOP是一种思想,它和具体的实现技术无关。任何一种符合AOP思想的技术实现,都可以看作是AOP的实现。 JDK 1.3以后,Java提供了动态代理的机制。通过Java的动态代理机制,就可以很容易地实现AOP的思想。实际上Spring的AOP也是建立在Java的代理机制之上的。
  • 15. Java代理回顾Proxy InvocationHandler 请参考例子:ProxyDemo 请参考例子:UseProxyClass
  • 16. 例子- Java代理MyInterface.do(…)MyInterfaceImplMyHandler.invoke(…)Proxy. newProxyInstance代理对象.do(…)
  • 17. 例子-代理的核心代码public class MyHandler implements InvocationHandler { private T t; private OtherFunc other; public MyHandler(T t,OtherFunc other){ this.t=t; this.other=other; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我知道马上要被调用的方法是:"+method.getName()); other.other("这是其他方法"); return method.invoke(t,args); } }连接点切入点(条件与表达式)引入通知目标对象
  • 18. AOP框架概念示意图
  • 19. 认识Spring AOP APIs Spring AOP API其实把我们上面的思想框架化的一组API,通过这组API我们可以很轻松的解决在企业级开发中的常见问题 :比如常见的事务管理.
  • 20. Spring AOP的实现关键产生advice 定义pointcuts 使用ProxyFactoryBean 理解自动代理
  • 21. 通知的生命周期 每个通知都是一个Spring bean。一个通知实例既可以被所有被通知的对象共享,也可以被每个被通知对象独占。 这根据设置类共享(per-class)或基于实例(per-instance)的参数来决定。 类共享通知经常会被用到。它很适合用作通用的通知例如事务advisor。这些advisor不依赖于代理对象的状态也不会向代理对象添加新的状态;它们仅仅在方法和参数上起作用。 基于实例的通知很适合用作导入器来支持混合类型。在这种情况下,通知向代理对象添加状态。 在同一个AOP代理里混合使用类共享和基于实例的通知是可能的。
  • 22. Advice的类型拦截around通知 前置通知 后置通知 异常通知 引入通知
  • 23. 例子-BeforeAdviceBefore通知只在JointPoint前执行, 实现Before通知的类需要实现接口MethodBeforeAdvice
  • 24. 例子-功能bean(1/5)代理的基本实现手段—接口 public interface IDAO { boolean insert(T bean); /* boolean save(T bean); boolean update(T bean); boolean deleteByObject(T bean); */ } 功能实现类 public class BookDAO implements IDAO { public boolean insert(Books bean) { //这里原来是准备写日志 System.out.println("在处理业务逻辑中...." + bean); //用线程替代具体的业务处理过程 try { Thread.currentThread().sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("在处理业务逻辑结束" + bean); //这里原来应该有日志代码,而且与log4j耦合 return false; } }请参考例子:AOPBeforeAdvice
  • 25. 例子-切面与advice(2/5)public class LogBeforAdvice implements MethodBeforeAdvice { private Logger logger =Logger.getLogger(this.getClass().getName()); public void before(Method method, Object[] args, Object target) throws Throwable { logger.log(Level.INFO, method + "开始被调用于"+ new Date()); } }
  • 26. 例子-advice部署描述(3/5) db.IDAO logbeforeadvice
  • 27. 例子-advice调用(4/5)ApplicationContext context = new FileSystemXmlApplicationContext("beans-config.xml"); IDAO daoProxy =(IDAO) context.getBean("bookdao"); Books book=new Books(); book.setId(1); book.setName("战争与和平"); book.setAuthor("俄国人"); boolean re=daoProxy.insert(book);
  • 28. 例子-体会(5/5) 业务逻辑与非业务逻辑的分割 AOP的几个基本概念就是掌握AOP的关键点. SpringAOP是用Proxy实现. 注意泛型的使用.在接口中使用泛型,配置接口的时候不需要指定.
  • 29. 例子-AroundAdviceAround通知会在JointPoint的前后执行. 需要实现接口MethodInterceptor . 包 org.aopalliance.intercept.MethodInterceptor; public Object invoke(MethodInvocation arg0) throws Throwable 参考提供的例子AOPAllDevice
  • 30. 例子-AfterAdviceAfter Returning通知只在JointPoint后执行. 实现After Returning通知的类需要实现接口AfterReturningAdvice. 参考提供的例子AOPAllDevice
  • 31. 例子-ThrowsAdviceThrow通知只在JointPoint抛出异常时执行. 实现Throw通知的类需要实现接口ThrowsAdvice. 该接口中没有任意的方法,但必须实现一个函数 afterThrowing([Method], [args], [target], Throwable subclass) 前面三个参数是可选的 参考提供的例子AOPAllDevice
  • 32. 例子-IntroductionIntroduction通知只在JointPoint调用完毕后执行. 实现Introduction通知的类需要实现接口IntroductionAdvisor和接口IntroductionInterceptor。 参考提供的例子AOPIntroduction
  • 33. Spring里的advisor(Advisor) 在Spring里,一个advisor是一个仅仅包含一个通知对象和与之关联的切入点表达式的切面。 除了引入这种特殊形式,任何advisor都可以和任何通知一起工作。 org.springframework.aop.support.DefaultPointcutAdvisor是最常用的advisor类。例如,它可以和: MethodInterceptor,BeforeAdvice 或者 ThrowsAdvice一起使用。 在Spring里有可能在同一个AOP代理里模糊advisor和通知类型。例如,你可以在一个代理配置里使用一个interception环绕通知,一个异常通知和一个前置通知:Spring将负责自动创建所需的拦截器链.
  • 34. Spring的切入点(Pointcut) Pointcut是Join Point的集合,它是程序中需要注入Advice的位置的集合。 Spring的切入点模型使得切入点可以独立于通知类型进行重用,这就使得针对不同 advice使用相同的pointcut成为可能。 Spring主要提供了3种切入点(Pointcut)的实现: 静态切入点. 动态切入点. 自定义切入点.
  • 35. 静态切入点 静态切入点只限于给定的方法和目标类,而不考虑方法的参数。 Spring在调用静态切入点时只在第一次的时候计算静态切入点的位置,然后把它缓存起来,以后就不需要再进行计算。就是Spring只在第一次调用方法时执行静态切入点:以后每次调用这个方法时就不需要再执行。 使用org. springframework. aop. support. RegexpMethodPointcut 可以实现静态切入点. RegexpMethodPointcut是一个通用的正则表达式切入点,它是通过Jakarta ORO来实现的,需要把jakarta-oro-2.0.8.jar加入到ClassPath中,它的正则表达式语法和Jakarta ORO的正则表达式语法是一样的。
  • 36. 例子-切入点接口(1/5)public interface IFunc { void doSomething(String info); void doAnothoer(); void print(String info); void println(String info); } 需要在上面函数切入一些功能处理.请参考例子:AOPStaticPointcut
  • 37. 例子-切入点接口实现(2/5)public class FuncImpl implements IFunc { public void doAnothoer() { System.out.println("调用do开头的方法 Another"); } public void doSomething(String info) { System.out.println("调用do开头的方法 Something"); } public void print(String info) { System.out.println("调用print开头的方法 print"); } public void println(String info) { System.out.println("调用print开头的方法 println"); } }
  • 38. 例子-切入点接口实现(3/5)切入的advice的实现 public class UseBeforeAdvice implements MethodBeforeAdvice { public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("切入的处理调用。"); } }
  • 39. 例子-切入点接口实现(4/5) .*print.* .*do.* IFunc regExpAdvisor
  • 40. 例子-思考(5/5)切入点就是是Advice要切入的条件. 体会切入点与连接点的概念. 体会Aspect与Advice的关系
  • 41. 动态切入点 动态切入点与静态切入点的区别是,它不仅限定于给点的方法和类,动态切入点还可以指定方法的参数。 因为参数的变化性,所以动态切入点不能缓存,需要每次调用的时候都进行计算,因此使用动态切入点有很大的性能损耗。 当切入点需要在执行时根据参数值来调用通知时,就需要使用动态切入点。Spring提供了一个内建的动态切入点:控制流切入点。此切入点匹配基于当前线程的调用堆栈。开发人员只有在当前线程执行时找到特定的类和特定的方法才返回true。 其实大多数的切入点可以使用静态切入点,所以很少有机会创建动态切入点。
  • 42. 控制流切入点 动态切入点的一个经典Spring实现是是控制流切入点。 控制流切入点是由org.springframework.aop.support.ControlFlowPointcut 类声明的。 在执行时控制流切入点的开销是非常昂贵的,甚至与其它动态切入点比起来也是如此。
  • 43. 例子-动态切入点参考提供的例子AOPDynamicPointcut
  • 44. 关于切入与织入的深入理解织入是实现AOP的一个重要机制,织入的实现机制有多种,基本上可以分为两类,静态织入与动态织入。 静态织入是指在业务功能代码中的适当位置,比如某段代码执行前,或执行后,将方面中的编码插入,从而形成混合的编码。方面中的编码在程序运行前,已被内联至业务功能代码中,因此,代码可以被优化,从而使织入产生的开销最小化,最终产生的混合代码,其执行速度接近为使用AOP方式编写的代码。 但是,静态织入无法做到在程序运行时,根据运行上下文动态的决定插入的方面代码,动态织入则可以做到这一点。 动态织入可以在程序运行时,根据上下文决定调用的方面,它们的先后顺序,增加或删除一个方面等。 而根据织入的时间,又可以分为三类,编译时,载入时,运行时。编译时织入可以在编译前进行预处理,将两种代码自动混合,将方面中的代码自动插入到功能模块代码的合适位置处,也可在编译后,对编译后的代码进行操作。载入时织入是在代码载入时,实现代码的织入。运行时织入则在运行时,根据对方法的调用执行适当的方面代码以实现织入。
  • 45. 自定义切入点 因为Spring中的切入点是Java类,而不是语言特性(如AspectJ),因此可以定义自定义切入点。
  • 46. 使用ProxyFactoryBean创建AOP代理 在Spring里创建一个AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean。这个类对应用的切入点和通知提供了完整的控制能力(包括它们的应用顺序)。
  • 47. ProxyFactoryBean类属性(1/2)指定你希望代理的目标对象 指定是否使用CGLIB proxyTargetClass:这个属性为true时,目标类本身被代理而不是目标类的接口。 optimize:用来控制通过CGLIB创建的代理是否使用激进的优化策略。除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置。目前这个属性仅用于CGLIB代理;对于JDK动态代理(缺省代理)无效。 frozen:用来控制代理工厂被配置之后,是否还允许修改通知。缺省值为false(即在代理被配置之后,不允许修改代理的配置)。 exposeProxy:决定当前代理是否被保存在一个ThreadLocal中以便被目标对象访问。(目标对象本身可以通过MethodInvocation来访问,因此不需要ThreadLocal。) 如果个目标对象需要获取代理而且exposeProxy属性被设为true,目标对象可以使用AopContext.currentProxy()方法。
  • 48. ProxyFactoryBean类属性(2/2)aopProxyFactory:使用AopProxyFactory的实现。这提供了一种方法来自定义是否使用动态代理,CGLIB或其它代理策略。 缺省实现将根据情况选择动态代理或者CGLIB。一般情况下应该没有使用这个属性的需要;它是被设计来在Spring 1.1中添加新的代理类型的。 proxyInterfaces:需要代理的接口名的字符串数组。如果没有提供,将为目标类使用一个CGLIB代理 单例:工厂是否应该返回同一个对象,不论方法getObject()被调用的多频繁。多个FactoryBean实现都提供了这个方法。缺省值是true。如果你希望使用有状态的通知--例如,有状态的mixin--可以把单例属性的值设置为false来使用原型通知。
  • 49. Spring中的自动代理提出的背景 不管是使用Java的动态代理还是使用CGLIB代理,虽然功能很强大,但是对于每一个类,都要在Spring的配置文档中建立相应的代理,如果只是一个很小的应用系统,还看不出来工作量有多大,但对于一个大型的企业应用来说,工作量就太大了,而且重复性的工作很多. Spring提供了一种自动代理的方式,可以减轻这部分工作。
  • 50. 自动代理实现要使用Spring中的动态代理,org.springframework.aop.framework.autoproxy包是必需的 . Spring提供两种自动代理产生器: BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator
  • 51. 例子-缺省自动代理DefaultAdvisorAutoProxyCreator的实施步骤: 直接去掉 IFunc regExpAdvisor 在获取代理的时候,不使用代理bean的名字,而是使用接口实现bean的名字。 请参考提供的例子代码。请参考例子:AOPAutoProxyDefault
  • 52. 例子-按方法名自动代理(1/4)要自动代理的两个Bean类(这里我们没有采用接口了) public class MyBeanOne { public void doMethodOne(){ System.out.println("Bean-1"); } } public class MyBeanTwo { public void doMethodTwo(String info){ System.out.println("要打印的信息:" +info); } } 请参考例子:AOPAutoProxyBeanName
  • 53. 例子-按方法名自动代理(2/4)切入实现 public class MyAroundAdvice implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable{ System.out.println("before"); Object retVal=invocation.proceed(); System.out.println("after"); return retVal; } }
  • 54. 例子-按方法名自动代理(3/4)自动代理的配置 beanOne beanT* advice
  • 55. 例子-按方法名自动代理(4/4)调用自动代理的bean public class TestAutoProxy{ public static void main(String[] args){ ApplicationContext context=new ClassPathXmlApplicationContext("beans-config.xml"); MyBeanOne one=(MyBeanOne)context.getBean("beanOne"); MyBeanTwo two=(MyBeanTwo)context.getBean("beanTwo"); one.doMethodOne(); two.doMethodTwo("Hello"); } }
  • 56. 其他代理例子静态代理例子 动态代理例子 注意通过该例子理解什么是代理. 练习:使用这种代理来实现数据库事务
  • 57. 思考:可以用AOP实现的应用Authentication 权限 Caching 缓存 Context passing 内容传递 Error handling 错误处理 Lazy loading 懒加载 Debugging  调试 logging, tracing, profiling and monitoring 记录跟踪 优化 校准 Performance optimization 性能优化 Persistence  持久化 Resource pooling 资源池 Synchronization 同步 Transactions 事务 Unit Test单元测试
  • 58. 例子-AOP的企业应用(1/3)User表的POJO类 public class User { private Integer id; private String name; private Integer age; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }请参考例子:AOPExampleTransactionManager
  • 59. 例子-AOP的企业应用(2/3)数据操作接口与实现 public class UserDAO implements IUserDAO { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); } public void insert(User user) { String name = user.getName(); int age = user.getAge().intValue(); int id =user.getId().intValue(); jdbcTemplate.update("INSERT INTO USERTAB (id,name,age) " + "VALUES("+id+",'" + name + "'," + age + ")"); //故意添加一个错误测试其中的事务回滚 jdbcTemplate.update("INSERT INTO USER (id,name,age) " + "VALUES("+id+",'" + name + "'," + age + ")"); } ... }
  • 60. 例子-AOP的企业应用(3/3)配置描述文件 IUserDAO PROPAGATION_REQUIRED
  • 61. 例子-AOP在WEB中应用参考提供的例子 体会AOP提供的页面保护. 参考提供的例子FirstSpringMVC.war
  • 62. 谢谢 MSN:louisyyy@hotmail.com