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

 

点赞