一、AOP的基本概念:
1、什么是aop:
AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,安全和缓存等。在不改变原有的逻辑的基础上,增加一些额外的功能。
AOP可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
2、AOP的相关概念:
(1)横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
(2)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(3)JointPoint(连接点):连接点是一个虚拟的概念,程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。
(4)Advice(通知):AOP在特定的切入点上执行的增强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕)
(5)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式。如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。
(6)weaveing(织入):将切面应用到目标对象并创建新的代理对象创建的过程
(7)introduction(引入):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
(8)AOP代理(AOP Proxy):AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
(9)目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO
3、Advice通知类型介绍:
(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可
(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值
(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名
来访问目标方法中所抛出的异常对象
(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式
(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint
4、AOP使用场景:
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging 调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence 持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务
二. AOP四种方式使用
a.基于代理的AOP
1.定义一个司机接口
public interface Driver {
//启动开车
void start();
}
2.定义一个实现类
public class DriverImpl implements Driver {
@Override
public void start() {
System.out.println("司机正在开车.........");
}
}
3.定义一个自动检测类,用于自动检测车辆设备有无损坏。这个类并不涉及核心操作。
public class AutoDetected implements MethodBeforeAdvice, AfterReturningAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("开车前,系统检测没有问题可以放心驾驶");
}
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("行程结束,系统自动检测驾驶过程中没有对车辆造成损坏");
}
}
4.定义Spring核心配置文件application.xml配置AOP
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<!-- 定义被代理者 -->
<bean id="driver" class="com.ljm.aopTest.DriverImpl"></bean>
<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
<bean id="autoDetected" class="com.ljm.aopTest.AutoDetected"></bean>
<!-- 定义切入点位置,这里定义到了start方法上 -->
<bean id="operatePointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*start"></property>
</bean>
<!-- 使切入点与通知相关联,完成切面配置 -->
<bean id="timeHandlerAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="autoDetected"></property>
<property name="pointcut" ref="operatePointcut"></property>
</bean>
<!-- 设置代理 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理的对象 -->
<property name="target" ref="driver"></property>
<!-- 使用切面 -->
<property name="interceptorNames" value="timeHandlerAdvisor"></property>
<!-- 代理接口 -->
<property name="proxyInterfaces" value="com.ljm.aopTest.Driver"></property>
</bean>
</beans>
5.定义测试类
public class Test {
public static void main(String[] args){
ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");
Driver driver=(Driver) appCtx.getBean("proxy");
driver.start();
}
}
6.测试结果打印
开车前,系统检测没有问题可以放心驾驶
司机正在开车.........
行程结束,系统自动检测驾驶过程中没有对车辆造成损坏
b.纯POJO切面(纯粹通过aop:fonfig标签配置)
1.定义一个司机接口
public interface Driver {
//启动开车
void start();
}
2.定义一个实现类
public class DriverImpl implements Driver {
@Override
public void start() {
System.out.println("司机正在开车.........");
}
}
3.定义一个自动检测类,用于自动检测车辆设备有无损坏。这个类并不涉及核心操作。
public class AutoDetected {
public void before() {
System.out.println("开车前,系统检测没有问题可以放心驾驶");
}
public void afterReturning() {
System.out.println("行程结束,系统自动检测驾驶过程中没有对车辆造成损坏");
}
}
4.定义配置文件
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="AutoDetected" class="com.ljm.aopTest2.AutoDetected"/>
<bean id="DriverImpl" class="com.ljm.aopTest2.DriverImpl"/>
<aop:config>
<aop:aspect ref="AutoDetected">
<aop:before method="before" pointcut="execution(* com.ljm.aopTest2.DriverImpl.start(..))" />
<aop:after method="afterReturning" pointcut="execution(* com.ljm.aopTest2.DriverImpl.start(..))" />
</aop:aspect>
</aop:config>
</beans>
5.定义测试类
public class Main
{
@Test
public void test(){
ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");
Driver driver=(Driver) appCtx.getBean("DriverImpl");
driver.start();
}
}
6.测试结果打印
开车前,系统检测没有问题可以放心驾驶
司机正在开车.........
行程结束,系统自动检测驾驶过程中没有对车辆造成损坏
c.@AspectJ注解驱动的切面
1.定义一个司机接口
public interface Driver {
//启动开车
void start();
}
2.定义一个实现类
@Component
public class DriverImpl implements Driver {
@Override
public void start() {
System.out.println("司机正在开车.........");
}
}
3.定义一个自动检测类,用于自动检测车辆设备有无损坏。这个类并不涉及核心操作。
@Aspect
@Component
public class AutoDetected {
@Before("execution(* com.ljm.aopTest2.DriverImpl.start(..))")
public void before() {
System.out.println("开车前,系统检测没有问题可以放心驾驶");
}
@AfterReturning("execution(* com.ljm.aopTest2.DriverImpl.start(..))")
public void afterReturning() {
System.out.println("行程结束,系统自动检测驾驶过程中没有对车辆造成损坏");
}
}
4.定义配置类
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.ljm.aopTest2")
public class AOPConfig {
}
5.定义测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AOPConfig.class)
public class Main
{
@Autowired
private DriverImpl driverImpl;
@Test
public void test(){
driverImpl.start();
}
}
6.测试结果打印
开车前,系统检测没有问题可以放心驾驶
司机正在开车.........
行程结束,系统自动检测驾驶过程中没有对车辆造成损坏
d.注入式AspectJ切面
driver和实现类不变。
1.定义通知类
public class AutoDetected {
public void before() {
System.out.println("开车前,系统检测没有问题可以放心驾驶");
}
public void afterReturning() {
System.out.println("行程结束,系统自动检测驾驶过程中没有对车辆造成损坏");
}
}
2.定义切面
public aspect DriverAspect {
private AutoDetected autoDetected;
public void setAutoDetected(AutoDetected autoDetected) {
this.autoDetected = autoDetected;
}
//定义DriverImpl构造器切点和前置通知
pointcut driver():execution(* com.ljm.aopTest2.DriverImpl.start(..));
before() : driver() {
autoDetected.before();
}
after() returning() : driver(){
autoDetected.afterReturning();
}
}
3.定义spring配置文件。
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="AutoDetected" class="com.ljm.aopTest2.AutoDetected"/>
<!--Spring需要通过静态方法aspectOf获得audience实例,Audience切面编译后的class文件附在文末-->
<bean class="com.ljm.aopTest2.DriverAspect" factory-method="aspectOf">
<property name="AutoDetected" ref="AutoDetected" /><!--通过Spring把协作的bean注入到切面中-->
</bean>
<bean id="DriverImpl" class="com.ljm.aopTest2.DriverImpl"/>
</beans>
4.定义测试类
public class Main
{
@Test
public void test(){
ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");
Driver driver=(Driver) appCtx.getBean("DriverImpl");
driver.start();
}
}
5.测试结果打印
开车前,系统检测没有问题可以放心驾驶
司机正在开车.........
行程结束,系统自动检测驾驶过程中没有对车辆造成损坏