Spring 传统AOP实例
2013-10-30 09:07
357 查看
参考AOP入门(一)中的例子,外面来实现用Spring
AOP加入各种统计的东东。
注:
这篇文章中,我用的是完全传统的Spring AOP,不带有任何AspectJ的东西。
在前面AOP实现一文中提到,Spring
AOP要求被代理类必须由Spring容器来管理,即是一个SpringBean。所以,我们要做的第一步,就是配置引入Spring容器管理。
在src/main/resources/下添加applicationContext.xml
(名字可任意)如下
修改main代码如下
这里传入的applicationContext.xml就是Spring配置文件的名字。定的是什么,这里就传什么。所以说,配置名任意。
运行下试试
报错了,为何?
[com.edi.poc.Zoo]: No default constructor found; nested exception is java.lang.NoSuchMethodException:
因为我们的zoo没有无参构造函数,Spring的BeanFactory默认会去调无参构造函数。修改如下:
得到结果:
好,现在加入整个公园的客流量,显然是在Zoo的enter方法出+1,即before
enter +1
创建统计类
创建一个简单的beforeadvice
修改配置文件
注:
Spring的ProxyFactory在创建Bean的时候,动态的去选择JDK或CGLIB来做反射生成目标类。可以通过上面蓝色部分强制声明为CGLIB。
运行下试试
这里原因就是我实现的Zoo类有一个非空参的构造函数,那么空参构造函数就必须自己声明,CGLIB要求必须提供一个空参构造器。
加上空参构造器后再执行,打印如下:
这里看到 "Before method"
打印了四次,因为zoo总共被调用了四次
Before .open()
Before .addHall()
Before.enter()
Before .close()
其实我们只要before.enter(),所以要指定改BeforeAdvice到enter()这个joinpoint,这个绑定就需要用到advisor了。advisor是Spring自己创造的一个专门做这种绑定的东东。修改配置如下:
这里定义了一个pointcut选取名字为“enter”的方法(joinpoint),定义了一个customadvisor把这个pointcut和advice关联起来。
执行结果:
Dinosaur hall is opened.
The People Zoo is opened.
Before method...
Charge Jack $1.00 for ticket.
Jack needs to be charged first.
Dianosaur hall charges Jack $2.00
Jack visited diano hall.
Dinosaur hall is closed.
The People Zoo is closed.
修改advice为我们真正的义务逻辑
再修改下main,把traffic打出来
运行,结果如下:
我们再来实现统计场馆收入的记录,即after visit
修改统计类,加入income记录如下:
好吧,饶过我吧……我偷懒了……这只是个例子,请无视多线程问题和其他问题……只要能跑就够了。
创建after advice,并修改配置文件
配置:
修改main,并运行
输出:
Dinosaur hall is opened.
The People Zoo is opened.
Before method...
Charge Jack $1.00 for ticket.
Jack needs to be charged first.
Dianosaur hall charges Jack $2.00
Jack visited diano hall.
After return...
Current traffic: 1
Income of DINOSAUR:
$2.00
Dinosaur hall is closed.
The People Zoo is closed.
这里看到有个问题,每个Target都得声明一个ProxyFactoryBean,这样非常麻烦,后期开发维护量巨大。为了相当程度的减轻这个问题,Spring提供了自动代理。
自动代理提供了AbstractAdivosrAutoProxyCreator,可以方便继承,原生提供了两个Creator:
BeanNameAutoProxyCreator
根据Bean名称创建代理(针对Bean所有方法)
DefaultAdvisorAutoProxyCreator
根据advisor本身包含信息创建代理(针对特定方法)
修改使用BeanNamesAutoProxyCreator
修改使用DefaultAdvisorAutoProxyCreator
这种方式感觉有点过于强大了,以至于很容易出错。本例用这个Creator就报错了。但是考虑到现在这种技术很少用了,就没有深究。这里只是介绍下有这种方式存在。如果以后遇到类似用法,再补坑。
总结
总的来说,传统的Spring AOP实现方式就是动态代理,但是有各种各样的限制和麻烦:
被代理类以及其父类(如果有)必须有无参构造函数;(有点像早期的序列化要求)
pointcut, advice,advisor都是成套出现,为实现代理一个方法,可能就得实现这三个的一套,显得重复而臃肿;
如果不使用自动代理,每个Target都有一个ProxyFactoryBean,及其臃肿;
如果使用动态代理,advice,advisor定义不是非常强大,aspect实现绑定比较麻烦。
本文源码:
https://github.com/EdisonXu/POC/tree/master/intro-aop
AOP加入各种统计的东东。
注:
这篇文章中,我用的是完全传统的Spring AOP,不带有任何AspectJ的东西。
在前面AOP实现一文中提到,Spring
AOP要求被代理类必须由Spring容器来管理,即是一个SpringBean。所以,我们要做的第一步,就是配置引入Spring容器管理。
在src/main/resources/下添加applicationContext.xml
(名字可任意)如下
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="zoo" class="com.edi.poc.Zoo"/> <bean id="dinoHall" class="com.edi.poc.DinoHall"/> <bean id="jack" class="com.edi.poc.Tourer"/> </beans> |
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Zoo zoo = (Zoo) ctx.getBean("zoo"); Hall dinoHall = (Hall)ctx.getBean("dinoHall"); Tourer jack = (Tourer)ctx.getBean("jack"); zoo.open(); jack.visit(zoo, HALL_NAME.DINOSAUR); zoo.close(); } |
运行下试试
十月 15, 2013 11:07:56 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1320a41: startup date [Tue Oct 15 11:07:56 CST 2013]; root of context hierarchy 十月 15, 2013 11:07:57 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] 十月 15, 2013 11:07:57 上午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@12ebf9a: defining beans [zoo,dinoHall,jack]; root of factory hierarchy 十月 15, 2013 11:07:57 上午 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons 信息: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@12ebf9a: defining beans [zoo,dinoHall,jack]; root of factory hierarchy Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'zoo' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.edi.poc.Zoo]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.edi.poc.Zoo.<init>() at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1007) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:953) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:487) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83) at com.edi.poc.Main.main(Main.java:12) Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.edi.poc.Zoo]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.edi.poc.Zoo.<init>() at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:83) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1000) ... 13 more Caused by: java.lang.NoSuchMethodException: com.edi.poc.Zoo.<init>() at java.lang.Class.getConstructor0(Unknown Source) at java.lang.Class.getDeclaredConstructor(Unknown Source) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78) ... 14 more |
[com.edi.poc.Zoo]: No default constructor found; nested exception is java.lang.NoSuchMethodException:
因为我们的zoo没有无参构造函数,Spring的BeanFactory默认会去调无参构造函数。修改如下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="zoo" class="com.edi.poc.Zoo"> <constructor-arg value="People"/> </bean> <bean id="dinoHall" class="com.edi.poc.DinoHall"/> <bean id="jack" class="com.edi.poc.Tourer"> <constructor-arg value="Jack"/> </bean> </beans> |
Dinosaur hall is opened. The People Zoo is opened. Charge Jack $1.00 for ticket. Jack needs to be charged first. Dianosaur hall charges Jack $2.00 Jack visited diano hall. Dinosaur hall is closed. The People Zoo is closed. |
enter +1
创建统计类
package com.edi.poc.statistic; public class Statistic { private static int totalTraffic = 0; public static void increaseTotalTraffic() { totalTraffic ++; } public static int getTotalTraffic() { return totalTraffic; } } |
package com.edi.poc.aop; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyBeforeMethod implements MethodBeforeAdvice { public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("Before method..."); } } |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="zoo" class="com.edi.poc.Zoo"> <constructor-arg value="People"/> </bean> <bean id="dinoHall" class="com.edi.poc.DinoHall"/> <bean id="jack" class="com.edi.poc.Tourer"> <constructor-arg value="Jack"/> </bean> <bean id="myBeforeAdvice" class="com.edi.poc.aop.MyBeforeMethod"/> <bean id="customServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="zoo"/> <property name="interceptorNames"> <list> <value>myBeforeAdvice</value> </list> </property> <!-- <property name="proxyTargetClass"> <value>true</value> </property> --> </bean> </beans> |
Spring的ProxyFactory在创建Bean的时候,动态的去选择JDK或CGLIB来做反射生成目标类。可以通过上面蓝色部分强制声明为CGLIB。
运行下试试
十月 15, 2013 11:45:36 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@e14d81: startup date [Tue Oct 15 11:45:36 CST 2013]; root of context hierarchy 十月 15, 2013 11:45:36 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] 十月 15, 2013 11:45:36 上午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@14c428f: defining beans [zoo,dinoHall,jack,myBeforeAdvice,customServiceProxy]; root of factory hierarchy Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customServiceProxy': FactoryBean threw exception on object creation; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.edi.poc.Zoo]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:149) at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:102) at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1454) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:249) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1117) at com.edi.poc.Main.main(Main.java:13) Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.edi.poc.Zoo]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:217) at org.springframework.aop.framework.ProxyFactoryBean.getProxy(ProxyFactoryBean.java:363) at org.springframework.aop.framework.ProxyFactoryBean.getSingletonInstance(ProxyFactoryBean.java:317) at org.springframework.aop.framework.ProxyFactoryBean.getObject(ProxyFactoryBean.java:243) at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142) ... 6 more Caused by: java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given at org.springframework.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:721) at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:499) at org.springframework.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33) at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:285) at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:205) ... 10 more |
加上空参构造器后再执行,打印如下:
Before method... Before method... Dinosaur hall is opened. The People Zoo is opened. Before method... Charge Jack $1.00 for ticket. Jack needs to be charged first. Dianosaur hall charges Jack $2.00 Jack visited diano hall. Before method... Dinosaur hall is closed. The People Zoo is closed. |
打印了四次,因为zoo总共被调用了四次
Before .open()
Before .addHall()
Before.enter()
Before .close()
其实我们只要before.enter(),所以要指定改BeforeAdvice到enter()这个joinpoint,这个绑定就需要用到advisor了。advisor是Spring自己创造的一个专门做这种绑定的东东。修改配置如下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <!-- Configure Target Objects --> <bean id="zoo" class="com.edi.poc.Zoo"> <constructor-arg value="People"/> </bean> <bean id="dinoHall" class="com.edi.poc.DinoHall"/> <bean id="jack" class="com.edi.poc.Tourer"> <constructor-arg value="Jack"/> </bean> <!-- Configure pointcut --> <bean id="customPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="enter"/> </bean> <!-- Configure Advice --> <bean id="myBeforeAdvice" class="com.edi.poc.aop.MyBeforeMethod"/> <!-- Configure Advisor --> <bean id="customAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut" ref="customPointcut"/> <property name="advice" ref="myBeforeAdvice"/> </bean> <bean id="customServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="zoo"/> <property name="interceptorNames"> <list> <value>customAdvisor</value> </list> </property> <!-- <property name="proxyTargetClass"> <value>true</value> </property> --> </bean> </beans> |
执行结果:
Dinosaur hall is opened.
The People Zoo is opened.
Before method...
Charge Jack $1.00 for ticket.
Jack needs to be charged first.
Dianosaur hall charges Jack $2.00
Jack visited diano hall.
Dinosaur hall is closed.
The People Zoo is closed.
修改advice为我们真正的义务逻辑
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("Before method..."); Statistic.increaseTotalTraffic(); } |
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Zoo zoo = (Zoo) ctx.getBean("customServiceProxy"); Hall dinoHall = (Hall)ctx.getBean("dinoHall"); zoo.addHall(HALL_NAME.DINOSAUR, dinoHall); Tourer jack = (Tourer)ctx.getBean("jack"); zoo.open(); jack.visit(zoo, HALL_NAME.DINOSAUR); System.out.println("Current traffic: " + Statistic.getTotalTraffic()); zoo.close(); } |
Dinosaur hall is opened. The People Zoo is opened. Before method... Charge Jack $1.00 for ticket. Jack needs to be charged first. Dianosaur hall charges Jack $2.00 Jack visited diano hall. Current traffic: 1 Dinosaur hall is closed. The People Zoo is closed. |
修改统计类,加入income记录如下:
public class Statistic { private static int totalTraffic = 0; private static Map<HALL_NAME, BigDecimal> incomeOfHalls = new HashMap<HALL_NAME, BigDecimal>(); public static void increaseTotalTraffic() { totalTraffic ++; } public static int getTotalTraffic() { return totalTraffic; } public static BigDecimal getIncome(HALL_NAME hallName) { BigDecimal currentAmt = incomeOfHalls.get(hallName); if(currentAmt!=null) return currentAmt; return BigDecimal.valueOf(0); } public static void increaseIncome(HALL_NAME hallName, BigDecimal amount) { BigDecimal currentAmount = incomeOfHalls.get(hallName); if(currentAmount==null) currentAmount = amount; else currentAmount = currentAmount.add(amount); incomeOfHalls.put(hallName, currentAmount); } } |
创建after advice,并修改配置文件
public class MyAfterMethod implements AfterReturningAdvice{ public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("After return..."); Statistic.increaseIncome(HALL_NAME.DINOSAUR, BigDecimal.valueOf(2l)); } } |
<bean id="customPointcut2" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="visit"/> </bean> <bean id="myAfterAdvice" class="com.edi.poc.aop.MyAfterMethod"/> <bean id="customAdvisor2" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut" ref="customPointcut2"/> <property name="advice" ref="myAfterAdvice"/> </bean> <bean id="customServiceProxy2" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="dinoHall"/> <property name="interceptorNames"> <list> <value>customAdvisor2</value> </list> </property> </bean> |
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Zoo zoo = (Zoo) ctx.getBean("customServiceProxy"); Hall dinoHall = (Hall)ctx.getBean("customServiceProxy2"); zoo.addHall(HALL_NAME.DINOSAUR, dinoHall); Tourer jack = (Tourer)ctx.getBean("jack"); zoo.open(); jack.visit(zoo, HALL_NAME.DINOSAUR); System.out.println("Current traffic: " + Statistic.getTotalTraffic()); NumberFormat currency = NumberFormat.getCurrencyInstance(); System.out.println("Income of " + HALL_NAME.DINOSAUR + ": "+ currency.format(Statistic.getIncome(HALL_NAME.DINOSAUR))); zoo.close(); } |
Dinosaur hall is opened.
The People Zoo is opened.
Before method...
Charge Jack $1.00 for ticket.
Jack needs to be charged first.
Dianosaur hall charges Jack $2.00
Jack visited diano hall.
After return...
Current traffic: 1
Income of DINOSAUR:
$2.00
Dinosaur hall is closed.
The People Zoo is closed.
这里看到有个问题,每个Target都得声明一个ProxyFactoryBean,这样非常麻烦,后期开发维护量巨大。为了相当程度的减轻这个问题,Spring提供了自动代理。
自动代理提供了AbstractAdivosrAutoProxyCreator,可以方便继承,原生提供了两个Creator:
BeanNameAutoProxyCreator
根据Bean名称创建代理(针对Bean所有方法)
DefaultAdvisorAutoProxyCreator
根据advisor本身包含信息创建代理(针对特定方法)
修改使用BeanNamesAutoProxyCreator
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!-- Configure the name filter --> <property name="beanNames" value="*zoo"></property> <property name="interceptorNames" value="customAdvisor"></property> </bean> |
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> |
总结
总的来说,传统的Spring AOP实现方式就是动态代理,但是有各种各样的限制和麻烦:
被代理类以及其父类(如果有)必须有无参构造函数;(有点像早期的序列化要求)
pointcut, advice,advisor都是成套出现,为实现代理一个方法,可能就得实现这三个的一套,显得重复而臃肿;
如果不使用自动代理,每个Target都有一个ProxyFactoryBean,及其臃肿;
如果使用动态代理,advice,advisor定义不是非常强大,aspect实现绑定比较麻烦。
本文源码:
https://github.com/EdisonXu/POC/tree/master/intro-aop
相关文章推荐
- Spring Aop实例
- spring 4.0 AOP (使用AspectJ的注解方式 的aop实现)简单实例
- Spring AOP 实例
- Spring的AOP使用实例
- Spring AOP通知实例 – Advice
- AspectJ spring aop 记录某些类中方法执行时间实例
- Spring Aop实例之xml配置
- Spring.Net AOP实例
- spring AOP代码实例
- Spring Aop实例
- spring aop 应用实例
- 05 Spring Aop实例(AOP 如此简单)@Aspect、@Around 注解方式配置
- Spring Aop实例之AspectJ注解配置
- Spring---AOP基本概念以及Advice5种类型的通知注解应用实例
- SpringAOP编程实例
- Spring AOP 入门实例
- Spring学习(2)一个简单的AOP实例
- Spring.Net AOP实例
- Spring AOP实例(Pointcut,Advisor)
- Spring Aop实例