切面编程学习"/>
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 面向切面编程学习
发布评论