Spring笔记(三):Aop详解

一、Aop原理

(一)动态代理

1、详见:java进阶(七):详解JAVA代理

2、主要是Proxy 与 InvocationHandle r接口

(二)Cglib 实现

1、主要是 Enhancer 和 MethodInterceptor 接口

2、实现代码如下:

<span style="font-size:18px;">	/**
	 * 利用 cglib 实现
	 * @param targrt
	 * @return
	 */
	public static Object getCgProxy(final Object targrt,final Advice advice){
		
		//利用cglib 中的Enhancer
		Enhancer enhancer = new Enhancer();
		
		//把目标类设置为代理的父类(继承目标类,覆盖其所有非final的方法)
		enhancer.setSuperclass(targrt.getClass());
		
		//设置回调
		enhancer.setCallback(new MethodInterceptor() {//实现MethodInterceptor 接口
			
			@Override
			public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				Object object = null;
				try{
					advice.before(method);//前置
					object = methodProxy.invoke(targrt, args);
					advice.after(method);//后置
				}catch (Exception e) {
					advice.afterThrow(method);//例外
				}finally{
					advice.afterFinally(method);//最终
				}

				return object;
			}
		});
		
		return enhancer.create();
	}
</span>


(三)环境及概念

1、需要的包:spring的包,还需要 aspectjweaver.jar,aopalliance.jar ,asm.jar 和cglib.jar 。

2、Aop的实现方式:Spring 接口方式,schema配置方式和注解的三种方式

3、概念

1)切面(aspect):用来切插业务方法的类。

2)连接点(joinpoint):是切面类和业务类的连接点,其实就是封装了业务方法的一些基本属性,作为通知的参数来解析。

3)通知(advice):在切面类中,声明对业务方法做额外处理的方法。

4)切入点(pointcut):业务类中指定的方法,作为切面切入的点。其实就是指定某个方法作为切面切的地方。

5)目标对象(target object):被代理对象。

6)AOP代理(aop proxy):代理对象。

7)前置通知(before advice):在切入点之前执行。

8)后置通知(after returning advice):在切入点执行完成后,执行通知。

9)环绕通知(around advice):包围切入点,调用方法前后完成自定义行为。

10、异常通知(after throwing advice):在切入点抛出异常后,执行通知。

二、Spring接口的aop实现

(一)详见:Spring AOP (上)

(二)分析

1、实现依赖比较麻烦,spring定义了一堆通知的接口,只要是实现即可。如前置通知接口 MethodBeforeAdvice

<span style="font-size:18px;">package main.java.com.spring.aop.apj;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

/**
 * Spring接口的前置通知
 * BaseBeforeAdvice
 * @title
 * @desc
 * @author SAM-SHO
 * @Jan 17, 2015
 */
public class BaseBeforeAdvice implements MethodBeforeAdvice {

    /**
     * method : 切入的方法 <br>
     * args :切入方法的参数 <br>
     * target :目标对象
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("===========进入beforeAdvice()============ \n");

        System.out.print("准备在" + target + "对象上用");
        System.out.print(method + "方法进行对 '");
        System.out.print(args[0] + "'进行删除!\n\n");

        System.out.println("要进入切入点方法了 \n");
    }

}</span>

2、需要 指定切点:实现接口 MethodBeforeAdvice 

<span style="font-size:18px;">/**
 * 定义一个切点,指定对应方法匹配。来供切面来针对方法进行处理<br>
 * 
 * 继承NameMatchMethodPointcut类,来用方法名匹配
 * 
 * @author 
 * 
 */
public class Pointcut extends NameMatchMethodPointcut {

    private static final long serialVersionUID = 3990456017285944475L;

    @SuppressWarnings("rawtypes")
    @Override
    public boolean matches(Method method, Class targetClass) {
        // 设置单个方法匹配
        this.setMappedName("delete");
        // 设置多个方法匹配
        String[] methods = { "delete", "modify" };
        
        //也可以用“ * ” 来做匹配符号
        // this.setMappedName("get*");
        
        this.setMappedNames(methods);

        return super.matches(method, targetClass);
    }

}</span>


3、需要配置。很麻烦。

三、spring利用aspectj 实现aop

(一)源码

1、业务类(target)

<span style="font-size:18px;">package main.java.com.spring.aop.service;

/**
 * 业务接口
 * PersonService
 * @title
 * @desc
 * @author SAM-SHO
 * @Jan 17, 2015
 */
public interface PersonService {
	/**
	 * 保存用户实体
	 * @param name
	 */
	public void save(String name);
	
