spring的面向切面编程AOP最强解释

 

 

 

面向切面编程 AOP

也就是面向横切面编程, 在软件原有流程中横向织入扩展功能代码, 在不改变原有功能的情况下扩展横切功能.

生活中的横切面功能:

面向切面编程解决的横切问题:

面向切面编程中的关键概念:

  1. 切面组件
  2. 通知
  3. 切入点
  4. 运行期间织入

1. 切面组件

切面组件本身也是一个被Spring管理的Bean组件, 这个组件用于封装面向切面的功能扩展方法.

在软件中执行切向功能的组件. 是一种"拦截器"组件.

案例:

@Component
@Aspect
public class DemoAspect {

    //通知(切入点) 通知+切入点=织入位置
    @Before(
    "execution(* cn.tedu.note.service.*Service.*(..))")
    public void hello(){
        System.out.println("Hello World!");
    }
}

使用如上功能必须导入 AspectJ 相关的包, Spring提供的AOP功能是利用 AspectJ 的API实现的.

    <dependency>
        <groupId>aspectj</groupId>
        <artifactId>aspectjlib</artifactId>
        <version>1.5.3</version>
    </dependency>
    <dependency>
        <groupId>aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.5.3</version>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
    </dependency>

2. 通知

用于指定发生切向事件的位置点:

通知有如下几种:

  • @Before 
  • @AfterRetuning
  • @AfterThrowing
  • @After
  • @Around

其在方法的切位置关系如下: 

try{
    // @Before 
    切入点 方法
    // @AfterRetuning
}catch(e){
    // @AfterThrowing
}finally{
    // @After
}

@Around 通知, 是在方法前后调用的通知:

@Around("bean(userService)")
public Object testAround(
        ProceedingJoinPoint target)
    throws Throwable{
    System.out.println("调用开始");
    //执行目标方法
    Object obj=target.proceed();
    System.out.println("调用结束");
    return obj;
}

3. 切入点

切入点是指明 通知发送的位置.

仅仅指定通知是不能精确定位 切面 的织入位置, 还要在通知中指定切入点. 

有3种:

3.1:Bean组件切入 (按照Bean对象进行切入)

bean(beanId) 
如: bean(userService)  
    bean(noteService)
    bean(*Service)

多个Bean
    bean(userService) || bean(noteService)

3.2:类限定名: 包和类进行切入

within(包名.类名)
如:
within(cn.tedu.note.service.UserService)
within(cn.tedu.note.service.*)
within(cn.tedu.note.service.*Service)
within(cn.tedu.note..*Service)

3.3方法限定名: 切入到特定方法

execution(* 包.类.方法(参数))
如:
execution(* cn.tedu.note.service.UserService.login(..))
execution(* cn.tedu.note.service.UserService.find*(..))
execution(* cn.tedu.note.service.*Service.find*(..))
execution(* cn.tedu.note.service.*.find*(..))
execution(* cn.tedu.note..*.find*(..))
execution(* cn.tedu.note.service.*.*(..))

织入位置

通过通知和切入点的组合,才能明确指定切面代码的位置, 也就是: 

通知+切入点=织入位置



AOP 的配置

AOP注解必须与Spring配合才能起到作用, Spring的配置如下:

 <context:component-scan 
    base-package="cn.tedu.aop"/>
 <!-- 注解驱动 aspectj AOP,
    使 @Aspect 和 @Before 等注解起作用  -->
 <aop:aspectj-autoproxy />

案例:

@Component
@Aspect
public class DemoAspect {

    //通知(切入点) 通知+切入点=织入位置
    @Before("execution(* cn.tedu.note.service.*Service.*(..))")
    public void hello(){
        System.out.println("Hello World!");
    }

    @Around("bean(userService)")
    public Object testAround( ProceedingJoinPoint target)
        throws Throwable{
        System.out.println("调用开始");
        //执行目标方法
        Object obj=target.proceed();
        System.out.println("调用结束");
        return obj;
    }
}

测试结果说明: 如上代码在执行时候, 会在所有的业务层方法中织入 hello() 方法, 也就是在所有业务方法之前执行 hello(). 

 

AOP 是如何做到的:

  1. 采用动态代理模式实现.
  2. 动态代理是利用反射技术实现的.

代理模式:

 

案例:

@Component("userServiceProxy")
public class UserServiceProxy 
    implements UserService{

    @Autowired
    DemoAspect demoAspect;

    @Autowired 
    UserService target;

    public User login(String name, String password) throws NameOrPasswordException {
        //调用切面组件的方法?
        demoAspect.hello();
        System.out.println("Hello Proxy");
        return target.login(name, password);
    }
    public User regist(String name, String nick, String password, String confirm) {
        System.out.println("Hello Proxy");
        return target.regist(name, nick, password, confirm);
    }
}

如上类就是一个静态代理类, 代理的业务层UserService的方法.

 

动态代理技术

  1. JDK 的反射API提供了动态代理 
    • JDK的动态代理必须依赖于接口
  2. CBLIB API 提供了基于类的动态代理
  3. Spring会自动的使用如上API, 在有接口时候 优先选JDK代理, 如果没有接口,会自动利用CGLIB代理.

        Spring的AOP底层就是利用动态代理技术实现的.

        提示: 可以查看异常出现时候的方法调用栈, 来了解Spring内部的调用关系:

如:

java.lang.NullPointerException
    at cn.tedu.note.service.NoteServiceImpl.createNote(NoteServiceImpl.java:76)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:51)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at com.sun.proxy.$Proxy24.createNote(Unknown Source)
    at cn.tedu.note.web.NoteController.addNote(NoteController.java:58)
    at cn.tedu.note.web.NoteController$$FastClassBySpringCGLIB$$fb9c5e1b.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:700)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
    at cn.tedu.aop.ExceptionAspect.process(ExceptionAspect.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:633)
    at cn.tedu.note.web.NoteController$$EnhancerBySpringCGLIB$$d281c6f4.addNote(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:855)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at cn.tedu.note.util.AccessFilter.checkdo(AccessFilter.java:66)
    at cn.tedu.note.util.AccessFilter.doFilter(AccessFilter.java:45)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

Spring事务处理

关键特性: ACID

  • A 原子性
  • C 一致性
  • I 隔离性
  • D 持久性

Oracle 支持ACID

MySQL 有两种数据库引擎: 

  • MyISAM 引擎 不支持 ACID, 适合数据一致性要求不高,性能好就行
  • INNODB 引擎 支持ACID, 适合数据一致性高的场合.

编程式事务控制:

经典代码

try{
    开启事务
    业务处理.
    业务处理.
    提交事务
}catch(e){
    回滚事务
}finally{
    回收资源
}

很明显, 编程式事务处理, 非常适合替换为AOP事务处理:

Spring 事务处理, 是AOP的经典用途. Spring的事务处理底层就是AOP.

使用Spring 事务

使用Spring 事务 并不需要AOP编程, 只需要使用注解即可

配置:

<!-- 添加 注解事务支持 -->
<bean id="txManager" 
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" 
        ref="dataSource" />
</bean>
<tx:annotation-driven
    transaction-manager="txManager"/>

使用:

@Transactional(readOnly=true,
        isolation=Isolation.READ_COMMITTED,
        propagation=Propagation.REQUIRED)
public Note loadNote(String noteId) {
    if(noteId==null || noteId.trim().isEmpty()){
        throw new IllegalArgumentException("noteId不能空");
    }
    return noteDao.findNoteById(noteId);
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

任错错

如果对您有帮助我很开心

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值