Spring AOP 面向切面编程学习

编程入门 行业动态 更新时间:2024-10-09 16:30:32

Spring AOP 面向<a href=https://www.elefans.com/category/jswz/34/1755292.html style=切面编程学习"/>

Spring AOP 面向切面编程学习

文章目录

  • AOP
  • 环境搭建
  • Schema-based 方式
      • 前置通知
      • 后置通知
      • 环绕通知
      • 异常通知
      • *和..的使用
  • AspectJ方式
      • 前置通知
      • 后置通知
            • 1. aop:after和aop:after-returing区别
            • 2.标签的好处
      • 环绕通知
      • 异常通知
      • AspectJ多参数
  • 代理模式
      • 静态代理模式
      • 动态代理模式
            • 1.jdk动态代理
            • 2.cglib 动态代理

AOP

AOP(Aspect Oriented Programming) ,面向切面编程


左边是传统方式,右边是面向切面方式
所谓切面编程就是在执行某个功能的时候,在前面添加点东西,还可以在后面添加点东西。而并没有去改变原有的代码


在程序原有纵向执行流程中,针对某一个或某一些方法添加通 知,形成横切面过程就叫做面向切面编程

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码,主要体现在事务处理,日志管理,权限控制,异常处理等方面,使开发人员在编写业务逻辑时可以专心于核心业务,提高了代码的可维护性。

AOP术语
Joinpoint(连接点):是指那些被拦截到的点
Pointcut(切入点):是指要对哪些Joinpoint进行拦截,即被拦截的连接点。也可以理解为原有的功能
Advice(通知):是指拦截到Joinpoint之后要做的事情,既对切入点增强的内容
before advice(前置通知):在切入点之前执行的功能.
after advice(后置通知):在切点之后执行的功能
throws advice(异常通知):在切点执行过程中出现异常,会触发异常通知.
Aspect(切面):切入点和通知的结合

Spring提供了2种AOP实现方式
1.Schema-based
在Schema-based方式中,每个通知都要实现特定的接口或类
并在spring配置文件中,添加<aop:config>标签,然后再<aop:config>标签下进行配置
2.AspectJ
在AspectJ方式中,每个通知不需要实现特定的接口或类
配置 spring 配置文件是在<aop:config>的子标签 <aop:aspect>中配置

环境搭建


导入相关jar包

Schema-based 方式

在Schema-based方式中,每个通知都要实现特定的接口或类

前置通知

新建一个前置通知类实现接口

package cn.advice;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class MyBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] objects, Object o) throws Throwable {System.out.println("前置通知");}
}

前置通知类需要实现MethodBeforeAdvice接口

Method method:切点方法对象
Object[] objects:切点方法参数
Object o:切点方法所在类的对象

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:aop=""xsi:schemaLocation="://www.springframework/schema/beans/spring-beans.xsd://www.springframework/schema/aop/spring-aop.xsd"><!--配置bean--><bean id="test" class="cn.test.Test"/><bean id="mybefore" class="cn.advice.MyBeforeAdvice"></bean><!--配置切面--><aop:config><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/><!--配置通知--><aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/></aop:config></beans>

在配置文件中,配置了在Test类下的method1方法下插入前置通知

package cn.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public void method1(){System.out.println("method1");}public void method2(){System.out.println("method3");}public void method3(){System.out.println("method4");}public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");   //spring实现aop,对象要交给spring管理Test test=ac.getBean("test",Test.class);test.method1();test.method2();test.method3();}
}

后置通知

后置通知:在切点的后面插入 实现AfterReturningAdvice

 import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;public class MyAfterAdvice implements AfterReturningAdvice {@Overridepublic void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {System.out.println("后置通知");}
}

Object o:切点方法返回值
Method method:切点方法对象
Object[] objects:切点方法参数
Object o1:切点方法所在类的对象

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:aop=""xsi:schemaLocation="://www.springframework/schema/beans/spring-beans.xsd://www.springframework/schema/aop/spring-aop.xsd"><!--配置bean--><bean id="test" class="cn.test.Test"/><bean id="mybefore" class="cn.advice.MyBeforeAdvice"></bean>   <!--前置通知所在类--><bean id="myafter" class="cn.advice.MyAfterAdvice"></bean>     <!--后置通知所在类--><!--配置切面--><aop:config><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/><!--配置通知--><aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>   <!--前置通知--><aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>     <!--后置通知--></aop:config></beans>
package cn.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public void method1(){System.out.println("method1");}public void method2(){System.out.println("method3");}public void method3(){System.out.println("method4");}public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");Test test=ac.getBean("test",Test.class);test.method1();test.method2();test.method3();}
}

