AOP
简介:
概念
AOP:Aspect Oriented Programming 面向对象编程,是OOP面向对象的一种补充。
将程序中交叉业务逻辑(事物、日志)代码提取出来,封装成切面,由AOP在合适的位置将它封装的切面动态的织入到具体业务逻辑中。
AOP不是spring特有的
原理;
spring原理就是使用动态代理
- 对于实现接口的目标类,使用的是jdk动态代理
对于没有实现任何接口的目标列,使用的是cglib动态代理
应用的场合
适用于具有很切逻辑的场合,比如事物管理、日志记录、性能检测、异常通知、访问控制等。
作用:
- 在不改变原有代码基础上动态添加新功能。
模块化
术语
- 连接点 JoinPoint
程序执行的某个特定位置,如方法调用前、方法调用后、方法抛出异常时、方法调用前后等。 - 切入点 Pointcut
定位查找到需要连接的点,即切点 - 增强Advice 也成为通知
在切点上执行一段代码程序,用来实现某些功能 - 目标对象 Traget
将执行增强处理的目标类 - 织入Weaving
将增强添加到目标类具体切入点上的过程 - 代理 Proxy
一个类被织入增强后,会产生一个代理类 - 切面 Aspect
切面和增强的组合 引介 Introduction 也成为引入
实现原理:
代理模式回顾
概念:
为其他对象提供一种代理,以控制对这个对象的访问,起到中介的作用。通过代理对象访问目标对象,可以增强额外的操作,扩展目标对象的功能。代理三要素
- 目标类的接口
- 目标类的实例
交叉业务逻辑,要执行的操作
代理分类
静态代理
- 解释:
代理类是程序员创建或者工具生成。所谓静态代理就是程序运行之前你在代理类的字节码文件 - 缺点:
代理对象需要和目标对象实现相同的接口,如果接口增加方法,目标对象和代理对象都要维护。 - 静态代理实操:
service 接口:
public interface UserService { void login(String username, String password) throws NoSuchMethodException; public String logout() throws NoSuchMethodException; }
service 接口实现类:
public class UserServiceImpl implements UserService { @Override public void login(String username, String password) { System.out.println("username:"+username+"password:"+password); } @Override public String logout() { System.out.println("UserServiceImpl.logout"); return "bytebyte"; } }
代理类:
public class UserServiceProxy implements UserService { //目标类的接口 private UserService userService=new UserServiceImpl(); //代理目标实例 @Override public void login(String username, String password) throws NoSuchMethodException { /* System.out.println("login start at:["+new Date().getTime()+"]"); userService.login(username, password);*/ invoke(userService.getClass().getMethod("login",String.class,String.class),username,password); //具体业务逻辑 } @Override public String logout() throws NoSuchMethodException { /* System.out.println("logout start at:["+new Date().getTime()+"]"); return userService.logout();*/ return invoke(userService.getClass().getMethod("logout")).toString(); } //封装到公共方法中 private Object invoke(Method method, Object... args) { //1.打印日记 System.out.println(method.getName()+"login start at:["+new Date().getTime()+"]"); try { return method.invoke(userService,args); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } }
main入口
public static void main(String[] args) { UserService userservice=new UserServiceProxy(); try { System.out.println(userservice.logout()); userservice.login("zhangsan","里斯"); } catch (NoSuchMethodException e) { e.printStackTrace(); } }
动态代理
代理类是程序在运行期间由JVM根据反射等机制动态生成的,自动生成代理类和代理对象。所谓动态代理就是程序在运行前不存在代理类的字节码文件。
动态代理的三种技术:
Proxy
特点
JDK自带技术,使用Jdk自带的Proxy必须实现一个或多个接口,如果没有实现任何接口,则无法使用jdk自带的Proxy技术。
用法
- 语法:
Proxy.newProxyInstance{ classLoader,//目标类的加载器 interfaces,// 目标累的接口 InvocationHandler 交叉业务逻辑 }
- 实现步骤:
- UserService
public interface UserService { void login(String username, String password) throws NoSuchMethodException; public String logout() throws NoSuchMethodException; }
- UserServiceImpl
public class UserServiceImpl implements UserService { @Override public void login(String username, String password) { System.out.println("username:"+username+"password:"+password); } @Override public String logout() { System.out.println("UserServiceImpl.logout"); return "bytebyte"; } }
- main:
public static void main(String[] args) throws NoSuchMethodException { UserService userservice = (aop02.service.UserService)Proxy.newProxyInstance( UserServiceImpl.class.getClassLoader(),//目标类的加载器 new Class[]{UserService.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法名:" + method.getName()); try{ System.out.println("开始事务"); Object obj=method.invoke(UserServiceImpl.class, args); System.out.println("提交事务"); return obj; } catch (Exception ex) { System.out.println("回滚事务"); } return null; } } ); System.out.println(userservice.logout()); userservice.login("张三", "123456"); }
cglib
解释
如果没有实现接口,可以使用cglib,它是通过继承来实现。
语法
Enhancer.create( Class type, Callback callback )
实现步骤
- 添加jar包
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency>
- 使用cglib
HelloWorld helloWorld = (HelloWorld) Enhancer.create( HelloWorld.class, //目标类的类型 new InvocationHandler() { //交叉业务逻辑 @Override public Object invoke(Object o, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + " start at:[" + new Date().getTime() + "]"); return method.invoke(new HelloWorld(), args); } } ); helloWorld.sayHello();
Spring AOP配置方式
Spring 增强的类型
通知类型 实现接口 描述
前置通知 MethodBeforeAdvice 在方法执行前添加功能
后置通知 AfterReturnAdvice 在方法执行后添加功能
环绕通知 MethedInterceptor 在方法前后执行
异常通知 ThrowsAdvice 异常通知
引入通知 IntroductionInterceptor 在目标类添加新方法和属性
注:多个通知之间不允许有耦合,即多个Advice之间不允许有业务交叉。像事务处理就必须使用环绕通知
Spring AOP1.x
使用ProxyFactoryBean 手动代理
使用步骤:
- 添加jar 包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency>
- 定义接口
public interface UserService { public void login(String username, String password) throws ClassNotFoundException; public String logout(); }
- 实现接口
public class UserServiceImpl implements UserService { @Override public void login(String username, String password) { System.out.println("username:"+username+"password:"+password); System.out.println("UserServiceImpl.login"); } @Override public String logout() { System.out.println("UserServiceImpl.logout"); return "bytebyte"; } }
- 配置Advice 实现相应的接口
public class logAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getName()+" start at:["+new Date().getTime()+"],args:"+ Arrays.toString(args)+",target:"+target); } }
- xml文件配置:
<bean class="aop05.service.impl.UserServiceImpl " id="UserServicetarget"/> <bean class="aop05.Advice.logAdvice " id="logAdvice"/> <bean class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor" id="logAdvisor"> <property name="advice" ref="logAdvice"/> <property name="mappedNames"> <list> <value>login</value> <value>logout</value> </list> </property> </bean> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="calcService"> <property name="target" ref="UserServicetarget"/> <property name="interfaces"> <list> <value>aop05.service.UserService </value> </list> </property> <property name="interceptorNames"> <list> <value>logAdvisor</value> </list> </property> </bean>
- 使用
public class test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("ioc04/spring.xml"); UserService userService = (UserService) ac.getBean("userService"); try { userService.login("sss", "ffff"); System.out.println(userService.logout()); System.out.println(userService.getClass()); } catch (Exception ex) { ex.printStackTrace(); } } }
Spring AOP 2.x 配置
简介
基于命名空间的配置,原来是使用后处理器,更简单
特点:
- 简化配置
- 非入侵性:编写通知时不需要实现任何接口
- 使用AspectJ表达式定义切点
介绍:
一种表达式,用来定义切点位置
两种用法- within(包名.类型)匹配类中的所有方法
- execution(返回值类型 包名.类名.方法名(参数类型)) 可使用通配符*和..
execution(void aop06.service.impl.UserServiceImpl.login(String,String))
四种写法
方法名 说明
public void 方法名(JoinPoint) 一般用于前置通知
public void 方法名(JoinPoint,Object) 有返回值,一般用户后置通知
public void 方法名(JoinPoint,Exception)
异常通知
public void 方法名(ProceedingJoinPoint)
环绕通知
步骤:
- 定义接口
public interface UserService { public void login(String username, String password) throws ClassNotFoundException; public String logout(); }
- 实现接口
public class UserServiceImpl implements UserService { @Override public void login(String username, String password) { System.out.println("username:"+username+"password:"+password); System.out.println("UserServiceImpl.login"); } @Override public String logout() { System.out.println("UserServiceImpl.logout"); return "bytebyte"; } }
- 定义增强类,不需要实现任何接口。
public class logAdvice { public void log(JoinPoint joinPoint) { System.out.println("joinPoint.getArgs():"+ Arrays.toString(joinPoint.getArgs())); System.out.println("joinPoint.getClass():"+joinPoint.getClass()); System.out.println("joinPoint.getThis():"+joinPoint.getThis()); System.out.println("joinPoint.getTarget():"+joinPoint.getTarget()); Signature signature = joinPoint.getSignature(); MethodSignature signaturemethod=(MethodSignature)signature; Method method = signaturemethod.getMethod(); System.out.println("method.getName():"+method.getName()); } }
- 配置
<!---Spring 2.x配置 --> <!---配置目標對象 --> <bean id="userService" class="aop06.service.impl.UserServiceImpl"/> <!---配置增強類 --> <bean id="logAdvice" class="aop06.advice.logAdvice"/> <!---配置pointCut 并织入 --> <aop:config> <!---配置pointCut --> <!-- <aop:pointcut id="pc" expression="within(aop06.service.impl.UserServiceImpl)"/>--> <!-- <aop:pointcut id="pc" expression="within(aop06.service.impl.*ServiceImpl)"/>--> <aop:pointcut id="pc" expression="execution(void aop06.service.impl.UserServiceImpl.login(String,String))"/> <aop:aspect ref="logAdvice"> <!---將logAdvice中的log方法以前入通知的方式织入到pc中--> <!-- <aop:before method="log" pointcut-ref="pc"/>--> <!-- <aop:after-returning method="log2" pointcut-ref="pc" returning="returnValue"/>--> <!--<aop:after-throwing method="log3" pointcut-ref="pc" throwing="ex"/>--> <aop:around method="log4" pointcut-ref="pc"/> </aop:aspect> </aop:config> </beans>
- 测试
public static void main(String[] args) { ApplicationContext ac=new ClassPathXmlApplicationContext("aop06/spring.xml"); UserService userServicetarget = (UserService)ac.getBean("userService"); try { userServicetarget.login("zhanghua","lisi"); userServicetarget.logout(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
来源:https://www.cnblogs.com/lilihai/p/10142086.html