举个栗子,生活中一般在打官司的时候都会请代理律师,为什么要请律师呢?是因为开庭的时候大部人对于打官司没有经验,只会说出自己案件的陈述,并不会根据法律等争取自己权益的最大化,此时就可以请律师帮助自己不仅完成对案件的陈述,还能争取权益最大化。那么Java中也是一样,如果要对功能进行增强就可以使用动态代理
请看如下例子:一个演员cxk的本质工作是唱歌,跳舞等,但是演出前后为了吸引粉丝和大量的关注就需要进行各种业务的增强,如:联系业务、互联网造势、演出前的宣传、结算费用并纳税等一系列的操作。所以就用到了动态代理,我们可以把这个代理看成是经纪公司。
这样操作发现出现了大量重复的代码,如果有十个、一百个演员需要同样的处理那么代码需要重复十次、一百次。当然我们可以把这些功能封装成一个增强方法,然后在功能方法中进行调用,但是也出现了方法的十处、一百处的调用操作,一旦增强方法名字改变,就需要完成所有调用处代码的修改。或者有一天不需要这些增强操作了,就再次需要在这十处、一百处删除方法调用。所以这种操作不适用于大型的项目开发的需求,此时我们就必须使用Java的动态代理机制。
动态代理不需要修改源代码的基础上对原有类的功能进行增强。
在Java开发中如果一个类中的方法在基本功能之外需要进行功能扩充或者功能增强,如:事务控制、权限判断、日志记录等等操作,此时可以使用动态代理机制。
Java的JDK中Proxy类可以实现基于接口的动态代理,实现步骤示例如下:
2.定义实现接口的子类,实现接口定义的方法,此方法只需要把核心功能实现即可,其他增强的操作可以在代理类中实现。
3.定义代理类,在代理类中对被代理对象进行方法增强
即:定义经纪公司,在cxk的演出中被经纪公司进行增强。
package com.shi.advice;
import com.shi.service.Actor;
import com.shi.service.impl.CXK;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JJGS {
public static void main(String[] args) {
//1.创建代理类的对象----具体的人物cxk
Actor cxk=new CXK();
//2.创建代理对象----具体某个人为上面cxk服务
/***
* ClassLoader loader:类的加载器---联系方式
* Class>[] interfaces:类的接口类型---被代理人的类型
* InvocationHandler h :处理器----我要帮你干什么
*/
Actor jjr= (Actor) Proxy.newProxyInstance(CXK.class.getClassLoader(), CXK.class.getInterfaces(), new InvocationHandler() {
/**
*
* @param proxy:被代理对象的引用,系统会自动创建被代理对象的一个映射
* @param method:被代理对象的方法
* @param args:被代理对象方法的参数
* @return
* @throws Throwable
*
* 返回值是 被代理对象执行后的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("演出前的增强:联系业务");
System.out.println("演出前的增强:互联网造势");
System.out.println("演出前的增强:演出前的宣传");
//被代理对象方法的执行,并获得返回值
Object result=null;
result=method.invoke(cxk,args);
System.out.println("演出后的增强:结算费用并纳税");
return result;
}
});
//执行功能,有代理对象执行方法的时候不再是被代理对象执行方法,而是由我们的代理类对象执行方法
jjr.sing(900);
}
}
运行结果:
在软件业,AOP为Aspect Oriented Programming的缩写,意为: 面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。
所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
代理的目标对象。
是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
一个类被 AOP 织入增强后,就产生一个结果代理类。
是切入点和通知(引介)的结合。
3.创建Loger类,这个类是用来做功能的增强。
package com.shi.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class Loger {
public void check(){
System.out.println("前置通知/增强:执行系统的权限验证");
}
public void logPrint(){
System.out.println("后置通知/增强:执行日志的打印");
}
public void exception(){
System.out.println("异常通知/增强:做出异常的处理");
}
public void distory(){
System.out.println("最终通知/增强:资源的释放");
}
public Object around(ProceedingJoinPoint pjp) {
try{
//前置增强
System.out.println("环绕通知————前置增强");
//通过ProceedingJoinPoint 完成代理对象的方法调用
Object result=null; //定义返回值变量
Object[] args = pjp.getArgs(); //获取参数列表
result = pjp.proceed(args); //核心类方法的执行
System.out.println("环绕通知————后置增强");
return result;
}catch (Throwable e){
//异常增强
System.out.println("环绕通知————异常增强");
throw new RuntimeException(e);
}finally {
System.out.println("环绕通知————最终增强");
}
}
}
4.在spring的beans.xml中开始进行AOP的配置 :
5.在测试类中测试
环绕通知
1.环绕通知是一种提供自由灵活对核心类方法进行增强的操作手段,首先还是在beans.xml中配置环绕通知。
2.接着我们需要在增强类中完善我们环绕通知的方法,这个方法需要定义一个ProceedingJoinPoint接口对象作为方法参数,且方法返回值是Object。
环绕通知的实现:其实就是通过ProceedingJoinPoint的实现类对象(spring框架会创建)获取要增强的那个类的方法参数、执行方法、获得方法返回值。然后在方法执行之前的操作就是前置通知、在方法执行后的操作就是后置通知、在异常处理中的操作就是异常通知、在finally中执行的操作就是最终通知。具体如下:
1.首先跟之前的步骤一样
跟前面的步骤一样,复制即可。
配置类
@Configuration:将想要的组件添加到容器中;
用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
@ComponentScan:表示该类是配置类
用于批量注册bean。这个注解会让spring去扫描某些包及其子包中所有的类,然后将满足一定条件的类作为bean注册到spring容器容器中。
@EnableAspectJAutoProxy:开启AOP;
测试类中的ClassPathXmlApplicationContext(配置文件)和AnnotationConfigApplicationContext(纯注解)