Spring AOP
Spring AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
日志记录,性能统计,安全控制,事务处理,异常处理等等
AOP与OOP区别
OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。
AOP相关术语
目标对象target:指的是需要被增强的对象,由于spring aop是通过代理模式实现,从而这个对象永远是被代理对象。
连接点(join point):所谓连接点是指那些被拦截到的点,在spring中这些点指的是方法,因为spring只支持方法类型的连接点
切入点(pointcut):简单说切入点是指我们要对哪些连接点进行拦截的定义
通知(advice):所谓通知是指拦截到连接点之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Advice 定义了在 pointcut 里面定义的程序点具体要做的操作
引介introduction:引介是一种特殊的通知,在不修改类代码的前提下,introduction可以在运行期为类动态地添加一些方法或属性
切面aspect:是切入点和通知的结合
织入weaving:织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期,类装载期,运行期进行。
Spring采用动态织入,而aspectj采用静态织入
代理Proxy:一个类被AOP织入增强后,就产生一个结果代理类
AOP底层:
JDK动态代理:在运行 ,在JVM内部动态生成class字节码对象(Class对象)
Jdk动态代理只针对于接口操作
newProxyInstance的三个参数:
第一个参数:目标类的类加载器对象
第二个参数:目标类的实现接口的Class[]
第三个参数:InvocationHandler它是一个接口,它的作用是是代理实例的调用处理程序 实现的接口,接口中定义了一个方法
下面举个例子:
public class ProxyFactory implements InvocationHandler{ private Object target; //在创建对象时,传入代理目标对象 public ProxyFactory(Object target){ this.target = target; } //创建代理对象 public Object createProxy() { //使用proxy创建代理对象 /* * 准备类加载器 * 准备实现接口的class[] * 实现invocationHandler * */ ClassLoader classLoader = target.getClass().getClassLoader(); Class [] interfaces = target.getClass().getInterfaces(); return Proxy.newProxyInstance(classLoader, interfaces, this); } //在代理对象上处理方法并返回结果 /** * 参数1就是代理对象 * 参数2就是调用方法的method对象 * 参数3调用方法的参数 * */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("日志操作"); return method.invoke(target, args); } }
@Service(value="userServiceImpl") public class UserServiceImpl implements UserService { @Override public void login(String username, String password) { System.out.println("登录操作!....."); } @Override public void regist() { System.out.println("注册操作!....."); } }
<context:component-scan base-package="com.learn"></context:component-scan>
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class TestProxy { //准备目标对象 @Autowired private UserService us; @Test public void test1() { //创建代理工厂 ProxyFactory pf = new ProxyFactory(us); //返回代理对象赋值给目标对象 us = (UserService) pf.createProxy(); us.regist(); } }
CGLIB动态代理
CGLIB(Code Generation Library)是一个开源项目,它可以在运行期扩展Java类与实现Java接口。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类
注意:jdk的动态代理只可以为接口去完成操作,而cglib它可以为没有实现接口的类去做代理,也可以为实现接口的类去做代理。
举个例子:
public class CGLIBFactory implements MethodInterceptor{ //准备目标对象 private Object target; //构造方法 public CGLIBFactory(Object target) { this.target = target; } //创建代理对象 public Object createProxy() { //创建Enhancer Enhancer enhancer = new Enhancer(); //传递目标对象的class enhancer.setSuperclass(target.getClass()); //设置回调操作 enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable { System.out.println("日志操作"); return method.invoke(target, arg2); } }
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class TestCGLB { //准备非接口目标对象 @Autowired private UserServiceImpl usi; @Test public void test() { //创建代理类对象,传递目标对象 CGLIBFactory cf = new CGLIBFactory(usi); //获得代理类对象重新赋值给usi usi = (UserServiceImpl) cf.createProxy(); usi.regist(); } }
setCallback传递的参数是Callback类型,我们使用的是MethodInterceptor
注意:cglib它可以为没有实现接口的类做代理,也可以为接口类做代理.
spring采用的是哪一种动态机制:
如果目标对象,有接口,优先使用jdk动态代理
如果目标对象,无接口,使用cglib动态代理。
Spring AOP编程
在传统的spring aop开发中它支持增强(advice)有五种:
1. 前置通知 目标方法执行前增强 org.springframework.aop.MethodBeforeAdvice
2. 后置通知 目标方法执行后增强 org.springframework.aop.AfterReturningAdvice
3. 环绕通知 目标方法执行前后进行增强 org.aopalliance.intercept.MethodInterceptor
4. 异常抛出通知 目标方法抛出异常后的增强 org.springframework.aop.ThrowsAdvice
5. 引介通知 在目标类中添加一些新的方法或属性(不做介绍)org.springframework.aop.IntroductionInterceptor
下面举个最古老的spring AOP
//准备目标对象 public class OrderServiceImpl implements OrderService { @Override public void add() { System.out.println("add方法执行了....."); } @Override public void update() { System.out.println("update方法执行了......."); } }
<?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"> <!-- 目标target --> <bean id="orderService" class="com.learn.service.impl.OrderServiceImpl"></bean> <!-- 通知advice --> <bean id="orderServiceAdvice" class="com.learn.utils.OrderHelper"></bean> <!-- 定义切点 --> <bean id="orderServicePointCut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedNames"> <list> <!-- 配置需要拦截的方法 --> <value>add</value> <value>update</value> </list> </property> </bean> <!-- 配置切面aspect=pointCut+advice --> <bean id="orderServiceAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="advice" ref="orderServiceAdvice"></property> <property name="pointcut" ref="orderServicePointCut"></property> </bean> <!-- 配置代理 --> <bean id="orderServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="orderService"></property> <property name="interceptorNames" value="orderServiceAspect"></property> <property name="proxyInterfaces" value="com.learn.service.OrderService"></property> </bean> </beans>
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class Aop1Test { @Autowired @Qualifier("orderServiceProxy") private OrderService os; @Test public void test() { os.update(); } }
在原有的基础上修改的Spring AOP编程
//准备目标对象 public class OrderServiceImpl implements OrderService { @Override public void add() { System.out.println("add方法执行了....."); } @Override public void update() { System.out.println("update方法执行了......."); } }
<?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="order"></bean> <!-- 配置通知 --> <bean id="orderServiceAdvice" class="com.learn.utils.OrderHelper"></bean> <!-- aop来描述切面和切点 --> <aop:config> <!-- 定义切点 --> <aop:pointcut expression="execution(* com.learn.service..*(..))" id="orderServicePointCut"/> <!-- 配置切面 --> <aop:advisor advice-ref="orderServiceAdvice" pointcut-ref="orderServicePointCut"/> </aop:config> </beans>
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class aop2Test { @Autowired @Qualifier("orderService") private OrderService os; @Test public void test() { os.update(); } }
基于aspectJ切点传统开发
@Service("customerService") public class CustomerServiceImpl implements CustomerService{ @Override public void add() { System.out.println("add........."); } @Override public String login() { System.out.println("login........"); return "你好"; } }
<?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" > <!-- target --> <bean id="userService" class="com.learn.service.impl.UserServiceImpl"></bean> <!-- advice --> <bean id="userServiceAdvice" class="com.learn.utils.UserHelper"></bean> <!-- aspectj配置切面 --> <aop:config proxy-target-class="true"> <aop:aspect ref="userServiceAdvice"> <aop:pointcut expression="execution(* com.learn.service..*(..))" id="delPointCut"/> <aop:before method="before" pointcut-ref="delPointCut"/> <aop:before method="before1" pointcut-ref="delPointCut"/> <aop:after-returning method="afterReturning" pointcut-ref="delPointCut"/> <aop:after-throwing method="afterThrowing" pointcut-ref="delPointCut"/> </aop:aspect> </aop:config> </beans>
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class AspectjTest { @Autowired @Qualifier("userService") private UserService us; @Test public void test() { us.regist(); } }
spring框架默认情况下,会对有接口的类使用proxy代理。没有接口的类使用cglib代理
Proxy-target-class的值默认是false,它代表有接口使用proxy代理
问题:如果现在对目标要使用cglib代理,只需要将proxy-target-class设置为true.
<aop:config>来声明要对aop进行配置
<aop:pointcut>它是用于声明切点(简单说就是对哪些方法进行拦截)
<aop:advisor> 定义传统的aop的切面,传统的aop切面它只能包含一个切点与一个增强
<aop:aspect>定义aspectj框架的切面.,它可以包含多个切点与多个通知
关于切点表达式的写法:
关于execution语法常用:
1. execution(public * *()) 所有的public的方法
2. execution(* cn.itheima.aop.*(..)) 所有的aop包下的所有类的方法(不包含子包)
3. execution(* cn.itheima.aop..*(..)) 所有的aop包及其子包下的所有类的方法
4. execution(* cn.itheima.aop.IOrderService.*(..)) IOrderService接口中定义的所有方法
5. execution(* cn.itheima.aop.IOrderService+.*(..)) 匹配实现特定接口所有类的方法
6. execution(* save*(..)) 区配所有的以save开头的方法
AspectJ框架它定义的通知类型有6种
1. 前置通知Before 相当于BeforeAdvice
2. 后置通知AfterReturning 相当于AfterReturningAdvice
3. 环绕通知 Around 相当于MethodInterceptor
4. 抛出通知AfterThrowing 相当于ThrowAdvice
5. 引介通知DeclareParents 相当于IntroductionInterceptor
6. 最终通知After 不管是否异常,该通知都会执行相比spring 的传统AOP Advice多了一个最终通知
下面举例用注解配置aop
@Service("customerService") public class CustomerServiceImpl implements CustomerService{ @Override public void add() { System.out.println("add........."); } @Override public String login() { System.out.println("login........"); return "你好"; } }
//声明一个实体 @Component("customerServiceAdvice") //声明为一个bean的切面 @Aspect public class AnnotationCustomerHelper { //声明前置通知 @Before("execution(* com.learn.service..*(..))") public void before() { System.out.println("前置通知"); } //声明后置通知 @AfterReturning(value="execution(* com.learn.service..*(..))",returning="value") public void afterReturning(JoinPoint jp,Object value) { System.out.println("后置通知,目标方法的返回是="+value); } //声明环绕通知 @Around(value="execution(* com.learn.service..*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("环绕前置通知"); Object value = pjp.proceed(); System.out.println("环绕后置通知"); return value; } //声明异常抛出通知 @AfterThrowing(value="execution(* com.learn.service..*(..))",throwing="exception") public void afterThrowing(JoinPoint jp,Throwable exception) { System.out.println("出问题了"+exception); } //声明最终通知 @After("execution(* com.learn.service..*(..))") public void after() { System.out.println("最终通知"); } }
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class AnnotationAopTest { @Autowired @Qualifier("customerService") private CustomerService cs; @Test public void test1() { //cs.add(); cs.login(); } }