还是这个例子,我们用完全annotation的方法来实现。代码我就不重复了,跟前面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">
<!-- 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>
<bean id="myAspect" class="com.edi.poc.aop.MyAspect"/>
<aop:aspectj-autoproxy/>
</beans>
- 修改Aspect类
@Aspect
public class MyAspect {
@Before("execution(* com.edi.poc.Zoo.enter(..))")
public void before(JoinPoint joinPoint)
{
System.out.println("Before method...");
Statistic.increaseTotalTraffic();
}
@After("execution(* com.edi.poc.ifc.Hall.visit(..))")
public void after(JoinPoint joinPoint)
{
System.out.println("After return...");
Statistic.increaseIncome(HALL_NAME.DINOSAUR, BigDecimal.valueOf(2l));
}
}
- 运行,输出为:
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.
一模一样。而且更加简单了。世界如此美好~如此清净……
- 可以看到用annotation来做AOP,实在是简单的不得了。这里唯一没有摆脱XML的,就是Spring的容器的配置文件。当然,Spring这段配置也可以完全用annotation来做,这样就完全不要applicationContext.xml这个文件了。参考如下步骤:
- 先把Main类改改,让它支持配置
@ComponentScan
@Configuration
@EnableAspectJAutoProxy
public class Main {
….
}
这里@ComponentScan是为了让Spring自动识别
@Configuration是让该类变成配置类
@EnableAspectJAutoProxy是启动AutoProxy
- 将几个让Spring自己加载的类变成Component,Spring可以自动识别加载
@Component
public class Zoo { … }
@Component
public class DinoHall implements Hall { … }
@Aspect
@Component
public class MyAspect { … }
如果不想让Spring来管理,可以在原类里面把@Component去掉,然后在Main类里面加上
public class Main {
@Bean(name="dinoHall")
public Hall getDinoHall()
{
return new DinoHall();
}
}
这样就可以通过ctx.getBean("dinoHall") 来获得实例。
- 最终修改调用过程如下:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Main.class);
Zoo zoo = (Zoo) ctx.getBean(Zoo.class);
zoo.init("People");
Hall dinoHall = (Hall)ctx.getBean("dinoHall");
zoo.addHall(HALL_NAME.DINOSAUR, dinoHall);
Tourer jack = new Tourer("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.
- 总结
完全的annotation是非常方便的,就像ejb。但是,Spring的annotation做的还不是非常的完善,有些地方还需要提高。例如,在
Hall dinoHall = (Hall)ctx.getBean("dinoHall");
这里,我们如果不定义
@Bean(name="dinoHall")
public Hall getDinoHall()
{
return new DinoHall();
}
在DinoHall上加上@Component后,用
Hall dinoHall = (Hall)ctx.getBean(Hall.class);
来获得实例,这里有个问题就是,后面传参的class,如果该类实现了接口,必须得传入其接口才行,否则就会报异常:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.edi.poc.DinoHall] is defined
这个我怀疑是因为Spring的容器智能的选择了JDK的动态代理来反射生成实际类,而JDK的动态代理要求Target必须实现接口。理论上改成强制CGLIB应该能解决问题,但是我找了下,并没找到如何用annotation来标示强制使用CGLIB……
还例如,Spring3现在支持JSR-250定义的@PostConsturct等生命周期管理的注解,但是,我也没有找到如何使用annotation来启用。网上和官方文档都是给出了修改配置文件来使其支持。
本文源码:
https://github.com/EdisonXu/POC/tree/master/intro-aop
Spring AOP @Aspect support annotation
最新推荐文章于 2023-05-12 18:44:29 发布