Spring Aop学习 【转】

转载 2013年12月04日 15:06:44

转自:http://www.2cto.com/kf/201208/149757.html 感谢原作者的劳动


程序员还是需要把基础打扎实,修炼自己的内功。” 所以赶紧把学习的东西总结一下,加深印象。 。基于代理模式,了解了jdk动态代理和cglib的用法。但是在真正的使用AOP的时候,不可能写这么厚重的方法。
  Spring有两大核心,IOC和AOP。IOC在java web项目中无时无刻不在使用。然而AOP用的比较少,的确也是一般的项目用的场所不多。事务控制基本都用,但却是Spring封装的不需要我们再去实现,但Spring的AOP远不止这些,不能因为项目中没有使用,而不去学习及理解。我觉得这是作为一个java web软件开发人员必须具备的技能。业内很多将AOP应用在日志记录上,可惜我们项目没这么做,后面需要学习下。在这先把Spring AOP的基本用法,在脑子里理一边,做一次积累。
1、概念术语  
  在开始之前,需要理解Spring aop 的一些基本的概念术语(总结的个人理解,并非Spring官方定义):
  切面(aspect):用来切插业务方法的类。
  连接点(joinpoint):是切面类和业务类的连接点,其实就是封装了业务方法的一些基本属性,作为通知的参数来解析。
  通知(advice):在切面类中,声明对业务方法做额外处理的方法。
  切入点(pointcut):业务类中指定的方法,作为切面切入的点。其实就是指定某个方法作为切面切的地方。
  目标对象(target object):被代理对象。
  AOP代理(aop proxy):代理对象。
  通知:
  前置通知(before advice):在切入点之前执行。
  后置通知(after returning advice):在切入点执行完成后,执行通知。
  环绕通知(around advice):包围切入点,调用方法前后完成自定义行为。
  异常通知(after throwing advice):在切入点抛出异常后,执行通知。
2、Spring AOP环境
  要在项目中使用Spring AOP 则需要在项目中导入除了spring jar包之外,还有aspectjweaver.jar,aopalliance.jar ,asm.jar 和cglib.jar 。
好了,前提工作准备完成,Spring 提供了很多的实现AOP的方式,在学习过程中,循序渐进。进行Spring 接口方式,schema配置方式和注解的三种方式进行学习。好了废话不多说了,开始spring aop学习之旅:
3、方式一:AOP接口
  利用Spring AOP接口实现AOP,主要是为了指定自定义通知来供spring AOP机制识别。主要接口:前置通知 MethodBeforeAdvice ,后置通知:AfterReturningAdvice,环绕通知:MethodInterceptor,异常通知:ThrowsAdvice 。见例子代码:
a、业务接口:
View Code

/**
 * 代理类接口,也是业务类接口<br>
 *
 * 利用接口的方式,spring aop 将默认通过jdk 动态代理来实现代理类<br>
 * 不利用接口,则spring aop 将通过cglib 来实现代理类
 *
 * @author yanbin
 *
 */
public interface IBaseBusiness {

    /**
     * 用作代理的切入点方法
     *
     * @param obj
     * @return
     */
    public String delete(String obj);

    /**
     * 这方法不被切面切
     *
     * @param obj
     * @return
     */
    public String add(String obj);

    /**
     * 这方法切不切呢?可以设置
     *
     * @param obj
     * @return
     */
    public String modify(String obj);

}

b、业务类:
View Code

/**
 * 业务类,也是目标对象
 *
 * @author yanbin
 *
 */
public class BaseBusiness implements IBaseBusiness {

    /**
     * 切入点
     */
    public String delete(String obj) {
        System.out.println("==========调用切入点:" + obj + "说:你敢删除我!===========\n");
        return obj + ":瞄~";
    }

    public String add(String obj) {
        System.out.println("================这个方法不能被切。。。============== \n");
        return obj + ":瞄~ 嘿嘿!";
    }

    public String modify(String obj) {
        System.out.println("=================这个也设置加入切吧====================\n");
        return obj + ":瞄改瞄啊!";
    }

}

c、通知类:
前置通知:
View Code

/**
 * 前置通知。
 *
 * @author yanbin
 *
 */
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");
    }

}

后置通知:
View Code

/**
 * 后置通知
 *
 * @author yanbin
 *
 */
public class BaseAfterReturnAdvice implements AfterReturningAdvice {

