AOP

  • AOP
  • 1、JDK 动态代理
  • 2、CGLIB 动态代理
  • 3、Spring 的注解形式 AOP
  • 第一步:引入 Spring 的 aop 的包
  • 第二步:引入头信息
  • 第三步:基于注解方式的切面声明
  • 第四步:切面定义
  • 4、使用配置文件做 AOP
  • 配置
  • 切面





源码工程文件为:spring3.2_07 & spring3.2_08 & spring3.2_09


AOP

AOP(Aspect Oriented Programming),面向切面编程,通过预编译的方式在运行期通过动态代理实现一种技术。AOP 是 OOP 延续,利用 AOP 可以实现业务和切面的逻辑分离,降低耦合度。程序的重用性提高。

Spring(11)_AOP_xml

1、JDK 动态代理

JDK 动态代理代理目标是接口实现类的形式。

代理的目标对象

public class UserServiceImpl implements UserService 
{
private User user;

public User getUser()
{
return user;
}

public void setUser(User user)
{
this.user = user;
}

public void save()
{
System.out.println("保存用户");
}

@Override
public void update()
{
System.out.println("修改用户");
}
}

代理

public class JDKProxy implements InvocationHandler 
{
/**
* 指定代理目标对象
*/
private Object targetObj;

/**
* 第一个参数:获得目标对象的类的加载器
* 第二个参数:获得目标对象的接口
* 第三个参数:设置回调对象,当前代理对象的方法被调用时,会委派该参数去调用invoke
* @param targetObj
* @return
*/
public Object createProxyInstance(Object targetObj)
{
this.targetObj = targetObj;
return Proxy.newProxyInstance(this.targetObj.getClass().getClassLoader(),
this.targetObj.getClass().getInterfaces(), this);
}

/**
* proxy:代理类
* Method:要调用的业务方法
* Object[]:调用的业务方法的参数
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
//获得目标对象的get方法
Method getMeth = this.targetObj.getClass().getMethod("getUser", null);
//通过反射来调用getUser
Object user = getMeth.invoke(targetObj, null);
Object obj = null;
if(user != null)
{
//obj是业务方法返回的值
obj = method.invoke(this.targetObj, args);
}
else
{
System.out.println("您还没有登录");
}
return obj;
}
}

2、CGLIB 动态代理

CGLIB 可以对普通类做动态代理,目标类不能是 final,目标类里面的方法也不能是 final。

public class UserServiceImpl 
{
private User user;

public User getUser()
{
return user;
}

public void setUser(User user)
{
this.user = user;
}

public void save()
{
System.out.println("保存用户");
}

public void update()
{
System.out.println("修改用户");
}
}

代理

public class CGLIBProxy implements MethodInterceptor 
{
/**
* 指定代理目标对象
*/
private Object targetObj;

public Object createProxyInstance(Object targetObj)
{
this.targetObj = targetObj;
Enhancer en = new Enhancer();
//设置代理类的父类
en.setSuperclass(this.targetObj.getClass());
//设置回调对象
en.setCallback(this);
//创建代理类
return en.create();
}

@Override
public Object intercept(Object arg0, Method method, Object[] arg2,
MethodProxy arg3) throws Throwable
{
Method getMeth = this.targetObj.getClass().getMethod("getUser", null);
//通过反射来调用getUser
Object user = getMeth.invoke(targetObj, null);
Object obj = null;
if(user != null)
{
obj = method.invoke(this.targetObj, arg2);
}
else
{
System.out.println("您还没有登录");
}
return obj;
}
}

3、Spring 的注解形式 AOP


  • Aspect(切面):对横切性关注点的一中抽象。
  • Jionpoint(连接点):要拦截的方法。
  • Pointcut(切点):是连接点的集合
  • Advice(通知):在连接点前后或异常情况来做的事情,前置通知,后置通知,返回通知,异常通知,环绕通知。
  • Target(目标对象):代理的目标对象
  • Weave(织入):切面应用到目标对象并且导致 proxy 对象创建的过程叫织入

第一步:引入 Spring 的 aop 的包

Spring(2 )_Spring 环境搭建 & Spring(8)_注解方式注入 提供包下载。

Spring(11)_AOP_xml_02

Spring(11)_AOP_spring_03

Spring(11)_AOP_目标对象_04

Spring(11)_AOP_spring_05

第二步:引入头信息

<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

</beans>

第三步:基于注解方式的切面声明

<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

<!-- 开启以注解形式切面的驱动 -->
<aop:aspectj-autoproxy/>
</beans>

第四步:切面定义

创建一个类,在上加入 @Aspect 注解

@Aspect
public class PermAspect
{
/**
* 第一个*:拦截方法的返回值
* 第一个..:子包
* 第二个*:所有的类
* 第三个*:所有的方法
* 第二个..:所有的参数
*/
@Pointcut("execution(* com.wyx.spring.service..*.*(..))")
public void anyMethod()
{

}
}

前置通知

/*@Aspect
public class PermAspect
{
/**
* 第一个*:拦截方法的返回值
* 第一个..:子包
* 第二个*:所有的类
* 第三个*:所有的方法
* 第二个..:所有的参数
*/
/*@Pointcut(value="execution(* com.wyx.spring.service..*.*(..))")
public void anyMethod()
{

}*/
/*@Pointcut(value="execution(* com.wyx.spring.service..*.*(..))&&args(param)", argNames="param")
public void anyMethod(User user)
{

}*/

