暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

SpringAOP

江边的读书男 2019-06-13
93

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

欢迎关注我的微信公众号:安卓圈

文章转载自江边的读书男,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论