	/**
	 * 删除用户实体
	 * @param id
	 */
	public void delete(Integer id);
	
	/**
	 * 查询编号为id的使用用户姓名
	 * @param id
	 */
	public String queryPersonName(Integer id);
	
	/**
	 * 更新实体
	 * @param name
	 * @param id
	 */
	public void update(String name ,Integer id);
}
</span>

<span style="font-size:18px;">package main.java.com.spring.aop.service.impl;

import main.java.com.spring.aop.service.PersonService;

/**
 * 业务实现类
 * PersionServiceImpl
 * @title
 * @desc
 * @author SAM-SHO
 * @Jan 17, 2015
 */
public class PersonServiceImpl implements PersonService {

	
	@Override
	public void save(String name) {
		System.out.println("我是save()方法");

	}

	@Override
	public void delete(Integer id) {
		System.out.println("我是 delete() 方法");

	}

	@Override
	public String queryPersonName(Integer id) {
		System.out.println("我是 query() 方法");
		return "XXX";
	}

	@Override
	public void update(String name, Integer id) {
		System.out.println("我是 update() 方法");

	}

}
</span>


2、切面类(aspect:类内部定义各种通知advice)

<span style="font-size:18px;">package main.java.com.spring.aop.apj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 
 * AspectAdvice
 * @title 切面类
 * @desc
 * @author SAM-SHO
 * @Jan 11, 2015
 */
public class AspectAdvice {

    /**
     * 前置通知
     * 
     * @param jp
     */
    public void doBefore(JoinPoint jp) {
        System.out.println("===========进入before advice============ \n");

        System.out.println("准备在" + jp.getTarget().getClass() + "对象上用");
        System.out.println(jp.getSignature().getName() + "方法进行对 '");
        System.out.println(jp.getArgs()[0] + "'进行删除!\n\n");

        System.out.println("要进入切入点方法了 \n");
    }

    /**
     * 后置通知
     * 
     * @param jp
     *            连接点
     * @param result
     *            返回值
     */
    public void doAfter(JoinPoint jp, String result) {
        System.out.println("==========进入after advice=========== \n");
        System.out.println("切入点方法执行完了 \n");

        System.out.print(jp.getArgs()[0] + "在");
        System.out.print(jp.getTarget().getClass() + "对象上被");
        System.out.print(jp.getSignature().getName() + "方法删除了");
        System.out.print("只留下:" + result + "\n\n");
    }

    /**
     * 环绕通知
     * 
     * @param pjp
     *            连接点
     */
    public void doAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("===========进入around环绕方法!=========== \n");

        // 调用目标方法之前执行的动作
        System.out.println("调用方法之前: 执行!\n");

        // 调用方法的参数
        Object[] args = pjp.getArgs();
        // 调用的方法名
        String method = pjp.getSignature().getName();
        // 获取目标对象
        Object target = pjp.getTarget();
        // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
        Object result = pjp.proceed();

        System.out.println("调用方法结束:之后执行!\n");
        
        System.out.println("输出:" + args[0] + ";" + method + ";" + target + ";" + result + "\n");
        
    }

    /**
     * 异常通知
     * 
     * @param jp
     * @param e
     */
    public void doThrow(JoinPoint jp, Throwable e) {
        System.out.println("删除出错啦");
    }

}</span>

3、配置。

<span style="font-size:18px;">    <!-- 声明一个业务类的Bean -->
    <bean id="personService" class="main.java.com.spring.aop.service.impl.PersonServiceImpl" /> 

    <!-- 声明通知类 -->
 	<bean id="aspectAdvice" class="main.java.com.spring.aop.apj.AspectAdvice" />

    <aop:config>
        <aop:aspect id="businessAspect" ref="aspectAdvice">
            <!--配置指定切入的对象  -->
            <aop:pointcut id="point_cut" expression="execution(* main.java.com.spring.aop.service..*.*(..))" />
            <!--只匹配add方法作为切入点  -->
            <!-- <aop:pointcut id="except_add" expression="execution(* aop.schema.*.add(..))" /> -->
            <!--前置通知  -->
            <aop:before method="doBefore" pointcut-ref="point_cut" />
            <!--后置通知 returning指定返回参数  -->
            <!-- <aop:after-returning method="doAfter" pointcut-ref="point_cut" returning="result" /> -->
            <!--环绕  -->
            <!-- <aop:around method="doAround" pointcut-ref="point_cut"/> -->
            <!--throw中  -->
            <!-- <aop:after-throwing method="doThrow" pointcut-ref="point_cut" throwing="e"/> -->
        </aop:aspect>
    </aop:config>
