Spring的AOP编程
AOP为Aspect Oriented Programming(面向切面编程)
AOP的好处:在不修改源代码的情况下,可以实现功能的增强
JDK动态代理
缺点:只能针对实现了接口的类实现代理
/** * Jdk的动态代理 * @author kevin */ public class JdkProxy implements InvocationHandler{ //要代理的对象 private CustomerDao customerDao; public JdkProxy(CustomerDao customerDao){ this.customerDao = customerDao; } /** * 生成代理对象的方法 * @return */ public CustomerDao createProxy(){ CustomerDao proxy = (CustomerDao) Proxy.newProxyInstance(customerDao.getClass().getClassLoader(), customerDao.getClass().getInterfaces(), this); return proxy; } /** * 增强的方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("权限校验..."); return method.invoke(customerDao, args); } } //编写测试代码: @Test public void test(){ CustomerDao customerDao = new CustomerDaoImpl(); JdkProxy jdkProxy = new JdkProxy(customerDao); CustomerDao proxy = jdkProxy.createProxy(); proxy.save(); }
CGLIB动态代理(了解)
可以针对那些没有实现接口的类实现代理
public class CglibProxy implements MethodInterceptor{ //要代理的对象 private LinkManDao linkManDao; public CglibProxy(LinkManDao linkManDao) { this.linkManDao = linkManDao; } public LinkManDao createProxy(){ //创建Cglib核心类 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(linkManDao.getClass()); //设置回调 enhancer.setCallback(this); //生成代理 LinkManDao proxy = (LinkManDao) enhancer.create(); return proxy; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("日志记录"); Object obj = methodProxy.invokeSuper(proxy, args); return obj; } } //编写测试代码 @Test public void test2(){ LinkManDao linkManDao = new LinkManDao(); CglibProxy cglibProxy = new CglibProxy(linkManDao); LinkManDao proxy = cglibProxy.createProxy(); proxy.save(); }
术语
Target: 目标对象。要对类增强,增强的类就称为目标对象。
JoinPoint: 连接点。可以被增强的点就称为JoinPoint。
PointCut: 切入点。在实际开发中真正被增强了的点就称为PointCut。
Advice: 增强(通知)。现在需要对save方法增强一个日志记录的功能,就需要编写一个日志记录的方法。那么这个日志记录的方法就称为Advice。
Weaving: 织入。 把Advice应用到目标的过程叫织入。
Introduction: 引介。 指的是类层面的增强。
Proxy: 代理。 增强之后的对象。
Aspect: 切面。 多个增强和多个切入点的组合就叫切面。
public class UserDaoImpl{ public void save(){} public void list(){} public void delete(){} public void update(){} }
SpringXML形式的AOP入门
配置
导入jar包(11)
红线画起来的表示:
AOP联盟的jar包:com.springsource.org.aopalliance-1.0.0.jar
Spring提供的AOP的jar包:spring-aop-4.2.4.RELEASE.jar
AspectJ的jar包:com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
Spring整合AspectJ的jar包:spring-aspects-4.2.4.RELEASE.jar
配置文件
<?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="customerDao" class="me.xueyao.dao.impl.CustomerDaoImpl"></bean> <bean id="myAspectXml" class="me.xueyao.aspect.MyAspectXml"></bean> <!-- AOP配置 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* me.xueyao.dao.impl.CustomerDaoImpl.save(..))" id="pointcut1"/> <!-- 配置切面 --> <aop:aspect ref="myAspectXml"> <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/> </aop:aspect> </aop:config> </beans>
切入点表达式
语法:[修饰符] 返回类型 包名.类名.方法名(形式参数)
常见写法:
execution(public * *(..)) 所有的public方法 execution(* set(..)) 所有set开头的方法 execution(* com.xyz.service.AccountService.*(..)) AccountService类中的所有方法 execution(* com.xyz.service.*.*(..)) com.xyz.service包下所有的方法 execution(* com.xyz.service..*.*(..)) com.xyz.service包及其子包下所有的方法
Spring中AOP的通知类型
1.前置通知: 在方法执行之前增强。可以获得切入点信息。
<!-- 配置切面 --> <aop:aspect ref="myAspectXml"> <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/> </aop:aspect>
2.后置通知: 在方法执行完之后增强。可以获取返回值信息。
<aop:aspect ref="myAspectXml"> <!-- 前置通知 --> <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/> <!-- 后置通知 --> <aop:after-returning method="afterReturn" pointcut-ref="pointcut2" returning="result"/> </aop:aspect>
3.环线通知: 在方法执行前后都进行增强。可以阻止方法的执行。
public class CustomerDaoImpl implements CustomerDao { @Override public void update() { System.out.println("持久层:客户更新"); } } public class MyAspectXml { public Object around(ProceedingJoinPoint joinpoint){ System.out.println("环绕前执行"); Object obj = null; try { obj = joinpoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("环绕后执行"); return obj; } }
<!-- AOP配置 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* me.xueyao.dao.impl.CustomerDaoImpl.update(..))" id="pointcut3"/> <!-- 配置切面 --> <aop:aspect ref="myAspectXml"> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pointcut3"/> </aop:aspect> </aop:config>
4.异常抛出通知: 当发生异常之后增强,可以获取异常信息
public class CustomerDaoImpl implements CustomerDao { @Override public void find() { System.out.println("持久层:查询"); int i = 10/0; } } public class MyAspectXml { public void afterThrowing(Exception ex){ System.out.println("抛出异常通知:" + ex.getMessage()); } }
<!-- AOP配置 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))" id="pointcut4"/> <!-- 配置切面 --> <aop:aspect ref="myAspectXml"> <!-- 抛出异常通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/> </aop:aspect> </aop:config>
5.最终通知: 不管是否有异常,都会执行的
public class CustomerDaoImpl implements CustomerDao { @Override public void find() { System.out.println("持久层:查询"); int i = 10/0; } } public class MyAspectXml { public void after(){ System.out.println("最终通知"); } }
<!-- AOP配置 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))" id="pointcut4"/> <!-- 配置切面 --> <aop:aspect ref="myAspectXml"> <!-- 最终通知 --> <aop:after method="after" pointcut-ref="pointcut4"/> </aop:aspect> </aop:config>
最终通知和后置通知的区别:
最终通知,不管异常与否,都执行;而后置通知在异常时不执行。
Spring注解形式的AOP入门 创建工程,引入jar包,创建核心配置文件(和XML引入的包相同)
配置文件
applicationContext.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" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="me.xueyao"></context:component-scan> <!-- 开启自动代理注解 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
创建接口和实现类
public interface CustomerDao { /** * 持久层:客户保存 */ public void save(); public int delete(); public void update(); public void find(); } @Repository("customerDao") public class CustomerDaoImpl implements CustomerDao { @Override public void save() { System.out.println("持久层:客户保存..."); } public int delete(){ System.out.println("持久层:客户删除..."); return 100; } @Override public void update() { System.out.println("持久层:客户更新"); } @Override public void find() { System.out.println("持久层:查询"); } }
编写切面类
@Component("myAspectAnnotation") @Aspect //可以省略id public class MyAspect { @Before("execution(* me.xueyao.dao.impl.CustomerDaoImpl.save(..))") public void checkPrivilege(JoinPoint joinPoint){ System.out.println("权限校验..." + joinPoint.toString()); } }
Spring的AOP中注解通知
1.前置通知
/** * 前置通知 * @param joinPoint */ @Before("execution(* me.xueyao.dao.impl.CustomerDaoImpl.save(..))") public void checkPrivilege(JoinPoint joinPoint){ System.out.println("权限校验..." + joinPoint.toString()); }
2.后置通知
@Aspectpublic class MyAspect { /** * 后置通知 * @param result */ @AfterReturning(value="execution(* me.xueyao.dao.impl.CustomerDaoImpl.delete(..))",returning="result") public void afterReturning(Object result){ System.out.println("后置通知:" + result); } }
3.环绕通知
@Around("execution(* me.xueyao.dao.impl.CustomerDaoImpl.update(..))") public Object after(ProceedingJoinPoint joinpoint) throws Throwable{ System.out.println("环绕通知前增强"); Object obj = joinpoint.proceed(); System.out.println("环绕通知后增强"); return obj; }
4.异常通知
@AfterThrowing(value="execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))",throwing="ex") public void afterThrowing(Exception ex){ System.out.println("抛出异常通知"); }
5.最终通知
@After("execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))")public void after(){ System.out.println("最终通知"); }
6.PointCut注解(了解)
作用:用于定义切入点表达式的一个注解。
@Component("myAspectAnnotation") @Aspect public class MyAspect { /** * 前置通知 * @param joinPoint */ @Before("execution(* me.xueyao.dao.impl.CustomerDaoImpl.save(..))") public void checkPrivilege(JoinPoint joinPoint){ System.out.println("权限校验..." + joinPoint.toString()); } @AfterReturning(value="execution(* me.xueyao.dao.impl.CustomerDaoImpl.delete(..))",returning="result") public void afterReturning(Object result){ System.out.println("后置通知:" + result); } @Around("execution(* me.xueyao.dao.impl.CustomerDaoImpl.update(..))") public Object aroung(ProceedingJoinPoint joinpoint) throws Throwable{ System.out.println("环绕通知前增强"); Object obj = joinpoint.proceed(); System.out.println("环绕通知后增强"); return obj; } // @AfterThrowing(value="execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))",throwing="ex") @AfterThrowing(value="MyAspect.pointcut()",throwing="ex") public void afterThrowing(Exception ex){ System.out.println("抛出异常通知"); } // @After("execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))") @After("MyAspect.pointcut()") public void after(){ System.out.println("最终通知"); } @Pointcut("execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))") public void pointcut(){ } }