基于代理类实现Spring AOP

Posted chy18883701161

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于代理类实现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指的就是目标方法。增强其实就是做一些额外的处理。

 

以上是关于基于代理类实现Spring AOP的主要内容,如果未能解决你的问题,请参考以下文章

Spring框架 AOP

基于代理类的AOP实现

Spring AOP(基于代理类的AOP实现)

Spring框架中2种生成代理对象的方法

Spring AOPSpring AOP之如何通过注解的方式实现各种通知类型的AOP操作进阶篇

基于代理类的AOP的实现