Spring的AOP概念
AOP思想
AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
代理机制
spring的AOP的底层用到两种代理机制:
- JDK的动态代理:针对实现了接口的类产生代理
- Cglib的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强技术生成当前类的子类对象
Java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
JDK动态代理
JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
接口
package cn.Pu1satilla.JDKproxy;
public interface People {
void speak();
void eat(String something);
}
接口实现类
package cn.Pu1satilla.JDKproxy;
public class Actor implements People {
@Override
public void speak() {
System.out.println("绿人");
}
@Override
public void eat(String something) {
System.out.println("吃双黄蛋" + something);
}
}
JDK代理类
package cn.Pu1satilla.JDKproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxy implements InvocationHandler {
// 生成需要代理的目标对象
private Object neededProxy;
// 获取当前目标代理对象
public Object getCurrentProxy(Object currentProxy) {
neededProxy = currentProxy;
return Proxy.newProxyInstance(
neededProxy.getClass().getClassLoader(),
neededProxy.getClass().getInterfaces(),
this
);
}
/**
* 强化方法
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("eat")) {
System.out.println("这里运行强化功能!");
return method.invoke(neededProxy, Arrays.toString(args));
} else
return method.invoke(neededProxy, args);
}
}
Cglib动态代理
Cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。JDK动态代理与Cglib动态代理军事实现Spring AOP的基础。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final 。
Cglib代理
package cn.Pu1satilla.proxy_demo;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
// 定义存放需要Cglib代理的对象
private Object neededProxy;
// 传入需要代理的对象
public CglibProxy(Object currentProxy) {
neededProxy = currentProxy;
}
public Object getCurrentProxy() {
// 创建Cglib核心类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(neededProxy.getClass());
// 设置回调
enhancer.setCallback(this);
// 生成代理
return enhancer.create();
}
/**
* 截取方法,强化方法
*
* @param o
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("强化方法");
return method.invoke(neededProxy, objects);
}
}
测试类
package cn.Pu1satilla.proxy_demo;
public class Demo {
public static void main(String[] args) {
// 获得JDK代理对象
People actorProxy = (People) new JdkProxy().getCurrentProxy(new Actor());
// 调用代理对象方法
actorProxy.eat("香肠");
// 获得Cglib代理对象
CglibProxy cglibProxy = new CglibProxy(new Actor());
People actorCglibProxy = (People) cglibProxy.getCurrentProxy();
// 调用代理对象方法
actorCglibProxy.speak();
}
}
测试结果
这里运行强化功能!
吃双黄蛋[香肠]
强化方法
绿人
aop名词学习
Joinpoint(连接点):目标对象中,所有可以增强的方法
Pointcut(切入点):目标对象,增强的方法
Advice(通知/增强):增强的代码
Target(目标对象):被代理对象
Weaving(织入):将通知应用到切入点的过程
Proxy(代理):将通知植入到目标对象之后,形成代理对象
aspect(切面):切入点+通知
Aop配置以及应用
AOP配置(XML)
导包

目标对象类
package cn.Pu1satilla.AOP;
import org.springframework.stereotype.Component;
@Component("cat")
public class Cat implements Animal {
@Override
public void speak() {
System.out.println("喵喵喵");
}
@Override
public void eat(String animal) {
System.out.println("猫吃" + animal);
}
@Override
public void run() {
System.out.println("猫追耗子");
}
@Override
public void shit() {
System.out.println("猫也拉屎");
int i = 1 / 0;
}
}
通知类
package cn.Pu1satilla.AOP;
import org.aspectj.lang.ProceedingJoinPoint;
public class AopAdvice {
// 前置通知
public void before(){
System.out.println("前置增强=============");
}
// 后置通知
public void afterReturning(){
System.out.println("运行后增强=============(出现异常不会调用)");
}
// 环绕通知
public Object around(ProceedingJoinPoint pjd) throws Throwable {
System.out.println("环绕增强前部分=============");
Object proceed = pjd.proceed();
System.out.println("环绕增强后部分=============");
return proceed;
}
// 异常通知
public void afterException() {
System.out.println("出现异常=============");
}
// 后置通知
public void after(){
System.out.println("运行后增强=============(出现异常也会调用)");
}
}
AOP表达式(配置)
execution(* com.sample.service.impl..*.*(..))
对应符号解释:
| 符号 | 含义 |
|---|---|
| execution() | 表达式主体 |
| 第一个“*”符号 | 标识返回值的类型任意 |
| com.sample.service.impl | AOP所切的服务的包名,即,我们的业务部分 |
| 包名后面的”..“ | 表示当前包及子包 |
| 第二个”*“ | 表示类名,*即所有类。此处可以自定义,下文有举例 |
| .*(..) | 表示任何方法名,括号表示参数,两个点表示任何参数类型 |
配置
设置使用Cglib作为spring的aop代理
<aop:aspectj-autoproxy proxy-target-class="true"/>
配置通知对象(增强方法的对象)
<!--配置通知对象-->
<bean name="AopAdvice" class="cn.Pu1satilla.AOP.AopAdvice"/>
AOP切面配置
<!--配置切面-->
<aop:aspect ref="AopAdvice">
<!--将名为AopAdvice的前置通知织入到名为strength的切入点-->
<aop:before method="before" pointcut-ref="strength"/>
<!--将名为AopAdvice的环绕通知织入到名为strength的切入点-->
<aop:around method="around" pointcut-ref="strength"/>
<!--将名为AopAdvice的后置通知(出现异常不会调用)织入到名为strength的切入点-->
<aop:after-returning method="afterReturning" pointcut-ref="strength"/>
<!--将名为AopAdvice的异常通知织入到名为strength的切入点-->
<aop:after-throwing method="afterException" pointcut-ref="strength"/>
<!--将名为AopAdvice的后置通知织入到名为strength的切入点-->
<aop:after method="after" pointcut-ref="strength"/>
</aop:aspect>
整合
<?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: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">
<!--activities annotation-->
<context:annotation-config/>
<!--扫描指定包-->
<context:component-scan base-package="cn.Pu1satilla.AOP cn.Pu1satilla.domain"/>
<!--配置通知对象-->
<bean name="AopAdvice" class="cn.Pu1satilla.AOP.AopAdvice"/>
<!--指定spring的代理为Cglib-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--进行aop配置-->
<aop:config>
<!--配置切入点表达式:哪些类的哪些方法需要进行增强-->
<aop:pointcut id="strength" expression="execution(* cn.Pu1satilla.AOP.Cat.*(..))"/>
<!--配置切面-->
<aop:aspect ref="AopAdvice">
<!--将名为AopAdvice的前置通知织入到名为strength的切入点-->
<aop:before method="before" pointcut-ref="strength"/>
<!--将名为AopAdvice的环绕通知织入到名为strength的切入点-->
<aop:around method="around" pointcut-ref="strength"/>
<!--将名为AopAdvice的后置通知(出现异常不会调用)织入到名为strength的切入点-->
<aop:after-returning method="afterReturning" pointcut-ref="strength"/>
<!--将名为AopAdvice的异常通知织入到名为strength的切入点-->
<aop:after-throwing method="afterException" pointcut-ref="strength"/>
<!--将名为AopAdvice的后置通知织入到名为strength的切入点-->
<aop:after method="after" pointcut-ref="strength"/>
</aop:aspect>
</aop:config>
</beans>
测试类
package cn.Pu1satilla.AOP;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
//由注解创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource(name = "cat")
private Cat cat;
@Test
public void demo1(){
cat.shit();
}
}
注解配置
导包
目标对象类
package cn.Pu1satilla.AOP;
import org.springframework.stereotype.Component;
@Component("cat")
public class Cat implements Animal {
@Override
public void speak() {
System.out.println("喵喵喵");
}
@Override
public void eat(String animal) {
System.out.println("猫吃" + animal);
}
@Override
public void run() {
System.out.println("猫追耗子");
}
@Override
public void shit() {
System.out.println("猫也拉屎");
int i = 1 / 0;
}
}
通知类
package cn.Pu1satilla.AOP;
import org.aspectj.lang.ProceedingJoinPoint;
public class AopAdvice {
// 前置通知
public void before(){
System.out.println("前置增强=============");
}
// 后置通知
public void afterReturning(){
System.out.println("运行后增强=============(出现异常不会调用)");
}
// 环绕通知
public Object around(ProceedingJoinPoint pjd) throws Throwable {
System.out.println("环绕增强前部分=============");
Object proceed = pjd.proceed();
System.out.println("环绕增强后部分=============");
return proceed;
}
// 异常通知
public void afterException() {
System.out.println("出现异常=============");
}
// 后置通知
public void after(){
System.out.println("运行后增强=============(出现异常也会调用)");
}
}
注解相关
表明该类是通知类,注解配置于类上方
@Aspect
public class AopAdvice {}
配置切入点表达式:哪些类的哪些方法需要在目标对象哪些位置进行增强
前置通知
// 前置通知
@Before("execution(* cn.Pu1satilla.AOP.Cat.*(..))")
public void before(){
System.out.println("前置增强=============");
}
后置通知(出现异常不会调用)
// 后置通知
@AfterReturning("execution(* cn.Pu1satilla.AOP.Cat.*(..))")
public void afterReturning(){
System.out.println("运行后增强=============(出现异常不会调用)");
}
环绕通知
// 环绕通知
@Around("execution(* cn.Pu1satilla.AOP.Cat.speak())")
public Object around(ProceedingJoinPoint pjd) throws Throwable {
System.out.println("环绕增强前部分=============");
Object proceed = pjd.proceed();
System.out.println("环绕增强后部分=============");
return proceed;
}
异常通知
// 异常通知
@AfterThrowing("execution(* cn.Pu1satilla.AOP.Cat.shit())")
public void afterException() {
System.out.println("出现异常=============");
}
后置通知(出现异常也会调用)
// 后置通知
@After("execution(* cn.Pu1satilla.AOP.Cat.*())")
public void after(){
System.out.println("运行后增强=============(出现异常也会调用)");
}
整合
package cn.Pu1satilla.AOP;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
//@Aspect //表明该类是一个通知类
public class AopAdvice {
// 前置通知
@Before("execution(* cn.Pu1satilla.AOP.Cat.*(..))")
public void before(){
System.out.println("前置增强=============");
}
// 后置通知
@AfterReturning("execution(* cn.Pu1satilla.AOP.Cat.*(..))")
public void afterReturning(){
System.out.println("运行后增强=============(出现异常不会调用)");
}
// 环绕通知
@Around("execution(* cn.Pu1satilla.AOP.Cat.speak())")
public Object around(ProceedingJoinPoint pjd) throws Throwable {
System.out.println("环绕增强前部分=============");
Object proceed = pjd.proceed();
System.out.println("环绕增强后部分=============");
return proceed;
}
// 异常通知
@AfterThrowing("execution(* cn.Pu1satilla.AOP.Cat.shit())")
public void afterException() {
System.out.println("出现异常=============");
}
// 后置通知
@After("execution(* cn.Pu1satilla.AOP.Cat.*())")
public void after(){
System.out.println("运行后增强=============(出现异常也会调用)");
}
}