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 {@Overridepublic void save() {System.out.println("保存用户");}@Overridepublic void update() {System.out.println("修改用户");}@Overridepublic void find() {System.out.println("查询用户");}@Overridepublic 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;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//判断方法名是不是saveif ("save".equals(method.getName())) {//增强System.out.println("权限校验的代码====");return method.invoke(userDao, args);}return method.invoke(userDao, args);}}
执行
import org.junit.Test;public class Demo {@Testpublic 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;}@Overridepublic 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 {@Testpublic 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 {@Overridepublic void save() {System.out.println("保存商品");}@Overridepublic void update() {System.out.println("修改商品");}@Overridepublic void find() {System.out.println("查询商品");int i = 1 / 0;}@Overridepublic 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;@Testpublic 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.*;@Aspectpublic 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 {@Resourceprivate OrderDao orderDao;@Testpublic void demo() {orderDao.save();orderDao.update();orderDao.delete();orderDao.find();}}
输出结果
前置通知保存订单前置环绕修改订单后置环绕删除订单后置增强==三傻查询订单最终通知异常抛出==/ by zero
简单来说,AOP动态代理是为了在不改变源码的前提下,在源码某个方法前,插入执行自己的方法。Android插件化中Hook也是用到的动态代理的思想,如出一辙
欢迎关注我的微信公众号:安卓圈





