Spring 教程(二)

一、Spring AOP介绍

开发其实就是在不断的重构,抽象重复代码,然后进行封装。从最原始的模块化编程到面向对象编程,代码的封装越来越整齐清晰,但是依然存在重复的代码,而这些重复代码几乎都是与业务逻辑无关的系统逻辑代码。比如在数据操作类中的插入、更新、删除数据等方法中都存在数据库事务的处理,重要业务逻辑方法中都有日志记录的逻辑等等。每个应用系统都存在着这种系统级的重复逻辑代码,而我们并没有更好的方法去将这些代码抽象出来并进行管理。然而AOP的出现弥补了这一缺陷,AOP可以在不改变原有业务逻辑代码的情况下对原有业务进行横切拦截,处理那些重复的系统逻辑。

与Ioc容器一样,AOP也是Spring的核心模块之一。AOP是Aspect-Oriented Programming的简称,现在通常称为面向切面编程。我们学了OOP,面向对象编程,而AOP并非是OOP的替代技术,它只是OOP的一个有益补充。

需要指出的是AOP的应用场合是受限的,它一般只适合于那些具有横切逻辑的应用场合:如性能监测、访问控制、事务管理以及日志记录,它并不适合处理具体的业务逻辑,分散处理业务逻辑会使得逻辑混乱、增加维护成本。

二、如何使用Spring AOP

下面以对用户操作类UserDao的AOP拦截演示Spring AOP的使用。

1、创建Java项目,添加Spring AOP依赖支持

aopalliance-1.0.jar

commons-logging-1.1.1.jar

spring-aop-3.2.0.RELEASE.jar

spring-beans-3.2.0.RELEASE.jar

spring-context-3.2.0.RELEASE.jar

spring-core-3.2.0.RELEASE.jar

spring-expression-3.2.0.RELEASE.jar

2、添加User及UserDao类

User类:

  1. public class User {
  2. private Integer id;
  3. private String name;
  4. }

UserDao类:

  1. public class UserDao {
  2. public void save(User user){
  3. System.out.println("save user....");
  4. }
  5. public void delete(int id){
  6. System.out.println("delete user....");
  7. }
  8. public void update(User user) {
  9. System.out.println("update user ....");
  10. }
  11. public User query(String name) {
  12. System.out.println("getUser ....");
  13. return new User();
  14. }
  15. }

3、添加AOP拦截处理

AOP前置通知:

  1. public class UserBeforeAdvice implements MethodBeforeAdvice {
  2. public void before(Method method, Object[] args, Object target) {
  3. System.out.println("调用方法:"+method.getName() + "()前拦截处理");
  4. }
  5. }

AOP后置通知:

  1. public class UserAfterAdvice implements AfterReturningAdvice {
  2. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {
  3. System.out.println("方法:"+method.getName() + "()返回后拦截处理");
  4. }
  5. }

AOP环绕通知:

  1. public class UserAroundAdvice implements MethodInterceptor {
  2. public Object invoke(MethodInvocation invocation) throws Throwable {
  3. System.out.println("调用方法:"+invocation.getMethod().getName() + "()前拦截处理");
  4. Object o = invocation.proceed();
  5. System.out.println("调用方法:"+invocation.getMethod().getName() + "()后拦截处理");
  6. return o;
  7. }
  8. }

4、添加Spring配置文件applicationContext.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
  6. <bean id="userDaoTarget" class="com.boya.spring.dao.UserDao" />
  7. <bean id="userBeforeAdvice" class="com.boya.spring.aop.UserBeforeAdvice" />
  8. <bean id="userAfterAdvice" class="com.boya.spring.aop.UserAfterAdvice" />
  9. <bean id="userAroundAdvice" class="com.boya.spring.aop.UserAroundAdvice" />
  10. <bean id="userDao" class="org.springframework.aop.framework.ProxyFactoryBean">
  11. <property name="interceptorNames">
  12. <list><value>userAroundAdvice</value></list>
  13. </property>
  14. <property name="target" ref="userDaoTarget"></property>
  15. </bean>
  16. </beans>

5、测试AOP

  1. public static void main(String[] args) {
  2. ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
  3. UserDao userDao = context.getBean("userDao", UserDao.class);
  4. userDao.save(new User());
  5. }

输出结果:

调用方法:save()前拦截处理

save user....

调用方法:save()后拦截处理

回过头来再看刚才的示例。

1、首先,原来的业务逻辑代码不变

不再关心重复的系统逻辑代码

2、编写AOP切面处理逻辑

把原业务逻辑中的重复代码抽象出来,封装入切面代码中,如上面示例的三种Advice通知封装不同的系统处理逻辑。

前置通知:实现MethodBeforeAdvice 接口,在调用业务方法前调用该接口

后置通知:实现AfterReturningAdvice 接口,在业务方法返回后调用该接口,在该接口中可以查看返回值(但不能修改返回值)

环绕通知:实现MethodInterceptor 接口,在该接口中invocation.proceed();这个方法会调用真实对象的方法
3、使用Spring配置文件将业务逻辑和AOP切面逻辑进行组装

