前言
最近,想给一些还不明白Aop如何使用的伙伴们,浅谈一下这个Aop,在日常开发生产中,该如何理解和使用。
一、Aop的基础
首先,我们大家都知道,spring有俩大核心ioc和aop;今天我们主要来聊一下Aop,它也被称为“面向切面编程”,什么意思呢,按我个人的理解,它就是一个公家的东西,谁需要它,就配置一下相关参数,然后就可以使用它了,按我个人的学习习惯,我不是很喜欢像其他博主一样,讲一大堆的概念,然后你花了很多时间和精力去看,然后看到后面,又感觉啥都没领悟到,然后你出去面试,面试官叫你讲解一下Aop,你只会生硬套概念,无法通俗易懂地表达出来。
看百度全科的解释是这样的:
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
其实,对于初学者来说,这个百度的解释,说了跟没说一样,压根不懂。那么对于作者来说,我个人认为呀,其实,敲代码来得痛快点,从代码中去理解,然后从代码中去表达出来,是最快最好的入门方法。
二、Aop的第1种实现方式
接下来,我直接通过代码来讲述Aop,首先,我先讲解第一种实现方式----配置;
由图所示,我在service包里面分别创建了一个消费者接口,和消费者实现类,这是第1步;
第2步呢,是在resources资源包下的test包里,创建一个applicationContext.xml的配置文件;
第3步呢,是在test包下创建一个测试类MyTest,待会主要是在这里测试。
好了,上面就是主要的目录结构了。
接下来,我先给出代码先,在一边讲解
1.先创建相关实例
消费者接口代码如下(示例):
package com.myy.service;
public interface CustomerService { // 消费者
void buy();
void eat();
void select();
}
首先,我们先创建一个消费者接口,为什么我们要创建它呢,因为在最后的测试类MyTest中,我们需要使用这个消费者接口的原始方法,使得它被实现类所实现。
消费者实现类代码如下(示例):
package com.myy.service;
public class CustomerServiceImpl implements CustomerService{ // 消费者实现类
@Override
public void buy() {
System.out.println("购买了一个面包");
}
@Override
public void eat() {
System.out.println("吃了一个面包");
}
@Override
public void select() {
System.out.println("选择了一款面包");
}
}
这里是实现了消费者的接口,然后重写了消费者的接口的原始的方法
log包下的AfterLog类的代码如下(示例):
package com.myy.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
/**
* 第一个参数为要执行目标对象的方法
* 第二个参数是
* 第三个参数是目标对象
* 第四个参数是返回值
* @param method
* @param args
* @param target
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue,Method method, Object[] args, Object target) throws Throwable{
System.out.println(target.getClass().getName() + "的方法"+method.getName()+";返回结果是:"+returnValue);
}
}
这里就是精华使用的地方了,也就是我所说的公共的东西,这里是log包下的类,也就是开发中所谈到的日志了,先看这个类,这个类,实际是实现了spring里面的AfterReturningAdvice接口,这是框架里面的接口,咱们就用就好了,没啥好解释的;然后重写它里面的一个方法afterReturning,根据二级英文,大概知道它是啥意思,就是完成后再返回嘛。
log包下的BeforeLog类的代码如下(示例):
package com.myy.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeLog implements MethodBeforeAdvice {
/**
* 第一个参数为要执行目标对象的方法
* 第二个参数是
* 第三个参数是目标对象
* @param method
* @param args
* @param target
* @throws Throwable
*/
@Override
public void before(Method method,Object[] args,Object target) throws Throwable{
System.out.println(target.getClass().getName() + "的方法"+method.getName()+"被执行");
}
}
同样,这里也是log包下的一个类,也同样实现了框架下面的MethodBeforeAdvice的接口,框架自带的哈,咱们不纠结它哈,同样的,它也重写了一个方法,在之前就返回,大概就这个意思。
2.配置applicationContext.xml文件
applicationContext.xml代码如下(示例):
第1种方式:配置 第1步,先注册bean,将实体类进行注册,注意,不是接口注册哈
<bean id="customerServiceImpl" class="com.myy.service.CustomerServiceImpl"></bean>
<bean id="beforeLog" class="com.myy.log.BeforeLog" ></bean>
<bean id="afterLog" class="com.myy.log.AfterLog" ></bean>
第2步,配置Aop切面
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.myy.service.CustomerServiceImpl.*(..))"/>
<!-- pointcut切入点 expression作为表达式,参数为切入的位置,CustomerServiceImpl.*表示类中所有方法都切入,.*(..)俩个点表示不指定参数-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
<!-- pointcut-ref 切入点参考 advice-ref 建议参考号-->
</aop:config>
由图中所指的,咱们,Aop实现的第1步,就是要在这里配置这些参数,为什么要配置呢?待会从后面的测试类,就可以明白了,我先提前说一下,就是在测试类中,会创建一个这个配置类,然后,通过这个applicationContext.xml配置文件,查找相关的Aop的信息,比如,第1个参数<bean>,由这个bean中的参数,就可以知道了分别有3个类在里面注册了,分别是customerServiceImpl类、beforeLog类、afterLog类,就可以通过这找到原始的这个类的位置进行调用了;接下来是,第2个参数<aop:config>,这个就是aop的相关配置了,从图中的配置知道:
第1行,是表明在哪个类中哪个方法来进行增加日志的功能的切入,很明显就是customerServiceImpl类嘛。
第2行,就是公家的东西了嘛,就是之前所述的log包下的这个BeforeLog类的调用了,这个的意思就是,在调用这个ustomerServiceImpl类(消费者实现类时的某个方法之前),就先调用这个BeforeLog类的before方法先输出日志先,就这个意思。
第3行,也是公家的东西,也是之前所述的log包下的这个AfterLog类的调用了,在调用这个ustomerServiceImpl类(消费者实现类时的某个方法完成之后),就调用这个AfterLog类的afterReturning方法后输出日志,就这个意思。
3.实例来测试
测试类MyTest代码如下(示例):
package com.myy.test;
import com.myy.service.CustomerService;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args){
// 1.第一步,加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.第二步,通过目标的bean id,获得代理对象
CustomerService proxy = (CustomerService)context.getBean("customerServiceImpl");
// System.out.println("顾客接口调用吃的方法====");
proxy.eat();
}
}
测试类中也讲解一下吧,这个第1步,其实就是我之前所示的配置类,new一个类,来获取这个applicationContext.xml配置文件里面的内容。
最后测试的效果如下:
这个就是最后测试出来的打印结果了,首先,BeforeLog类的before方法先被打印出来;接着是customerServiceImpl类(消费者实现类)的eat方法,最后才是AfterLog类的afterReturning方法被打印出来。
总结
本章,主要是对使用配置applicationContext.xml文件来实现Aop,进行了深入的讲解,然后通过我们平常java开发过程中使用到的记录日志功能的这个实例,进行了通俗易懂的讲解面向切面编程。