什么是AOP?
学习Java之前,我们可能经常听到一个词,那就是OOP(Object Oriented Programming),它的名字叫做面向对象编程,它可以说是我们面向对象语言的核心思想,而AOP(Aspect Oriented Programming)面向切面编程,它可以说是OOP的一种延续,主要是因为其可以通过预编译的方式和运行期的动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际上是GOF设计模式的延续,设计模式一直追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。我们常常在业务中需要做一些非业务的事情:如日志,事务,安全等都在我们的每项业务中都要体现,而这些业务和我们的主要业务往往没什么关系,这些代码,我们也常常是前篇一律,直接复制粘贴即可。但是,大量的这种代码对于我们维护程序的时候来说,是及其不方便的,所以,AOP就实现了把这些业务需求于系统需求分开来做,这种机制也称为代理机制。
AOP的专业术语解释
像这样的编程思想,自然有很多个专业的术语,我们可以了解了解。但无论术语有多晦涩,其主要含义还是很简单的
**横切关注点:**跨越应用程序多个模块的方法或功能。即是,与我们的业务逻辑无关但一个完整的业务仍是需要的那部分业务,这些业务就是我们的横切关注点。
**切面:(Aspect)?*横切关注点,被模块化的特殊对象。
**通知(Adivice)?*切面必须要完成的工作。
**目标(Target)?*被通知的对象。
**代理(Proxy)?*向目标对象应用通知后创建的对象。
切入点:切面通知执行的“地点”的定义。
**连接点:**与切入点匹配的执行点
在SpringAop中,通过Advice定义横切逻辑,Spring中支持五种类型的Advice
Spring框架中AOP所体现的功能就是在不改变原有代码的情况下,去增加新的功能。
接下来,我们使用SpringAPI实现AOP(也就是采用通过Spring提供的功能性标签来实现AOP)
首先编写一个业务类
定义一个接口:
public interface Userfunction {
public void add();
public void delete();
public void update();
public void select();
}
写一个用户接口实现类
public class UserImpl implements Userfunction {
public void add() {
System.out.println("我是一个增加的方法");
}
public void delete() {
System.out.println("我是一个删除的方法");
}
public void update() {
System.out.println("我是一个更新的方法");
}
public void select() {
System.out.println("我是一个查找的方法");
}
}
实现MethodBeforeAdvice接口的前置增强类
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
//这里是实现一个MethodBeforeAdvice接口,这个接口规范了Before方法,该方法表示在原始方法前进行的增强添加
public class Log implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
//这里我们模拟的是一个针对于方法的实现的简单日志的实现
//method:要执行的目标对象的方法
//object:要被调用的方法参数
//o:目标对象
System.out.println(o.getClass().getName()+"执行了"+method.getName());
}
}
实现AfterReturningAdvice接口的后置增强类
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
//o:返回值
//method :被调用的方法
//objects:被调用的方法对象的参数
//o1:被调用的目标对象
System.out.println(
"被调用的方法为"+method.getName()+"刚被被执行了,调用者是"
+o1.getClass().getName());
}
}
编写Spring的核心配置文件:
<?xml version="1.0" encoding="UTF-8"?><!--注册bean-->
<bean id="userService" class="pojo.UserImpl"/>
<!--注册日志类的bean-->
<bean id="log" class="log.Log"/>
<bean id="afterLog" class="log.AfterLog"/>
<!--使用spring的aop切入
1.导入约束:
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
2.aop:config
-->
<aop:config>
<!--切入点
expression 表达式,表示要切入的位置
语法:execution([类的修饰符] [类的全路径] [方法] [参数])
-->
<aop:pointcut id="pointcut" expression
="execution(* pojo.UserImpl.*(..))"/>
<!--执行通知,增强-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
编写一个测试类:
import pojo.Userfunction;
public class Test {
@org.junit.Test
public void test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Spring-config.xml");
Userfunction userService = (Userfunction) context.getBean("userService");
userService.add();
userService.delete();
userService.select();
userService.update();
}
}
测试结果:
注意点:进行测试之前,一定要导入AOP织入支持的jar包aspectjweaver
maven依赖导入:
< dependency >
< groupId>org.aspectj< /groupId >
< artifactId>aspectjweaver< /artifactId >
< version>1.8.9< /version>
< /dependency >
当然自己可以去maven 仓库寻找,适合自己的才是最好的。
总结 Spring中的Aop就是将一些公共的业务代码(例如日志,安全,事务等),和业务类(真实对象)结合起来。实现公共业务的重复利用,实现解耦,究其本质,还是使用了动态代理进行的。
-
Spring 还为我们提供了自定义类实现AOP的标签,我们来实现一下‘
同上中实现方式一样,我们先定义一个接口规范:
定义一个接口:public interface Userfunction {
public void add();
public void delete();
public void update();
public void select();
}
写一个用户接口实现类
public class UserImpl implements Userfunction {
public void add() {
System.out.println("我是一个增加的方法");
}
public void delete() {
System.out.println("我是一个删除的方法");
}
public void update() {
System.out.println("我是一个更新的方法");
}
public void select() {
System.out.println("我是一个查找的方法");
}
}
接下来,不同于上一种做法的就是,我们不用在将前置增强方法和后置增强方法写在两个类里面了,而是写一个自定义的类,里面包含了我i们定义的前置增强和后置增强方法。
自定义增强类:
public class MyAop {
public void Before(){
System.out.println("我是一个自定义的前置增强方法");
}
public void After(){
System.out.println("我是一个自定义的后置增强方法");
}
}
编写配置文件:
<?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-->
<bean id="userImpl" class="pojo.UserImpl"/>
<bean id="MyAop" class="log.MyAop"/>
<!--使用spring的aop切入
1.导入约束:
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
2.aop:config
-->
<aop:config>
<!--切入点
expression 表达式,表示要切入的位置
语法:execution([类的修饰符] [类的全路径] [方法] [参数])
-->
<aop:aspect ref="MyAop">
<aop:pointcut id="pointcut" expression
="execution(* pojo.UserImpl.*(..))"/>
<!--执行通知,增强-->
<aop:after method="After" pointcut-ref="pointcut"/>
<aop:before method="Before" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
测试结果:
框架还可以简之又简,那就是Spring还提供了注解实现Aop
-
注解实现Aop
同样,我们的增强对象不变,我们需要编写一个增强的类,使用注解之前,我们需要注意两点: -
需要将类注解为切面
-
在方法上就是切入点增强
同样使用前面的用户接口和用户实现类
定义一个接口:public interface Userfunction {
public void add();
public void delete();
public void update();
public void select();
}
写一个用户接口实现类
public class UserImpl implements Userfunction {
public void add() {
System.out.println("我是一个增加的方法");
}
public void delete() {
System.out.println("我是一个删除的方法");
}
public void update() {
System.out.println("我是一个更新的方法");
}
public void select() {
System.out.println("我是一个查找的方法");
}
}
编写增强类:
package log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Around;
@Aspect
public class AutoAop {
@Before("execution(* pojo.UserImpl.*(..))")
public void star(){
System.out.println("使用注解实现的前置增强");
}
@After("execution(* pojo.UserImpl.*(..))")
public void end(){
System.out.println("使用注解实现的后置增强");
}
@Around("execution(* pojo.UserImpl.*(..))")
public void around(ProceedingJoinPoint jp ) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());//获得执行的切入点
//执行目标方法
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed); //null
}
}
编写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-->
<bean id="UserImpl" class="pojo.UserImpl"/>
<!--注册日志类的bean-->
<bean id="Auto" class="log.AutoAop"/>
<aop:aspectj-autoproxy/>
</beans>
测试结果:
AOP小结
- 本质就是动态代理
- 需要到一个包,用来进行aop织入的包: aspectjweaver
- 注意别遗漏了切面;
- 三种实现AOP的方法
- 使用SpringAPI来实现AOP
- 使用自定义类来实现AOP
- 使用注解实现AOP