什么是AOP?
AOP(Aspect-Oriented Programming)面向切面编程。可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。在我看来,这种方式的作用就相当于语文考试写作文,在写完一句话后你发现少了东西,要在其中添加几个字,然后你就在一个字的前面或后面拉了添加符号,写上了缺少的文字。在前面和后面添加这就对应了前置增强和后置增强,当然AOP的原理和机制不可能是这样简单的,这只是它运行起来看起来像这样而已。
理解AOP你需要知道几个东西
1.切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;
2.连接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;
3.通知(Advice):通知就是在切面的某个连接点上执行的操作,也就是事务管理、日志管理等;
4.切入点(Pointcut):切入点就是描述某一类选定的连接点,也就是指定某一类要织入通知的方法;
5.目标对象(Target):就是被AOP动态代理的目标对象;
让我们先看一段使用注解实现的例子
import org.springframework.stereotype.Service;
@Service
public class CalculatorService implements ICalculatorService {
public int mul(int a,int b) {
return a*b;
}
public int div(int a,int b) {
return a/b;
}
}
我们先定义一个小计算器然后通过@Service标注将这个类交给Spring容器管理
同时在Spring的XML中加入
<context:component-scan base-package="com.jd"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
如果创建目标对象的目标类中的方法与AspectJ切面中切入点表达式匹配,则自动为该目标对象生成代理对象,该代理对象默认使用JDK动态代理,
@Aspect
@Component
public class CalculatorAspect {
//前置增强。
@Before("execution(public int com.jd.CalculatorService.*(..))")
public void b(JoinPoint jp) {
Object []args = jp.getArgs();
Signature signature = jp.getSignature();
String name = signature.getName();
System.out.println("The "+name+" method begin");
System.out.println("The "+name+" method args:["+args[0]+","+args[1]+"]");
}
}
@Aspect是将该类声明为切面类
@component把该类放入IOC容器中
@Before("execution(public int com.jd.CalculatorService.*(..))")
@Before这个就是通知,一共五个。befor,after,afterRunturning,afterThorwing,around
"execution(public int com.jd.CalculatorService.*(..))"这个筛选条件就是切点,它会将com.jd.CalculatorService.*(..))中放回值为int的所有符合条件的类筛选出来
切面即通知+切点
连接点:符合上面筛选条件的方法就是连接点
此时我们编写一个测试类
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("app.xml");
//执行该行代码时创建IOC容器,由于CalculatorService类中方法与CalculatorAspect切面类中切入点表
//所以默认情况下IOC自动为CalculatorService类创建代理类并创建相应的代理对象,
//如果采用懒加载方式创建CalculatorService对象,则在获取对象时自动为该对象创建代理对象。
ICalculatorService calculatorService = applicationContext.getBean(ICalculatorService.class);
System.out.println(calculatorService.div(2, 2));
applicationContext.close();
}
}
运行..。此时,前置增强类的输出先于程序调用输出显示出来。
下面看另一种使用XML实现AOP的方法
我们将刚才的CalculatorAspect中的注解删掉。这里我建了另一个类来
public class MethodAspect {
public void before(JoinPoint jp) {
Signature signature = jp.getSignature();
String name = signature.getName();
System.out.println("The "+name+" method begin");
}
}
可以看到这个方法与之前的befor增强相似
然后我们在XML中将 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 删去,并添加上下面这一段代码
<bean id="argAspect" class ="com.jd.aspect.ArgAspect"></bean>
<bean id ="methodAspect" class = "com.jd.aspect.MethodAspect"></bean>
<!-- 配置AOP-->
<aop:config>
<!--配置切点表达式-->
<aop:pointcut expression="execution(public int com.jd.CalculatorService.*(..))" id="pointCut"/>
<!--配置切面及增强类型-->
<aop:aspect ref="methodAspect">
<aop:before method="before" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
此时我们返回刚才的Test类,依然执行刚才的测试代码。