Spring AOP
1.概述
*1. 作用: 利用aop可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性, 同时提高了开发效率.
*2. 主要功能:
日志记录, 性能统计, 安全控制, 事务处理, 异常处理...
*3. 主要目的:
将日志记录, 性能统计...等代码从业务逻辑代码中划分出来,通过对这些行为的分离. 我们将他们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码.
2.AOP和OOP的区别
OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分.
AOP则是针对业务处理过程中的切面进行提取, 它所面对的是处理过程中的某个步骤或阶段, 以获得逻辑过程中各部分之间低耦合的隔离效果.
这两种思想有着本质的区别, OOP面向名词领域, AOP面向动词领域.
3.相关术语
举例:
Proxy.newProxyInstance(UserDao类加载器, userDao实现的接口数组[], new InvocationHandler(){
Proxy object = public Object invoke(Object proxy, Method method, Object[] args){
}
})
1. 目标对象 (target)
指的是需要被增强的对象, 由于spring aop 是通过动态代理模式实现, 从而这个对象永远是被代理对象.
* 指的是一个具体的类.
2. 连接点 (join point)
所谓连接点就是目标对象中哪些被拦截的点, 这些点指的就是方法.
*** 需要被增强的方法
3. 切入点 (pointcut)
表示一组连接点. 切入点就是指我们要对哪些连接点进行拦截的定义.
4. 通知 (advice)
通知是指拦截到连接点之后所要做的事情就是通知. 简单说就是增强.
通知分为前置通知, 后置通知, 异常通知, 最终通知, 环绕通知.
5. 切面 (aspect)
切入点 + 通知 = 切面;
6. 代理Proxy
一个类被aop织入增强后, 就产生一个结果代理类.
3.1 JDK动态代理: 只能为接口的实现类做动态代理.
*注: 在运行时, 在JVM内部动态生成class字节码对象(class对象)
/**
* 创建一个使用JDK的proxy完成动态代理的工具
*/
public class JDKProxyFactory implements InvocationHandler {
private Object target;
public JDKProxyFactory(Object object) {
this.target = object;
}
public Object createProxy(){
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
return proxy;
}
/**
*
* @param proxy 代理对象, 一般不使用
* @param method 调用的方法的method对象
* @param args 调用的方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法增强了...");
return method.invoke(target, args);
}
}
public class test {
@Test
public void test(){
IUserService iUserService = new IUserServiceImpl();
JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(iUserService);
IUserService proxy = (IUserService) jdkProxyFactory.createProxy();
proxy.login();
}
}
3.2 CGLIB动态代理: 不仅可以为实现接口的类做代理, 也可以为没有实现接口的类做代理.
*注: CGLIB底层是通过使用一个小而快的字节码处理框架ASM, 来转换字节码并生成新的类.
public class CglibProxyFactory implements MethodInterceptor {
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}
public Object createProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("功能增强");
return method.invoke(target , args);
}
}
@Test
public void cglib(){
IUserService iUserService = new IUserServiceImpl();
CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(iUserService);
IUserService proxy = (IUserService) cglibProxyFactory.createProxy();
proxy.login();
}
4.Spring使用那种动态代理机制?
* 如果目标对象, 有接口, 优先使用 jdk动态代理;
* 如果目标对象, 没有接口, 使用 Cglid动态代理;
5.Spring AOP编程
5.1.1 Spring的传统AOP编程(了解)
1. Spring的传统AOP编程(了解)
1) 传统的spring aop支持五种形式的增强:
* 前置通知
* 后置通知
* 环绕通知
* 异常抛出通知
* 引介通知
2) 流程:
* 第一步: 编写目标 target;
* 第二步: 增强 advice
public interface IOrderService {
public void addOrder();
public void updateOrder();
}
public class OrderHelper implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("环绕前....");
Object value = mi.proceed();
System.out.println("环绕后....");
return value;
}
}
<?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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="orderService" class="cn.orange.aop.OrderServiceImpl"></bean>
<bean id="orderServiceAdvice" class="cn.orange.aop.OrderHelper"></bean>
<bean id="orderServicePointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*Order"></property>
</bean>
<bean id="orderServiceAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="orderServiceAdvice"/>
<property name="pointcut" ref="orderServicePointCut"/>
</bean>
<bean id="orderServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="orderService"/>
<property name="interceptorNames" value="orderServiceAspect"/>
<property name="proxyInterfaces" value="cn.itheima.aop.IOrderService"/>
</bean>
</beans>
5.1.2传统基于aspectJ切点AOP开发
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="orderService" class="cn.itheima.aop.OrderServiceImpl"></bean>
<bean id="orderServiceAdvice" class="cn.itheima.aop.OrderHelper"></bean>
<aop:config>
切点表达式
<aop:pointcut expression="execution(* cn.orange.aop.IOrderService.*(..))"
id="orderServicePointCut" />
<aop:advisor advice-ref="orderServiceAdvice" pointcut-ref="orderServicePointCut" />
</aop:config>
</beans>
5.2.1 Spring整合aspectj框架实现的AOP编程. (目前的主流)
aspectj有六种通知, 在其基础上添加了 最终通知after.
开发步骤:
* 1. 定义目标 target
* 2. 创建通知 advice, 相较于传统的aop开发(需要实现接口), aspectj只需定义增强,通过配置文件完成增强
* 3. 在配置文件中配置信息
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="cn.itheima.aspectj.UserServiceImpl"/>
<bean id="userServiceAdvice" class="cn.itheima.aspectj.UserServiceHelper"/>
<aop:config proxy-target-class="true">
<aop:aspect ref="userServiceAdvice">
<aop:pointcut expression="execution(* *.del(..))" id="delPointCut"/>
<aop:before method="before" pointcut-ref="delPointCut"/>
<aop:before method="before1" pointcut-ref="delPointCut"/>
<aop:after-returning method="afterReturning" pointcut-ref="delPointCut" returning="val"/>
<aop:around method="around" pointcut-ref="delPointCut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="delPointCut" throwing="ex"/>
<aop:after method="after" pointcut-ref="delPointCut"/>
</aop:aspect>
</aop:config>
</beans>
public class UserServiceHelper(){
public Object around(ProceedingJoinPoint pjp){
System.out.println("环绕前.");
Object value = pjp.proceed();
System.out.println("环绕后.");
return value;
}
}
5.2.2 通知上的参数详解
public class UserServiceHelper {
public void before(JoinPoint jp) {
System.out.println("拦截的目标类:" + jp.getSignature().getDeclaringTypeName());
System.out.println("拦截的方法名称:" + jp.getSignature().getName());
System.out.println("前置通知");
}
public void afterReturning(JoinPoint jp, Object val) {
System.out.println("目标方法返回值:" + val);
System.out.println("后置通知");
}
6.代理方式的选择: proxy-target-class=”true”选择使用Cglib代理
<aop:config proxy-target-class="true">
<aop:aspect ref="userServiceAdvice">
<aop:pointcut expression="execution(* *.del(..))" id="delPointCut"/>
<aop:before method="before" pointcut-ref="delPointCut"/>
<aop:after-returning method="afterReturning" pointcut-ref="delPointCut" returning="val"/>
</aop:aspect>
</aop:config>
7.重点: 注解开发
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="cn.oranges" />
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
@Component
@Aspect
public class CustomerServiceHelper {
@Pointcut("execution(* *.s*(..))")
private void mypointcut(){}
@Pointcut("execution(* *.update(..))")
private void mypointcut1(){}
@Before("mypointcut()||mypointcut1()")
public void before() {
System.out.println("前置通知...");
}
@AfterReturning(value = "execution(* *.update(..))", returning = "value")
public void afterReturning(JoinPoint jp, Object value) {
System.out.println("后置通知,目标方法的返回是" + value);
}
@Around("mypointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前...");
Object value = pjp.proceed();
System.out.println("环绕后");
return value;
}
@AfterThrowing(value = "mypointcut()", throwing = "ex")
public void afterThrowing(JoinPoint jp, Throwable ex) {
System.out.println("异常抛出通知:" + ex);
}
@After("mypointcut()")
public void after() {
System.out.println("最终通知");
}
}