需求:
帖子模块、评论模块、消息模块
AOP的概念:
Aspect Oriented Programing,即面向方面(切面)编程。
AOP是一种编程思想,是对OOP的补充,可以进一步提高编程的效率。
AOP的术语
Target是目标对象(bean),封装业务需求的组件是Aspect。JoinPoint(连接点)是织入位置(可以是属性、构造器、静态块、成员方法)。方面组件(Aspect,额外的bean)里,Pointcut是声明要把代码织入到哪些对象的哪些位置,Advice是通知(具体的系统逻辑,做的位置在哪里,比如织入到成员方法里,那是织入成员方法的开头还是结束的位置还是抛异常的地方还是有返回值的地方)。
AOP的实现:
(1)AspectJ
AspectJ是语言级的实现,它扩展了Java语言,定义了AOP语法。
AspectJ在编译期织入代码,它有一个专门的编译器,用来生成遵守Java字节码规范的class文件。
(2)Spring AOP
Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要 特殊的类装载器。
Spring AOP在运行时通过代理的方向织入代码,只支持方法类型的连接点。(%80的情况下是织入方法)
Spring支持对AspectJ的集成。
Spring AOP:
(1)JDK动态代理
Java提供的动态代理技术,可以在运行时创建接口的代理实例。
Spring AOP默认采用此种方式,在接口的代理实例中织入代码。
(2)CGLib动态代理
采用底层的字节码技术,在运行时创建子类代理实例。
当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码。
写一个小例子,新建一个aspect包,新建一个AlphaAspect类
@Component //把bean交给spring
@Aspect
public class AlphaAspect {
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))") //第一个*表示接收所有类型的返回值,第二个*表示所有类,后面的*表示所有方法,(..)表示所有参数
public void pointcut(){
}
@Before("pointcut()")
public void before(){
System.out.println("before");
}
@After("pointcut()")
public void after(){
System.out.println("after");
}
@AfterReturning("pointcut()")
public void afterRetuning(){
System.out.println("afterRetuning");
}
@AfterThrowing("pointcut()")
public void afterThrowing(){
System.out.println("afterThrowing");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("around before");
Object obj = joinPoint.proceed();
System.out.println("around after");
return obj;
}
}
新建ServiceLogAspect类,实现统一记录日志的功能。
@Component
@Aspect
public class ServiceLogAspect {
private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))") //第一个*表示接收所有类型的返回值,第二个*表示所有类,后面的*表示所有方法,(..)表示所有参数
public void pointcut(){
}
@Before("pointcut()")
public void before(JoinPoint joinPoint){
//用户[1.2.3.4]在[xxx],访问了[com.nowcoder.community.service.xxx()].
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); //强转2成子类型
HttpServletRequest request = attributes.getRequest(); //通过这种方式获取request
String ip = request.getRemoteHost(); //注意:以这种方式获取客户端ip
String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(); //获取目标的类名和方法名
logger.info(String.format("用户[%s],在[%s],访问了[%s].",ip,now,target));
}