AOP代理Bean类型需要设置为org.springframework.aop.framework.ProxyFactoryBean

必须设置代理目标(target属性设置)和通知类型(interceptorNames属性设置)

代理目标并非必须实现接口,作为POJO被代理时,会对目标所有方法进行拦截

三、AOP实现原理

Spring AOP是基于Java反射和动态代理实现的。在讲解动态代理之前,我们先回顾一下代理模式。

代理模式,就是为某一对象提供一个代理,通过代理操作该对象的方法。通常情况下,真实对象和代理对象需要实现相同的接口,在代理对象中保存真实对象的引用,以此来控制操作真实对象。

我们以班长代理老师签到来演示代理模式。

创建签到接口:

  1. public interface SignInterface {
  2. public Object sign(String nameList);
  3. }

创建真实对象,Teacher类:

  1. public   class  Teacher  implements  SignInterface {
  2. public  Object sign(String nameList) {
  3. System. out .println( "Teacher sign..." );
  4. return   new  Object();
  5. }
  6. }

创建代理对象,Leader类:

  1. public class Leader implements SignInterface {
  2. private Teacher teacher;
  3. public Object sign(String nameList) {
  4. if (teacher == null) {
  5. teacher = new Teacher();
  6. }
  7. Object o = teacher.sign(nameList);
  8. return o;
  9. }
  10. }

测试代理:

  1. public static void main(String[] args) {
  2. SignInterface s = new Leader();
  3. s.sign("names");
  4. }

以上就是一个代理模式的例子,代理类在编译时就已经被创建了,而动态代理是在运行时动态创建代理类来实现代理模式。如下代码:

  1. public class ProxyObject implements InvocationHandler {
  2. private Object proxy_obj;
  3. ProxyObject(Object obj) {
  4. this.proxy_obj = obj;
  5. }
  6. public static Object getProxy(Object obj) {
  7. Class cls = obj.getClass();
  8. // 通过Proxy类的newProxyInstance方法来返回代理对象
  9. return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new ProxyObject(obj));
  10. }
  11. /**
  12. * 实现InvocationHandler接口的invoke
  13. */
  14. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  15. System.out.println("调用方法:" + method + "()之前拦截处理");
  16. if (args != null) {
  17. System.out.println("方法有" + args.length + "个参数");
  18. for (int i = 0; i < args.length; i++) {
  19. System.out.println(args[i]);
  20. }
  21. }
  22. // 利用反射机制动态调用真实对象的方法
  23. Object o = method.invoke(proxy_obj, args);
  24. System.out.println("调用方法:" + method + "()之后拦截处理");
  25. return o;
  26. }
  27. // 测试代码
  28. public static void main(String agr[]) {
  29. SignInterface si = (SignInterface) getProxy(new Teacher());
  30. si.sign("names");
  31. }
  32. }

以上就是使用JDK的Proxy实现的动态代理,不过JDK的动态代理实现只支持针对接口的动态代理实现。Spring AOP实现默认也是动态代理方式,不过,Spring AOP支持CGLib Proxy的实现方式,可以针对POJO进行动态代理,实现AOP拦截。

我们来看一下CGLib实现的一个简单AOP拦截

创建业务POJO:

  1. public class CGLibTeacher {
  2. public Object sign(String nameList) {
  3. System.out.println("Teacher sign...");
  4. return new Object();
  5. }
  6. }

创建AOP拦截:

  1. public class CGLibAop implements MethodInterceptor {
  2. public Object intercept(Object arg0, Method arg1, Object[] arg2,
  3. MethodProxy arg3) throws Throwable {
  4. System.out.println("before...");
  5. Object o = arg3.invokeSuper(arg0, arg2);
  6. System.out.println("after...");
  7. return o;
  8. }
  9. }

CGLib代理对象创建及测试:

  1. public class CGLibProxy {
  2. public static CGLibTeacher create(CGLibAop aop){
  3. Enhancer en = new Enhancer();
  4. //进行代理
  5. en.setSuperclass(CGLibTeacher.class);
  6. en.setCallback(aop);
  7. //生成代理实例
  8. return (CGLibTeacher)en.create();
  9. }
  10. public static void main(String[] args) {
  11. CGLibTeacher t = CGLibProxy.create(new CGLibAop());
  12. t.sign("names");
  13. }
  14. }

从CGLib的代理对象创建中可以看到,代理对象需要设置代理目标以及AOP拦截实现,和Spring AOP的实现非常类似。

时间: 2024-11-12 15:42:09

Spring 教程(二)的相关文章

Spring 教程(二) 体系结构

体系结构 Spring 有可能成为所有企业应用程序的一站式服务点,然而,Spring 是模块化的,允许你挑选和选择适用于你的模块,不必要把剩余部分也引入.下面的部分对在 Spring 框架中所有可用的模块给出了详细的介绍. Spring 框架提供约 20 个模块,可以根据应用程序的要求来使用. 核心容器 核心容器由核心,Bean,上下文和表达式语言模块组成,它们的细节如下: 核心模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能. Bean 模块提供 BeanFactory,它是一个工厂

