本文旨在将清楚AOP是什么,以及Spring AOP具体如何使用,有何注意事项。 我们先来看一个iphone: 在APPLE早期,大概1977年的时候,APPLE刚刚起步,规模不是很大,对于APPLE来说,它有一个总工厂,它主要负责一下这些事务: 随着APPLE的不断壮大,它的各个业务都变得异常庞大,单单靠APPLE总工厂来处理这些业务的话,已经吃不消了,所以,它需要对现在的这个业务体制进行改革。 改革的方式是:请来代理商,让它们来对产品进行销售和售后,自己只负责生产产品和研发,但是它们也必须保证各个方面符合APPLE的标准。 以上的这个例子就是代理模式的思想。 我们来看一下什么是代理模式: 代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式。 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 通俗一点来理解代理模式:本来由A做的事情,现在由B进行代理,并且可以对A的方法进行增强。以前访问一个对象需要通过A,现在只需要通过B就可以进行对该对象的访问。 在Java中,使用代理的方式有两种,静态代理和动态代理: 下面是两种方式的代码演示,仅演示了需要代理的部分。 首先确定一下接口: 然后是工厂类,需要实现接口: 然后是代理类,同样需要实现接口: 最后main方法测试一下: 测试截图: 动态代理的核心是使用反射创建对象。 动态代理用到了JDK官方提供的 使用jdk生成的动态代理的前提是目标类必须有实现的接口。 在这个代理的过程中JDK动态生成了一个类去实现接口。 接口和工厂类与上面一致,不同的是代理类: 最后测试一下: 测试截图: 使用JDK动态代理需要目标类必须有实现的接口。如果需要去代理一个普通的类,需要使用Cglib提供的 使用Cglib的话,被代理类不能被 Cglib是以动态生成的子类继承目标的方式实现,在运行期动态的在内存中构建一个子类。 在明白了上述代理,动态代理的远离和优势之后,对于AOP就好理解了,因为AOP的核心思想就是动态代理。 我们先来看一下官方的一些术语: 看了这些术语后,可能关于代理的部分好理解了,但是,切面与这些切入点,连接点怎么处理呢? 我们来看一个例子,假如你现在需要对数据库里面的信息做一个操作:将所有男生的财产增加100万,那么对于的关系应该是下面这样的。 总结一下对AOP的理解,AOP其实就是面对这个切面来编写程序,选择需要增强的对象(切入点),然后增强(通知)。 在配置Spring AOP前,先把需要使用到的类配置到相关的IOC容器中。 在书写切入点表达式前需要加上关键字 可以单独配置标签(写在当前切面标签内也可以写在切面外,不过要写在切面之前),也可以直接写在通知标签内,单独配置标签方式如下: 标准切入点表达式: 例如: 省略访问修饰符: 返回值可以使用通配符,表示任意返回值: 包名可以使用通配符,表示任意包。但是有几级包,就需要写几个 包名可以使用 类名和方法名都可以使用 参数的写法: 全通配切入点表达式: 一般切到业务层实现类下的所有方法,如 先在xml进行相关的配置: 在上面我们使用动态代理的时候,使用到了两种模式的动态代理,一种是基于JDK的,需要实现接口,一种是基于Cfglib的。在Spring的最底层,集成了这两种方法。 查看源码: 通过上述源码可以发现,Spring生成代理对象的步骤: 注意:如果目标类没有实现接口,且class为final修饰的,则不能使用Spring AOP。 正常: 参考书籍: 感谢耐心的您看到了这,谢谢您的支持! ATFWUS –Writing By 2020–04-22
AOP(Aspect Oriented Programming)
1.概念
2.生活中的例子(代理模式)
这里不是打广告(~~~)。
对这个APPLE这个总工厂来说,它需要在保证APPLE的核心产品和服务标准的情况下,进行生产产品,研发产品,销售,售后四大主要的业务。随着时间的推移,代理商也不满足于销售和售后了,于是,它可以在原本APPLE总工厂没有这项服务的前提下,提供一些个性化服务,比如,碎屏险,贴膜,手机壳。
现在,你需要购买APPLE的任何产品,只需要在代理商那去买,可以同样享受APPLE的服务,而且还会有个性化的服务。
3.代理模式的实现
静态代理实现:
public interface IAppleCore { //销售 void sale(); //售后 void saleAfter(); }
public class AppleFactory implements IAppleCore { @Override public void sale() { System.out.println("销售了产品"); } @Override public void saleAfter(){ System.out.println("进行了售后服务"); } }
public class AppleProxy implements IAppleCore { private IAppleCore target=new AppleFactory(); @Override public void sale() { System.out.println("代理已开启"); target.sale(); System.out.println("代理结束"); } @Override public void saleAfter() { System.out.println("代理已开启"); target.saleAfter(); System.out.println("代理结束"); } }
public class Client { public static void main(String[] args) { //使用代理对象 IAppleCore proxy=new AppleProxy(); //使用代理对象执行方法 proxy.sale(); proxy.saleAfter(); } }
动态代理实现-JDK(基于接口):
Proxy
类。newProxyInstance
方法参数说明:
ClassLoader
:类加载器,用于加载被代理对象的字节码,与被代理对象使用相同的类加载器。Claa[]
:字节码数组,让代理对象和被代理对象有相同的方法。InvocationHandler
:提供增强的代码,通常采用匿名内部类,谁使用,谁写此类。import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class AppleProxy{ private Object target; public AppleProxy(IAppleCore target) { this.target = target; } public Object getProxyInstance(){ Object proxy= Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), //增强的方法 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName=method.getName(); Object result=null; if("saleAfter".equals(methodName)){ System.out.println("检测到售后方法,开始代理"); result=method.invoke(target,args); System.out.println("代理成功"); }else{ result=method.invoke(target,args); } return result; } } ); return proxy; } }
public class Client { public static void main(String[] args) { IAppleCore target=new AppleFactory(); //代理对象 IAppleCore proxy=(IAppleCore)new AppleProxy(target).getProxyInstance(); System.out.println("target:"+target.getClass()); System.out.println("proxy:"+proxy.getClass()); proxy.sale(); proxy.saleAfter(); } }
动态代理实现-Cglib(基于子类):
Enhancer
类。final
修饰,既需要可以创建子类create
方法参数:
Class
:指定被代理对象的字节码。Callback
:提供增强的方法。
代码如下:import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class Client { public static void main(String[] args) { final IAppleCore target=new AppleFactory(); IAppleCore proxy=(IAppleCore) Enhancer.create(target.getClass(), new MethodInterceptor() { @Override public Object intercept(Object oproxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { String methodName=method.getName(); Object result=null; if("saleAfter".equals(methodName)){ System.out.println("检测到售后方法,开始代理"); result=method.invoke(target,args); System.out.println("代理成功"); }else{ result=method.invoke(target,args); } return result; } }); System.out.println("target:"+target.getClass()); System.out.println("proxy:"+proxy.getClass()); proxy.sale(); proxy.saleAfter(); } }
4.AOP再理解
5.Spring AOP的简单配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="https://www.springframework.org/schema/beans" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop" xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 配置srping的Ioc,把service对象配置进来--> <bean id="accountService" class="cservice.impl.ATServiceImpl"></bean> <!-- 配置一个日志类 --> <bean id="logger" class="utils.Loggers"></bean> <!--配置AOP--> <aop:config> <!--配置切面 --> <aop:aspect id="logAdvice" ref="logger"> <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联--> <aop:before method="printLog" pointcut="execution(* service.impl.*.*(..))"></aop:before> </aop:aspect> </aop:config> </beans>
<aop:config>
表示开始进行Spring AOP的配置。aop:aspect
表示配置切面
id
:切面提供一个唯一标识。ref
:指定通知类bean的id。6.Spring AOP 通知类型与切入点表达式
aop:aspect
标签的内部需要使用对应标签来配置通知的类型。通知的类型:
aop:before
:用于配置前置通知。指定增强的方法在切入点方法之前执行。
method
:用于指定通知类中的增强方法名称。ponitcut-ref
:用于指定切入点的表达式的引用。poinitcut
:用于指定切入点表达式。aop:after-returning
:用于配置后置通知。切入点方法正常执行之后。它和异常通知只能有一个执行。属性同上。aop:after-throwing
:用于配置异常通知。切入点方法执行产生异常后执行。它和后置通知只能执行一个 。属性同上。aop:after
:于配置最终通知。无论切入点方法执行时是否有异常,它都会在其后面执行。属性同上。aop:around:
用于配置环绕通知。它是 spring框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。属性同上。
ProceedingJoinPoint
,它可以作为环绕通知的方法参数。在环绕通知执行时,spring框架会为我们提供该接口的实现类对象,我们可以直接使用。<aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
public Object aroundPringLog(ProceedingJoinPoint pjp){ Object rtValue = null; try{ Object[] args = pjp.getArgs();//得到方法执行所需的参数 System.out.println("前置"); rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法) System.out.println("后置"); return rtValue; }catch (Throwable t){ System.out.println("异常"); throw new RuntimeException(t); }finally { System.out.println("最终"); } }
切入点表达式:
execution(表达式)
。
<aop:pointcut id="pt01" expression="excution(* com.atfwus.service.impl.*.*(..))"></aop:pointcut>
访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
public void com.atfwus.service.impl.ATServiceImpl.proxy()
例如:void com.atfwus.service.impl.ATServiceImpl.proxy()
例如:* com.atfwus.service.impl.ATServiceImpl.proxy()
*
:
例如:* *.*.*.*.ATServiceImpl.proxy()
..
表示当前包及其子包:
例如:* *..ATServiceImpl.proxy()
*
来实现通配:
例如:* *..*.*()
int
java.lang.String
..
表示有无参数均可,有参数可以是任意类型* *..*.*(..)
* com.atfwus.service.impl.*.*(..)
7.基于注解的Spring AOP
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="https://www.springframework.org/schema/beans" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop" xmlns:context="https://www.springframework.org/schema/context" xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置spring创建容器时要扫描的包--> <context:component-scan base-package="com.atfwus"></context:component-scan> <!-- 配置spring开启注解AOP的支持 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
在通知类上使用
@Aspect
注解声明为切面 :
@Aspect
注解表示把当前类声明为切面类。通知类型注解:
@Before
:把当前方法看成是前置通知。属性value
:用于指定切入点表达式,还可以指定切入点表达式的引用。@AfterReturning
:把当前方法看成是后置通知。 属性同上。@AfterThrowing
:把当前方法看成是异常通知。属性同上。@After
:把当前方法看成是最终通知。属性同上。@Around
:把当前方法看成是环绕通知。属性同上。切入点表达式注解
@Pointcut
:
value
:指定表达式的内容。@Pointcut("execution(* com.atfwus.service.impl.*.*(..))") private void pt1() {}
完全注解配置:
@EnableAspectJAutoProxy
注解就可以了。8.Spring AOP的创建代理对象原理(部分源码分析)
org.springframework.aop.framework.DefaultAopProxyFactory
。public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { public DefaultAopProxyFactory() { } public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { // 如果指定了 optimize为true 或者是proxyTargetClass 为true 或者是 没有实现接口 if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) { return new JdkDynamicAopProxy(config); } else { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) {// 目标类找不到 抛出异常 throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation."); } else { // 目标类是接口 或者是 class是由代理类动态通过getProxyClass方法 或者 newProxyInstance方法生成 使用jdk 动态代理 否则 cglib 代理 return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config)); } } } private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] ifcs = config.getProxiedInterfaces(); return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]); } }
9.Spring AOP实例
配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="https://www.springframework.org/schema/beans" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop" xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 配置srping的Ioc,把service对象配置进来--> <bean id="accountService" class="com.atfwus.service.impl.ATServiceImpl"></bean> <!-- 配置Logger类 --> <bean id="logger" class="com.atfwus.utils.Logger"></bean> <!--配置AOP--> <aop:config> <aop:pointcut id="pt1" expression="execution(* com.stfwus.service.impl.*.*(..))"></aop:pointcut> <!--配置切面 --> <aop:aspect id="logAdvice" ref="logger"> <!-- 配置环绕通知--> <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around> </aop:aspect> </aop:config> </beans>
Service层:
package com.atfwus.service.impl; import com.atfwus.service.IATService; public class ATServiceImpl implements IATService{ @Override public void save() { System.out.println("保存信息"); // int i=1/0; } @Override public void update(int i) { System.out.println("更新信息"+i); } @Override public int delete() { System.out.println("删除信息"); return 0; } }
日志类:
package com.atfwus.utils; import org.aspectj.lang.ProceedingJoinPoint; /** * 记录日志 */ public class Logger { public Object aroundPringLog(ProceedingJoinPoint pjp){ Object rtValue = null; try{ Object[] args = pjp.getArgs();//得到方法执行所需的参数 System.out.println("前置记录日志"); rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法) System.out.println("后置记录日志"); return rtValue; }catch (Throwable t){ System.out.println("异常日志记录"); throw new RuntimeException(t); }finally { System.out.println("最终日志记录"); } } }
main方法测试:
import com.atfwus.service.IAccountService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 测试AOP的配置 */ public class AOPTest { public static void main(String[] args) { //1.获取容器 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.获取对象 IATService obj = (IATService)ac.getBean("ATService"); //3.执行方法 obj.save(); } }
测试截图:
异常:10.AOP的优点与用途
优势分析:
用途:
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算