1、什么是AOP?
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,就是在不影响原来业务的情况下动态的新增功能。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2 、Aop在Spring中的作用
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 。
我们一般常用的就是前置通知Before和后置通知After。
好,大家现在看不懂没关系,我们等下写个程序测试一下就了解了~
3、使用Spring实现AOP!
3.1、方式一 : 配置增强类
3.1.1、首先,我们创建一个maven项目,导入aop的依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver
aop所需要的依赖
-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!--这个是测试所需要的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
3.1.2、写实现接口和实现类
package com.gang.service;
/*
Description:实现接口
Author:32259
Time:2020二月2020/2/2512:44
*/
//用户业务
public interface UserSerevice {
public void add();
}
package com.gang.service.impl;
/*
Description:实现类
Author:32259
Time:2020二月2020/2/2512:45
*/
import com.gang.service.UserSerevice;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
public class UserServiceImpl implements UserSerevice {
//正常操作下,我们的业务只能输出一句话 System.out.println("增加成功");
//现在,我想动态的给他增加一些日志,我又不希望改动源代码
//增加用户的功能
//这个是目标方法,我希望在执行目标方法之前执行前置操作 也就是这里
public void add() {
//System.out.println("执行了add()方法"); 想增加这么一个日志
System.out.println("增加成功");
}//在这里执行后置操作
}
3.1.3、写出增强类
注意:不要导错包了!
package com.gang.config;
/*
Description:增强类1
Author:32259
Time:2020二月2020/2/2512:49
*/
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
//后置
public class LogAfter implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(method.getClass().getName()+"中的"+method.getName()+"被调用了,返回值为"+o);
}
}
package com.gang.config;
/*
Description:增强类2
Author:32259
Time:2020二月2020/2/2512:48
*/
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
//前置
public class LogBefore implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(method.getClass().getName()+"中的"+method.getName()+"被调用了");
}
}
这两个类有什么作用呢?只要将这两个类放在Spring里面,他就会自动的给你增加日志!
3.1.4、写Spring的配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注意:aop和context的约束文件一定要导过来-->
<!--注册bean-->
<bean id="userService" class="com.gang.service.impl.UserServiceImpl"/>
<bean id="logBefore" class="com.gang.config.LogBefore"/>
<bean id="logAfter" class="com.gang.config.LogAfter"/>
<!--aop的设置-->
<aop:config>
<!--设置一个切入点 哪些方法要被增强
expression 表达式就是类名的全称 public com.gang.service.impl.UserServiceImpl.add()
我这里这样写是这个包下所有的方法 *.(..)就是所有方法的可变参数
-->
<aop:pointcut id="pc-userservice" expression="execution(* com.gang.service.impl.UserServiceImpl.*(..))"/>
<!--环绕(官方说的通知)-->
<!--前置增强-->
<aop:advisor advice-ref="logBefore" pointcut-ref="pc-userservice"/>
<!--后置增强-->
<aop:advisor advice-ref="logAfter" pointcut-ref="pc-userservice"/>
</aop:config>
</beans>
3.1.5、写测试类,查看结果!
/*
Description:测试类
Author:32259
Time:2020二月2020/2/2512:56
*/
import com.gang.service.UserSerevice;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserSerevice userService = (UserSerevice) context.getBean("userService");
userService.add();
}
}
运行程序,得到结果:
说白了,前置通知他就是在你执行你的方法之前,先执行他的方法,后置方法同理~
就是我不去改原来的代码,就新增了我想要的功能,这种思想就是面向切面编程!
我只写了两个增强类,然后通过Spring横切进去,就很方便!
可能会有人说,这些这么多类,还得配置,多麻烦啊,可是你想想,未来你进公司的代码可不像我这个例子一样只有一个方法,这一两个你自己加很方便,要是几百个呢?那粘贴复制也得把人烦死吧!
这个能看得懂吧,aop其实用的 还是反射
3.2、方式二:切面配置
按照刚才的基础来啊,我们稍作改动就可以啦~
切面就是一个类,他里面有我们想要的增强功能!
3.2.1、自定义一个增强类(所谓的切面)
package com.gang.config;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/*
Description:自定义切面类
Author:32259
Time:2020二月2020/2/2513:27
*/
public class DiyPointCut {
public void before(){
System.out.println("在前面执行");
}
public void after(){
System.out.println("在后面执行");
}
}
注意:这只是一个普通的类,那么我们怎么把他变成一个切面呢?往下看…
里面有我随便写的两个方法,只需要把他注入进去就可以了
3.2.2、更改配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.gang.service.impl.UserServiceImpl"/>
<bean id="logBefor" class="com.gang.config.LogBefor"/>
<bean id="logAfter" class="com.gang.config.LogAfter"/>
<!--注册bean-->
<bean id="diyPointCut" class="com.gang.config.DiyPointCut"/>
<aop:config>-->
<!--配置切面:这里需要配置标签来控制增强(注意标签的变换)-->
<aop:aspect ref="diyPointCut">
<!--切入点 就是目标方法-->
<aop:pointcut id="pc-userservice" expression="execution(* com.gang.service.impl.UserServiceImpl.*(..))"/>
<!--这里的方法来自于切面,在切入点前后切入-->
<aop:before method="before" pointcut-ref="pc-userservice"/>
<aop:after method="after" pointcut-ref="pc-userservice"/>
</aop:aspect>
</aop:config>
</beans>
3.2.3、测试
执行成功了!这个方式是不是比第一种方式简单呢~
3.3、方式三:使用注解实现增强类(切面的原理)
他就是把xml文件里的操作全部换成注解来实现,就更加简单啦~
3.3.1、给自定义的切面加上注解
package com.gang.config;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/*
Description:注解实现切面
Author:32259
Time:2020二月2020/2/2513:27
*/
@Aspect//将这个类变成切面
public class DiyPointCut { //注意:注解不要导错包了
//直接使用注解增强(前置)
@Before("execution(* com.gang.service.impl.UserServiceImpl.*(..))")
public void before(){
System.out.println("在前面执行");
}
//后置
@After("execution(* com.gang.service.impl.UserServiceImpl.*(..))")
public void after(){
System.out.println("在后面执行");
}
}
3.3.2、将xml文件中多余的操作删掉
注意:这里需要开启注解支持!
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
<bean id="userService" class="com.gang.service.impl.UserServiceImpl"/>
<bean id="logBefor" class="com.gang.config.LogBefor"/>
<bean id="logAfter" class="com.gang.config.LogAfter"/>
<bean id="diyPointCut" class="com.gang.config.DiyPointCut"/>
</beans>
3.3.3、测试结果
好了,增强成功!
现在大家对这种动态的增加功能的方式理解了嘛~