使用JAVA Spring AOP实现动态代理
开启AOP配置
package com.example.springdemo.aopdemo;
import com.example.springdemo.aopdemo.Aspect.LogAspect;
import org.springframework.context.annotation.*;
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
@Bean
public AspectService aspectService() {
return new AspectServiceImpl();
}
@Bean
public LogAspect logAspect() {
return new LogAspect();
}
}
使用@Configuration
注解一个类时,@Configuration
类本身被注册为一个bean定义,类中所有声明的@Bean
方法也被注册为bean定义。
@EnableAspectJAutoProxy表示启动AspectJ的自动代理
Spring默认的main方法的类注解@SpringBootApplication
默认会设置bean扫描范围为主类所在package下的所有类. 所以正常开发的时候使用@Service这类注解注入就可以被扫描到. 但因为我打算在main方法测试功能的时候中使用@Configuration
配置的IOC容器,所以 AopConfig
里面的两个Bean注入是必要的. 当然也可以配置@ComponentScan(basePackages = "com.xxx")
package com.example.springdemo.aopdemo.Aspect;
import com.example.springdemo.aopdemo.AopConfig;
import com.example.springdemo.aopdemo.AspectService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component // 切面本身也是一个bean,需要注入到容器中
@Aspect // 声明为一个切面
public class LogAspect {
@Pointcut("execution(public int com.example.springdemo.aopdemo.AspectService.*(..))")
// 切入点表达式,切点描述带包名,一般都很长,写成方法形式可以复用,增加可读性和可维护性
public void pointCut(){
}
@Before("pointCut()")
// 前置通知,在目标方法运行之前执行
public void logStart(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName() + " 运行开始,参数列表是:{"+ Arrays.asList(joinPoint.getArgs()) +"}");
}
@After("pointCut()")
// 后置通知,在目标方法运行之后但在返回之前执行
public void logEnd(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName() + " 运行结束");
}
@AfterReturning(value = "pointCut()", returning = "result")
// 返回通知,在方法正常返回之后执行
public void logReturn(JoinPoint joinPoint, Object result){
System.out.println(joinPoint.getSignature().getName() + " 正常返回,运行结果:{"+result+"}");
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
// 异常通知,在方法抛出异常之后执行
public void logException(JoinPoint joinPoint, Exception exception){
System.out.println(joinPoint.getSignature().getName() + " 异常,异常信息:{"+exception+"}");
}
public static void main(String[] args) {
// 通过注解配置类加载spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
// 获取bean,因为配置了EnableAspectJAutoProxy表示开启AOP,所以会自动创建代理对象,此时获取的是代理对象
AspectService aspectService = context.getBean(AspectService.class);
aspectService.save(new AspectBean());
}
}
Spring的AnnotationConfigApplicationContext部分,是Spring3.0中新增的。 这是一个强大的ApplicationContext
实现,不仅能解析@Configuration
注解类,也能解析@Componnet
注解的类和使用JSR 330标准注解的类。当提供@Component
和JSR-330类时,它们被注册为bean定义,并且假定在必要时在这些类中使用DI元数据,例如@Autowired
或@Inject
。
其他基础类
先写一个最基础的service
实体类
public class AspectBean {
}
service接口
package com.example.springdemo.aopdemo;
import com.example.springdemo.aopdemo.Aspect.AspectBean;
public interface AspectService {
public int save(AspectBean aspectBean);
}
service实现
package com.example.springdemo.aopdemo;
import com.example.springdemo.aopdemo.Aspect.AspectBean;
import org.springframework.stereotype.Service;
@Service
public class AspectServiceImpl implements AspectService {
@Override
public int save(AspectBean aspectBean) {
System.out.println("save");
return 1;
}
}