AOP复习
AOP
- AOP
- 静态代理
- 动态代理
- Proxy
- CGLIB
AOP
面向切面编程
优点:
- 提高代码的可重用性
- 业务代码编码更简洁
- 业务代码维护更高效
- 业务功能扩展更便捷
Joinpoint(连接点) | 就是方法 |
Pointcut(切入点) | 就是挖掉共性功能的方法 |
Advice(通知) | 就是共性功能,最终以一个方法的形式呈现 |
Aspect(切面) | 是共性功能与挖的位置的对应关系 |
Target(目标对象) | 就是挖掉功能的方法对应的类产生的对象,这种对象是无法直接完成最终工作的 |
Weaving(织入) | 就是将挖掉的功能回填的动态过程 |
Proxy(代理) | 目标对象无法直接完成工作,需要对其进行功能回填,通过创建原始对象的代理对象实现 |
Introduction(引入/引介) | 是对原始对象无中生有的添加成员变量或成员方法 |
使用到的注解
定义切面
@Aspect
定义再类的上方,设置当前类为切面类
@Aspect
public class AopAdvice {
}
切入点引用
@Pointcut
作用在方法上使用当前的方法名称作为切入点引用名称
举例:
@Pointcut("execution(* *(..))")
public void pt() {
}
五种通知
注解 | 名称 | 解释说明 |
---|---|---|
@Before | 前置通知 | 原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行 |
@After | 后置通知 | 原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知 |
@AfterReturning | 当前方法作为返回后通知 | 原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行 |
@AfterThrowing | 异常后通知 | 原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行 |
@Around | 当前方法作为环绕通知 | 在原始方法执行前后均有对应执行执行,还可以阻止原始方法的执行 |
举例:
@Pointcut("execution(* *(..))")
public void pt() {
}
@Before("pt()")
public void before(){
}
@After("pt()")
public void after(){
}
@AfterReturning(value="pt()",returning = "ret")
public void afterReturning(Object ret) {
}
@AfterThrowing(value="pt()",throwing = "t")
public void afterThrowing(Throwable t){
}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {Object ret = pjp.proceed();return ret;
}
完整的举例:
Component
@Aspect//设置切面
public class AnnotationAop {//设置切入点为有有注解LogAnnotation的方法@Pointcut("@annotation(com.springAop.annotation.LogAnnotation)")public void pointCut() {}@Around("pointCut()")public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("环绕前增强1");//获取方法签名MethodSignature signature = (MethodSignature) pjp.getSignature();Method method1 = signature.getMethod();//获取接口的方法Method method = pjp.getTarget().getClass().getMethod(method1.getName(), method1.getParameterTypes());//获取实现类的方法System.out.println("实现类 = " + method1);String name = method.getName();//获取方法名System.out.println("获取方法名 = " + name);Class<?> returnType = method.getReturnType();//获取返回值类型String name1 = returnType.getName();System.out.println("返回值类型 = " + name1);Class<?>[] parameterTypes = method.getParameterTypes();//获取参数类型for (int i = 0; i < parameterTypes.length; i++) {String name2 = parameterTypes[i].getName();//获取参数类型System.out.println("parameterTypes = " + name2);}//获取参数的值Object[] args = pjp.getArgs();for (int i = 0; i < args.length; i++) {System.out.println("i = " + args[i]);}Object proceed = pjp.proceed();LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);//获取方法上的注解Annotation[] annotations = method.getDeclaredAnnotations();//获取方法上的注解for (Annotation a : annotations) {String annotationName = a.annotationType().getName();//获取注解名Class<? extends Annotation> aClass = a.annotationType();if (a instanceof LogAnnotation) {System.out.println("我是注解LogAnnotation已经执行了 ");}System.out.println("annotationName = " + annotationName);// 在这里可以使用获取到的 annotationName 进行逻辑处理}System.out.println("环绕后增强1");return proceed;}//前置增强@Before("pointCut()")public void before() {System.out.println("前置增强1");}@After("pointCut()")public void after() {System.out.println("后置增强1");}@AfterThrowing("pointCut()")public void afterThrowing() {System.out.println("异常增强1");}@AfterReturning("pointCut()")public void afterReturning() {System.out.println("返回后通知1");}
}/*** 自定义日志注解*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface LogAnnotation {String value() default "";String name() default "";String type() default "";
}@Service("bookService")
public class BookServiceImpl implements BookService {@Autowiredprivate BookDao bookDao;@Overridepublic void save(Book book) {bookDao.save(book);}@Overridepublic void update(Book book) {bookDao.update(book);}@Overridepublic void delete(Integer id) {bookDao.delete(id);}@Override@LogAnnotation1231(value = "查询value1231",type = "查询type1231",name= "查询name1231")@LogAnnotation(value = "查询value",type = "查询type",name= "查询name")public Book findById(Integer id) {System.out.println("id ====================== " + id);return bookDao.findById(id);}@Overridepublic List<Book> findAll() {System.out.println("===================================== ");return bookDao.findAll();}
}
静态代理
装饰者模式(Decorator Pattern):在不惊动原始设计的基础上,为其添加功能
举例:
public class UserServiceDecorator implements UserService{private UserService userService;public UserServiceDecorator(UserService userService) {this.userService = userService;}public void save() {//原始调用userService.save();//增强功能(后置)System.out.println("刮大白");}
}
动态代理
Proxy
JDKProxy动态代理是针对对象做代理,要求原始对象具有接口实现,并对接口方法进行增强
举例:
public class UserServiceJDKProxy {public UserService createUserServiceJDKProxy(final UserService userService){//获取被代理对象的类加载器ClassLoader classLoader = userService.getClass().getClassLoader();//获取被代理对象实现的接口Class[] classes = userService.getClass().getInterfaces();//对原始方法执行进行拦截并增强InvocationHandler ih = new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//前置增强内容Object ret = method.invoke(userService, args);//后置增强内容System.out.println("刮大白2");return ret;}};//使用原始被代理对象创建新的代理对象UserService proxy = (UserService) Proxy.newProxyInstance(classLoader,classes,ih);return proxy;}
}
CGLIB
- CGLIB(Code Generation Library),Code生成类库
- CGLIB动态代理不限定是否具有接口,可以对任意操作进行增强
- CGLIB动态代理无需要原始被代理对象,动态创建出新的代理对象
举例
public class UserServiceImplCglibProxy {public static UserServiceImpl createUserServiceCglibProxy(Class clazz){//创建Enhancer对象(可以理解为内存中动态创建了一个类的字节码)Enhancer enhancer = new Enhancer();//设置Enhancer对象的父类是指定类型UserServerImplenhancer.setSuperclass(clazz);Callback cb = new MethodInterceptor() {public Object intercept(Object o, Method m, Object[] a, MethodProxy mp) throws Throwable {Object ret = mp.invokeSuper(o, a);if(m.getName().equals("save")) {System.out.println("刮大白");}return ret;}};//设置回调方法enhancer.setCallback(cb);//使用Enhancer对象创建对应的对象return (UserServiceImpl)enhancer.create();}
}
总结
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象
还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的
在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程
中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物>管理,记录日志等
公用操作处理的过程就是面向切面编程的思想。AOP 底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用
CGLIB 方式实现动态代理。
Java 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler 来处理。
而 cglib 动态代理是利用 asm 开源包,对代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP
2、如果目标对象实现了接口,可以强制使用 CGLIB 实现 AOP
3、如果目标对象没有实现了接口,必须采用 CGLIB 库,spring 会自动在 JDK 动态代理和 CGLIB 之间转换