spring注解驱动开发(AOP功能)

一.AOP概述

1.AOP说明:指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。

几点说明:

1) 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
2) AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
3) 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
4) AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
5) 经典应用:事务管理、性能监视、安全检查、缓存 、日志等
6) Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
7) AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入

2.AOP实现原理

a. aop底层将采用代理机制进行实现。
b. 接口 + 实现类 :spring采用 jdk 的动态代理Proxy。
c. 实现类:spring 采用 cglib字节码增强。

3.AOP术语

1.target:目标类,需要被代理的类。例如:UserService
2.Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
3.PointCut 切入点:已经被增强的连接点。例如:addUser()
4.advice 通知/增强,增强代码。例如:after、before
5.Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
6.proxy 代理类
7.Aspect(切面): 是切入点pointcut和通知advice的结合
	一个切入点和一个通知,组成成一个特殊的面。

4.通知类型:

@Before:前置通知(应用:各种校验)
	在方法执行前执行,如果通知抛出异常,阻止方法运行
@AfterReturning:后置通知(应用:常规数据处理)
	方法正常返回后执行,如果方法中抛出异常,通知无法执行
	必须在方法执行后才执行,所以可以获得方法的返回值。
@around:环绕通知(应用:十分强大,可以做任何事情)
	方法执行前后分别执行,可以阻止方法的执行
	必须手动执行目标方法
@afterThrowing:抛出异常通知(应用:包装异常信息)
	方法抛出异常后执行,如果方法没有抛出异常,无法执行
@after:最终通知(应用:清理现场)
	方法执行完毕后执行,无论方法中是否出现异常

5.切入点表达式

语法:execution(修饰符  返回值  包.类.方法名(参数) throws异常)

修饰符,一般省略
	public		公共方法
	*			任意

返回值,不能省略
	void		返回没有值
	String		返回值字符串
	* 			任意

包,[省略]
	com.gyf.crm			    固定包
	com.gyf.crm.*.service	crm包下面子包任意 (例如:com.gyf.crm.staff.service)
	com.gyf.crm..			crm包下面的所有子包(含自己)
	com.gyf.crm.*.service..	crm包下面任意子包,固定目录service,service目录任意包

类,[省略]
	UserServiceImpl			指定类
	*Impl					以Impl结尾
	User*					以User开头
	*						任意

方法名,不能省略
	addUser					固定方法
	add*					以add开头
	*Do					    以Do结尾
	*						任意

(参数)
	()						无参
	(int)					一个整型
	(int ,int)				两个
	(..)					参数任意

二.AOP示例

1.需求

假设有一个业务逻辑:
一个数学计算类(MathCalculator),里面有一个除法方法,现在要在除法方法运行的时候将日志进行打印(方法之前,方法运行结束。。。)

2.代码

(1) 配置类 MainConfig.java

package com.miracle.springAnnotation.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.miracle")
@EnableAspectJAutoProxy // 给配置类中添加这个注解,来开启基于注解版的切面功能
public class MainConfig {

}

(2) 目标类 MathCalculator.java

package com.miracle.springAnnotation.aop;

import org.springframework.stereotype.Component;

@Component // 将目标类加入容器中
public class MathCalculator {

    public int div(int i, int j) {
        return i / j;
    }
}

(3) 切面类 LogAspects.java

package com.miracle.springAnnotation.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;

/**
 *  切面类
 *  切点表达式书写位置:
 *      第一种:写到通知方法上面的注解里
 *      第二种:抽取公共的切入点表达式
 */
@Component // 将切面类加入容器中
@Aspect // 仅仅加入容器中还不够,还要告诉Spring当前类是一个切面类
public class LogAspects {

    // 切点表达式 第二种 书写位置
    // @Pointcut() 注解内写公共的切点表达式,然后其他方法要想用,直接引用@Pointcut()注解修饰的方法即可
    @Pointcut("execution(public int com.miracle.springAnnotation.aop.MathCalculator.*(int, int))")
    public void pointCutMethod() {

    }

