AOP

面向切面编程.百度百科里的定义是:在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

在Spring中使用AOP

定义一个组件

package com.spring.service.aop;

import org.springframework.stereotype.Component;

/**
 * Created by sky on 16-1-5.
 */
@Component
public class Hello {

    public String sayHello(){
        return "hello world from hello";
    }
}

组件的使用

    @Autowired
    private Hello hello;

    public String index() {
        return hello.sayHello();
    }

在其他组件中,调用Hello组件.

使用AOP进行无侵入记录调用Hello.sayHello的日志

package com.spring.service.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * Created by sky on 16-1-5.
 */
@Aspect
@Component
public class HelloAop {

    @After("execution(* com.spring.service.aop.Hello.sayHello() )")
    public void sayAfter(){
        System.out.println("aop after sayHello");
    }

}

Spring配置

<?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:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	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/task
			http://www.springframework.org/schema/task/spring-task-3.2.xsd
			http://www.springframework.org/schema/aop
			http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
			http://www.springframework.org/schema/tx
			http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
			http://www.springframework.org/schema/mvc
			http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

	<context:annotation-config />
    <context:component-scan base-package="com.spring" />

	<!-- 使用CGLIB 方式进行代理,这样子可以在普通类上,也可以进行切入,而不需要接口-->
	<aop:aspectj-autoproxy proxy-target-class="true" />

</beans>

当其他组件调用Hello.sayHello时,就会打印出如下文本:

aop after sayHello

注意事项

要切入的组件(Hello.sayHello()) 必须是Spring接管的(这里使用了@Component,让Spring扫描到,然后放到BeanFactory里)

AOP的用途

上面的例子,只是简单的记录下日志,那AOP最常见的用途是什么呢?在Spring里,最常用的,可能是声明式事务了,即(@Transactional)这个注解。

实现声明式事务

定义一个事务注解

package com.spring.aop;

import java.lang.annotation.*;

/**
 * Created by sky on 16-1-5.
 */

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyTransactional {
}

定义一个DB Service

package com.spring.aop;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * Created by sky on 16-1-5.
 */
@Component
public class MyService {

    @Autowired
    private DataSource dataSource;

    @MyTransactional
    public void doDB(String hello) throws SQLException {
        Connection connection = TransactionAOP.connectionThreadLocal.get();
        if(connection==null){
            connection = dataSource.getConnection();
            TransactionAOP.connectionThreadLocal.set(connection);
        }
        // do with connection.
        System.out.println("update db for table " + hello);
    }
}

定义一个事务的AOP

package com.spring.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLDataException;
import java.sql.SQLException;

/**
 * Created by sky on 16-1-5.
 */
@Aspect
@Component
public class TransactionAOP {

    @Pointcut("@annotation(com.uniweibov2.aop.MyTransactional)")
    public void transactionPointCut() {

    }

    @Autowired
    private DataSource dataSource;

    private static final ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<>();

    @Before("transactionPointCut()")
    public void doTransaction() throws SQLException {
        Connection connection = connectionThreadLocal.get();
        if (connection == null) {
            connection = dataSource.getConnection();
            connectionThreadLocal.set(connection);
        }
        connection.setAutoCommit(false);
        System.out.println("开启事务");
    }

    @After("transactionPointCut()")
    public void commit() throws SQLException {
        Connection connection = connectionThreadLocal.get();
        if (connection != null) {
            connection.commit();
            connection.close();
            System.out.println("提交事务,并关闭连接");
        } else {
            throw new SQLDataException("connection is closed ...");
        }

    }
}

Controller 层使用Db service


    @RequestMapping("/index")
    public String index() throws SQLException {
        myService.doDB("[hello world]");
        return "hello";
    }

执行结果如下:

开启事务
update db for table [hello world]
提交事务,并关闭连接

这样子,我们就完成了一个简单的声明式事务管理啦.当然,还有很多要完善的地方,这里只是演示下,如何实现.