    /**
     * returnValue :切入点执行完方法的返回值,但不能修改 <br>
     * method :切入点方法 <br>
     * args :切入点方法的参数数组 <br>
     * target :目标对象
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("==========进入afterReturning()=========== \n");
        System.out.println("切入点方法执行完了 \n");

        System.out.print(args[0] + "在");
        System.out.print(target + "对象上被");
        System.out.print(method + "方法删除了");
        System.out.print("只留下:" + returnValue + "\n\n");
    }

}

环绕通知:
View Code

/**
 * 环绕通知
 *
 * @author yanbin
 *
 */
public class BaseAroundAdvice implements MethodInterceptor {

    /**
     * invocation :连接点
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("===========进入around环绕方法!=========== \n");

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

        // 调用方法的参数
        Object[] args = invocation.getArguments();
        // 调用的方法
        Method method = invocation.getMethod();
        // 获取目标对象
        Object target = invocation.getThis();
        // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
        Object returnValue = invocation.proceed();

        System.out.println("===========结束进入around环绕方法!=========== \n");
       
        System.out.println("输出:" + args[0] + ";" + method + ";" + target + ";" + returnValue + "\n");

        System.out.println("调用方法结束:之后执行!\n");

        return returnValue;
    }

}

异常通知:
View Code

/**
 * 异常通知,接口没有包含任何方法。通知方法自定义
 *
 * @author yanbin
 *
 */
public class BaseAfterThrowsAdvice implements ThrowsAdvice {

    /**
     * 通知方法,需要按照这种格式书写
     *
     * @param method
     *            可选:切入的方法
     * @param args
     *            可选:切入的方法的参数
     * @param target
     *            可选:目标对象
     * @param throwable
     *            必填 : 异常子类,出现这个异常类的子类,则会进入这个通知。
     */
    public void afterThrowing(Method method, Object[] args, Object target, Throwable throwable) {
        System.out.println("删除出错啦");
    }

}

d、定义指定切点:
View Code

/**
 * 定义一个切点,指定对应方法匹配。来供切面来针对方法进行处理<br>
 *
 * 继承NameMatchMethodPointcut类,来用方法名匹配
 *
 * @author yanbin
 *
 */
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);
    }

}

e、配置:
View Code

<?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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="    
          http://www.springframework.org/schema/beans    
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
          http://www.springframework.org/schema/context    
          http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/aop    
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
    default-autowire="byName">

    <!-- ==============================利用spring自己的aop配置================================ -->
    <!-- 声明一个业务类 -->
    <bean id="baseBusiness" class="aop.base.BaseBusiness" />
   
    <!-- 声明通知类 -->
    <bean id="baseBefore" class="aop.base.advice.BaseBeforeAdvice" />
    <bean id="baseAfterReturn" class="aop.base.advice.BaseAfterReturnAdvice" />
    <bean id="baseAfterThrows" class="aop.base.advice.BaseAfterThrowsAdvice" />
    <bean id="baseAround" class="aop.base.advice.BaseAroundAdvice" />

    <!-- 指定切点匹配类 -->
    <bean id="pointcut" class="aop.base.pointcut.Pointcut" />

    <!-- 包装通知,指定切点 -->
    <bean id="matchBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="pointcut">
            <ref bean="pointcut" />
        </property>
        <property name="advice">
            <ref bean="baseBefore" />
        </property>
    </bean>

    <!-- 使用ProxyFactoryBean 产生代理对象 -->
    <bean id="businessProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 代理对象所实现的接口 ,如果有接口可以这样设置 -->
        <property name="proxyInterfaces">
            <value>aop.base.IBaseBusiness</value>
        </property>

        <!-- 设置目标对象 -->
        <property name="target">
            <ref local="baseBusiness" />
        </property>
        <!-- 代理对象所使用的拦截器 -->
        <property name="interceptorNames">
            <list>
                <value>matchBeforeAdvisor</value>
                <value>baseAfterReturn</value>
                <value>baseAround</value>
            </list>
        </property>
    </bean>
</beans>

f、测试类:
View Code

public class Debug {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("aop/schema_aop.xml");
        AspectBusiness business = (AspectBusiness) context.getBean("aspectBusiness");
        business.delete("猫");
    }

}

