AOP是什么?
Aspect Oriented Programming,面向切面编程,它是一种设计方法,通过预编译方法和运行期间动态代理实现,在不改变源代码的基础上,添加新的功能;对业务逻辑的各个部分进行隔离,降低耦合度
什么是动态代理?
动态代理对象是原对象的代理,可以使用原对象的方法,在原方法基础上添加新的功能。
实现动态代理的方式
1.有接口的JDK动态代理:通过Proxy.newProxyInstance方法,第三个参数即将代理的对象,返回一个代理对象。
public class Demo {
/**
* 演示有接口情况加下的动态代理方式
*/
@Test
public void demo1(){
Class[] classes = {UserDao.class};
UserDao o = (UserDao)Proxy.newProxyInstance(Demo.class.getClassLoader(), classes,new InvocationHandlerImpl(new UserDaoImpl()));
o.update();
}
}
class InvocationHandlerImpl implements InvocationHandler{
private Object obj;
InvocationHandlerImpl(Object obj){
this.obj = obj;
}
//这里添加增加的方法的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + "方法执行前做的处理");
Object result = method.invoke(this.obj, args);
System.out.println(method.getName() + "方法执行后的做的处理");
return result;
}
}
2.无接口的CGLIB动态代理:通过继承父类,类似super().method这种形式调用原方法。
各种概念
连接点:可以被增强的方法
切入点:实际被增强的方法
通知:增强的方法中被增加的逻辑
- 前置通知 @Before
- 后置通知 @AfterReturning (有异常则不执行)
- 环绕通知 @Round
- 异常通知 @AfterThrowing
- 最终通知 @After (不管有无异常抛出,都执行)
切面:把通知应用到切入点的过程
切面表达式:各种通知内的value值,execution(访问修饰符.被代理的类的全路径.方法名(..))
@Pointcut:相同切入点的抽取
@Order:在增强类上面添加该注解,值也小优先级越高
基于XML配置实现AOP操作
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:contex="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">
<!--启用使用Spring AOP @AspectJ风格的-->
<aop:aspectj-autoproxy>
</aop:aspectj-autoproxy>
<bean id="xmlBook" class="aop.bean.XmlBook"/>
<bean id="myAspectj" class="aop.aspect.XmlForAspectJ"/>
<aop:config>
<aop:aspect ref="myAspectj">
<aop:pointcut id="point" expression="execution(* aop.bean.XmlBook.*(..))"/>
<!--环绕通知和前置通知,哪个位置在前,就先调用哪个-->
<aop:after method="after" pointcut-ref="point"/>
<aop:around method="around" pointcut-ref="point"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after-returning method="afterReturning" pointcut-ref="point"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="point" throwing="e"/>
</aop:aspect>
</aop:config>
</beans>
动态代理类或者说增强的类:
package aop.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class XmlForAspectJ {
public void before(JoinPoint joinPoint) {
System.out.println("xml 前置通知:" + joinPoint.getSignature().getName());
}
public void after(JoinPoint joinPoint) {
System.out.println("xml 最终通知:" + joinPoint.getSignature().getName());
}
public void afterReturning(JoinPoint joinPoint) {
System.out.println("xml 后置通知:" + joinPoint.getSignature().getName());
}
public void afterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("xml 异常通知:" + joinPoint.getSignature().getName() + ":" + e.getMessage());
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("xml 环绕通知 before..");
Object proceed = pjp.proceed();
System.out.println("xml 环绕通知 after..");
return proceed;
}
}
测试类:
@Test
public void demo3(){
ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
XmlBook xmlBook = (XmlBook) context.getBean("xmlBook");
xmlBook.add();
}
正常执行情况下,输出:
xml 环绕通知 before..
xml 前置通知:add
Xml book add function
xml 最终通知:add
xml 环绕通知 after..
xml 后置通知:add
当代理的方法出现异常时,输出:
xml 环绕通知 before..
xml 前置通知:add
xml 最终通知:add
xml 异常通知:add:/ by zero
基于完全注解方式实现AOP操作
定义配置类:
@Configuration
@ComponentScan(basePackages = "aop")
@EnableAspectJAutoProxy(exposeProxy = true) // 相当于aop.xml中<aop:aspectj-autoproxy/>
public class SpringConfig {
}
定义代理类:
package aop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AnnForAspectJ {
// 声明切入点(实际被增强的方法),这里AnnBook所有的方法都可以被增强
@Pointcut(value = "execution(* aop.bean.AnnBook.*(..))")
public void point(){}
@Before(value = "point()")
public void before(){
System.out.println("Ann before...");
}
@After(value = "point()")
public void after(){
System.out.println("Ann after...");
}
@AfterReturning(value = "point()")
public void afterReturning(){
System.out.println("Ann after returning...");
}
@AfterThrowing(value = "point()")
public void afterThrowing(){
System.out.println("Ann after throwing");
}
@Around(value = "point()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Ann around before..");
Object proceed = pjp.proceed();
System.out.println("Ann around after..");
return proceed;
}
}
测试:
@Test
public void demo2(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
AnnBook annBook = (AnnBook) context.getBean("annBook");
annBook.add();
}