赞
踩
官方话就不说了,直接上自己见解和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();
}
二:创建接口方法的实现类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("删除用户"); } }
三:创建UserTwoService的接口
这个没啥好说的,直接复制UserTwo的接口方法即可
public interface UserTwoService {
public void findUser();
public String adduser();
public void updateUser();
public void deleteUser();
}
四:实现接口方法
注意:这里抽出来的共用对象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(); } }
五:然后我们创造切面类Aspect模拟事务开启
总结的时候在具体说,现在可以理解成这里就是我们要插入的方法
public class InsertAspect {
public void before(){
System.out.println("开启事务");
}
public void after(){
System.out.println("关闭事务");
}
}
六:通过反射创造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; } }
七:创建测试类
//注意类名的第一个子明大写,我这里输入法抽风了
public class test {
@Test
public void test(){
//我们不new UserTwoService.而是从工厂拿的原因,就是,假如你还是new,那么就相当于还是调用的本身的方法,如果我们获得是经过拦截的,那么我就就可以切入自己的操作了.这就是根本变化
UserTwoService userTwoService= UserTwoFactory.createUserService();
userTwoService.findUser();
userTwoService.adduser();
userTwoService.deleteUser();
userTwoService.updateUser();
}
}
八:当调用完方法之后,我们的控制台应该显示
开启事务 查找所有用户 参数:null 关闭事务 开启事务 添加用户,并且返回用户 参数:张三 关闭事务 开启事务 删除用户 参数:null 关闭事务 开启事务 更新用户 参数:null 关闭事务 Process finished with exit code 0
这里再看,我们是不是获得拦截后的对象,就完成了我们自己要的事务操作.
我认为,要去理解,如果你要是直接看官方的术语,很容易就懵逼了,你看那些point cut(切入点)advice(通知)aspect(切面)等七个,在初学的时候很容易被劝退.那么我们白话浅显的理解下
1.我们上面实现了四个方法对吧.我们在执行其中一个方法的时候,是不是要去调用?那么我们所谓的切入,肯定是要切入这个方法之前,或者之后,拿事务来说,先不考虑回滚.你在添加用户,更新用户的时候,肯定要开启事务之后采取执行,完毕还要关闭事务.那么我们可不可以这样想,切入点,就是切它的执行前,执行后?
2.advice,可以这样理解,他就是你要切进去的方法,比如我要吃饭,那你肯定要做饭,要刷碗,那么做饭刷碗,就是切进去的方法,就是执行参数.
3.aspect,这个东西就是吧上面两联合在一切,是不是就形成了一个可以被使用的块,那么这个块是不是就可以定义为一个切面,也就是aspect?
所以,浅显的理解来说,aop就是直接暴力横向插入你要执行的方法前后,他是有缺点的,因为直接破坏了结构,就跟goto一样,但是我认为优点更大于缺点.你将少些很多代码.可以当场解耦.
//注意,这里用的不是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(); } }
有接口,有实现类,那么就可以用java实现proxy.
没有接口,只有实现类,那么就可以用cglib字节码增强来实现
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。