添加前置通知和后置通知并没有改变原有代码

环绕通知

环绕通知:在切点前后都加功能

package cn.advice;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;public class MyArroundAdvice implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation methodInvocation) throws Throwable {System.out.println("环绕执行前");Object result=methodInvocation.proceed();   //放行System.out.println("环绕通知后");return result;}
}

实现MethodInterceptor接口

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:aop=""xsi:schemaLocation="://www.springframework/schema/beans/spring-beans.xsd://www.springframework/schema/aop/spring-aop.xsd"><!--配置bean--><bean id="test" class="cn.test.Test"/><bean id="mybefore" class="cn.advice.MyBeforeAdvice"></bean>   <!--前置通知所在类--><bean id="myafter" class="cn.advice.MyAfterAdvice"></bean>     <!--后置通知所在类--><bean id="myarround" class="cn.advice.MyArroundAdvice"></bean>    <!--环绕通知所在类--><!--配置切面--><aop:config><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/><!--配置通知--><aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>   <!--前置通知--><aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>     <!--后置通知--><aop:advisor advice-ref="myarround" pointcut-ref="mypoint"/>   <!--环绕通知--></aop:config></beans>
package cn.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public void method1(){System.out.println("method1");}public void method2(){System.out.println("method3");}public void method3(){System.out.println("method4");}public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");Test test=ac.getBean("test",Test.class);test.method1();test.method2();test.method3();}
}

异常通知

当切点执行出现错误时,执行异常通知

定义异常通知,需要实现ThrowsAdvice接口


查看源码,发现ThrowsAdvice接口并没有定义方法,

在实现ThrowsAdvice接口时,需要自己写方法,并且方法名必须叫afterThrowing

package cn.advice;import org.springframework.aop.ThrowsAdvice;public class MyThrowAdvice implements ThrowsAdvice {public void afterThrowing(Exception e) throws Throwable{System.out.println("异常通知:"+e.getMessage());}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:aop=""xsi:schemaLocation="://www.springframework/schema/beans/spring-beans.xsd://www.springframework/schema/aop/spring-aop.xsd"><!--配置bean--><bean id="test" class="cn.test.Test"/><bean id="mybefore" class="cn.advice.MyBeforeAdvice"></bean>   <!--前置通知所在类--><bean id="myafter" class="cn.advice.MyAfterAdvice"></bean>     <!--后置通知所在类--><bean id="myarround" class="cn.advice.MyArroundAdvice"></bean>    <!--环绕通知所在类--><bean id="mythrow" class="cn.advice.MyThrowAdvice"></bean>     <!--配置异常通知--><!--配置切面--><aop:config><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/><!--配置通知--><aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>   <!--前置通知--><aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>     <!--后置通知--><aop:advisor advice-ref="myarround" pointcut-ref="mypoint"/>   <!--环绕通知--><aop:advisor advice-ref="mythrow" pointcut-ref="mypoint"/>    <!--异常通知--></aop:config></beans>
package cn.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public void method1(){Test test=null;test.method3();    //定义了一个异常System.out.println("method1");}public void method2(){System.out.println("method3");}public void method3(){System.out.println("method4");}public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");Test test=ac.getBean("test",Test.class);test.method1();test.method2();test.method3();}
}

实现接口中方法的参数还可以写成四个参数

import java.lang.reflect.Method;public class MyThrowAdvice implements ThrowsAdvice {
/*    public void afterThrowing(Exception e) throws Throwable{System.out.println("异常通知:"+e.getMessage());}*/public void afterThrowing(Method m, Object[] args, Object o, Exception e) {System.out.println("异常通知:"+e.getMessage());}
}

注:
参数中的异常对象最好定义为Exception的对象

在method1()方法中,出现的异常为NullPointerException,所以可以方法中参数可以定义为NullPointerException


public class MyThrowAdvice implements ThrowsAdvice {public void afterThrowing(NullPointerException e) throws Throwable{System.out.println("异常通知:"+e.getMessage());}
}


可以看出是执行了异常通知了的

如果将参数换做不是NullPointerException的异常对象,假如换做ClassNotFoundException

public class MyThrowAdvice implements ThrowsAdvice {public void afterThrowing(ClassNotFoundException e) throws Throwable{System.out.println("异常通知:"+e.getMessage());}
}


可以看出没有执行异常通知

*和…的使用

在配置切点是可以使用*来指定包或类

1.

<!--配置切点-->
<aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/> 

这表示只能是Test类下的method1方法,并且method1必须是无参数的

将method1变为有参数的,可以看出并没有执行通知

2.

 <!-- 配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1(..))"/>

