Spring之 Aop

目录

目录什么是AOPAOP应用场景Spring AOP代理机制Spring AOP的术语核心概念重要通知分类织入时期为什么使用AOPSpring AOP实现基本配置pom.xmlplugins 插件配置添加依赖web.xmlJava.classIBookBiz.javaBookBizImpl.javaresource包新建 spring.xml前置通知ProxyFactoryBean 常用属性后置通知环绕通知异常通知适配器完整代码spring.xmlDemo.java

什么是AOP

OOP三大特点:封装、继承、多态

AOP

AOP(Aspect Oriented Programming)是一种面向切面的编程思想。面向切面编程是将程序抽象成各个切面,即解剖对象的内部, 将那些影响了多个类的公共行为抽取到一个可重用模块里 ,减少系统的重复代码,降低模块间的耦合度,增强代码的可操作性和可维护性。

提取公共行为抽取到一个类中

中间是具体的核心代码(核心关注点),上边和下边为 重复代码(横切关注点)

publicvoidadd(){

Connectioncon=DBHelper.getConnection(); //获取连接对象:连接数据库

PrepareStatementps=con....

ResultSetrs=ps....

核心代码

finally{

DBHelper.close(con,ps,rs); //关闭连接

}

}

-> 交付由 AOP管理

publicvoidadd(){

<------面向切面:从横切面切入代码

核心代码

<------面向切面

}

AOP把软件系统分为两个部分:核心关注点(每一个方法中的核心功能)和横切关注点(多个方法里的公共行为)。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理、增强处理。

AOP带来的好处:让我们可以 “专心做事

应用场景

  • 场景一:记录日志

  • 场景二:监控方法运行时间 (监控性能)

方法执行前和方法执行后,不知道核心代码需要多长时间执行完成。由 结束时间 - 开始时间 得到 核心代码时长,然后就可以根据时长进行方法调优

  • 场景三:权限控制

  • 场景四:缓存优化 (

第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )

  • 场景五:事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )

比如:开启连接,关闭连接

publicvoidadd(){

Connectioncon=DBHelper.getConnection();

Transactiontran=conn.beginTransaction();

<------面向切面:从横切面切入代码

核心代码

<------面向切面

tran.commit()/tran.rollback();

}

Spring AOP代理机制

Spring 在运行期会为目标对象生成一个动态代理对象,并在代理对象中实现对目标对象的增强。Spring AOP 的底层是通过以下 2 种动态代理机制,为目标对象(Target Bean)执行横向织入的。

  • JDK动态代理:Spring AOP 默认的动态代理方式,若目标对象实现了若干接口,Spring 使用 JDK 的 java.lang.reflect.Proxy 类进行代理。

  • CGLIB动态代理:若目标对象没有实现任何接口,Spring 则使用 CGLIB 库生成目标对象的子类,以实现对目标对象的代理。

注意:由于被标记为 final 的方法是无法进行覆盖的,因此这类方法不管是通过 JDK 动态代理机制还是 CGLIB 动态代理机制都是无法完成代理的。

Spring AOP的术语

核心概念重要

  • Joinpoint(连接点)

指那些被拦截到的点(程序执行过程中明确的点,如 方法的调用,或者异常的抛出),在 Spring 中,可以被动态代理拦截目标类的方法。

  • Pointcut(切入点)

多个连接点的集合,定义了通知应该应用到哪些连接点。

  • Advice(通知)

指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。

  • Target(目标)

指代理的目标对象。

  • Weaving(织入)

指把增强代码应用到目标上,生成代理对象的过程。

  • Proxy(代理)

指生成的代理对象(将通知应用到目标对象后创建的对象)。

  • Aspect(切面)

切入点和通知的结合。

术语

含义

连接点

程序执行过程中明确的点,方法的调用

切入点

多个连接点的集合,只有满足条件的才能将通知应用到目标上 (if 条件,筛选)

通知

公共行为,横切关注点

目标

具体执行核心业务的代码

代理

将通知应用到目标上所的创建对象 (通知+目标 = 代理)

切面

通知 + 切入点 【满足条件】