</span>

4、测试。

<span style="font-size:18px;">	@Test
	public void sopTest(){
		ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationAopContext.xml");
		PersonService personService = (PersonService) cxt.getBean("personService");
		personService.save("shaoxiaobao...");
//		tAspectBusiness.delete("zhaoxioaniu");
	} 
</span>

四、注解实现aop

(一)实现配置

1、开打自动扫描,注解实现Bean的管理与注入。(目标与切面都交由spring管理)

<span style="font-size:18px;">    <!-- 开打自动扫描 -->
    <context:component-scan base-package="main.java.com.spring" /></span>

2、开发Aop的注解

<span style="font-size:18px;">    <!-- 打开aop 注解 -->
    <aop:aspectj-autoproxy /></span>

(二)注解实现切面类。

<span style="font-size:18px;">package main.java.com.spring.aop.apj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component//让spring管理bean
@Aspect//定义切面类
public class AnnotationAspectAdvice {
	/*
     * 指定切入点匹配表达式,注意它是以方法的形式进行声明的。
     * 分析:
     *  execution 是方法的织入语言
     * 	第一个 * :返回任意类型
     *  main.java.com.spring.aop.service: 包。
     *  ..:service包以及其子包。
     *  第二个 * :service包以及其子包下的任意类。
     *  第三个 * :service包以及其子包下的任意类的任意方法。
     *  (..) :方法的参数为任意。
     *  总结:对 main.java.com.spring.aop.service包以及其子包下的任意类的任意方法作切入
     */
    @Pointcut("execution(* main.java.com.spring.aop.service..*.*(..))")
    public void anyMethod() {
    }

    /**
     * 前置通知
     * 
     * @param jp
     */
    @Before(value = "execution(* main.java.com.spring.aop.service..*.*(..))")
    public void doBefore(JoinPoint jp) {
        System.out.println("===========进入before advice============ \n");

        System.out.print("准备在" + jp.getTarget().getClass() + "对象上用");
        System.out.print(jp.getSignature().getName() + "方法进行对 '");
        System.out.print(jp.getArgs()[0] + "'进行删除!\n\n");

        System.out.println("要进入切入点方法了 \n");
    }

    /**
     * 后置通知
     * 
     * @param jp
     *            连接点
     * @param result
     *            返回值
     */
    @AfterReturning(value = "anyMethod()", returning = "result")
    public void doAfter(JoinPoint jp, String result) {
        System.out.println("==========进入after advice=========== \n");
        System.out.println("切入点方法执行完了 \n");

        System.out.print(jp.getArgs()[0] + "在");
        System.out.print(jp.getTarget().getClass() + "对象上被");
        System.out.print(jp.getSignature().getName() + "方法删除了");
        System.out.print("只留下:" + result + "\n\n");
    }

    /**
     * 环绕通知
     * 
     * @param pjp
     *            连接点
     */
    @Around(value = "execution(* main.java.com.spring.aop.service..*.*(..))")
    public void doAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("===========进入around环绕方法!=========== \n");

        // 调用目标方法之前执行的动作
        System.out.println("调用方法之前: 执行!\n");

        // 调用方法的参数
        Object[] args = pjp.getArgs();
        // 调用的方法名
        String method = pjp.getSignature().getName();
        // 获取目标对象
        Object target = pjp.getTarget();
        // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
        Object result = pjp.proceed();

        System.out.println("输出:" + args[0] + ";" + method + ";" + target + ";" + result + "\n");
        System.out.println("调用方法结束:之后执行!\n");
    }

    /**
     * 异常通知
     * 
     * @param jp
     * @param e
     */
    @AfterThrowing(value = "execution(* main.java.com.spring.aop.service.*.*(..))", throwing = "e")
    public void doThrow(JoinPoint jp, Throwable e) {
        System.out.println("删除出错啦");
    }


}
</span>


1、JoinPoint :切入点,可以得到切入代理对象的信息

1)getArgs():获取方法参数。

2)getTarget():得到目标对象。

3)getSignature():得到方法。

2、ProceedingJoinPoint:环绕方法的使用。

1)proceed():执行目标对象的方法。

3、Throwable:异常获取。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值