这表示是method1是有参数的

3.

<!--配置切点-->
<aop:pointcut id="mypoint" expression="execution(* cn.test.Test.*())"/> 

这表示是Test类下的任意无参方法

4.

<!--配置切点-->
<aop:pointcut id="mypoint" expression="execution(* cn.test.Test.*(..))"/> 

这表示是Test类下的任意方法,不管是有参还是无参

5.

<!--配置切点-->
<aop:pointcut id="mypoint" expression="execution(* cn.test.*.*(..))"/> 

这表示test包下的任意类的任意方法,不管是有参还是无参

以此类推

AspectJ方式

AspectJ方式不需要实现接口
可以自定义一个类,类下写方法,使用标签来说明类下的方法是什么类型的通知
使用配置文件的方式
<aop:config>下的<aop:aspect >标签下进行配置
<aop:aspect >标签的ref属性是指引用自己定义的通知类,方法在哪个类中

<bean id="test" class="cn.test.Test"/>
<bean id="before" class="cn.advice.Advice"></bean>   <!--配置通知所在类--><aop:config><!--配置切面--><aop:aspect ref="before"><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/>  <aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知--></aop:aspect></aop:config>

<aop:before>前置通知标签
<aop:after/> 后置通知,是否出现异常都执行
<aop:after-returing/> 后置通知,只有当切点正确执行时
<aop:after-throwing/>异常通知
<aop:after/><aop:after-returing/><aop:after-throwing/>执行顺序和配置顺序有关

<aop: xxxx/> 表示什么通知
method属性: 当触发这个通知时,调用哪个方法

前置通知

随便定义一个类,里面写一个方法,方法名也随便起

package cn.advice;public class Advice {public void beforeAdvice(){System.out.println("前置通知");}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:aop=""xsi:schemaLocation="://www.springframework/schema/beans/spring-beans.xsd://www.springframework/schema/aop/spring-aop.xsd"><!--配置bean--><bean id="test" class="cn.test.Test"/><bean id="before" class="cn.advice.Advice"></bean>   <!--配置通知所在类--><aop:config><!--配置切面--><aop:aspect ref="before"><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/>  <aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知--></aop:aspect></aop:config>
</beans>

method属性: 当触发这个通知时,调用哪个方法 pointcut-ref属性:引用哪个切点


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public void method1(){System.out.println("method1 ");}public void method2(){System.out.println("method3");}public void method3(){System.out.println("method4");}public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");Test test=ac.getBean("test",Test.class);test.method1();test.method2();test.method3();}
}

后置通知

package cn.advice;public class Advice {public void beforeAdvice(){System.out.println("前置通知");}public void afterAdvice(){System.out.println("后置通知");}
}
    <bean id="test" class="cn.test.Test"/><bean id="before" class="cn.advice.Advice"></bean>   <!--配置通知所在类--><aop:config><!--配置切面--><aop:aspect ref="before"><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/><aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知--><aop:after method="afterAdvice" pointcut-ref="mypoint"></aop:after>     <!--配置后置通知--></aop:aspect></aop:config>
package cn.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public void method1(){System.out.println("method1 ");}public void method2(){System.out.println("method3");}public void method3(){System.out.println("method4");}public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");Test test=ac.getBean("test",Test.class);test.method1();test.method2();test.method3();}
}

1. aop:after和aop:after-returing区别

后置通知还有个标签是 <aop:after-returing/>
它与<aop:after>标签的区别是 <aop:after-returing/> ,在切点出现异常时<aop:after>依然能通知

 <aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知--><aop:after method="afterAdvice" pointcut-ref="mypoint"></aop:after>     <!--配置后置通知-->


可以看出依然执行了后置通知

<aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知-->
<aop:after-returning method="afterAdvice" pointcut-ref="mypoint"></aop:after-returning>

可以看出并没有执行后置通知

2.标签的好处

因为是使用标签来指定方法到底是什么类型的通知,所以标签可以随意指定方法

 <aop:before method="afterAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知--><aop:after method="beforeAdvice" pointcut-ref="mypoint"></aop:after>     <!--配置后置通知-->

将两个标签所指定的方法互换一下

可以看出互换成功

环绕通知

只需在Advice类中添加环绕通知的方法,并且要在参数中添加 ProceedingJoinPoint 的对象

