@ghimi
2018-08-02T07:23:08.000000Z
字数 4526
阅读 933
spring
AOP : 面向切面编程,相对于OOP面向对象编程.
Spring 的 AOP 的存在目的是为了解耦.AOP可以让一组类共享相同的行为.在OOP中只能通过继承类和实现接口,来时代码的耦合度增强,且类继承只能为单继承,阻止了更多的类添加到一组类上,AOP弥补了OOP的不足.
Spring 支持 AspectJ 的注解是切面编程.
1. 使用@Aspect 声明是一个切面.
2. 使用@After,@Before,@Around 定义建言(advice),可直接将拦截规则(切点作为参数).
3. 其中@After,@Before,@Around 参数的拦截规则为切点(PointCut),为了是切点复用,可使用@PointCut 专门定义拦截规则,然后再@After,@Before,@Around 的参数中调用,
4. 其中符合条件的每一个被拦截处为连接点(JoinPoint)
本节示例将演示基于注解拦截和基于方法规则拦截的两种方式,演示一种模拟记录操作的日志系统的实现.其中注解式拦截能够很好的控制要拦截的粒度和获得更丰富的信息,Spring本身在事务处理(@Transcational)和数据缓冲(@Cacheable等)上面都使用此种形式的拦截.
2.1 首先编写注解类
package spring4.ch1.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
//其实没有必要添加@Document的注解
@Documented
public @interface Action {
String name();
}
2.2 对需要拦截方法添加注解
package spring4.ch1.aop;
import org.springframework.stereotype.Service;
@Service
//对需要切入的方法上添加自定义的注解
public class DemoAnnotationService {
@Action(name="注解式拦截的add操作")
public void add(){
System.out.println("DemoAnnotation 类的add操作本身!");
}
@Factor(path = "Annotation from Factor and delete() method!")
public void delete(){
System.out.println("DemoAnnotation 类的delete操作本身");
}
}
3.1 直接编写类(不需要任何其他操作)
package spring4.ch1.aop;
import org.springframework.stereotype.Service;
@Service
//直接编写类,不需要其他任何操作
public class DemoMethodService {
public void add(){}
}
在切面中,我们使用 @After
和 @Before
,将两种拦截切面都做了拦截
package spring4.ch1.aop;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
@Pointcut("@annotation(spring4.ch1.aop.Action)")
public void annotationPointCut(){}
@Pointcut("@annotation(spring4.ch1.aop.Factor)")
public void annotationPointCut2(){}
@Before("annotationPointCut()")
public void after(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Action action = method.getAnnotation(Action.class);
System.out.println("注解式拦截 "+ action.name());
}
@Before("annotationPointCut2()")
public void after2(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
Factor factor = method.getAnnotation(Factor.class);
System.out.println("注解式拦截2 "+ factor.path());
}
@After("execution(* spring4.ch1.aop.DemoMethodService.*(..))")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法规则式拦截, " + method.getName());
}
}
在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点"
例如定义切入点表达式 execution(* com.sample.service.impl..*.*(..))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型,*号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示类名,*号表示所有的类。
5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import spring4.ch1.aop.DemoAnnotationService;
import spring4.ch1.aop.DemoMethodService;
import spring4.ch1.di.UseFunctionService;
@SpringBootApplication
@ComponentScan("spring4.ch1")
@EnableAspectJAutoProxy
//使用扫描注解方法建立context
//然后执行我们的几个测试方法
public class Demo1Application {
public static void main(String[] args) {
// ConfigurableApplicationContext run = SpringApplication.run(Demo1Application.class, args);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Demo1Application.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String string : beanDefinitionNames) {
System.out.println(string);
}
UseFunctionService bean = context.getBean(UseFunctionService.class);
// System.out.println(bean.sayHello("di"));
DemoAnnotationService bean2 = context.getBean(DemoAnnotationService.class);
DemoMethodService bean3 = context.getBean(DemoMethodService.class);
bean3.add();
bean2.add();
bean2.delete();
context.close();
}
}
测试结果输出
方法规则式拦截, add
注解式拦截 注解式拦截的add操作
DemoAnnotation 类的add操作本身!
注解式拦截2 Annotation from Factor and delete() method!
DemoAnnotation 类的delete操作本身