*AOP是一种新的编程方式,它和OOP不同,OOP把系统看作多个对象的交互,AOP把系统分解为不同的关注点,或者称之为切面(Aspect)。
*
AOP原理
**Spring的AOP实现是基于JVM的动态代理。**由于JVM的动态代理要求必须实现接口,如果一个普通类没有业务接口,就需要通过CGLIB或者Javassist这些第三方库实现。
装配AOP(使用AspectJ)
我们以UserService和MailService为例,这两个属于核心业务逻辑,现在,我们准备给UserService的每个业务方法执行前添加日志,给MailService的每个业务方法执行前后添加日志.
@Aspect
@Component
public class LoggingAspect{
@Before("execution(public * com.itranswarp.learnjava.service.UserService.*(..))")
public void doAccessCheck() {
System.err.println("[Before] do access check...");
}
@Around("execution(public * com.itranswarp.learnjava.service.MailService.*(..))")
public Object doLogging(ProceedingJoinPoint pjp) throws Throwable {
System.err.println("[Around] start " + pjp.getSignature());
Object retVal = pjp.proceed();
System.err.println("[Around] done " + pjp.getSignature());
return retVal;
}
}
在LoggingAspect类的声明处,除了用@Component表示它本身也是一个Bean外,我们再加上@Aspect注解,表示它的@Before标注的方法需要注入到UserService的每个public方法执行前,@Around标注的方法需要注入到MailService的每个public方法执行前后。
紧接着,我们需要给@Configuration类加上一个@EnableAspectJAutoProxy注解:
@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AppConfig {
...
}
如果我们想要把LoggingAspect.doAccessCheck()方法注入到UserService的每个public方法中,最简单的方法是编写一个子类:
public UserServiceAopProxy extends UserService{
public UserService target;
public LoggingAspect aspect;
public UserServiceAopProxy(UserService target, LoggingAspect aspect){
this.target = target;
this.aspect = aspect;
}
public User login(String email, String password){
aspect.doAccessCheck();
return target.login(email, password);
}
}
使用注解装配AOP
如果我们自己写的Bean希望在一个数据库事务中被调用,就标注上@Transactional:
@Component
public class UserService {
// 有事务:
@Transactional
public User createUser(String name) {
...
}
// 无事务:
public boolean isValidName(String name) {
...
}
// 有事务:
@Transactional
public void updateUser(User user) {
...
}
}
演示如何使用注解实现AOP装配,我们定义一个性能监控的注解:
@Target(METHOD)
@Retention(RUNTIME)
public @interface MetricTime{
String value();
}
在需要被监控的关键方法上标注该注解:
@Component
public class UserService{
@MetricTime("register")
public User register(String email, String password, String name){
...}
}
然后,我们定义MetricAspect:
@Aspect
@Component
public class MetricAspect{
@Around("@annotation(metricTime)")
public Object metric(ProceedingJoinPoint joinPoint, MetricTime metricTime) throws Throwable {
String name = metricTime.value();
long start = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long t = System.currentTimeMillis() - start;
// 写入日志或发送至JMX:
System.err.println("[Metrics] " + name + ": " + t + "ms");
}
}
}
注意metric()方法标注了@Around("@annotation(metricTime)"),它的意思是,符合条件的目标方法是带有@MetricTime注解的方法,因为metric()方法参数类型是MetricTime(注意参数名是metricTime不是MetricTime),我们通过它获取性能监控的名称。
有了@MetricTime注解,再配合MetricAspect,任何Bean,只要方法标注了@MetricTime注解,就可以自动实现性能监控。