博客
关于我
Spring (AOP、静态代理、动态代理)
阅读量:258 次
发布时间:2019-03-01

本文共 6987 字,大约阅读时间需要 23 分钟。

1. AOP

 

1.1 AOP简介

 

  1. AOP Aspect Oriented Programing 面向切面编程

  2. AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)

  3. Spring中的Aop是纯Java来实现的,使用动态代理的方式增强代码

  4. Spring使用动态代理的机制是判断委托类是否实现了接口,如果实现了接口则使用jdk的动态代理,如果没有实现接口则使用cglib的动态代理

  5. AOP不是由Spring提出来的,是由AOP联盟定义的

 

1.2 AOP的专业术语

 

Joinpoint(连接点) :委托类中可以被增强的方法

Pointcut(切入点) :切点 ,要被增强的方法

Advice(通知/增强) :增强的代码

Target(目标对象) :委托对象

Weaving(织入) :增强应用切点的过程

Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类

Aspect(切面): 切点通知的结合

 

1.3 Spring中的AOP实现

1.3.1 传统的Spring的AOP

一个切点和一个通知的组合

1.3.2 基于Aspectj的AOP

  1. AspectJ是一个基于Java语言的面向切面的AOP框架

  2. Spring2.0以后新增了对AspectJ切点表达式支持

  3. @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面

  4. 新版本Spring框架,建议使用AspectJ方式来开发AOP

1.3.3 Aspectj的切点表达式

  • 语法:execution(表达式)

  • execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)

  • public * *(..) ---检索所有的public方法

  • execution(“* cn.uplooking.spring4.demo1.dao.*(..)”) ---只检索当前包

  • execution(“* cn.uplooking.spring4.demo1.dao..*(..)”) ---检索包及当前包的子包.

 

1.3.4 Aspect的增强类型

  • @Before 前置通知,相当于BeforeAdvice

@Before("execution(* com.uplooking.aop.UserDao.add*(..))")public void beforeAdvice() {System.out.println("前置通知....");}
  • @AfterReturning 后置通知,相当于AfterReturningAdvice

@AfterReturning(value = "execution(* com.uplooking.aop.UserDao.add*(..))", returning = "ret")public void afterReturningAdvice(String ret) {System.out.println("后置通知.." + ret);}
  • @Around 环绕通知,相当于MethodInterceptor

@Around("execution(* com.uplooking.aop.UserDao.add*(..))")public void arounrAdvice(ProceedingJoinPoint pjp) throws Throwable {System.out.println("环绕通知前..");pjp.proceed();System.out.println("环绕通知后..");}
  • @AfterThrowing抛出通知,相当于ThrowAdvice

@AfterThrowing("execution(* com.uplooking.aop.UserDao.add*(..))")public void throwAdvice() {System.out.println("异常通知....");}
  • @After 最终final通知,不管是否异常,该通知都会执行

@After(value = "execution(* com.uplooking.aop.UserDao.add*(..))")public void afterAdvice() {System.out.println("最终通知....");}

 

1.4 AOP的编程(AspectJ)

 

1.4.1 导入pom依赖

org.springframework
spring-context
4.3.12.RELEASE
org.springframework
spring-aspects
4.3.12.RELEASE
org.springframework
spring-test
4.3.12.RELEASE
junit
junit
4.12

 

引入约束

1.4.2(前置通知)

1.4.2.1 要被增强的类

package com.ma.aop;import org.springframework.stereotype.Repository;@Repositorypublic class UserDao {public void addUser(){System.out.println("添加用户...");}public void deleteUser(){System.out.println("删除用户...");}public void updateUser(){System.out.println("修改用户...");}public void selectUser(){System.out.println("查找用户...");}}

1.4.2.2 定义切面

package com.ma.aop;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;/*** 切面 = 切点(切点表达式)+ 通知 (方法)*/@Aspect@Componentpublic class MyAspect {@Before("execution(* com.ma.aop.UserDao.addUser())")public void beforeAdvice(){System.out.println("前置通知...");}}

1.4.2.3测试

import com.ma.aop.UserDao;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class TestUserDao {@Autowiredprivate UserDao userDao;@Testpublic void testAdd(){userDao.addUser();}}

 

1.4.2.5结果

 

1.4.3(后置通知)

 

1.4.3.1 要被增强的方法

package com.ma.aop;import org.springframework.stereotype.Repository;@Repositorypublic class UserDao {public void addUser(){System.out.println("添加用户...");}public String deleteUser(){System.out.println("删除用户...");return "已删除!";}public void updateUser(){System.out.println("修改用户...");}public void selectUser(){System.out.println("查找用户...");}}

1.4.3.2 定义切面

@AfterReturning(value = "execution(* com.ma.aop.UserDao.delete*())",returning = "ret")public void afterReturningAdvice(String ret){System.out.println("后置通知..."+"ret");}

注意:可以得到返回值。

 

1.4.3.3 测试

@Testpublic void testDelete(){userDao.deleteUser();}

 

1.4.3.4 结果

 

1.4.4(环绕通知)

 

1.4.4.1 要被增强的方法

package com.ma.aop;import org.springframework.stereotype.Repository;@Repositorypublic class UserDao {public void addUser(){System.out.println("添加用户...");}public String deleteUser(){System.out.println("删除用户...");return "已删除!";}public void updateUser(){System.out.println("修改用户...");}public void selectUser(){System.out.println("查找用户...");}}

1.4.4.2 定义切面

@Around("execution(* com.ma.aop.UserDao.update*())")public void afterReturningAdvice(){System.out.println("环绕通知...");}

