什么是面向切面编程?
将系统逻辑定义为切面,使业务逻辑不需要关注我们的系统逻辑的实现,由切面来负责系统逻辑的具体实现
Spring AOP 术语
> 通知 (Advice)
通知定义了切面是什么,以及什么时候使用切面。
- 前置通知 (Befor) : 目标方法调用前调用通知
- 后置通知 (After) : 目标方法调用后调用通知
- 返回通知 (After-returning) : 目标方法返回成功后调用通知
- 环绕通知 (Around) : 目标方法调用前与调用后都会调用通知
- 异常通知 (After-throwing) : 目标方法抛出异常后调用通知
> 连接点 (Join point)
链接点是指在应用执行过程中能够插入切面的一个具体的点。
> 切点 (Poincut)
切点定义了何处使用通知。定义匹配通知所要织入的一个或多个连接点。通常使用明确的类和方法名,或是 利用正则表达式配。
> 切面 (Aspect)
切面就是通知和切点的结合
> 织入 (Weaving)
把切面应用到目标对象,并创建新的代理对象的过程。
Spring 支持的 AOP
1.基于代理方式切面
2.纯POJO切面 -->常用
3.注解Aspect切面 -->常用
4. 注入式AspectJ切面
Spring 基于代理方式
MethodBeforAdvice :前置拦截器
befor (Method method, Object[] args, Object target) throws Throwable {
method : 被代理的方法名
args : 被代理方法的参数数组
target : 具体被代理的对象
}
AfterReturningAdvice :后置拦截器
afterReturning (Method method, Object[] args, Object target) throws Throwable {
returnValue : 被代理方法的返回值
method : 被代理的方法名
args : 被代理方法的参数数组
target : 具体被代理的对象
}
MethodInterceptor :环绕拦截器
invoke (MethodInvacation invacation) throws Throwable {
invacation.proceed() : 执行被代理的具体方法
invacation.getMethod() : 被代理的方法名
invacation.getArguments() : 被代理方法的参数数组
invacation.getThis() : 具体被代理的对象
}
applicationContext配置信息
<?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:c="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<bean id="Student" class="com.prosay.entity.Student">
<property name="age" value="18"></property>
<property name="name" value="刘亿"></property>
</bean>
<bean id="StudentService" class="com.prosay.service.StudentService">
<property name="student" ref="Student"></property>
</bean>
<bean id="LogAspect" class="com.prosay.aspect.LogAspect"></bean>
<!-- 切点 -->
<bean id="StudentAspect" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理的接口 -->
<property name="interfaces">
<value>com.prosay.service.impl.StudentServiceImpl</value>
</property>
<!-- 实现类 -->
<property name="target" ref="StudentService"></property>
<!-- 通知 -->
<property name="interceptorNames">
<list>
<value>LogAspect</value>
</list>
</property>
</bean>
</beans>
LogAspect类
package com.prosay.aspect;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
public class LogAspect implements MethodBeforeAdvice,AfterReturningAdvice,MethodInterceptor{
//前置通知
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("开始");
}
//后置通知
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("关闭");
}
//环绕通知
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("1");
invocation.proceed();
System.out.println("2");
return null;
}
}
Demo类
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.prosay.service.impl.StudentServiceImpl;
public class Demo01 {
public static void main(String[] args) {
AbstractApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//这里使用的是代理对象
StudentServiceImpl ssi = (StudentServiceImpl) app.getBean("StudentAspect");
ssi.insertStudent();
}
}
纯POJO代理
通知配置:
<aop:pointcut /> : 配置一个切点
<aop:befor /> : 配置前置拦截器
<aop:after /> : 配置后置拦截器
<aop:after-throwing /> : 配置后置异常拦截器
<aop:after-returning /> : 配置后置正常拦截器
<aop:around /> : 配置环绕拦截器
切面参数获取:
ProceedingJoinPoint.proceed() : 执行被代理的具体方法
JoinPoint.getSignature().getName() : 被代理的方法名
JoinPoint.getArgs() : 被代理方法的参数数组
JoinPoint.getTarget : 具体被代理的对象
applicationContext配置文件
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
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-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<bean id="Student" class="com.pojo.entity.Student">
<property name="age" value="18"></property>
<property name="name" value="刘亿"></property>
</bean>
<bean id="StudentService" class="com.pojo.service.StudentService">
<!-- 通过构造函数注入 -->
<constructor-arg ref="Student"></constructor-arg>
</bean>
<!-- 代理类 -->
<bean id="LogAspect" class="com.pojo.aspect.LogAspect"></bean>
<!-- aop配置 -->
<aop:config>
<!-- 切面 -->
<aop:aspect ref="LogAspect">
<!-- 切点 ** 什么都行 ..参数 -->
<aop:pointcut id="Mypointcut" expression="execution(** com.pojo.service.StudentService.insertStudent(..))"/>
<!-- 通知方式 -->
<aop:after method="after" pointcut-ref="Mypointcut"/>
<aop:before method="before" pointcut-ref="Mypointcut"/>
<aop:around method="around" pointcut-ref="Mypointcut"/>
</aop:aspect>
</aop:config>
</beans>
LogAspect类
package com.pojo.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class LogAspect {
public void before() {
System.out.println("开始切面");
}
public void after() {
System.out.println("结束切面");
}
public void around(ProceedingJoinPoint point) {
before();
try {
point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
after();
}
}
Test类
package com.pojo;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.pojo.service.StudentService;
import com.pojo.service.impl.StudentServiceImpl;
public class Test {
public static void main(String[] args) {
AbstractApplicationContext app = new ClassPathXmlApplicationContext("com\\pojo\\applicationContext.xml");
//1.使用配置中的名字 StudentService.class会报错
//2.StudentServiceImpl 使用接口去接收 生成的是代理对象
StudentServiceImpl ss = (StudentServiceImpl) app.getBean("StudentService");
ss.insertStudent();
}
}
Spring 注解AOP
通知配置:
@Pointcut : 切点配置
@Befor : 前置拦截器注解
@After : 后置拦截器注解
@AfterThrowing : 后置异常拦截器注解
@AfterReturning : 后置正常拦截器注解
@Around : 环绕拦截器注解
applicationContext配置文件
<?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:c="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:component-scan base-package="com.annotation"></context:component-scan>
</beans>
LogAspect类
package com.annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Component
@Aspect
//启动代理
@EnableAspectJAutoProxy
public class LogAspect {
//这种写法简单
@Pointcut("execution(** com.annotation.StudentService.insertStudent(..))")
public void point() {}
@Before("point()")
public void before() {
System.out.println("开始切面");
}
@After("point()")
public void after() {
System.out.println("结束切面");
}
@Around("point()")
public void around(ProceedingJoinPoint point) {
before();
try {
point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
after();
}
}
获得参数和使用
@Pointcut("execution(** com.prosay.aop.aspectj.Computer.work(..)) && args(arg)")
@Around("aopDemo(arg)")
public void aroundMethod(ProceedingJoinPoint proc,Person arg){}
Test类
package com.annotation;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
AbstractApplicationContext app = new ClassPathXmlApplicationContext("com\\annotation\\applicationContext.xml");
//1.使用配置中的名字 StudentService.class会报错
//2.StudentServiceImpl 使用接口去接收 生成的是代理对象
//3.动态代理的名字首字母小写
StudentServiceImpl ss = (StudentServiceImpl) app.getBean("studentService");
ss.insertStudent();
}
}