当前位置:   article > 正文

Spring的AOP 面向切面编程------Spring学习日志3_面向切面编程 日志 demo csdn

面向切面编程 日志 demo csdn

官方话就不说了,直接上自己见解和demo,总之就是OOP的一个补充,也就是面向对象编程一个扩展,自己感觉最重要的是大大减少了代码的耦合,提高了程序的重用
比如说我们要用传统的jsp模式来写,那么肯定要用反射抽公用的BaseService或者BaseServlet.
一个两个可能看不出来,假如我们要做商城那种大项,用这种模式,第一是写的怀疑人生,第二就是二者直接基本上是百分之百的耦合.
这对于开发是一件很严重的事情.
以前都是直接子承父业,纵向线走,现在是直接横向切面,总的来说,AOP可以说是Spring框架的核心,必须掌握,必须熟练掌握,必须花大功夫学习

AOP采用代理机制实现
一种就是动态代理,基于Java自身实现的Proxy(接口+实现类)
一种是采用cglib字节码增强实现(实现类)

接下来我们手写一个AOP简单实现下这个很麻烦的事情,因为本来就是一件很麻烦的事情,当然,肯定还要用到反射的机制,毕竟Java最精华的部分嘛,后发先制.,因为我之前写了个User,我懒得再开项目了,所以我就命名为UserTwo

一.首先创建UserTwo的接口

里面有四个方法,其中一个有返回值,分别对应查找所有用户,增加用户,更新用户.删除用户

public interface UserTwo {
    public void findUser();
    public String adduser();
    public void updateUser();
    public void deleteUser();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

二:创建接口方法的实现类UserTwoImp

再添加用户方法addUser中,我们手动返回了一个参数"张三",那么等会我们调用这个方法的时候,他肯定会给我们返回一个参数,那就是用户名"张三"

public class UserTwoImp implements UserTwo {

    @Override
    public void findUser() {
        System.out.println("查找所有用户");
    }

    @Override
    public String adduser() {
        System.out.println("添加用户,并且返回用户");
        return "张三";
    }