package cn.advice;import org.aspectj.lang.ProceedingJoinPoint;public class Advice {public void beforeAdvice(){System.out.println("前置通知");}public void afterAdvice(){System.out.println("后置通知");}public Object arroundAdvice(ProceedingJoinPoint p) throws Throwable {System.out.println("环绕前置通知");Object result=p.proceed();System.out.println("环绕后置通知");return result;}}

在xml中使用<aop:around>标签

<bean id="test" class="cn.test.Test"/>
<bean id="before" class="cn.advice.Advice"></bean>   <!--配置通知所在类-->
<aop:config><!--配置切面--><aop:aspect ref="before"><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/><aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知--><aop:after-returning method="afterAdvice" pointcut-ref="mypoint"></aop:after-returning><aop:around method="arroundAdvice" pointcut-ref="mypoint"></aop:around></aop:aspect></aop:config>
package cn.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public void method1(){System.out.println("method1 ");}public void method2(){System.out.println("method3");}public void method3(){System.out.println("method4");}public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");Test test=ac.getBean("test",Test.class);test.method1();test.method2();test.method3();}
}

异常通知

在Advice类中添加异常通知的方法,并且要在参数中添加 Exception 的对象


package cn.advice;import org.aspectj.lang.ProceedingJoinPoint;public class Advice {public void beforeAdvice(){System.out.println("前置通知");}public void afterAdvice(){System.out.println("后置通知");}public Object arroundAdvice(ProceedingJoinPoint p) throws Throwable {System.out.println("环绕前置通知");Object result=p.proceed();System.out.println("环绕后置通知");return result;}public void throwAdvice(Exception e){System.out.println("异常通知:"+e.getMessage());}
}

定义<aop:after-throwing>标签定指定异常通知方法

<bean id="test" class="cn.test.Test"/>
<bean id="before" class="cn.advice.Advice"></bean>   <!--配置通知所在类--><aop:config><!--配置切面--><aop:aspect ref="before"><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1())"/><aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知--><aop:after method="afterAdvice" pointcut-ref="mypoint"></aop:after>     <!--配置后置通知-->
<!--            <aop:after-returning method="afterAdvice" pointcut-ref="mypoint"></aop:after-returning>--><aop:around method="arroundAdvice" pointcut-ref="mypoint"></aop:around><aop:after-throwing method="throwAdvice" pointcut-ref="mypoint" throwing="e"></aop:after-throwing></aop:aspect></aop:config>

<aop:after-throwing>标签中要添加一个throwing属性,指明异常对象

package cn.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public void method1(){Test test=null;test.method2();System.out.println("method1 ");}public void method2(){System.out.println("method3");}public void method3(){System.out.println("method4");}public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");Test test=ac.getBean("test",Test.class);test.method1();test.method2();test.method3();}
}

AspectJ多参数

切点的方法有参数,通知如何获取参数中的内容

以前置通知举例

public class Advice {public void beforeAdvice(String name,String sex,int age){System.out.println("前置通知: "+name+" "+sex+" "+age);}
}
Test下的method1方法public void method1(String name,String sex,int age){System.out.println("method1 "+name+" "+sex+" "+age);}
<bean id="test" class="cn.test.Test"/>
<bean id="before" class="cn.advice.Advice"></bean>   <!--配置通知所在类--><aop:config><!--配置切面--><aop:aspect ref="before"><!--配置切点--><aop:pointcut id="mypoint" expression="execution(* cn.test.Test.method1(String,String,int)) and args(name,sex,age) "/><aop:before method="beforeAdvice" pointcut-ref="mypoint" arg-names="name,sex,age"></aop:before>   <!--配置前置通知--></aop:aspect></aop:config>

1.首先在execution(* )中为method1添加参数类型,严格与method1对应
2.在execution(* ) 后添加 and args()
3.在args()添加参数名称,可以随便起,但要和beforeAdvice(String name,String sex,int age)的参数名对应
4.在<aop:before>标签中添加arg-names属性,属性内容和args()中的内容对应

package cn.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public void method1(String name,String sex,int age){System.out.println("method1 "+name+" "+sex+" "+age);}public void method2(){System.out.println("method3");}public void method3(){System.out.println("method4");}public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");Test test=ac.getBean("test",Test.class);test.method1("kryie","男",10);test.method2();test.method3();}
}

其他通知类似,注意要一一对应

代理模式

AOP的原理基于动态代理,是代理模式的一种