公共部分的代码写在哪里的? 通知里面,只有完整的代理对象才具备 AOP的特性

通知分类

AOP 联盟为通知(Advice)定义了一个 org.aopalliance.aop.Interface.Advice 接口。

Spring AOP 按照通知(Advice)织入到目标类方法的连接点位置,为 Advice 接口提供了 6 个子接口。

通知类型

接口

描述

前置通知

org.springframework.aop.MethodBeforeAdvice

在目标方法执行实施增强。

后置通知

org.springframework.aop.AfterAdvice

在目标方法执行实施增强。

后置返回通知

org.springframework.aop.AfterReturningAdvice

在目标方法执行完成,并返回一个返回值后实施增强。

环绕通知

org.aopalliance.intercept.MethodInterceptor

在目标方法执行前后实施增强。

异常通知

org.springframework.aop.ThrowsAdvice

在方法抛出异常后实施增强。

引入通知

org.springframework.aop.IntroductionInterceptor

在目标类中添加一些新的方法和属性。

织入时期

织⼊(Weaving):代理的⽣成时机(通知 + 目标)。织⼊是把切⾯应⽤到⽬标对象并创建新的代理对象的过程,切⾯在指定的连接点被织⼊到⽬标对象中。

在⽬标对象的⽣命周期⾥有多个点可以织⼊:

时期

说明

编译期

切⾯在⽬标类编译时被织⼊。这种⽅式需要特殊的编译器。AspectJ的织⼊编译器就是以这种⽅式织⼊切⾯的。

类加载期

切⾯在⽬标类加载到JVM时被织⼊。这种⽅式需要特殊的类加载器(ClassLoader),它可以在⽬标类被引⼊应⽤之前增强该⽬标类的字节码。AspectJ5的加载时织⼊(load-time weaving. LTW)就⽀持以这种⽅式织⼊切⾯。

运行期

切⾯在应⽤运⾏的某⼀时刻被织⼊。⼀般情况下,在织⼊切⾯时,AOP容器会为⽬标对象动态创建⼀个代理对象。SpringAOP就是以这种⽅式织⼊切⾯的。

为什么使用AOP

举个例子

  • 病人做手术

  1. 术前准备工作,护士和麻醉师 -> 通知 ,公共行为 (一个手术室可能有多台手术,同一时间)

  1. 病人做手术 -> 目标

  1. 做手术 -> 切入点(明确的一个点:只有做手术才会触发 准备工作、打麻药...)

  1. 护士、麻醉师和医生一起做手术 -> 代理

  1. 如果没有护士、麻醉师,医生一个人完成手术,不可能吧

  1. 如果没有医生,只有护士、麻醉师,都不知道从哪里下刀

所以,通知 + 目标 = 代理,一个完成的 代理对象才具有 AOP的特性

  1. 做完手术,清理工具 -> 通知,公共行为(做完手术都要 清理)

  1. 麻醉师打麻醉,方便做手术 -> 切面(切入点+ 通知)

  • 在指定方法被调用之前记录日志

在指定方法被调用之前记录日志。在传统方式下通过在每个方法之前先添加日志收集代码,再执行方法本身的核心业务代码

如果遇到记录日志方法业务变动的情况,后期的代码更新和维护将是一大难题。
假设有一百个方法,每个方法都要记录日志,有一个改动都要维护100次,
如果给 AOP管理,则只用修改一次

publicvoiddoSameBusiness (longlParam,StringsParam){

// 记录日志:在方法调用之前

log.info("调用 doSameBusiness方法,参数是:"+lParam);

//核心方法: 输入合法性验证

if (lParam<=0){

throwsnewIllegalArgumentException("xx应该大于0");

}

if (sParam==null||sParam.trim().equals("")){

throwsnewIllegalArgumentException("xx不能为空");

}

// 异常处理

try{ ...

}catch(...){

}

// 事务控制

tx.commit();

}

Spring AOP实现

基本配置

基于idea创建maven项目,配置pom.xml并导入依赖。

pom.xml

plugins 插件配置

<!--插件-->

<plugins>