    @Override
    public void updateUser() {
        System.out.println("更新用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("删除用户");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

三:创建UserTwoService的接口

这个没啥好说的,直接复制UserTwo的接口方法即可

public interface UserTwoService {

    public void findUser();
    public String adduser();
    public void updateUser();
    public void deleteUser();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

四:实现接口方法

注意:这里抽出来的共用对象userTwo必须加修饰符final,不然会报nullPointException,空指针,因为我们用的是内部类的写法,要访问外部成员就要加final修饰,如果用jdk8的话,可以再静态工厂(UserTwoFactory,后面会提到)里面不用final

public class UserTwoServiceImp implements UserTwoService {
	//注意,这里必须要加上final的修饰,不然会报空指针
  final private UserTwo userTwo=new UserTwoImp();

    @Override
    public void findUser() {

        userTwo.findUser();
    }

    @Override
    public String adduser() {

        return userTwo.adduser();
    }

    @Override
    public void updateUser() {

        userTwo.updateUser();
    }

    @Override
    public void deleteUser() {

        userTwo.deleteUser();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

五:然后我们创造切面类Aspect模拟事务开启
总结的时候在具体说,现在可以理解成这里就是我们要插入的方法

public class InsertAspect {

    public void before(){
        System.out.println("开启事务");
    }

    public  void after(){
        System.out.println("关闭事务");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

六:通过反射创造UserTwoFactory获得UserTwoService

public class UserTwoFactory {
	//这里使用了静态修饰符static,因为工厂类其实就可以当作工具类,毕竟造轮子嘛,所以我们调用可以直接用类名去点
    public static UserTwoService createUserService(){
		//下面两个分别创造了切面类Aspect,和User TwoService类,注意JDK8其实这里可以不用修饰final.但是建议还是加上
        final UserTwoService userTwoService=new UserTwoServiceImp();
         final InsertAspect insertAspect=new InsertAspect();
		//这里就是核心代码,通过Java自己的代理类,Proxy,直接点出newProxyInstance创造代理,里面有三个参数,分别是是当前类的类加载器getClassLoader(),接口(也就是我们上面new出来的UserTwoService实体对象,还有一个就是回调,或者说就是处理的过程)
        UserTwoService userTwoService1=(UserTwoService) Proxy.newProxyInstance(
                UserTwoFactory.class.getClassLoader(),
                userTwoService.getClass().getInterfaces(),
                new InvocationHandler() {
                //我们只需要关注里面的两个参数,一个就是method:这个是拦截的方法,一个是args,这个是拦截的参数
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //注意,这里调用了InsertAspect的方法,开启了事务
                        insertAspect.before();
                        //注意,这里我们用了当前方法,也就是拦截到的方法method,和拦截到的参数args.假如我们调用返回类型为void的方法,那么肯定就是null,如果是别的类型,那就肯定是对应的类型,所以返回的是一个基类型Object
                       Object o=method.invoke(userTwoService,args);
                       //这里的语句相当于一个测试语句
                        System.out.println("参数:"+o);
						//注意,这里调用了InsertAspect的方法,关闭了事务
                        insertAspect.after();
                        ..将获得的参数返回
                        return o;
                    }
                }


        );
		//注意,这里返回的并不是我们用来拦截的对象,而是代理对象,再Proxy的创建代理方法执行完毕后,可以返回一个对象,这就是拦截的对象,然后转给外面,我们用新对象userTwoService1去接收,所以返回的就是我们代理拦截后的对象,这才形成了最简单的AOP.
        return userTwoService1;
    }



}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

七:创建测试类

//注意类名的第一个子明大写,我这里输入法抽风了
public class test {

    @Test
    public void test(){
		//我们不new UserTwoService.而是从工厂拿的原因,就是,假如你还是new,那么就相当于还是调用的本身的方法,如果我们获得是经过拦截的,那么我就就可以切入自己的操作了.这就是根本变化
        UserTwoService userTwoService= UserTwoFactory.createUserService();
        userTwoService.findUser();
        userTwoService.adduser();
        userTwoService.deleteUser();
        userTwoService.updateUser();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

八:当调用完方法之后,我们的控制台应该显示


开启事务
查找所有用户
参数:null
关闭事务
开启事务
添加用户,并且返回用户
参数:张三
关闭事务
开启事务
删除用户
参数:null
关闭事务
开启事务
更新用户
参数:null
关闭事务

Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这里再看,我们是不是获得拦截后的对象,就完成了我们自己要的事务操作.
我认为,要去理解,如果你要是直接看官方的术语,很容易就懵逼了,你看那些point cut(切入点)advice(通知)aspect(切面)等七个,在初学的时候很容易被劝退.那么我们白话浅显的理解下

1.我们上面实现了四个方法对吧.我们在执行其中一个方法的时候,是不是要去调用?那么我们所谓的切入,肯定是要切入这个方法之前,或者之后,拿事务来说,先不考虑回滚.你在添加用户,更新用户的时候,肯定要开启事务之后采取执行,完毕还要关闭事务.那么我们可不可以这样想,切入点,就是切它的执行前,执行后?

2.advice,可以这样理解,他就是你要切进去的方法,比如我要吃饭,那你肯定要做饭,要刷碗,那么做饭刷碗,就是切进去的方法,就是执行参数.

3.aspect,这个东西就是吧上面两联合在一切,是不是就形成了一个可以被使用的块,那么这个块是不是就可以定义为一个切面,也就是aspect?

所以,浅显的理解来说,aop就是直接暴力横向插入你要执行的方法前后,他是有缺点的,因为直接破坏了结构,就跟goto一样,但是我认为优点更大于缺点.你将少些很多代码.可以当场解耦.

这里再实现下用cglib字节码增强实现动态代理

//注意,这里用的不是UserTwoService
public class UserServiceFactory {

    public static UserService getUserService(){

        //创建两个对象
        UserService userService=new UserServiceImp();
        UserAspect userAspect=new UserAspect();

        //创建cgLib字节码增强对象
        Enhancer enhancer=new Enhancer();
        //设置父类
        enhancer.setSuperclass(userService.getClass());
        //设置回调拦截
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                userAspect.before();
                Object os= method.invoke(userService,objects);
                userAspect.after();
                return os;
            }
        });

		//这里返回的是增强好的对象,直接调用create()方法,返回的就是增强好的对象,当然,要进行强转
        return (UserService) enhancer.create();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

有接口,有实现类,那么就可以用java实现proxy.
没有接口,只有实现类,那么就可以用cglib字节码增强来实现

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/876631
推荐阅读
相关标签
  

闽ICP备14008679号