AOP面向切面编程
Spring是基于Aspectj的AOP开发java
AOP的底层原理就是动态代理spring
动态代理分两种
JDK动态代理:只能对实现了接口的类产生代理
Cglib动态代理:第三方代理技术,对没有实现接口的类产生代理对象,生成子类对象,能够动态添加类的属性和方法express
Spring会根据是否有接口自动选择相应的代理编程
术语:
链接点:能够被拦截的点
切入点:真正被拦截的点
通知:加强方法
引介:类的加强
目标:被加强的对象
织入:将加强应用到目标的过程
代理:织入加强后产生的对象
切面:切入点和通知的组合微信
通知类型:ide
前置通知:
目标方法执行以前进行操做,能够得到切入点信息
后置通知:
目标方法执行以后进行操做,能够得到方法的返回值
环绕通知:
目标方法执行以前和以后进行操做,能够阻止目标方法的执行
异常抛出通知:
程序出现异常时进行操做,能够得到抛出的异常信息
最终通知:
不管代码知否有异常,老是会执行this
切入点表达式语法
[访问修饰符] 方法返回值 包名.类名.方法名(参数)
public void com.jinke.spring.CustomerDao.save(..)
* *.*.*.*Dao.save(..)
* com.jinke.spring.CustomerDao+.save(..)
* com.jinke.spring..*.*(..)spa
先介绍下两种动态代理插件
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也是用到的动态代理的思想,一模一样
欢迎关注个人微信公众号:安卓圈