    // @Before在目标方法之前执行,后面是切点表达式
    // 切点表达式 第一种 书写位置,在@Before注解里面直接写表达式
    // @Before("public int com.miracle.springAnnotation.aop.MathCalculator.div(int, int)")
    @Before("pointCutMethod()")
    public void logStart(JoinPoint joinPoint) {
        // 这里在logStart方法 中传入 joinPoint对象 可以来获取目标方法参数
        System.out.println(joinPoint.getSignature().getName() + "运行。。。@Before:参数列表是:{" + Arrays.asList(joinPoint.getArgs()) + "}");
    }

    // @After最终通知,在目标方法之后执行
    // 切点表达式 第二种 书写位置 @After("pointCutMethod()") 内部直接引用pointCutMethod()即可
    // 如果在类的外部想用抽取的切点表达式,只需写方法的全路径名
    // 即:@After("com.miracle.springAnnotation.aop.LogAspects.pointCut()")
    @After("pointCutMethod()") // 类内部用抽取的切点表达式
    public void logEnd(JoinPoint joinPoint) {
        // 通过joinPoint 获取方法名
        System.out.println(joinPoint.getSignature().getName() +"结束。。。@After");
    }     

    // 第一个参数:接收返回值 value = "pointCutMethod()"
    // 第二个参数:将返回值 returning = "result" ,将返回值以result的名字传入到logReturn的参数中
    // 注意:JoinPoint joinPoint, Object result,JoinPoint要出现在第一位
    @AfterReturning(value = "pointCutMethod()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result) {
        System.out.println(joinPoint.getSignature().getName() + "正常返回。。。@AfterReturning:运行结果:{" + result + "}");
    }

    // 获取异常,同上
    @AfterThrowing(value = "pointCutMethod()",throwing = "exception")
    public void logException(JoinPoint joinPoint, Exception exception) {
        System.out.println(joinPoint.getSignature().getName() + "异常。。。@AfterThrowing异常信息:{" + exception.getStackTrace() + "}");
    }

}

(4) 测试

// 创建无参的AnnotationConfigApplicationContext 对象
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
MathCalculator mathCalculator = (MathCalculator)applicationContext.getBean("mathCalculator");
int result = mathCalculator.div(1, 0);
System.out.println(result);

三.声明式事务

spring注解版开启事务步骤:
(1).在配置类上 增加 @EnableTransactionManagement 注解,开启基于注解的事务管理功能
(2).向容器中注册 TransactionManager
(3).在业务层 需要事务的地方添加 @Transactional 注解

1.编写配置类 MainConfig.java

package com.miracle.springAnnotation.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;

/**
 * spring注解版事务开启步骤:
 *      1.在配置类 增加 @EnableTransactionManagement 注解,开启基于注解的事务管理功能
 *      2.向容器中注册 TransactionManager
 *      3.在业务层 需要事务的地方添加 @Transactional 注解
 */

@EnableTransactionManagement
@ComponentScan(value = "com.miracle")
@PropertySource(value = {"classpath:/dbconfig.properties"})
@Configuration
public class MainConfig {

    @Bean
    public DataSource dataSource(@Value("${db.user}") String user,
         @Value("${db.password}") String password,
         @Value("${db.driverClass}") String driverClass,
         @Value("${db.jdbcurl}") String jdbcUrl) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(password);
        dataSource.setDriverClass(driverClass);
        dataSource.setJdbcUrl(jdbcUrl);
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        return jdbcTemplate;
    }

    // 向容器中注册 TransactionManager
    @Bean
    public PlatformTransactionManager transactionManager(@Autowired DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

2.编写service层,dao层

package com.miracle.springAnnotation.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.UUID;

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert() {
        String sql = "insert into tbl_user (username, age) values (?, ?)";
        String username = UUID.randomUUID().toString().substring(0, 5);
        jdbcTemplate.update(sql, username, 19);
    }
}
package com.miracle.springAnnotation.service;

import com.miracle.springAnnotation.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    // 给方法上标注 @Transactional 表示当前方法是一个事务
    // 同时还要在配置文件中增加 @EnableTransactionManagement 注解,开启事务
    @Transactional
    public void insertUser() {
        userDao.insert();
        System.out.println("插入完成...");
        int i = 10/0;
    }
}

3.编写测试方法

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
UserService userService = context.getBean(UserService.class);
userService.insertUser();

你可能感兴趣的:(spring)