<!--第一步就是配置maven-compiler-plugin插件,其他的插件统统删除-->

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.7.0</version>

<configuration>

<source>${maven.compiler.source}</source>

<!--jdk 1.8-->

<target>${maven.compiler.target}</target>

<encoding>${project.build.sourceEncoding}</encoding>

</configuration>

</plugin>

</plugins>

添加依赖

<!--设置版本号-->

<properties>

<junit.version>4.12</junit.version>

<spring.version>5.0.8.RELEASE</spring.version>

</properties>

<!--动态加载依赖-->

<dependencies>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>${junit.version}</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-orm</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aspects</artifactId>

<version>${spring.version}</version>

</dependency>

</dependencies>

web.xml

<web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"

id="WebApp_ID"version="3.0">

<display-name>Archetype Created Web Application</display-name>

</web-app>

Java.class

导入书本业务类IBookBiz、BookBizImpl

IBookBiz.java

publicinterfaceIBookBiz {

// 购书

publicbooleanbuy(StringuserName, StringbookName, Doubleprice);

// 发表书评

publicvoidcomment(StringuserName, Stringcomments);

}

BookBizImpl.java

publicclassBookBizImplimplementsIBookBiz {

publicBookBizImpl() {

super();

}

publicbooleanbuy(StringuserName, StringbookName, Doubleprice) {

// 通过控制台的输出方式模拟购书

/*if (null == price || price <= 0) {

throw new PriceException("book price exception");

}*/

System.out.println(userName+" buy "+bookName+", spend "+price);

returntrue;

}

publicvoidcomment(StringuserName, Stringcomments) {

// 通过控制台的输出方式模拟发表书评

System.out.println(userName+" say:"+comments);

}

}

resource包

新建 spring.xml

用于 AOP 管理

前置通知

前置通知(org.springframework.aop.MethodBeforeAdvice):在连接点之前执行的通知。

1)创建前置通知类

packagecom.zking.spring02_aop.advice;

importorg.springframework.aop.MethodBeforeAdvice;

importjava.lang.reflect.Method;

importjava.util.Arrays;

/**

* 前置通知

*/

publicclassBeforeAdviceimplementsMethodBeforeAdvice {

/**

* 前置通知

* @param method 目标方法

* @param params 目标所执行的参数

* @param target 目标对象

* @throws Throwable

*/

@Override

publicvoidbefore(Methodmethod, Object[] params, Objecttarget) throwsThrowable {

//1. 获取目标方法名

StringmethodName=method.getName();

//2. 获取 目标对象的 类的全路径名

StringclassPath=target.getClass().getName();

System.out.println("【前置通知】 "+classPath+"."+methodName+"执行参数:"+Arrays.toString(params));//数组类型需要 转成字符串输出

}

}

2)创建spring.xml配置ProxyFactoryBean创建代理对象

Spring 能够基于 org.springframework.aop.framework.ProxyFactoryBean 类,根据目标对象的类型(是否实现了接口)自动选择使用 JDK 动态代理或 CGLIB 动态代理机制,为目标对象(Target Bean)生成对应的代理对象(Proxy Bean)

<?xmlversion="1.0" encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans " target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--target 目标

目标:被通知(被代理)对象 注:执行具体业务逻辑的方法对象-->

<beanid="bookTarget"class="com.zking.spring02_aop.biz.BookBizImpl"></bean>

<!--Advice 通知

通知:在特定连接点上执行的动作 注:完成AOP面向切面编程-->

<!--前置通知-->

<beanid="before"class="com.zking.spring02_aop.advice.BeforeAdvice"></bean>

<!--代理(Proxy) = 通知+目标-->

<beanid="proxy"class="org.springframework.aop.framework.ProxyFactoryBean">

<!--需要注入原料:通知和 目标-->

<propertyname="target">

<!--引入 目标 target:被通知的目标对象-->

<refbean="bookTarget"/>

</property>

<propertyname="interceptorNames">

<!--引入 通知,可能有多个通知-->

<list>

<!--前置通知的 id-->

<value>before</value>

</list>

</property>

