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也是用到的动态代理的思想,如出一辙