Spring的AOP
Posted anni-qianqian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring的AOP相关的知识,希望对你有一定的参考价值。
AOP面向切面编程
Spring是基于Aspectj的AOP开发
AOP的底层原理就是动态代理
动态代理分两种
JDK动态代理:只能对实现了接口的类产生代理
Cglib动态代理:第三方代理技术,对没有实现接口的类产生代理对象,生成子类对象,可以动态添加类的属性和方法
Spring会根据是否有接口自动选择相应的代理
术语:
连接点:可以被拦截的点
切入点:真正被拦截的点
通知:增强方法
引介:类的增强
目标:被增强的对象
织入:将增强应用到目标的过程
代理:织入增强后产生的对象
切面:切入点和通知的组合
通知类型:
前置通知:
目标方法执行之前进行操作,可以获得切入点信息
后置通知:
目标方法执行之后进行操作,可以获得方法的返回值
环绕通知:
目标方法执行之前和之后进行操作,可以阻止目标方法的执行
异常抛出通知:
程序出现异常时进行操作,可以获得抛出的异常信息
最终通知:
无论代码知否有异常,总是会执行
切入点表达式语法
[访问修饰符] 方法返回值 包名.类名.方法名(参数)
public void com.jinke.spring.CustomerDao.save(..)
* *.*.*.*Dao.save(..)
* com.jinke.spring.CustomerDao+.save(..)
* com.jinke.spring..*.*(..)
先介绍下两种动态代理
JDK的动态代理
public interface UserDao
public void save();
public void update();
public void find();
public void delete();
public class UserDaoImpl implements UserDao
@Override
public void save()
System.out.println("保存用户");
@Override
public void update()
System.out.println("修改用户");
@Override
public void find()
System.out.println("查询用户");
@Override
public void delete()
System.out.println("删除用户");
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxy implements InvocationHandler
private UserDao userDao;
public JdkProxy(UserDao userDao)
this.userDao = userDao;
public UserDao createProxy()
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(), this);
return userDaoProxy;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
//判断方法名是不是save
if ("save".equals(method.getName()))
//增强
System.out.println("权限校验的代码====");
return method.invoke(userDao, args);
return method.invoke(userDao, args);
执行
import org.junit.Test;
public class Demo
@Test
public void demo()
UserDao userDao = new UserDaoImpl();
UserDao proxy = new JdkProxy(userDao).createProxy();
proxy.save();
proxy.update();
proxy.find();
proxy.delete();
输出结果
权限校验的代码====
保存用户
修改用户
查询用户
删除用户
Cglib的动态代理
public class CustomerDao
public void save()
System.out.println("保存用户");
public void update()
System.out.println("修改用户");
public void find()
System.out.println("查询用户");
public void delete()
System.out.println("删除用户");
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
private CustomerDao customerDao;
public CglibProxy(CustomerDao customerDao)
this.customerDao = customerDao;
public CustomerDao createProxy()
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(customerDao.getClass());
//设置回调(类似于InvocationeHnadler对象)
enhancer.setCallback(this);
//创建代理对象
CustomerDao proxy = (CustomerDao) enhancer.create();
return proxy;
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable
if ("save".equals(method.getName()))
//增强
System.out.println("权限校验的代码====");
methodProxy.invokeSuper(proxy, args);
return methodProxy.invokeSuper(proxy, args);
执行
import org.junit.Test;
public class Demo
@Test
public void demo()
CustomerDao customerDao = new CustomerDao();
CustomerDao proxy = new CglibProxy(customerDao).createProxy();
proxy.save();
proxy.update();
proxy.find();
proxy.delete();
输出结果
权限校验的代码====
保存用户
保存用户
修改用户
查询用户
删除用户
AOP和IOC一样,也有XML和注解两种方式
XML方式:
public interface ProductDao
public void save();
public void update();
public void find();
public String delete();
public class ProductDaoImpl implements ProductDao
@Override
public void save()
System.out.println("保存商品");
@Override
public void update()
System.out.println("修改商品");
@Override
public void find()
System.out.println("查询商品");
int i = 1 / 0;
@Override
public String delete()
System.out.println("删除商品");
return "二傻";
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面类
*/
public class MyAspectXML
public void checkPri(JoinPoint joinPoint)
System.out.println("权限校验===" + joinPoint);
public void writeLog(Object result)
System.out.println("日志记录===" + result);
public Object around(ProceedingJoinPoint joinPoint) throws Throwable
System.out.println("环绕前通知====");
Object obj = joinPoint.proceed();
System.out.println("环绕后通知====");
return obj;
public void afterThrowing(Throwable ex)
System.out.println("异常抛出通知===" + ex);
public void after()
System.out.println("最终通知");
配置文件ApplicationComtext4.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置目标对象:被增强的对象-->
<bean id="productDao" class="com.jinke.aopxml.ProductDaoImpl"/>
<!--将切面类交给Spring管理-->
<bean id="myAspect" class="com.jinke.aopxml.MyAspectXML"/>
<!--通过AOP的配置完成对目标类产生代理-->
<aop:config>
<!--表达式配置哪些类的那些方法需要进行增强-->
<aop:pointcut id="pointcut1" expression="execution(* com.jinke.aopxml.ProductDaoImpl.save(..))"/>
<aop:pointcut id="pointcut2" expression="execution(* com.jinke.aopxml.ProductDaoImpl.delete(..))"/>
<aop:pointcut id="pointcut3" expression="execution(* com.jinke.aopxml.ProductDaoImpl.update(..))"/>
<aop:pointcut id="pointcut4" expression="execution(* com.jinke.aopxml.ProductDaoImpl.find(..))"/>
<!--配置切面-->
<aop:aspect ref="myAspect">
<!--前置通知-->
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
<!--后置通知-->
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
<!--环绕通知-->
<aop:around method="around" pointcut-ref="pointcut3"/>
<!--异常抛出通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
<!--最终通知-->
<aop:after method="after" pointcut-ref="pointcut4"/>
</aop:aspect>
</aop:config>
</beans>
执行
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext4.xml")
public class SpringDemo
@Resource(name = "productDao")
private ProductDao productDao;
@Test
public void demo()
productDao.save();
productDao.update();
productDao.find();
productDao.delete();
输出结果
权限校验===execution(void com.jinke.aopxml.ProductDao.save())
保存商品
环绕前通知====
修改商品
环绕后通知====
查询商品
最终通知
异常抛出通知===java.lang.ArithmeticException: / by zero
注解的方式
public class OrderDao
public void save()
System.out.println("保存订单");
public void update()
System.out.println("修改订单");
public String delete()
System.out.println("删除订单");
return "三傻";
public void find()
System.out.println("查询订单");
int i = 1 / 0;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class MyAspectAnno
@Before(value = "MyAspectAnno.pointcutSave()")
public void before()
System.out.println("前置通知");
@AfterReturning(value = "MyAspectAnno.pointcutDelete()", returning = "result")
public void afterReturn(Object result)
System.out.println("后置增强==" + result);
@Around(value = "MyAspectAnno.pointcutUpdate()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable
System.out.println("前置环绕");
joinPoint.proceed();
System.out.println("后置环绕");
@AfterThrowing(value = "MyAspectAnno.pointcutFind()", throwing = "ex")
public void afterThrowing(Throwable ex)
System.out.println("异常抛出==" + ex.getMessage());
@After(value = "MyAspectAnno.pointcutFind()")
public void after()
System.out.println("最终通知");
/**
* 切入点注解
*/
@Pointcut(value = "execution(* com.jinke.aopanno.OrderDao.find())")
private void pointcutFind()
@Pointcut(value = "execution(* com.jinke.aopanno.OrderDao.save())")
private void pointcutSave()
@Pointcut(value = "execution(* com.jinke.aopanno.OrderDao.update())")
private void pointcutUpdate()
@Pointcut(value = "execution(* com.jinke.aopanno.OrderDao.delete())")
private void pointcutDelete()
配置文件ApplicationContext5.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--在配置文件中开启注解AOP的开发-->
<aop:aspectj-autoproxy/>
<bean id="orderDao" class="com.jinke.aopanno.OrderDao"/>
<bean id="myAspect" class="com.jinke.aopanno.MyAspectAnno"/>
</beans>
执行
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext5.xml")
public class SpringDemo
@Resource
private OrderDao orderDao;
@Test
public void demo()
orderDao.save();
orderDao.update();
orderDao.delete();
orderDao.find();
输出结果
前置通知
保存订单
前置环绕
修改订单
后置环绕
删除订单
后置增强==三傻
查询订单
最终通知
异常抛出==/ by zero
简单来说,AOP动态代理是为了在不改变源码的前提下,在源码某个方法前,插入执行自己的方法。android插件化中Hook也是用到的动态代理的思想,如出一辙
欢迎关注我的微信公众号:安卓圈
以上是关于Spring的AOP的主要内容,如果未能解决你的问题,请参考以下文章