注意:此时环绕通知可以阻止目标方法的执行,需要对目标方法进行放行。

@Around("execution(* com.ma.aop.UserDao.update*())")public void afterReturningAdvice(ProceedingJoinPoint pjp) throws Throwable {System.out.println("环绕通知前...");pjp.proceed();System.out.println("环绕通知后...");}

 

 

1.4.4.3 测试

@Testpublic void testUpdate(){userDao.updateUser();}

 

1.4.4.4 效果

 

1.4.5(异常通知)

 

1.4.5.1 要被增强的方法

package com.ma.aop;import org.springframework.stereotype.Repository;@Repositorypublic class UserDao {public void addUser(){System.out.println("添加用户...");}public String deleteUser(){System.out.println("删除用户...");return "已删除!";}public void updateUser(){System.out.println("修改用户...");}public void selectUser(){int i = 10/0;System.out.println("查找用户...");}}

1.4.5.2 定义切面

@AfterThrowing("execution(* com.ma.aop.UserDao.selectUser())")public void throwAdvice(){System.out.println("异常通知...");}

 

 

1.4.5.3 测试

@Testpublic void testSelect(){userDao.selectUser();}

 

1.4.5.4 效果

 

2.代理模式

不使用对象的真实操作,使用我们自己创建的代理对象来操作

 

2.1 静态代理

委托类和代理类共同实现同一个接口,在代理类中有委托类的对象。

1、公共接口

package com.ma.aop.agent;public interface WindWomen {String say();}

 

 

2、委托类

package com.ma.aop.agent;/*** 委托类*/public class Pan implements WindWomen {public String say() {return "晚上十点小河边见......";}}

3、代理类

package com.ma.aop.agent;public class Wang implements WindWomen {private WindWomen windWomen;//委托对象public Wang(WindWomen windWomen){/*** 在代理的构造器中持有一个委托的对象*/this.windWomen = windWomen;}public String say() {System.out.println("她找我让我给你带个话:");System.out.println(windWomen.say());System.out.println("记得啊!");return null;}}

 

4、测试类

package com.ma.aop.agent;public class AgentTest {public static void main(String[] args) {Pan pan = new Pan();Wang wang = new Wang(pan);wang.say();}}

5、效果

 

 

 

 

2.1动态代理

 

2.2.1 基于原生的JDK的动态代理

package com.ma.aop.agent;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class AgentTest {public static void main(String[] args) {final Pan pan = new Pan();WindWomen windWomen = (WindWomen) Proxy.newProxyInstance(AgentTest.class.getClassLoader(), Pan.class.getInterfaces(), new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("她找我让我给你带个话:");Object object = method.invoke(pan,args);System.out.println("记得啊!");return object;}});windWomen.say();}}

实现

 

 

 

效果

2.2.2 基于CGLIB的动态代理

因为原生的jdk的动态代理存在缺陷,代理类和委托类必须实现同一个接口

所以有个开源的动态代理框架出现了(CGLIB)

CGLIB不要求委托类必须实现接口:因为CGLIB底层是基于继承实现的

 

 

 

 

 

 

 

你可能感兴趣的文章
Nginx配置负载均衡到后台网关集群
查看>>
Nginx配置限流,技能拉满!
查看>>
Nginx配置静态代理/静态资源映射时root与alias的区别,带前缀映射用alias
查看>>
Nginx面试三连问:Nginx如何工作?负载均衡策略有哪些?如何限流?
查看>>
Nginx:NginxConfig可视化配置工具安装
查看>>
ngModelController
查看>>
ngrok | 内网穿透,支持 HTTPS、国内访问、静态域名
查看>>
ngrok内网穿透可以实现资源共享吗?快解析更加简洁
查看>>
NHibernate学习[1]
查看>>
NHibernate异常:No persister for的解决办法
查看>>
NIFI1.21.0_java.net.SocketException:_Too many open files 打开的文件太多_实际操作---大数据之Nifi工作笔记0051
查看>>
NIFI1.21.0_Mysql到Mysql增量CDC同步中_日期类型_以及null数据同步处理补充---大数据之Nifi工作笔记0057
查看>>
NIFI1.21.0_Mysql到Mysql增量CDC同步中_补充_更新时如果目标表中不存在记录就改为插入数据_Postgresql_Hbase也适用---大数据之Nifi工作笔记0059
查看>>
NIFI1.21.0_NIFI和hadoop蹦了_200G集群磁盘又满了_Jps看不到进程了_Unable to write in /tmp. Aborting----大数据之Nifi工作笔记0052
查看>>
NIFI1.21.0最新版本安装_连接phoenix_单机版_Https登录_什么都没改换了最新版本的NIFI可以连接了_气人_实现插入数据到Hbase_实际操作---大数据之Nifi工作笔记0050
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表多表增量同步_增删改数据分发及删除数据实时同步_通过分页解决变更记录过大问题_02----大数据之Nifi工作笔记0054
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表多表增量同步_插入修改删除增量数据实时同步_通过分页解决变更记录过大问题_01----大数据之Nifi工作笔记0053
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表或全表增量同步_实现指定整库同步_或指定数据表同步配置_04---大数据之Nifi工作笔记0056
查看>>
NIFI1.23.2_最新版_性能优化通用_技巧积累_使用NIFI表达式过滤表_随时更新---大数据之Nifi工作笔记0063
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_根据binlog实现数据实时delete同步_实际操作04---大数据之Nifi工作笔记0043
查看>>