剑铭 2011-05-24
一、AOP是Aspect Oriented Programing 的简称,面向方面(切面)编程。
(1)、AOP的应用场合:适用于那些具有横切逻辑的应用场合,如:性能检测、访问控制、事务管理以及日志记录。
(2)、面向切面编程的概念:假设我们将ForumService业务类看成一段圆木,将removeTopic()方法和createForum()方法看成圆木的一截,我们会发现性能检测和事务管理的代码就好像一个年轮,而业务代码就是圆木的树心,这也正是横切代码概念的又来。而AOP希望将这些分散的各个业务逻辑代码中的相同代码,通过横向切割的方式抽取到一个独立的模块中,
还业务逻辑一个清新的世界。当让,我们知道将这些重复的横切逻辑独立出来很容易,但如何将这些独立的逻辑融合到业务逻辑中完成和原来一样的业务操作,这才是事情的关键,也正式AOP要解决的主要问题。
(3)、AOP术语
1、连接点(joinPoint)和切点(pointCut):每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事务。但在这为数众多的连接点中,如何定位到敢兴趣的连接点呢?AOP通过"切点(pointCut)"定位特定连接点。通过数据库查询的概念来理解切点和连接点的关系再合适不过了;连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。
2、增强(Advince):是织入到目标类连接点上的一段程序代码。
3、目标对象(Target):增强逻辑织入的目标类。
4、织入(weaving):织入是将增强添加对目标类具体连接点的过程。AOP就想一台织布机,将目标类、增强或者引介通过AOP这台织布机天衣无缝的编织在一起。
根据不同的织入技术,AOP有三种织入方式:
1)、编译期织入,这要求使用特殊的Java编译器。
2)、类装载期织入,这要求使用特殊的类装载器。
3)、动态代理织入,在运行期为目标类添加增强生成子类的方式。
5、代理(Proxy):一个类被AOP织入增强以后,就产生了一个结果类,它是融合了源类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同
接口的类,也可能是原来类的子类,所以我们可以采用调用原来类相同的方式调用代理类。
6、切面(Aspect):切面是由切点和增强(引介)组成的,它既包含了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
(4)、AOP的工作重心:如何将增强应用于目标连接点上,这是首先包括两个工作:第一,如何通过切点和增强定位到连接点上;第二:如何在增强中编写切面的代码。
(5)、AOP的实现原理:Spring AOP使用动态代理技术在运行期间织入增强的代码,Spring AOP使用两种代理机制;一种是基于Jdk的动态代理;另一种是基于CGlib的动态代理,很大程度上JDK本身只提供接口的代理,而不支持类的代理。二、简单的示例:
package com.yt.spring.user; /** * @Description:定义接口类 * @ClassName: IUserBean * @Project: spring-aop * @Author: zxf * @Date: 2011-5-23 */ public interface IUserBean { public UserBean getUser(String userId); public int deleteUser(String userId); public void insertUser(UserBean user); }
package com.yt.spring.user; import org.apache.log4j.Logger; /** * @Description: 实现类 * @ClassName: UserBeanImpl * @Project: spring-aop * @Author: zxf * @Date: 2011-5-23 */ public class UserBeanImpl implements IUserBean { Logger log = Logger.getLogger(UserBeanImpl.class); @Override public UserBean getUser(String userId) { log.info("查找用户.............."); return null; } @Override public int deleteUser(String userId) { log.info("删除用户.............." + userId); return 100; } @Override public void insertUser(UserBean user) { log.info("添加用户.............."); } }
package com.yt.spring.user; /** * @Description: 实体类 * @ClassName: UserBean * @Project: spring-aop * @Author: zxf * @Date: 2011-5-23 */ public class UserBean { public String userId; public String userName; public String userAddress; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserAddress() { return userAddress; } public void setUserAddress(String userAddress) { this.userAddress = userAddress; } }
(1)、使用注解的方式来声明代理类
package com.yt.spring; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; /** * @Description: 使用注解的方式来声明切面类 * @ClassName: MyInterceptor * @Project: spring-aop * @Author: zxf * @Date: 2011-5-23 */ @Aspect public class MyInterceptor { /** * 定义切入点 * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截,(!void表示有返回值) * 第一个..表示com.royzhou.aop包及其子包 * 倒数第二个*表示包下的所有Java类都被拦截 * 最后一个*表示类的所有方法都被拦截 * (..)表示方法的参数可以任意多个如[(java.lang.String,java.lang.Integer)表示第一个参数是String, * 第二个参数是int的方法才会被拦截] */ @SuppressWarnings("unused") @Pointcut("execution(* com.yt.spring.user.*.*(..))") private void pointCutMethod() { } // 定义置前通知 @Before("pointCutMethod()&&args(userId)") public void doBefore(String userId) { System.out.println("定义前置通知.........方法获取参数:" + userId); } // 定义后置通知 @AfterReturning(pointcut = "pointCutMethod()", returning = "result") public void doAfterReturning(int result) { System.out.println("定义置后通知.........方法返回值:" + result); } // 定义例外通知 @AfterThrowing(pointcut = "pointCutMethod()", throwing = "e") public void doAfterException(String e) { System.out.println("定义例外通知.........出现异常:" + e); } // 定义最终通知 @After("pointCutMethod()") public void doAfter() { System.out.println("定义最终通知........."); } // 定义环绕通知 @Around("pointCutMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); // 必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
对应的applicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:jms="http://www.springframework.org/schema/jms" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:ehcache="http://www.springmodules.org/schema/ehcache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd"> <!-- 打开aspectj注解处理器,proxy-target-class="true"表示使用CGlib实现类代理--> <aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 配置切面bean --> <bean id="MyInterceptor" class="com.yt.spring.MyInterceptor"></bean> <bean id="userBean" class="com.yt.spring.user.UserBeanImpl"></bean> </beans>
(二)、使用配置的方式来声明切面类
package com.yt.spring; import org.aspectj.lang.ProceedingJoinPoint; /** * @Description: 使用xml配置的方式来声明切面类 * @ClassName: MyInterceptor * @Project: spring-aop * @Author: zxf * @Date: 2011-5-23 */ public class MyInterceptorXml { // 定义置前通知 public void doBefore(String userId) { System.out.println("定义前置通知.........方法获取参数:" + userId); } // 定义后置通知 public void doAfterReturning(int result) { System.out.println("定义置后通知.........方法返回值:" + result); } // 定义例外通知 public void doAfterException(String e) { System.out.println("定义例外通知.........出现异常:" + e); } // 定义最终通知 public void doAfter() { System.out.println("定义最终通知........."); } // 定义环绕通知 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); // 必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
applicationContext.xml对应的配置:
<bean id="MyInterceptorXml" class="com.yt.spring.MyInterceptorXml"></bean> <aop:config> <aop:aspect id="asp" ref="MyInterceptorXml"> <aop:pointcut id="mycut" expression="execution(* com.yt.spring.user.*.*(..))"/> <aop:before pointcut="execution(* com.yt.spring.user.*.*(..)) and args(userId)" method="doBefore"/> <aop:after-returning pointcut-ref="mycut" method="doAfterReturning" returning="result"/> <aop:after-throwing pointcut-ref="mycut" method="doAfterException" throwing="e"/> <aop:after pointcut-ref="mycut" method="doAfter"/> <aop:around pointcut-ref="mycut" method="doAround"/> </aop:aspect> </aop:config>
最后贴出测试类:
package com.yt.spring; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.yt.spring.user.IUserBean; /** * @Description:测试AOP * @ClassName: Test * @Project: spring-aop * @Author: zxf * @Date: 2011-5-23 */ public class Test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext( "applicationContext.xml"); // 接口代理 IUserBean userBean = (IUserBean) ctx.getBean("userBean"); // UserBeanImpl userBean = (UserBeanImpl) ctx.getBean("userBean"); userBean.deleteUser("419688091"); } }