基于代理类实现Spring AOP
目录
proxyfactorybean类
虽然直接使用代理就可以创建代理的实例,但需要自己写创建代理的方法,比如jdk动态代理:
1 ........ 2 //创建代理方法,参数是目标接口的实例 3 public object createproxy(userinterface user){ 4 this.user=user; //初始化目标接口实例 5 6 classloader classloader=jdkproxy.class.getclassloader(); //获取当前类的类加载器,当前类类名.class.getclassloader() 7 class[] classarr=user.getclass().getinterfaces(); //获取目标接口实例实现的全部接口 8 9 //参数:当前类的类加载器,目标接口实例实现的所有接口,当前类的实例 10 return proxy.newproxyinstance(classloader,classarr,this); 11 }
cglib代理:
1 //创建代理,参数是object类型 2 public object createproxy(object target){ 3 enhancer enhancer=new enhancer(); //创建一个动态类对象 4 enhancer.setsuperclass(target.getclass()); //将这个动态类对象的父类/基类设置为目标类(需要增强的类) 5 enhancer.setcallback(this); //设置回调 6 return enhancer.create(); //返回创建的代理 7 }
什么乱七八糟的过程、方法,我哪记得住。proxyfactorybean类可解决此问题。
proxyfactorybean是factorybean接口的一个实现类,factorybean接口的作用是实例化一个bean,proxyfactorybean类的作用实例化一个bean的代理,我们在xml文件中配置代理即可,不必手写创建代理的方法。
基于jdk动态代理的spring aop实现
1、新建包user,用于写被代理的类、接口。包下新建接口userinterface
1 public interface userinterface { 2 public void adduser(); 3 public void alteruser(); 4 public void deleteuser(); 5 }
再新建一个实现类user
1 public class user implements userinterface { 2 @override 3 public void adduser() { 4 //模拟添加用户 5 system.out.println("正在添加用户"); 6 system.out.println("添加用户成功"); 7 } 8 9 @override 10 public void alteruser() { 11 //模拟修改用户信息 12 system.out.println("正在修改用户信息"); 13 system.out.println("修改用户信息成功"); 14 } 15 16 @override 17 public void deleteuser() { 18 //模拟删除用户 19 system.out.println("正在删除用户"); 20 system.out.println("删除用户成功"); 21 } 22 }
2、新建包aspect,用来写切面类。包下新建类myaspect,需实现methodinterceptor接口,此接口是org.aopalliance.intercept包下的接口,不要import导入错了。(需要aopalliance.jar包的支持)
1 public class userproxy implements methodinterceptor { 2 @override 3 public object invoke(methodinvocation methodinvocation) throws throwable { 4 checkpermission(); //前增强 5 object object=methodinvocation.proceed(); //通过invoke()的参数调用 6 log(); //后增强 7 return object; 8 } 9 10 public void checkpermission(){ 11 //模拟检查权限 12 system.out.println("正在检查权限"); 13 system.out.println("权限已够"); 14 } 15 16 public void log(){ 17 //模拟记录日志 18 system.out.println("正在记录日志"); 19 system.out.println("日志已记录"); 20 } 21 }
当然,可以实现其它的接口(spring的通知类型)。不同的接口,提供的功能不同。
3、在xml中配置代理
<!-- 目标类--> <bean id="user" class="user.user" /> <!-- 切面类--> <bean id="myaspect" class="aspect.myaspect" /> <!-- 这才是真正的代理类,class要指定为proxyfactorybean类--> <bean id="userproxy" class="org.springframework.aop.framework.proxyfactorybean"> <!--指定接口,如果实现了多个接口,用子元素list来写--> <property name="proxyinterfaces" value="user.userinterface" /> <!--指定目标类的实例--> <property name="target" ref="user" /> <!--指定切面类的id/name,只能用value,不能用ref--> <property name="interceptornames" value="myaspect" /> <!--指定使用的代理,proxytargetclass是问是否代理类,jdk动态代理是代理接口,cglib代理是代理类。false即不是代理类,即使用jdk动态代理--> <!--默认就是false,jdk动态代理。此句配置可缺省--> <property name="proxytargetclass" value="false" /> <!--返回的代理类实例是否是单实例,默认为true,单实例。此句配置可缺省--> <property name="singleton" value="true" /> </bean>
proxyfactorybean类已经写好了创建代理代码,这个类使用setter方法注入依赖,我们只需要用<property>注入它需要的参数即可。
4、新建包test,包下新建测试类test
1 public class test { 2 public static void main(string[] args) { 3 applicationcontext applicationcontext=new classpathxmlapplicationcontext("applicationcontext.xml"); 4 //因为使用的jdk动态代理,代理的是接口,所以要使用接口来声明 5 userinterface user=applicationcontext.getbean("userproxy", userinterface.class); 6 user.adduser(); 7 } 8 }
运行,控制台输出如下
正在检查权限 权限已够 正在添加用户 添加用户成功 正在记录日志 日志已记录
代理成功。
基于cglib代理的spring aop实现
jdk动态代理代理的是接口,cglib代理的类,所以我们只需要把上例中有关接口的部分去掉即可。也可一步步从头开始。
1、新建包user,包下新建类user
1 public class user{ 2 public void adduser() { 3 //模拟添加用户 4 system.out.println("正在添加用户"); 5 system.out.println("添加用户成功"); 6 } 7 8 public void alteruser() { 9 //模拟修改用户信息 10 system.out.println("正在修改用户信息"); 11 system.out.println("修改用户信息成功"); 12 } 13 14 public void deleteuser() { 15 //模拟删除用户 16 system.out.println("正在删除用户"); 17 system.out.println("删除用户成功"); 18 } 19 }
2、新建包aspect,包下新建切面类myaspect
1 public class myaspect implements methodinterceptor { 2 @override 3 public object invoke(methodinvocation methodinvocation) throws throwable { 4 checkpermission(); //前增强 5 object object=methodinvocation.proceed(); //通过invoke()的参数调用 6 log(); //后增强 7 return object; 8 } 9 10 public void checkpermission(){ 11 //模拟检查权限 12 system.out.println("正在检查权限"); 13 system.out.println("权限已够"); 14 } 15 16 public void log(){ 17 //模拟记录日志 18 system.out.println("正在记录日志"); 19 system.out.println("日志已记录"); 20 } 21 }
3、在xml中配置代理
<!-- 目标类--> <bean id="user" class="user.user" /> <!-- 切面类--> <bean id="myaspect" class="aspect.myaspect" /> <!-- 这才是真正的代理类--> <bean id="userproxy" class="org.springframework.aop.framework.proxyfactorybean"> <!--指定目标类的实例--> <property name="target" ref="user" /> <!--指定切面类的id/name,只能用value,不能用ref--> <property name="interceptornames" value="myaspect" /> <!--指定使用的代理,proxytargetclass是问是否代理类,jdk动态代理是代理接口,cglib代理是代理类。false即不是代理类,即使用jdk动态代理--> <!--默认就是false,jdk动态代理。此句配置可缺省--> <property name="proxytargetclass" value="false" /> <!--返回的代理类实例是否是单实例,默认为true,单实例。此句配置可缺省--> <property name="singleton" value="true" /> </bean>
去掉指定接口的那句配置就ok。
4、新建包test,包下新建测试类test
1 public class test { 2 public static void main(string[] args) { 3 applicationcontext applicationcontext=new classpathxmlapplicationcontext("applicationcontext.xml"); 4 //要使用目标类来声明 5 user user=applicationcontext.getbean("userproxy", user.class); 6 user.adduser(); 7 } 8 }
运行,控制台输出如下
正在检查权限 权限已够 正在添加用户 添加用户成功 正在记录日志 日志已记录
代理成功。
说明
需要2个包:spring-aop.jar,这个是spring自带的,不用管。aopalliance.jar,如果使用了maven或idea的自动下载spring所需的包,会自动添加这个包,不用管,如果是手动添加spring需要的包,则还需要自己去下载、添加这个包。
spring的通知类型
在例子的切面类中,我们实现的是methodinterceptor接口,这个接口是环绕通知的接口,可在目标方法前后实施增强,可用于日志、事务管理等。
spring的5种通知类型
通知类型 | 对应接口 | 增强时间 | 常见应用 |
环绕通知 | methodinterceptor | 在目标方法执行前后实施增强 | 日志、事务处理 |
前置通知 | methodbeforeadvice | 在目标方法执行前进行增强 | 权限管理 |
后置通知 | afterreturningadvice | 在目标方法执行后进行增强 | 关闭流、上传文件、删除临时文件等 |
异常通知 | throwsadvice | 在方法抛出异常后进行增强 | 处理异常、记录日志等 |
引介通知 | introductioninterceptor | 在目标类中添加一些新的属性、方法 | 修改旧版程序 |
可根据需要,选择实现接口。
advice指的就是目标方法。增强其实就是做一些额外的处理。