目录
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指的就是目标方法。增强其实就是做一些额外的处理。