<!--代理对象需要实现的接口 proxyInterfaces:代理接口对象(List)-->

<propertyname="interfaces">

<list>

<!--接口的路径-->

<value>com.zking.spring02_aop.biz.IBookBiz</value>

</list>

</property>

</bean>

</beans>

ProxyFactoryBean 常用属性

属性

描述

target

需要代理的目标对象(Bean)

proxyInterfaces

代理需要实现的接口,如果需要实现多个接口,可以通过 <list> 元素进行赋值。

proxyTargetClass

针对类的代理,该属性默认取值为 false(可省略), 表示使用 JDK 动态代理;取值为 true,表示使用 CGlib 动态代理

interceptorNames

拦截器的名字,该属性的取值既可以是拦截器、也可以是 Advice(通知)类型的 Bean,还可以是切面(Advisor)的 Bean。

singleton

返回的代理对象是否为单例模式,默认值为 true。

optimize

是否对创建的代理进行优化(只适用于CGLIB)。

3)测试代码

//示例2:前置通知

// 报错: Bean named 'proxy' is expected to be of type 'com.zking.spring02_aop.biz.BookBizImpl'

// but was actually of type 'com.sun.proxy.$Proxy4' -> 强转报错:不同类型不能转换

/*BookBizImpl proxy = ac.getBean("proxy",BookBizImpl.class);

proxy.buy("饭饭","撒野",145d);

proxy.comment("饭饭","丞哥无处不在");*/

/*Object proxy1 = ac.getBean("proxy");

System.out.println(proxy1.getClass()); //输出 class com.sun.proxy.$Proxy4*/

//请使用 接口接收 代理对象!!! 代理对象实现的是 接口

IBookBizproxy=ac.getBean("proxy", IBookBiz.class);

proxy.buy("饭饭","撒野",145d);

proxy.comment("饭饭","丞哥无处不在");

使用错误方式运行代码时,出现:
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy4 cannot be cast to com.zking.spring02.dao.BookBizImpl
提示:无法将com.sun.proxy.$Proxy4 无法转换成com.zking.spring02.dao.BookBizImpl
请使用接口接收返回的代理对象!!!请使用接口接收返回的代理对象!!!请使用接口接收返回的代理对象!!!

后置通知

后置通知(org.springframework.aop.AfterReturningAdvice):在连接点正常完成后执行的通知。

packagecom.zking.spring02_aop.advice;

importorg.springframework.aop.AfterReturningAdvice;

importjava.lang.reflect.Method;

importjava.util.Arrays;

/**

* 后置通知

*/

publicclassAfterAdviceimplementsAfterReturningAdvice {

/**

*

* @param returnValue 目标方法返回值

* @param method 目标方法

* @param params 目标方法执行参数

* @param target 目标对象

* @throws Throwable

*/

@Override

publicvoidafterReturning(ObjectreturnValue, Methodmethod, Object[] params, Objecttarget) throwsThrowable {

System.out.println("【后置通知】 "+target.getClass().getName()+"."+method.getName()+"执行参数:"+Arrays.toString(params)+

"返回值:"+returnValue);

}

}

环绕通知

环绕通知(org.aopalliance.intercept.MethodInterceptor):包围一个连接点的通知,最大特点是可以修改返回值,由于它在方法前后都加入了自己的逻辑代码,因此功能异常强大。它通过MethodInvocation.proceed()来调用目标方法(甚至可以不调用,这样目标方法就不会执行)。

packagecom.zking.spring02_aop.advice;

importorg.aopalliance.intercept.MethodInterceptor;

importorg.aopalliance.intercept.MethodInvocation;

importjava.lang.reflect.Method;

importjava.util.Arrays;

/**

* 环绕通知

*/

