AOP面向切面编程

基于动态代理的,可以使用JDK,cglib两种代理方式

  aop就是动态代理的规范化,把动态代理的实现步骤定义,让开发人员用统一的方式使用动态代理。

aop: Aspect Orient Programming
  Aspect:切面,给目标类增加的功能,就是切面
    特点:一般都是非业务方法,可以独立使用的;

  Orient:面向
  Programming:编程

怎么理解面向切面编程?
  1.需要在分析项目功能时找出切面
  2.合理的安排切面的执行时间,在目标方法之前还是之后
  3.合理的安排切面执行的位置,在哪个类,哪个方法增加增强功能

1.切面:
  表示增强的功能,完成某一个功能,非业务的,常见的有日志,事务、统计信息
2.JoinPoint:连接点,链接业务方法和切面的位置,某类中的业务方法

3.Pointcut:切入点,指多个连接点方法的集合,多个方法

4.目标对象:给哪个类的方法增加功能,这个类就叫做目标类

5.Advice:通知,表示切面功能执行的时间

一个切面有3个关键要素
  1.切面的功能代码,作用
  2.切面的执行位置,使用Pointcut表示切面执行的位置
  3.切面的执行时机,使用Advice表示执行时间,在目标方法之前还是目标方法执行之后

public class MyUtil {

//切面功能
public static void log(){
System.out.println("增加日志功能");
}

public static void trans(){
System.out.println("增加事务处理");
}
}

  

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
String name = method.getName();
if ("some".equals(name)){ //joinPoint 多个点是Pointcut
MyUtil.log();
obj = method.invoke(target,args);
MyUtil.trans();
}else {
obj = method.invoke(target,args);
}
return obj;
}

  


aop的实现
  aop是一个规范,是动态的一个规范化,一个标准
  aop的技术实现框架:
  spring:主要在事务处理时使用aop,很少使用,比较笨重

aspectJ:开源的,专门做aop的框架
  1.使用xml配置文件
  2.使用注解(一般使用注解)

学习aspectJ框架的使用:aspectJ的5个通知注解
  1.切面的执行时间,在规范中叫做Advice(通知,增强)
    1)@Before
    2)@AfterReturning
    3)@Around
    4)@AfterThrowing
    5)@After

  2.表示切面执行的位置,使用的是切入点表达式。(*表示需要有的,?表示可选,各部分之间使用空格隔开)
    execution(访问权限? 方法的返回值* 方法声明*(参数) 异常类型?)

    * 0至多个任意字符
    .. 用在方法参数中,表示任意多个参数;用在包名后表示当前包及其子包路径
    + 用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类

    示例:execution(public * *(..))
    解释:指定切入点为任意公共方法
    execution(* set*(..))
    解释:任何一个以set开始的方法
    execution(* com.xyz.service.*.*(..))
    解释:指定包下的所有类的方法(不包含子包)
    execution(* com.xyz.service..*.*(..))
    解释:指定包下的所有类的方法(包含子包)

 aop的实现步骤总结

  1.创建maven项目,加入spring和aspectJ依赖

  2.创建接口和实现类

  3.创建切面类,类上添加@Aspects注解,添加增加功能的方法,方法上标注执行的时机和执行的位置,例如@Before(value="excution(* *..impl.*.*(..))")

  4.创建spring配置文件,声明目标对象和代理类对象,声明自动代理 <aop:aspectj-autoproxy/>

  5.创建测试类,获取目标类对象,执行方法

实现步骤:
  1.新建maven项目
  2.加入依赖
    spring
    aspectJ
    junit
  3.创建目标类,接口和实现类
    目的:给类中的方法增加功能
  4.创建切面类
    在类的上面加注解@Aspect
    在类中定义方法,方法就是切面要执行的功能代码
    在方法的上面加入aspectJ中的通知注解,例如@Before
    需要指定切入点表达式execution()

  5.创建spring配置文件,声明对象,把对象交给容器统一管理
    使用注解或则使用配置文件的bean标签
    1.声明目标对象
    2.声明切面类对象
    3.声明AspectJ框架中的自动代理生成器标签
    自动代理生成器:用来完成代理对象的自动创建功能的

  6.创建测试类
    从spring容器中获取目标对象(实际就是代理对象),通过代理执行方法,实现aop的功能增强

示例:

public interface SomeService {
    void some(String name,Integer age);
}

  

public class SomeServiceImpl implements SomeService{

    @Override
    public void some(String name, Integer age) {
        System.out.println("some service 执行");
    }
}

  

@Aspect
public class MyAspects {
    @Before(value = "execution(* *..impl.*.*(..))")
    public void exe(){
        System.out.println("执行时间:"+new Date());
    }

}

  

 <bean class="com.demo.service.impl.SomeServiceImpl" id="service"/>
    <bean class="com.demo.service.aspect.MyAspects" id="aspects"/>

    <aop:aspectj-autoproxy/>

  

public class MyTest {
    @Test
    public void test(){
        String config = "ApplicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ac.getBean("service");
        service.some("lise",20);
    }
}

  

参数joinPoint使用:

@Aspect
public class MyAspects {
    @Before(value = "execution(* *..impl.*.*(..))")
    public void exe(JoinPoint joinPoint){
        System.out.println("方法的签名(定义)="+joinPoint.getSignature());
        System.out.println("方法的名称"+joinPoint.getSignature().getName());
        //方法的实参
        Object[] args = joinPoint.getArgs();
        for (Object arg:args){
            System.out.println(arg);
        }
        System.out.println("执行时间:"+new Date());
    }

}

  

后置通知:

public interface SomeService {
    void doSome(String name,Integer age);

    String doOther(String name,Integer age);
}

  

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name, Integer age) {
        System.out.println("dosome 方法执行成功");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("doother 方法执行成功");
        return "abc";
    }
}

  

@Aspect
public class MyAspect {

    @Before(value = "execution(* *..ba01.*impl.*(..))")
    public void some(){
        System.out.println("执行时间:"+new Date());
    }

    /**
     * @AfterReturning 后置通知
     *  value表示切入点表达式
     *  returning自定义的变量,表示目标方法的返回值,自定义变量名必须和通知方法的形参名一样
     *
     *      特点:
     *      在目标方法执行之后执行
     *      能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
     *      可以修改这个返回值
     *      相当于:Object res = doOther();
     * @param res
     */

    @AfterReturning(value = "execution(* *..*Impl.doOther(..))",returning = "res")
    public void myAfterReturning(Object res){
        //res是目标方法执行之后的返回值,根据返回值做切面的处理功能
        System.out.println("目标方法执行之后获取到的返回值是:"+res);
        if (res.equals("abc")){
            System.out.println("操作成功 ,提交事务");
        }else {
            System.out.println("发生异常 ,事务回滚");
        }
    }
}

  

<bean class="com.demo.ba01.SomeServiceImpl" id="service"/>
    <bean class="com.demo.ba01.MyAspect" id="aspect"/>
    <aop:aspectj-autoproxy/>

  

public class MyTest {
    @Test
    public void test01(){
        String config = "ApplicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ac.getBean("service");
        //service.doSome("lisi",20);
        //System.out.println("====================");
        String other = service.doOther("mike", 21);

    }
}

  

思考:后置通知是在目标方法执行之后执行,如果目标方法返回值是一个引用类型,在后置通知方法中对引用类型的属性值就行修改,会不会影响最后的调用结果?

原文地址:https://www.cnblogs.com/ethnic/p/14581454.html