- spring的aop特点
- spring的aop的专业术语
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 过滤通知
- spring上下文过滤通知(过滤通知)
1.spring的aop特点
app的简介
解决的问题:解决了需求的改变,造成了原有没必要改变的代码,需要去改变它;
比如:书籍的增删改,本身只需要完成增删改的功能即可,这是如果需要添加日志功能,那么需要在原有的代码基础上,去修改添加日志功能,受牵连的方法就三个(add/edit/del)了;
AOP是什么?
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP 是 OOP(面向对象编程) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
特点:非业务的核心代码
2.spring的aop的专业术语
连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
目标(Target):被通知(被代理)的对象
注1:完成具体的业务逻辑
通知(Advice):在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
注2:完成切面编程
代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知),
例子:外科医生+护士
注3:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的
切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。
(也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)
适配器(Advisor):适配器=通知(Advice)+切入点(Pointcut)
面向切面总结(概念):面向切面是将原有的代码从上至下的顺序做出改变。加入了spring的aop面向编程思想,代码就不再是从上往下执行。而是,当代码走到了目标对象时,要查看连接点上是否有前置通知,如果有前置通知,选执行前置通知,再执行目标方法。如果没有前置通知,就直接执行目标方法。最后再看连接点上是否有后置通知,如果有执行后置通知代码,如果有,代码执行完毕。
3.前置通知
package com.zlj.aop.advice; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; import java.util.Arrays; /** * 买书、评论前加系统日志 * @author Administrator * */ public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { // 在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去 String target = arg2.getClass().getName(); String methodName = arg0.getName(); String args = Arrays.toString(arg1); System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+")被调用了"); } }
package com.zlj.aop.biz.Impl; import com.zlj.aop.biz.IBookBiz; import com.zlj.aop.exception.PriceException; public class BookBizImpl implements IBookBiz { public BookBizImpl() { super(); } public boolean buy(String userName, String bookName, Double price) { // 通过控制台的输出方式模拟购书 if (null == price || price <= 0) { throw new PriceException("book price exception"); } System.out.println(userName + " buy " + bookName + ", spend " + price); return true; } public void comment(String userName, String comments) { // 通过控制台的输出方式模拟发表书评 System.out.println(userName + " say:" + comments); } }
package com.zlj.aop.biz; /** * @author zlj * @create 2023-08-17 15:51 */ public interface IBookBiz { // 购书 public boolean buy(String userName, String bookName, Double price); // 发表书评 public void comment(String userName, String comments); }
package com.zlj.aop.demo; import com.zlj.aop.biz.IBookBiz; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author zlj * @create 2023-08-17 16:23 */ public class demo1 { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml"); IBookBiz bookBiz = (IBookBiz) context.getBean("bookProxy"); System.out.println(bookBiz); bookBiz.buy("nb","金瓶梅",9.9d); bookBiz.comment("nb","看得入迷,睡不着"); } }
package com.zlj.aop.exception; /** * @author zlj * @create 2023-08-17 16:06 */ public class PriceException extends RuntimeException { public PriceException() { super(); } public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } public PriceException(String message, Throwable cause) { super(message, cause); } public PriceException(String message) { super(message); } public PriceException(Throwable cause) { super(cause); } }
<!--讲解aop相关的javaBean--> <!--目标对象--> <bean class="com.zlj.aop.biz.Impl.BookBizImpl" id="bookBiz"></bean> <!--通知--> <bean class="com.zlj.aop.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean> <!--代理--> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"> <!-- 配置目标对象--> <property name="target" ref="bookBiz"></property> <!-- 配置代理的接口,目标对象的接口 扩展:JDK动态代理和CGLIB代理都是实现Spring框架中AOP的代理方式,它们的实现原理和应用场景有所不同, 具体区别如下: 1. 实现原理: JDK动态代理是基于Java反射机制实现的,它要求目标类必须实现一个或多个接口,代理对象 在运行时动态创建,通过实现目标类接口的方式来代理目标类。 CGLIB代理则是基于ASM字节码框架实现的, 它可以代理没有实现接口的目标类。CGLIB在运行时通过动态生成目标类的子类来实现代理。 2. 性能表现: JDK动态代理因为需要实现目标类接口,所以它的性能相对较低,但是它的应用场景更为广泛, 适用于大多数情况下的代理需求。 CGLIB代理则因为不需要实现目标类接口,所以它的性能相对较高,但是它 不能代理final类和final方法,以及一些无法生成子类的类。 3. 应用场景: JDK动态代理适用于代理接口的场景,例如Spring中的事务处理、日志记录等。 CGLIB代理适 用于代理类的场景,例如Spring中的AOP切面编程等。 总之,JDK动态代理和CGLIB代理都是Spring框架中常用的代理方式,各自有不同的应用场景和性能表现,开发 人员需要根据具体需求进行选择。--> <property name="proxyInterfaces"> <list> <value>com.zlj.aop.biz.IBookBiz</value> </list> </property> <!-- 配置通知--> <property name="interceptorNames"> <list> <value>methodBeforeAdvice</value> </list> </property> </bean> </beans>
4.后置通知
<!--讲解aop相关的javaBean--> <!--目标对象--> <bean class="com.zlj.aop.biz.Impl.BookBizImpl" id="bookBiz"></bean> <!--通知--> <bean class="com.zlj.aop.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean> <bean class="com.zlj.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean> <!--代理--> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"> <!-- 配置目标对象--> <property name="target" ref="bookBiz"></property> <!-- 配置代理的接口,目标对象的接口 扩展:JDK动态代理和CGLIB代理都是实现Spring框架中AOP的代理方式,它们的实现原理和应用场景有所不同, 具体区别如下: 1. 实现原理: JDK动态代理是基于Java反射机制实现的,它要求目标类必须实现一个或多个接口,代理对象 在运行时动态创建,通过实现目标类接口的方式来代理目标类。 CGLIB代理则是基于ASM字节码框架实现的, 它可以代理没有实现接口的目标类。CGLIB在运行时通过动态生成目标类的子类来实现代理。 2. 性能表现: JDK动态代理因为需要实现目标类接口,所以它的性能相对较低,但是它的应用场景更为广泛, 适用于大多数情况下的代理需求。 CGLIB代理则因为不需要实现目标类接口,所以它的性能相对较高,但是它 不能代理final类和final方法,以及一些无法生成子类的类。 3. 应用场景: JDK动态代理适用于代理接口的场景,例如Spring中的事务处理、日志记录等。 CGLIB代理适 用于代理类的场景,例如Spring中的AOP切面编程等。 总之,JDK动态代理和CGLIB代理都是Spring框架中常用的代理方式,各自有不同的应用场景和性能表现,开发 人员需要根据具体需求进行选择。--> <property name="proxyInterfaces"> <list> <value>com.zlj.aop.biz.IBookBiz</value> </list> </property> <!-- 配置通知--> <property name="interceptorNames"> <list> <value>methodBeforeAdvice</value> <value>myAfterReturningAdvice</value> </list> </property> </bean> </beans>
package com.zlj.aop.demo; import com.zlj.aop.biz.IBookBiz; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author zlj * @create 2023-08-17 16:23 */ public class demo1 { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml"); // IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz"); IBookBiz bookBiz = (IBookBiz) context.getBean("bookProxy"); // System.out.println(bookBiz); bookBiz.buy("nb","金瓶梅",9.9d); bookBiz.comment("nb","看得入迷,睡不着"); } }
5.环绕通知
package com.zlj.aop.advice; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import java.util.Arrays; /** * 环绕通知 * 包含了前置和后置通知 * * @author Administrator * */ public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation arg0) throws Throwable { String target = arg0.getThis().getClass().getName(); String methodName = arg0.getMethod().getName(); String args = Arrays.toString(arg0.getArguments()); System.out.println("【环绕通知调用前:】:"+target+"."+methodName+"("+args+")被调用了"); // arg0.proceed()就是目标对象的方法 Object proceed = arg0.proceed(); System.out.println("【环绕通知调用后:】:该方法被调用后的返回值为:"+proceed); return proceed; } }
<!--讲解aop相关的javaBean--> <!--目标对象--> <bean class="com.zlj.aop.biz.Impl.BookBizImpl" id="bookBiz"></bean> <!--通知--> <bean class="com.zlj.aop.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean> <bean class="com.zlj.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean> <bean class="com.zlj.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"></bean> <bean class="com.zlj.aop.advice.MyThrowsAdvice" id="myThrowsAdvice"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="RegexpMethodPointcutAdvisor"> <property name="advice" ref="myAfterReturningAdvice"></property> <property name="pattern" value=".*buy"></property> </bean> <!--代理--> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"> <!-- 配置目标对象--> <property name="target" ref="bookBiz"></property> <!-- 配置代理的接口,目标对象的接口 扩展:JDK动态代理和CGLIB代理都是实现Spring框架中AOP的代理方式,它们的实现原理和应用场景有所不同, 具体区别如下: 1. 实现原理: JDK动态代理是基于Java反射机制实现的,它要求目标类必须实现一个或多个接口,代理对象 在运行时动态创建,通过实现目标类接口的方式来代理目标类。 CGLIB代理则是基于ASM字节码框架实现的, 它可以代理没有实现接口的目标类。CGLIB在运行时通过动态生成目标类的子类来实现代理。 2. 性能表现: JDK动态代理因为需要实现目标类接口,所以它的性能相对较低,但是它的应用场景更为广泛, 适用于大多数情况下的代理需求。 CGLIB代理则因为不需要实现目标类接口,所以它的性能相对较高,但是它 不能代理final类和final方法,以及一些无法生成子类的类。 3. 应用场景: JDK动态代理适用于代理接口的场景,例如Spring中的事务处理、日志记录等。 CGLIB代理适 用于代理类的场景,例如Spring中的AOP切面编程等。 总之,JDK动态代理和CGLIB代理都是Spring框架中常用的代理方式,各自有不同的应用场景和性能表现,开发 人员需要根据具体需求进行选择。--> <property name="proxyInterfaces"> <list> <value>com.zlj.aop.biz.IBookBiz</value> </list> </property> <!-- 配置通知--> <property name="interceptorNames"> <list> <value>methodBeforeAdvice</value> <value>myAfterReturningAdvice</value> <value>myMethodInterceptor</value> <!-- <value>myThrowsAdvice</value>--> <!-- <value>RegexpMethodPointcutAdvisor</value>--> </list> </property> </bean> </beans>
package com.zlj.aop.demo; import com.zlj.aop.biz.IBookBiz; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author zlj * @create 2023-08-17 16:23 */ public class demo1 { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml"); // IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz"); IBookBiz bookBiz = (IBookBiz) context.getBean("bookProxy"); // System.out.println(bookBiz); bookBiz.buy("nb","金瓶梅",9.9d); bookBiz.comment("nb","看得入迷,睡不着"); } }
6.异常通知
package com.zlj.aop.advice; import org.springframework.aop.ThrowsAdvice; import com.zlj.aop.exception.PriceException; /** * 出现异常执行系统提示,然后进行处理。价格异常为例 * @author Administrator * */ public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(PriceException ex) { System.out.println("【异常通知】:当价格发生异常,那么执行此处代码块!!!"); } }
<!--讲解aop相关的javaBean--> <!--目标对象--> <bean class="com.zlj.aop.biz.Impl.BookBizImpl" id="bookBiz"></bean> <!--通知--> <bean class="com.zlj.aop.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean> <bean class="com.zlj.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean> <bean class="com.zlj.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"></bean> <bean class="com.zlj.aop.advice.MyThrowsAdvice" id="myThrowsAdvice"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="RegexpMethodPointcutAdvisor"> <property name="advice" ref="myAfterReturningAdvice"></property> <property name="pattern" value=".*buy"></property> </bean> <!--代理--> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"> <!-- 配置目标对象--> <property name="target" ref="bookBiz"></property> <!-- 配置代理的接口,目标对象的接口 扩展:JDK动态代理和CGLIB代理都是实现Spring框架中AOP的代理方式,它们的实现原理和应用场景有所不同, 具体区别如下: 1. 实现原理: JDK动态代理是基于Java反射机制实现的,它要求目标类必须实现一个或多个接口,代理对象 在运行时动态创建,通过实现目标类接口的方式来代理目标类。 CGLIB代理则是基于ASM字节码框架实现的, 它可以代理没有实现接口的目标类。CGLIB在运行时通过动态生成目标类的子类来实现代理。 2. 性能表现: JDK动态代理因为需要实现目标类接口,所以它的性能相对较低,但是它的应用场景更为广泛, 适用于大多数情况下的代理需求。 CGLIB代理则因为不需要实现目标类接口,所以它的性能相对较高,但是它 不能代理final类和final方法,以及一些无法生成子类的类。 3. 应用场景: JDK动态代理适用于代理接口的场景,例如Spring中的事务处理、日志记录等。 CGLIB代理适 用于代理类的场景,例如Spring中的AOP切面编程等。 总之,JDK动态代理和CGLIB代理都是Spring框架中常用的代理方式,各自有不同的应用场景和性能表现,开发 人员需要根据具体需求进行选择。--> <property name="proxyInterfaces"> <list> <value>com.zlj.aop.biz.IBookBiz</value> </list> </property> <!-- 配置通知--> <property name="interceptorNames"> <list> <value>methodBeforeAdvice</value> <value>myAfterReturningAdvice</value> <value>myMethodInterceptor</value> <value>myThrowsAdvice</value> <!-- <value>RegexpMethodPointcutAdvisor</value>--> </list> </property> </bean> </beans>
package com.zlj.aop.demo; import com.zlj.aop.biz.IBookBiz; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author zlj * @create 2023-08-17 16:23 */ public class demo1 { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml"); // IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz"); IBookBiz bookBiz = (IBookBiz) context.getBean("bookProxy"); // System.out.println(bookBiz); bookBiz.buy("nb","金瓶梅",-9.9d); bookBiz.comment("nb","看得入迷,睡不着"); } }
7.spring上下文过滤通知(过滤通知)
<!--讲解aop相关的javaBean--> <!--目标对象--> <bean class="com.zlj.aop.biz.Impl.BookBizImpl" id="bookBiz"></bean> <!--通知--> <bean class="com.zlj.aop.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean> <bean class="com.zlj.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean> <bean class="com.zlj.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"></bean> <bean class="com.zlj.aop.advice.MyThrowsAdvice" id="myThrowsAdvice"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="RegexpMethodPointcutAdvisor"> <property name="advice" ref="myAfterReturningAdvice"></property> <property name="pattern" value=".*buy"></property> </bean> <!--代理--> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"> <!-- 配置目标对象--> <property name="target" ref="bookBiz"></property> <!-- 配置代理的接口,目标对象的接口 扩展:JDK动态代理和CGLIB代理都是实现Spring框架中AOP的代理方式,它们的实现原理和应用场景有所不同, 具体区别如下: 1. 实现原理: JDK动态代理是基于Java反射机制实现的,它要求目标类必须实现一个或多个接口,代理对象 在运行时动态创建,通过实现目标类接口的方式来代理目标类。 CGLIB代理则是基于ASM字节码框架实现的, 它可以代理没有实现接口的目标类。CGLIB在运行时通过动态生成目标类的子类来实现代理。 2. 性能表现: JDK动态代理因为需要实现目标类接口,所以它的性能相对较低,但是它的应用场景更为广泛, 适用于大多数情况下的代理需求。 CGLIB代理则因为不需要实现目标类接口,所以它的性能相对较高,但是它 不能代理final类和final方法,以及一些无法生成子类的类。 3. 应用场景: JDK动态代理适用于代理接口的场景,例如Spring中的事务处理、日志记录等。 CGLIB代理适 用于代理类的场景,例如Spring中的AOP切面编程等。 总之,JDK动态代理和CGLIB代理都是Spring框架中常用的代理方式,各自有不同的应用场景和性能表现,开发 人员需要根据具体需求进行选择。--> <property name="proxyInterfaces"> <list> <value>com.zlj.aop.biz.IBookBiz</value> </list> </property> <!-- 配置通知--> <property name="interceptorNames"> <list> <value>methodBeforeAdvice</value> <!-- <value>myAfterReturningAdvice</value>--> <value>myMethodInterceptor</value> <value>myThrowsAdvice</value> <value>RegexpMethodPointcutAdvisor</value> </list> </property> </bean> </beans>
package com.zlj.aop.demo; import com.zlj.aop.biz.IBookBiz; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author zlj * @create 2023-08-17 16:23 */ public class demo1 { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml"); // IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz"); IBookBiz bookBiz = (IBookBiz) context.getBean("bookProxy"); // System.out.println(bookBiz); bookBiz.buy("nb","金瓶梅",9.9d); bookBiz.comment("nb","看得入迷,睡不着"); } }
应用:不管是前置通知,后置通知,环绕通知,异常通知,过滤通知,代码都是非业务核心代码,如日志,事务的管理(开启,提交,回滚)
扩展:建立数据字典有以下几个步骤:
- 确定数据字典的目的和范围: 首先要明确数据字典的目的, 是为了管理数据、提高数据质量、帮助项目开发人员理解数据等. 然后确定数据字典的范围, 即哪些数据要包含在字典中.
- 确定数据字典的内容: 确定数据字典的内容是很重要的, 应该包含数据的定义、数据的来源、数据的格式、数据的限制、数据的使用方式等信息.
- 建立数据字典模板: 建立一个模板, 方便收集数据字典的信息, 并且可以使字典信息统一、易于管理.
- 收集数据字典信息: 开始收集数据字典信息, 可以找相关人员协助, 或者从系统文档、数据库设计文档等收集信息.
- 整理数据字典信息: 将收集的信息整理成统一的格式, 并确保信息准确、完整.
- 审核数据字典: 最后, 应该对数据字典进行审核, 确保信息准确无误, 并及时更新字典中的信息.