publicclassAroundAdviceimplementsMethodInterceptor {

/**

*

* @param methodInvocation

* @return

* @throws Throwable

*/

@Override

publicObjectinvoke(MethodInvocationmethodInvocation) throwsThrowable {

//1. 获取 目标对象

Objecttarget=methodInvocation.getThis();

//2. 获取目标方法

Methodmethod=methodInvocation.getMethod();

//3. 获取目标方法的执行参数

Object[] params=methodInvocation.getArguments();

//执行目标方法,并返回结果(环绕通知能控制目标方法的执行)

ObjectreturnValue=methodInvocation.proceed();

System.out.println("【环绕通知】 "+target.getClass().getName()+"."+method.getName()+" 执行参数:"

+Arrays.toString(params)+"返回值:"+returnValue);

returnreturnValue;

}

}

异常通知

1)创建自定义异常类

publicclassPriceExceptionextendsRuntimeException {

publicPriceException() {

super();

}

publicPriceException(Stringmessage, Throwablecause, booleanenableSuppression, booleanwritableStackTrace) {

super(message, cause, enableSuppression, writableStackTrace);

}

publicPriceException(Stringmessage, Throwablecause) {

super(message, cause);

}

publicPriceException(Stringmessage) {

super(message);

}

publicPriceException(Throwablecause) {

super(cause);

}

}

2)设置异常抛出点

BookBizImpl 的买书方法

publicbooleanbuy(StringuserName, StringbookName, Doubleprice) {

// 通过控制台的输出方式模拟购书

if (null==price||price<=0) {

thrownewPriceException("book price exception");

}

System.out.println(userName+" buy "+bookName+", spend "+price);

returntrue;

}

3)定义异常通知

publicclassMyThrowsAdviceimplementsThrowsAdvice {

publicvoidafterThrowing(PriceExceptione) {

System.out.println("[异常通知] 价格异常,撤销订单!");

}

}

4)配置异常通知

<!-- 异常通知 -->

<beanid="myThrowsAdvice"class="com.zking.spring02.action.MyThrowsAdvice">

</bean>

<!--

代理:目标+通知

将目标和通知对象注入到代理工厂对象中

target:被通知的目标对象

proxyInterfaces:代理接口对象(List)

interceptorNames:需要应用到目标对象上的通知Bean的名字(List)

-->

<beanid="bookBiz"class="org.springframework.aop.framework.ProxyFactoryBean">

<propertyname="target">

<refbean="bookBizTarget"/>

</property>

<propertyname="proxyInterfaces">

<list>

<value>com.zking.spring02.dao.IBookBiz</value>

</list>

</property>

<propertyname="interceptorNames">

<list>

<value>myThrowsAdvice</value>

</list>

</property>

</bean>

适配器

<!-- 适配器=通知+切入点 通过正则表达式的方式对调用方法进行切入 -->

<beanid="regexpMethodPointcutAdvisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

<propertyname="advice"ref="myAfterReturningAdvice">

</property>

<!-- 定义规则 -->

<propertyname="patterns">

<value>.*buy</value>

</property>

</bean>

<!--

代理:目标+通知

将目标和通知对象注入到代理工厂对象中

target:被通知的目标对象

proxyInterfaces:代理接口对象(List)

interceptorNames:需要应用到目标对象上的通知Bean的名字(List)

-->

<beanid="bookBiz"class="org.springframework.aop.framework.ProxyFactoryBean">

<propertyname="target">

<refbean="bookBizTarget"/>

</property>

<propertyname="proxyInterfaces">

<list>

<value>com.zking.spring02.dao.IBookBiz</value>

</list>

</property>

<propertyname="interceptorNames">

<list>

<value>myMethodBeforeAdvice</value>

<!-- 将后置通知更换成适配器 -->

<value>regexpMethodPointcutAdvisor</value>

<value>myMethodInterceptor</value>

<value>myThrowsAdvice</value>

</list>

</property>

</bean>

完整代码

spring.xml

<?xmlversion="1.0" encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans " target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--target 目标-->

<beanid="bookTarget"class="com.zking.spring02_aop.biz.BookBizImpl"></bean>

<!--Advice 通知-->

<!--前置-->

<beanid="before"class="com.zking.spring02_aop.advice.BeforeAdvice"></bean>

<!--后置-->

<beanid="after"class="com.zking.spring02_aop.advice.AfterAdvice"></bean>

<!--环绕-->