比如:
明星都有经济人,活动的安排都需要先经过经纪人。安排一个商演活动,活动方要找某位明星做商业演出。但找这个明星需要先通过他的经纪人,经纪人得到消息就可以查看明星的日程是否有冲突。若没有冲突,则就通知明星,出席商业演出。商业演出结束后,明星离开。结束后,活动方和经纪人商谈后续事宜。

在这个例子中,明星就是真实对象,经纪人就是代理对象,商业演出就是抽象对象(也叫抽象功能)

可以对比AOP
经纪人得到消息就可以查看明星的日程是否有冲突,相当于前置通知,在商演前执行点某个功能
明显出席商业演出,相当于切点,执行真正的功能
结束后活动方和经纪人商谈后续事宜,相当于后置通知。在商演结束后执行点某个功能

代理设计模式的优点:
1.保护真实对象 。
保护明星
2.让真实对象职责更明确.。
明星只管唱歌,演出。活动安排交给经济人
3.扩展
对比AOP

静态代理模式

首先来看静态代理模式

package cn;public interface Performance {public void show();
}

定义一个接口,代表一个抽象对象(抽象功能)

package cn;public class Star implements Performance {@Overridepublic void show() {System.out.println("明星演出");}
}

定义明星对象实现Performance 接口

package cn;public class Agent implements Performance {Star star=new Star();@Overridepublic void show() {System.out.println("查看日程");star.show();System.out.println("商量后续");}
}

定义经纪人对象实现Performance接口

package cn;public class Company {public static void main(String[] args) {Agent agent=new Agent();agent.show();}
}

公司对象

静态代理模式就是由代理对象代理所有真实对象的功能.
并且自己编写代理类
每个代理的功能需要单独编写

静态代理设计模式的缺点: 当代理功能比较多时,代理类中方法需要写很多

动态代理模式

动态代理可以解决静态代理中要写很多代理方法的缺点

动态代理有JDK动态代理,和cglib动态代理

1.jdk动态代理

jdk 自带,不需要额外导入 jar
真实对象必须实现接口
利用反射机制.效率不高.

package cn;public interface Performance {public void show();
}
package cn;public class Star implements Performance {@Overridepublic void show() {System.out.println("明星演出");}
}

真实对象实现接口

package cn;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class Agent implements InvocationHandler {private Star star=new Star();@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("查看日程");Object result=method.invoke(star,args);System.out.println("商量后续");return result;}
}

代理对象需要实现InvocationHandler 接口
重写invoke方法

package cn;import java.lang.reflect.Proxy;public class Company {public static void main(String[] args) {Performance performance= (Performance) Proxy.newProxyInstance(Company.class.getClassLoader(),new Class[]{Performance.class},new Agent());performance.show();}
}

使用Proxy的newProxyInstance()静态方法

第一个参数:反射时使用的类加载器 因为java只有一个类加载器,所以随便哪个类的类名.class.getClassLoader()都行
第二个参数:代理对象需要实现什么接口
第三个参数:通过接口对象调用方法时,需要调用哪个类的invoke方法,即代理对象

接口对象是不能转换为真实对象的,会报异常

public static void main(String[] args) {Performance performance= (Performance) Proxy.newProxyInstance(Company.class.getClassLoader(),new Class[]{Performance.class},new Agent());performance.show();Star star= (Star) performance;   //转换为真实对象}

2.cglib 动态代理

cglib 是基于字节码的,它会生成真实对象的子类。而JDK动态代理是基于反射的,所以运行效率高于 JDK 动态代理.
cglib不需要实现接口,但需要导入jar包

package cn;public class Star {public void show(){System.out.println("明星演出");}
}
package cn;import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class Agent implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("查看日程");Object result=methodProxy.invokeSuper(o,objects);System.out.println("商量后续");return result;}
}

代理对象实现MethodInterceptor 接口

o:生成的子类对象
method:代理的方法
objects:参数
methodProxy: 子类的代理方法(重写的方法) intercept

package cn;import net.sf.cglib.proxy.Enhancer;public class Company {public static void main(String[] args) {Enhancer enhancer=new Enhancer();enhancer.setSuperclass(Star.class);enhancer.setCallback(new Agent());Star star= (Star) enhancer.create();star.show();}
}

在SpringAOP中,默认是使用JDK动态代理。所以代理对象是不能转换为真实对象的。当出现 Proxy 和真实对象转换异常,可以使用标签将SpringAOP的代理模式设置为cglib动态代理

true:表示使用cglib动态代理
false:表示使用jdk动态代理

更多推荐

Spring AOP 面向切面编程学习

本文发布于:2024-02-07 02:26:48,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1752108.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:切面   Spring   AOP

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!