g、测试结果:运行下测试类,清晰明了。由于结果呈现太长就不贴了。
  具体的代码实现可以从代码注释中很容易理解 接口方式的实现。结果也可想而知,前置方法会在切入点方法之前执行,后置会在切入点方法执行之后执行,环绕则会在切入点方法执行前执行同事方法结束也会执行对应的部分。主要是调用proceed()方法来执行切入点方法。来作为环绕通知前后方法的分水岭。然后在实现的过程中,有几点却是可以细揣摩一下的。
  可以看出在xml 配置 businessProxy这个bean的时候,ProxyFactoryBean类中指定了,proxyInterfaces参数。这里我把他配置了IBaseBusiness接口。因为在项目开发过程中,往往业务类都会有对应的接口,以方便利用IOC解耦。但Spring AOP却也能支持没有接口的代理。这就是为什么需要导入cglib.jar的包了。看过spring的源码,知道在目标切入对象如果有实现接口,spring会默认走jdk动态代理来实现代理类。如果没有接口,则会通过cglib来实现代理类。
  这个业务类现在有 前置通知,后置通知,环绕三个通知同时作用,可能以及更多的通知进行作用。那么这些通知的执行顺序是怎么样的?就这个例子而言,同时实现了三个通知。在例子xml中,则显示执行before通知,然后执行around的前处理,执行切点方法,再执行return处理。最后执行around的后处理。经过测试,知道spring 处理顺序是按照xml配置顺序依次处理通知,以队列的方式存放前通知,以压栈的方式存放后通知。所以是前通知依次执行,后通知到切入点执行完之后,从栈里在后进先出的形式把后通知执行。
  在实现过程中发现通知执行对应目标对象的整个类中的方法,如何精确到某个方法,则需要定义一个切点匹配的方式:spring提供了方法名匹配或正则方式来匹配。然后通过DefaultPointcutAdvisor来包装通知,指定切点。

本来是想一口气梳理完的。但是大晚上时间不够(无奈一场奥运篮球总决赛耗费掉了2小时,不过的确相当精彩),又考虑到篇幅太长,阅读性比较差,所以将后半部分更偏于应用的重起一篇随笔。
  利用方式一的配置起来,可见代码还是非常的厚重的,定义一个切面就要定义一个切面类,然而切面类中,就一个通知方法,着实没有必要。所以Spring提供了,依赖aspectj的schema配置和基于aspectj 注解方式。这两种方式非常简介方便使用,也是项目中普遍的使用方式。梳理之:
4、方式二:schema配置
a、业务类:
View Code

/**
 * 业务类
 *
 * @author yanbin
 *
 */
public class AspectBusiness {

    /**
     * 切入点
     */
    public String delete(String obj) {
        System.out.println("==========调用切入点:" + obj + "说:你敢删除我!===========\n");
        return obj + ":瞄~";
    }

    public String add(String obj) {
        System.out.println("================这个方法不能被切。。。============== \n");
        return obj + ":瞄~ 嘿嘿!";
    }

    public String modify(String obj) {
        System.out.println("=================这个也设置加入切吧====================\n");
        return obj + ":瞄改瞄啊!";
    }

}

b、切面类:切面类中,包含了所有的通知
View Code

/**
 * 定义一个切面
 *
 * @author yanbin
 *
 */
public class AspectAdvice {

    /**
     * 前置通知
     *
     * @param jp
     */
    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
     *            返回值
     */
    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("输出:" + args[0] + ";" + method + ";" + target + ";" + result + "\n");
        System.out.println("调用方法结束:之后执行!\n");
    }

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

}

c、配置文件:
View Code

<?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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="    
          http://www.springframework.org/schema/beans    
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
          http://www.springframework.org/schema/context    
          http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/aop    
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
    default-autowire="byName">

    <!-- ==============================利用spring 利用aspectj来配置AOP================================ -->

    <!-- 声明一个业务类 -->
    <bean id="aspectBusiness" class="aop.schema.AspectBusiness" />

    <!-- 声明通知类 -->
    <bean id="aspectAdvice" class="aop.schema.advice.AspectAdvice" />

    <aop:config>
        <aop:aspect id="businessAspect" ref="aspectAdvice">
            <!-- 配置指定切入的对象 -->
            <aop:pointcut id="point_cut" expression="execution(* aop.schema.*.*(..))" />
            <!-- 只匹配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"/>
            <aop:after-throwing method="doThrow" pointcut-ref="point_cut" throwing="e"/>
        </aop:aspect>
    </aop:config>
</beans>

d、测试类:
View Code

public class Debug {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("aop/schema_aop.xml");
        AspectBusiness business = (AspectBusiness) context.getBean("aspectBusiness");
        business.delete("猫");
    }

}

5、方式三:aspectj注解
注解在项目中已经到处都是了,撇开一些优劣不提,开发的便利性和可读性是非常的方便的。用来配置Spring AOP也非常简单便利
a、业务类:
View Code

