spring AOP
本节要点:
- 掌握AOP概念
- 掌握AOP的有关术语
- 掌握spring AOP框架的实现方式
在文章“spring静态代理和动态代理”中演示了如何使用jdk动态代理功能实现一个最简单的AOP。使用代理对象将日志记录与业务逻辑无关的动作或任务提取出来,设计为一个服务类,这样的类可以称之为aspect(切面).
1 AOP定义
- u AOP把软件系统分成两部分:核心关注点和横切关注点。所谓核心关注点,是业务处理的主要流程,也就是说这个解决方案要做的事。所谓横切关注点,是与核心关注点无关的部分,常常发生在核心关注点的多处,而各处基本相似,如日志 、权限等。
- u 将Cross-cutting concern设计为通用,不介入特定业务对象的一个职责清楚的Aspect对象,这就是Aspect-oriented programming,缩写即AOP.
AOP并不会取代OOP,而是作为OOP的补充。
- u AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
2 AOP有关术语
- u Advice (通知).
Aspect的具体实现称之为Advice(处理).上例中DynaProxyHandler类中invoke方法就是Advice的一个具体实现.
- u Joinpoint(连接点)
Advice在应用程序中被执行的时机称为Joinpoint(连接点),这个时机可能是某个方法被执行之前或之后(或两者都有).
- u Pointcut(切入点)
一组连接点的集合,用于指定哪些Aspect在哪些Joinpoint中织入到应用程序之上.
- u Target(目标对象)
一个Advice被应用的目标对象.(被AOP框架进行增强处理的对象)
- u Weave(织入)
Advice被应用至目标对象之上的过程称这为织入(Weave)
- u AOP代理(AOP Proxy):
由AOP框架创建的目标对象的代理对象。是被插入了advice的Target Object 。
- u 引入:
将方法者字段添加到被处理的类中。
术语图解
3 spring AOP框架的实现方式
Spring中的AOP代理由Spring的Ioc容器负责生成、管理,其依赖关系也由Ioc容器负责管理。
AOP编程只需要程序员参加3个部分:
- u 定义普通业务组件
- u 定义切入点
- u 定义增强处理 (定义切面)
进行AOP的关键就是定义切入点和增强处理。一旦定义了合适的切入点和增强处理,AOP框架将会自动生成AOP代理。
通常采用AspectJ方式来定义切入点和增强处理,在这种方式下,Spring有如下两种选择来定义切入点和增强处理:
- 基于注解(Annotation)方式:使用@aspect、@Pointcut等Annotation来标注切入点和增强处理。
- 基于XML配置文件的管理方式:使用spring配置文件来定义切入点和增强处理
3.1 通过xml配置的方式实现
加入jar包: aopalliance-1.0.jar,aspectjweaver.jar
Log.java前置通知
public class Log implements MethodBeforeAdvice{ /** * @param method 被调用的方法对象,如addUser()方法 * @param args 被调用的方法的参数 * @param target 被调用的方法的目标对象 */ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("执行"+target.getClass().getName()+"的 "+method.getName()+"方法"); } }
afterLog.java后置通知
public class AfterLog implements AfterReturningAdvice{ /** * 目标方法执行后执行的通知 * returnValue 返回值 * method 被调用的方法对象 * args 被调用方法对象的参数 * target 被调用方法对象的目标对象 */ @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("after "+target.getClass().getName()+" 的 "+method.getName()+" 被执行"); } }
UserService
public interface UserService { public void getUser(); public void addUser(); public void delUser(String ss); public void updUser(); }
UserServiceImpl
public class UserServiceImpl implements UserService{ @Override public void getUser() { System.out.println("get user"); } @Override public void addUser() { System.out.println("add user"); } @Override public void delUser( String ss) { System.out.println("delete user="+ss); } @Override public void updUser() { System.out.println("update user"); } }
Beans.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" 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"> <bean name="userService" class="com.silvan.service.UserServiceImpl"></bean> <!-- 定义切面 --> <bean name="log" class="com.silvan.log.Log"></bean> <bean name="afterLog" class="com.silvan.log.AfterLog"/> <aop:config> <!-- aop:pointcut指定哪些对象的哪些方法订阅切面, expression指定该切入点关联的切入点表达式 id指定该切入点的标识符--> <aop:pointcut expression="execution(* com.silvan.service.*.*(..))" id="pointcut"/> <!--aop:advisor指定切面关联哪个切入点 advice-ref指定切面 pointcut-ref指定切入点 --> <aop:advisor advice-ref="log" pointcut-ref="pointcut" /> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
Test
public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); UserService us = (UserService) ac.getBean("userService"); // us.delUser("zhaoliyin"); us.updUser(); } }
使用log4j输出日志:
加入jar包:log4j-1.2.14.jar
配置文件:log4j.properties
public class Log implements MethodBeforeAdvice{ private Logger logger = Logger.getLogger(Log.class) ; @Override public void before(Method method, Object[] args, Object target) throws Throwable { logger.info("执行"+target.getClass().getName()+"的 "+method.getName()+"方法"); } }
3.2 通过注释的方式实现
修改配置文件
<?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" 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"> <aop:aspectj-autoproxy/> <bean name="userService" class="com.silvan.service.UserServiceImpl"></bean> <bean name="log" class="com.silvan.log.Log"></bean> </beans>
修改切面内容
import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class Log{ @Pointcut("execution(* com.silvan.service.*.*(..))") private void anyMethod(){}//定义一个切入点 @Before("anyMethod() && args(name)") public void before(String name){ System.out.println("前置通知 "+name); } @AfterReturning("anyMethod()") public void doAfter(){ System.out.println("后置通知"); } }