<beanid="around"class="com.zking.spring02_aop.advice.AroundAdvice"></bean>

<!--异常-->

<beanid="exception"class="com.zking.spring02_aop.advice.ExceptionAdvice"></bean>

<!-- 适配器: 切面= 通知+目标-->

<beanid="aspect"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

<propertyname="advice">

<refbean="after"/>

</property>

<propertyname="pattern">

<value>.*buy</value>

</property>

<!-- <property name="patterns">

<value></value>

<value></value>

</property>-->

</bean>

<!--代理(Proxy) = 通知+目标-->

<beanid="proxy"class="org.springframework.aop.framework.ProxyFactoryBean">

<!--,需要注入原料:通知和 目标-->

<propertyname="target">

<!--引入 目标-->

<refbean="bookTarget"/>

</property>

<propertyname="interceptorNames">

<!--引入 通知,可能有多个通知-->

<list>

<!--前置通知的 id-->

<!--<value>before</value>-->

<!--后置-->

<!-- <value>after</value>-->

<!--环绕-->

<!--<value>around</value>-->

<!--异常-->

<value>exception</value>

<!--适配器-->

<value>aspect</value>

</list>

</property>

<!--代理对象需要实现的接口-->

<propertyname="interfaces">

<list>

<!--接口的路径-->

<value>com.zking.spring02_aop.biz.IBookBiz</value>

</list>

</property>

</bean>

</beans>

Demo.java

packagecom.zking.spring02_aop.util;

importcom.zking.spring02_aop.biz.BookBizImpl;

importcom.zking.spring02_aop.biz.IBookBiz;

importorg.springframework.context.ApplicationContext;

importorg.springframework.context.support.ClassPathXmlApplicationContext;

publicclassDemo {

// /j enter 方法注释

publicstaticvoidmain(String[] args){

//1. 什么是 AOP

//面向切面,“专心做事”

//2. 核心概念

//1) 连接点(JoinPoint): 程序执行过程中明确的点。例如:方法的调用、异常的抛出

//2) 通知(advice): 公共行为,指拦截到 Joinpoint 之后要做的事情

//3) 目标(Target): 核心代码,指代理的目标对象

//4) 代理(Proxy): 将通知应用到目标上所创建的对象。 通知+目标=代理

//注:公共部分的代码是写在通知中的:只有完整的代理对象才具备 AOP的特性

//5) 切入点(PointCut) :连接点的集合

//6) 切面(Aspect):通知+切入点(限制条件)。 只有满足条件的目标对象,才将通知应用到目标上

//3. 通知分类:

ApplicationContextac=newClassPathXmlApplicationContext("spring.xml");//初始化 IOC容器

//示例:买书、书评

/* BookBizImpl bookBiz = ac.getBean("bookTarget",BookBizImpl.class);

bookBiz.buy("饭饭","撒野",145d);

bookBiz.comment("饭饭","丞哥无处不在");*/

//示例2:前置通知 //示例3:后置通知 //示例4:环绕通知 //示例5:异常通知

// 报错: Bean named 'proxy' is expected to be of type 'com.zking.spring02_aop.biz.BookBizImpl'

// but was actually of type 'com.sun.proxy.$Proxy4' -> 强转报错:不同类型不能转换

/*BookBizImpl proxy = ac.getBean("proxy",BookBizImpl.class);

proxy.buy("饭饭","撒野",145d);

proxy.comment("饭饭","丞哥无处不在");*/

/*Object proxy1 = ac.getBean("proxy");

System.out.println(proxy1.getClass()); //输出 class com.sun.proxy.$Proxy4*/

//请使用 接口接收 代理对象!!! 代理对象实现的是 接口

IBookBizproxy=ac.getBean("proxy", IBookBiz.class);

proxy.buy("饭饭","撒野",145d);

proxy.comment("饭饭","丞哥无处不在");

/*

方法的调用 -> 连接点

将通知应用到我的目标上 -> 代理对象

公共代码 -> 通知

*/

//示例6:适配器

//只有符合条件,才会输出

//比如:只有购买书籍,才可返利 2元,书评不可以获得

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

youdabi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值