/**
 * 业务类
 *
 * @author yanbin
 *
 */
@Component
public class Business {

    /**
     * 切入点
     */
    public String delete(String obj) {
        System.out.println("==========调用切入点:" + obj + "说:你敢删除我!===========\n");
        return obj + ":瞄~";
    }

    public String add(String obj) {
        System.out.println("================这个方法不能被切。。。============== \n");
        return obj + ":瞄~ 嘿嘿!";
    }

    public String modify(String obj) {
        System.out.println("=================这个也设置加入切吧====================\n");
        return obj + ":瞄改瞄啊!";
    }

}

b、切面类:
View Code

/**
 * 定义切面
 *
 * @Aspect : 标记为切面类
 * @Pointcut : 指定匹配切点
 * @Before : 指定前置通知,value中指定切入点匹配
 * @AfterReturning :后置通知,具有可以指定返回值
 * @AfterThrowing :异常通知
 *
 * @author yanbin
 *
 */
@Component
@Aspect
public class AspectAdvice {

    /**
     * 指定切入点匹配表达式,注意它是以方法的形式进行声明的。
     */
    @Pointcut("execution(* aop.annotation.*.*(..))")
    public void anyMethod() {
    }

    /**
     * 前置通知
     *
     * @param jp
     */
    @Before(value = "execution(* aop.annotation.*.*(..))")
    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(* aop.annotation.*.*(..))")
    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(* aop.annotation.*.*(..))", throwing = "e")
    public void doThrow(JoinPoint jp, Throwable e) {
        System.out.println("删除出错啦");
    }

}

c、配置:
View Code

<?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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="    
          http://www.springframework.org/schema/beans    
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
          http://www.springframework.org/schema/context    
          http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/aop    
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
    default-autowire="byName">

    <context:component-scan base-package="aop.annotation" />
    <!-- 打开aop 注解 -->
    <aop:aspectj-autoproxy />

</beans>

d、测试类:
View Code

/**
 * 测试类
 *
 * @author yanbin
 *
 */
public class Debug {

    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("aop/annotation_aop.xml");
        Business business = (Business) context.getBean("business");
        business.delete("猫");
    }

}

Spring学习,依赖注入(DI)、控制反转(IoC)和面向切面(AOP)

依赖注入DI和控制反转IoC Spring容器中的依赖注入DI和控制反转IoC xml配置 注解配置 面向切面AOP 面向切面的应用动态代理 Spring容器中的面向切面 xml配置 注解配置 Spr...
  • Nicorui
  • Nicorui
  • 2017年02月13日 15:16
  • 339

Spring学习一:IOC(控制反转)和AOP(面向切面)的xml配置和注解方式

Spring框架的作用:为了简化java开发 Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On...

Spring IOC AOP学习示例

  • 2016年06月22日 17:33
  • 4.57MB
  • 下载

spring aop学习实例

  • 2008年01月10日 16:40
  • 11KB
  • 下载

java+spring切面编程(aop)spring控制反转(ioc)+hibenrate对象关系映射(ORM) 学习网址

java+spring切面编程(aop)spring控制反转(ioc)+hibenrate对象关系映射(ORM) 学习网址 java线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程...

Spring框架运行机制(控制反转,依赖注入,面向切面AOP)

文章属于入门级水平,重要事情说三遍,入门级,入门级,入门级 Mybatis: 实现IOC的轻量级的一个Bean的容器 Inversion of control 控制反转:由容器控制程序之间的关系,...

Spring基础、IOC(控制反转)、AOP(面向切面编程)、Log4j、注解配置

1.什么是Spring,有什么作用  --Spring框架属于一个解决方案框架,可以对其他技术和框架进行整合应用。  --*好处是:将程序中的各个组件和框架技术进行解耦,便于日后系统维护,升级和扩展操...

idea 实现Spring讲解(Ioc-控制反转)/Aop(面向切面的编程)

IOC 一.IOC(控制反转) 1.Spring框架的作用:管理我们项目业务中的各项Bean(service,dao,Action)实例化类。 2.Spring体系结构图       3.控制...

JavaEE框架——Spring入门基础(控制反转Ioc和切面技术Aop)

一.简介:      Spring在英语中含义是春天,对于JavaEE开发者来说,Spring框架出现确实带来了一股全新的春天的气息。早在2002年,Rod Johson在其编著的《Expert ...

【转】Java Spring AOP详解

一.前言     在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Spring Aop学习 【转】
举报原因:
原因补充:

(最多只允许输入30个字)