AOP与SpringAOP
AOP(Aspect Oriented Programming),面向切面编程,通过预编译和动态代理实现的一种技术。也是Spring框架的一部分利用AOP可以对业务逻辑的各个部分进行隔离,各部分业务逻辑明确耦合性降低,提高程序的可重用性,同时这也是动态代理的好处。
AOP是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许定义纵向的关系,但并不适合定义横向的关系。
SpringAOP的要素
横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
通知(Advice):执行增强的方法,也就是对目标方法拦截后所作的处理
目标(Target):被通知对象
代理(Proxy):目标对象应用通知后的对象
切面(Aspect):通常是一个类,里面可以定义通知及通知的方式
切入点(Pointcut):没必要区分与JointPoint(连接点),拦截这个词相当恰当,定义为切入点的方法在执行过程中会被拦截,执行增强。
环境搭建
导入依赖aop
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
配置文件添加约束
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
自定义类的实现
以用户业务逻辑为例
用户业务逻辑接口
public interface UserService {
public void create();
public void delete();
public void retrieve();
public void update();
}
用户业务逻辑实现(目标对象)
public class UserServiceImp implements UserService{
@Override
public void create() {
System.out.println("创建了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void retrieve() {
System.out.println("检索了一个用户");
}
@Override
public void update() {
System.out.println("更新了一个用户");
}
}
创建日志(切面)
public class Log {
public void beforeLog(){
System.out.println("执行前置日志");
}
public void afterLog(){
System.out.println("执行后置日志");
}
}
编写applicationContext.xml文件
注册bean
<!--注册bean-->
<bean id="log" class="com.jas.Log"/>
<bean id="userServiceImp" class="com.jas.service.UserServiceImp"/>
<!--配置aop:定义切面,定义切入点,配置通知切入切入点的方式-->
<aop:config>
<!--定义切面-->
<aop:aspect ref="log">
<!--expression表达式定义切入点,即在目标对象中需要被拦截的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.jas.service.UserServiceImp.*(..))"/>
<!--定义切面中的通知围绕切点的方式:before,after,after-returning,after-throwing,around-->
<aop:before pointcut-ref="pointcut" method="beforeLog"/>
<aop:after pointcut-ref="pointcut" method="afterLog"/>
</aop:aspect>
</aop:config>
测试
@Test
public void Test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意使用接口: Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
UserService serviceImp = (UserService) context.getBean("userServiceImp");
//调用目标对象方法
serviceImp.create();
}
//output:
执行前置日志
创建了一个用户
执行后置日志
注解实现
//定义切面
@Aspect
public class AnnotationLog {
//切入点
@Before("execution(* com.jas.service.UserServiceImp.*(..))")
public void beforeLog(){
System.out.println("执行前置日志");
}
@After("execution(* com.jas.service.UserServiceImp.*(..))")
public void afterLog(){
System.out.println("执行后置日志");
}
}
配置xml文件注册bean和注解支持
<!--注册bean-->
<bean id="userServiceImp" class="com.jas.service.UserServiceImp"/>
<bean id="annotationLog" class="com.jas.AnnotationLog"/>
<!--增加aop注解支持-->
<aop:aspectj-autoproxy/>
测试
@Test
public void Test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意使用接口: Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
UserService serviceImp = (UserService) context.getBean("userServiceImp");
//调用目标对象方法
serviceImp.create();
}
//output:
执行前置日志
创建了一个用户
执行后置日志
继承Spring API实现
以这种方式类似于直接定义通知,无需切面
前置日志
public class BeforeLog implements MethodBeforeAdvice {
/*
o:目标对象
method:切入点,即被通知的方法
objects:被通知方法的参数
这三个参数也符合动态代理的参数
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getCanonicalName() + "的" + method.getName() + "方法执行了前置日志");
}
}
后置日志
public class AfterLog implements AfterReturningAdvice {
/*
* o1:目标对象
* o:返回对象
* */
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(o1.getClass().getCanonicalName()+"的"+method.getName()+"方法执行了后置日志");
}
}
注册bean
<!--注册bean-->
<bean id="userServiceImp" class="com.jas.service.UserServiceImp"/>
<bean id="afterLog" class="com.jas.AfterLog"/>
<bean id="beforeLog" class="com.jas.BeforeLog"/>
配置aop
<!--aop配置-->
<aop:config>
<!--无需切面-->
<aop:pointcut id="pointcut" expression="execution(* com.jas.service.UserServiceImp.*(..))"/>
<!--将通知切入切点-->
<aop:advisor pointcut-ref="pointcut" advice-ref="afterLog"/>
<aop:advisor pointcut-ref="pointcut" advice-ref="beforeLog"/>
</aop:config>
测试
@Test
public void Test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意使用接口: Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
UserService serviceImp = (UserService) context.getBean("userServiceImp");
//调用目标对象方法
serviceImp.create();
}
//output:
com.jas.service.UserServiceImp的create方法执行了前置日志
创建了一个用户
com.jas.service.UserServiceImp的create方法执行了后置日志