Spring Cloud 入门教程(二): 配置管理

使用Config Server,您可以在所有环境中管理应用程序的外部属性.客户端和服务器上的概念映射与Spring Environment和PropertySource抽象相同,因此它们与Spring应用程序非常契合,但可以与任何以任何语言运行的应用程序一起使用.随着应用程序通过从开发人员到测试和生产的部署流程,您可以管理这些环境之间的配置,并确定应用程序具有迁移时需要运行的一切.服务器存储后端的默认实现使用git,因此它轻松支持标签版本的配置环境,以及可以访问用于管理内容的各种工具.很容易添加

Spring3.x教程(二) Spring AOP

一.Spring AOP介绍 开发其实就是在不断的重构,抽象重复代码,然后进行封装.从最原始的模块化编程到面向对象编程,代码的封装越来越整齐清晰,但是依然存在重复的代码,而这些重复代码几乎都是与业务逻辑无关的系统逻辑代码.比如在数据操作类中的插入.更新.删除数据等方法中都存在数据库事务的处理,重要业务逻辑方法中都有日志记录的逻辑等等.每个应用系统都存在着这种系统级的重复逻辑代码,而我们并没有更好的方法去将这些代码抽象出来并进行管理.然而AOP的出现弥补了这一缺陷,AOP可以在不改变原有业务逻辑代

C#微信公众号开发系列教程二(新手接入指南)

http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可直接跳过,也欢迎大神吐槽. 目录 C#微信公众号开发系列教程一(调试环境部署) C#微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) 微信公众平台消息接口的工作原理大概可以这样理解:从用户端到公众号端一个流程是这样的,用户发送消息到微信服务器,微

C语言快速入门教程(二)

C语言快速入门教程(二) C语言的基本语法 本节学习路线图: 引言: C语言,顾名思义就是一门语言,可以类比一下英语; 你要说出一个英语的句子需要:  单词  +  语法!  将单词按照一定的语法拼凑起来就成了一个英语句子了; C语言同样是这样,只不过单词可以理解为一些固定的知识点,而语法可以理解为算法(可以理解为解决问题的方法) 在这一节中我们就对固定知识点中的语言描述与数据存储进行解析! 1.C语言的基本元素 1.1  标识符 什么是标识符? 答:在C语言中,符号常量,变量,数组,函数等都需

攻城狮在路上(贰) Spring(二)--- Spring IoC概念介绍

一.IoC的概念: IoC(控制反转)是Spring容器的核心.另一种解释是DI(依赖注入),即让调用类对某一个接口的依赖关系由第三方注入,以移除调用类对某一个接口实现类的一览. 定义如此,由此可见,在面向接口编程的情况下,IoC可以很好的实现解耦,可以以配置的方式为程序提供所需要的接口实现类. 在实际程序开发中,我们只需要提供对应的接口及实现类,然后通过Spring的配置文件或者注解完成对依赖类的装配.二.IoC的类型: 1.通过构造函数: 此种方式的缺点是,在构造函数中注入之后一般会作为一个

DataVeryLite入门教程(二) Entity篇

DataVeryLite 是基于.net 4.0的数据库持久化ORM框架. 目前支持的数据库有Sqlserver,Mysql,Oracle,Db2,PostgreSql,Sqlite和Access. 最好先阅读DataVeryLite入门教程(一) 配置篇,然后再阅读本篇.如果你觉得麻烦也可以跳过. Entity是ORM中的核心对象之一,一个继承Entity的对象对应于数据库中的一个表. Entity提供丰富的API对表中的单条数据进行操作. 比如根据id或其他条件,加载,删除,插入,更新和部分

jQuery 入门教程(二): 基本语法

学习jQuery之前需要你有下面几个方面的基本知识 HTML CSS JavaScript jQuery 的基本语法 $(selector).action() $ 符合定义这是一个jQuery语句 (selector) 用来选择某个HTML元素,其语法和CSS的selector语法一样. action() 定义操作该HTML元素的方法. 比如: $(this).hide() – 隐藏当前元素. $("p").hide() – 隐藏所以 <p> 元素. $(".te

HTML/CSS基础教程 二

表 表是十分有用的,用它存储表状的数据更易于阅读.当你想用行列来显示信息时---你猜到了,<table>标签正是你需要的,有许多和表有关的标签,它们都以<table>开始. 表就是将一组数据用行列的形式排列,我们用<tr>标签创建一个表行(table row),<td>标签表示表数据(table date) <!DOCTYPE html> <html> <head> <title>Table Time</

Quartz教程二:API,Job和Trigger

原文链接 | 译文链接 | 翻译:nkcoder | 校对:方腾飞 本系列教程由quartz-2.2.x官方文档翻译.整理而来,希望给同样对quartz感兴趣的朋友一些参考和帮助,有任何不当或错误之处,欢迎指正:有兴趣研究源码的同学,可以参考我对quartz-core源码的注释(进行中). Quartz API Quartz API核心接口有: Scheduler – 与scheduler交互的主要API: Job – 你通过scheduler执行任务,你的任务类需要实现的接口: JobDeta