/*@Pointcut(value="execution(!java.lang.String com.wyx.spring.service..*.*(..))&&args(param)", argNames="param")
public void anyMethod(User user)
{

}*/
@Pointcut(value="execution(!java.lang.String com.wyx.spring.service..*.*(com.wyx.spring.model.User,..))")
public void anyMethod()
{

}
/**
* @Before前置通知
* anyMethod():指定前置通知的切点
* 拦截带有user参数的业务方法
* args中参数名必须和方法的参数名称一致
*/
/*@Before(value="anyMethod()&&args(user)")
public void preAdive(User user)
{
System.out.println(user);
user.setUsername("魏宇轩");
System.out.println("执行前置通知");
System.out.println("------------------------------------------");
}*/
/*@Before(value="anyMethod()&&args(param)", argNames="param")
public void preAdive(User user1)
{
System.out.println(user1);
user1.setUsername("魏宇轩");
System.out.println("执行前置通知");
System.out.println("------------------------------------------");
}*/

/*@Before(value="anyMethod(param)", argNames="param")
public void preAdive(User user1)
{
System.out.println(user1);
user1.setUsername("任亮");
System.out.println("执行前置通知");
System.out.println("------------------------------------------");
}*/
@Before(value="anyMethod()")
public void preAdive()
{
System.out.println("执行前置通知");
System.out.println("------------------------------------------");
}
/*@Before(value="anyMethod()")
public void preAdive()
{
System.out.println("执行前置通知");
System.out.println("------------------------------------------");
}*/
}

后置通知

定义一个 void 方法,在方法上加 @After 注解,在业务方法执行之后来执行,其余使用方法和前置拦截一样。

返回通知

/**
* @AfterReturning:返回通知的注解,在返回通知里面可以对业务方法的返回值做最后的统一加工。
* pointcut:指定返回通知的切点
* returning:指定返回值和返回通知的方法的参数名称要一致
*
* @param user
*/
@AfterReturning(pointcut="anyMethod()", returning="user")
public void returnAdvice(User user)
{
user.setUsername("魏宇轩");
System.out.println("执行返回通知");
}

例外通知

我们主要使用例外通知做运行期监控,获得项目的异常信息。

/**
* @AfterThrowing:异常通知的注解
* pointcut:切点
* throwing:抛出的异常
* @param ex
*/
@AfterThrowing(pointcut="anyMethod()", throwing="ex")
public void exceptionAdvice(Exception ex)
{
System.out.println("执行例外通知");
ex.printStackTrace();
}

环绕通知

@Aspect
public class PermAspect2
{
/**
* 第一个*:拦截方法的返回值
* 第一个..:子包
* 第二个*:所有的类
* 第三个*:所有的方法
* 第二个..:所有的参数
*/
@Pointcut(value="execution(* com.wyx.spring.service..*.*(..))")
public void anyMethod()
{}

/**
* 环绕通知:可以实现前置通知,后置通知,返回通知,例外通知的所有功能
* @Around:环绕通知的注解,指定切点
* @param jp:指定的连接点(拦截的业务方法)
* @return
*/
@Around("anyMethod()")
public Object doAroundAdvice(ProceedingJoinPoint jp)
{
//获得拦截的业务方法的参数
Object[] args = jp.getArgs();
System.out.println("环绕通知----前"+"-------传递的参数:"+args[0]);
Object obj = null;
User user = null;
try
{
//执行业务方法,也可以使用无参数,返回值就是业务方法的返回值
obj = jp.proceed(args);
user = (User) obj;
} catch (Throwable e)
{
e.printStackTrace();
}
user.setUsername("魏宇轩");
System.out.println("环绕通知----后"+"-------返回值:"+obj);
return obj;
}
}

4、使用配置文件做 AOP

配置

<aop:config>
<!--
切点配置:
expression:切点的表达式
id:唯一标识
-->
<aop:pointcut expression="execution(* com.rl.spring.service..*.*(..))" id="mycut"/>
<!-- 切面配置
ref:要关联的切面类
-->
<aop:aspect ref="permApect">
<!-- 前置通知
method:切面类中方法
pointcut-ref:切点
-->
<aop:before method="preAdvice" pointcut-ref="mycut"/>
<!-- 后置通知 -->
<aop:after method="afterAdvice" pointcut-ref="mycut"/>
<!-- 返回通知
returning:业务方法返回的值
-->
<aop:after-returning method="returnAdvice" pointcut-ref="mycut" returning="returnval"/>
<!--
例外外通
throwing:抛出的异常的变量名知
-->
<aop:after-throwing method="exceptionAdvice" pointcut-ref="mycut" throwing="ex"/>
<!--
环绕通知
-->
<aop:around method="aroundAdvice" pointcut-ref="mycut"/>
</aop:aspect>
</aop:config>

切面

public class PermAspect 
{
public void preAdvice(JoinPoint jp)
{
Object[] args = jp.getArgs();
if(args.length > 0)
System.out.println("执行前置通知-----------业务参数:"+args[0]);
}

public void afterAdvice(JoinPoint jp)
{
Object[] args = jp.getArgs();
if(args.length > 0)
System.out.println("执行后置通知-----------业务参数:"+args[0]);
}

public void returnAdvice(JoinPoint jp, Object returnval)
{
if(returnval instanceof User)
{
User user = (User) returnval;
user.setUsername("任亮");
}
System.out.println("返回通知----------返回的值"+returnval);
}

public void exceptionAdvice(JoinPoint jp, Exception ex)
{
System.out.println("例外通知------------");
ex.printStackTrace();
}

public Object aroundAdvice(ProceedingJoinPoint jp)
{
Object[] objs = jp.getArgs();
if(objs.length>0)
System.out.println("执行环绕通知前-----------业务参数:"+objs[0]);
Object obj = null;
try
{
obj = jp.proceed();
System.out.println("执行环绕通知后-----------业务参数:"+objs[0]);
} catch (Throwable e)
{
e.printStackTrace();
}
return obj;
}
}

如有错误,欢迎指正!