• Spring 教程(二)


    一、Spring AOP介绍
    开发其实就是在不断的重构,抽象重复代码,然后进行封装。从最原始的模块化编程到面向对象编程,代码的封装越来越整齐清晰,但是依然存在重复的代码,而这些重复代码几乎都是与业务逻辑无关的系统逻辑代码。比如在数据操作类中的插入、更新、删除数据等方法中都存在数据库事务的处理,重要业务逻辑方法中都有日志记录的逻辑等等。每个应用系统都存在着这种系统级的重复逻辑代码,而我们并没有更好的方法去将这些代码抽象出来并进行管理。然而AOP的出现弥补了这一缺陷,AOP可以在不改变原有业务逻辑代码的情况下对原有业务进行横切拦截,处理那些重复的系统逻辑。
     
    与Ioc容器一样,AOP也是Spring的核心模块之一。AOP是Aspect-Oriented Programming的简称,现在通常称为面向切面编程。我们学了OOP,面向对象编程,而AOP并非是OOP的替代技术,它只是OOP的一个有益补充。
     
    需要指出的是AOP的应用场合是受限的,它一般只适合于那些具有横切逻辑的应用场合:如性能监测、访问控制、事务管理以及日志记录,它并不适合处理具体的业务逻辑,分散处理业务逻辑会使得逻辑混乱、增加维护成本。
     
     
    二、如何使用Spring AOP
    下面以对用户操作类UserDao的AOP拦截演示Spring AOP的使用。
    1、创建Java项目,添加Spring AOP依赖支持
    aopalliance-1.0.jar
    commons-logging-1.1.1.jar
    spring-aop-3.2.0.RELEASE.jar
    spring-beans-3.2.0.RELEASE.jar
    spring-context-3.2.0.RELEASE.jar
    spring-core-3.2.0.RELEASE.jar
    spring-expression-3.2.0.RELEASE.jar
     
    2、添加User及UserDao类
    User类:
    1. public class User {  
    2.     private Integer id;  
    3.     private String name;  
    4. }  
    UserDao类:
    1. public class UserDao {  
    2.     public void save(User user){  
    3.         System.out.println("save user....");  
    4.     }  
    5.       
    6.     public void delete(int id){  
    7.         System.out.println("delete user....");  
    8.     }  
    9.       
    10.     public void update(User user) {  
    11.         System.out.println("update user ....");  
    12.     }  
    13.       
    14.     public User query(String name) {  
    15.         System.out.println("getUser ....");  
    16.         return new User();  
    17.     }  
    18. }  
    3、添加AOP拦截处理
    AOP前置通知:
    1. public class UserBeforeAdvice implements MethodBeforeAdvice {      
    2.     public void before(Method method, Object[] args, Object target) {  
    3.         System.out.println("调用方法:"+method.getName() + "()前拦截处理");  
    4.     }     
    5. }  
    AOP后置通知:
    1. public class UserAfterAdvice implements AfterReturningAdvice {  
    2.     public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {  
    3.         System.out.println("方法:"+method.getName() + "()返回后拦截处理");  
    4.     }  
    5. }  
    AOP环绕通知:
    1. public class UserAroundAdvice implements MethodInterceptor {  
    2.     public Object invoke(MethodInvocation invocation) throws Throwable {  
    3.         System.out.println("调用方法:"+invocation.getMethod().getName() + "()前拦截处理");  
    4.         Object o = invocation.proceed();  
    5.         System.out.println("调用方法:"+invocation.getMethod().getName() + "()后拦截处理");  
    6.         return o;  
    7.     }  
    8. }  
    4、添加Spring配置文件applicationContext.xml
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    4.        xsi:schemaLocation="http://www.springframework.org/schema/beans   
    5.            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">  
    6.     <bean id="userDaoTarget" class="com.boya.spring.dao.UserDao" />  
    7.     <bean id="userBeforeAdvice" class="com.boya.spring.aop.UserBeforeAdvice" />  
    8.     <bean id="userAfterAdvice" class="com.boya.spring.aop.UserAfterAdvice" />  
    9.     <bean id="userAroundAdvice" class="com.boya.spring.aop.UserAroundAdvice" />  
    10.     <bean id="userDao" class="org.springframework.aop.framework.ProxyFactoryBean">  
    11.         <property name="interceptorNames">  
    12.             <list><value>userAroundAdvice</value></list>  
    13.         </property>  
    14.         <property name="target" ref="userDaoTarget"></property>  
    15.     </bean>  
    16. </beans>  
    5、测试AOP
    1. public static void main(String[] args) {  
    2.     ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");  
    3.     UserDao userDao = context.getBean("userDao", UserDao.class);  
    4.     userDao.save(new User());  
    5. }  
    输出结果:
    调用方法:save()前拦截处理
    save user....
    调用方法:save()后拦截处理
     
    回过头来再看刚才的示例。
    1、首先,原来的业务逻辑代码不变
        不再关心重复的系统逻辑代码
    2、编写AOP切面处理逻辑
        把原业务逻辑中的重复代码抽象出来,封装入切面代码中,如上面示例的三种Advice通知封装不同的系统处理逻辑。
        前置通知:实现MethodBeforeAdvice 接口,在调用业务方法前调用该接口
        后置通知:实现AfterReturningAdvice 接口,在业务方法返回后调用该接口,在该接口中可以查看返回值(但不能修改返回值)
        环绕通知:实现MethodInterceptor 接口,在该接口中invocation.proceed();这个方法会调用真实对象的方法
    3、使用Spring配置文件将业务逻辑和AOP切面逻辑进行组装
        AOP代理Bean类型需要设置为org.springframework.aop.framework.ProxyFactoryBean
        必须设置代理目标(target属性设置)和通知类型(interceptorNames属性设置)
        代理目标并非必须实现接口,作为POJO被代理时,会对目标所有方法进行拦截
     
    三、AOP实现原理
    Spring AOP是基于Java反射和动态代理实现的。在讲解动态代理之前,我们先回顾一下代理模式。
    代理模式,就是为某一对象提供一个代理,通过代理操作该对象的方法。通常情况下,真实对象和代理对象需要实现相同的接口,在代理对象中保存真实对象的引用,以此来控制操作真实对象。
     
    我们以班长代理老师签到来演示代理模式。
    创建签到接口:
    1. public interface SignInterface {  
    2.     public Object sign(String nameList);  
    3. }  
    创建真实对象,Teacher类:
    1. public   class  Teacher  implements  SignInterface {  
    2.      public  Object sign(String nameList) {  
    3.         System. out .println( "Teacher sign..." );  
    4.          return   new  Object();  
    5.     }  
    6. }  
    创建代理对象,Leader类:
    1. public class Leader implements SignInterface {  
    2.     private Teacher teacher;  
    3.     public Object sign(String nameList) {  
    4.         if (teacher == null) {  
    5.             teacher = new Teacher();  
    6.         }  
    7.         Object o = teacher.sign(nameList);  
    8.         return o;  
    9.     }  
    10. }  
    测试代理:
    1. public static void main(String[] args) {  
    2.     SignInterface s = new Leader();  
    3.     s.sign("names");  
    4. }  
    以上就是一个代理模式的例子,代理类在编译时就已经被创建了,而动态代理是在运行时动态创建代理类来实现代理模式。如下代码:
    1. public class ProxyObject implements InvocationHandler {  
    2.     private Object proxy_obj;  
    3.     ProxyObject(Object obj) {  
    4.         this.proxy_obj = obj;  
    5.     }  
    6.     public static Object getProxy(Object obj) {  
    7.         Class cls = obj.getClass();  
    8.         // 通过Proxy类的newProxyInstance方法来返回代理对象  
    9.         return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new ProxyObject(obj));  
    10.     }  
    11.     /** 
    12.      * 实现InvocationHandler接口的invoke 
    13.      */  
    14.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
    15.         System.out.println("调用方法:" + method + "()之前拦截处理");  
    16.         if (args != null) {  
    17.             System.out.println("方法有" + args.length + "个参数");  
    18.             for (int i = 0; i < args.length; i++) {  
    19.                 System.out.println(args[i]);  
    20.             }  
    21.         }  
    22.         // 利用反射机制动态调用真实对象的方法  
    23.         Object o = method.invoke(proxy_obj, args);  
    24.         System.out.println("调用方法:" + method + "()之后拦截处理");  
    25.         return o;  
    26.     }  
    27.     // 测试代码  
    28.     public static void main(String agr[]) {  
    29.         SignInterface si = (SignInterface) getProxy(new Teacher());  
    30.         si.sign("names");  
    31.     }  
    32. }  
    以上就是使用JDK的Proxy实现的动态代理,不过JDK的动态代理实现只支持针对接口的动态代理实现。Spring AOP实现默认也是动态代理方式,不过,Spring AOP支持CGLib Proxy的实现方式,可以针对POJO进行动态代理,实现AOP拦截。
     
    我们来看一下CGLib实现的一个简单AOP拦截
    创建业务POJO:
    1. public class CGLibTeacher {  
    2.     public Object sign(String nameList) {  
    3.         System.out.println("Teacher sign...");  
    4.         return new Object();  
    5.     }  
    6. }  
    创建AOP拦截:
    1. public class CGLibAop implements MethodInterceptor {  
    2.         public Object intercept(Object arg0, Method arg1, Object[] arg2,     
    3.                 MethodProxy arg3) throws Throwable {     
    4.             System.out.println("before...");  
    5.             Object o = arg3.invokeSuper(arg0, arg2);  
    6.             System.out.println("after...");  
    7.             return o;  
    8.         }     
    9. }  
    CGLib代理对象创建及测试:
    1. public class CGLibProxy {  
    2.     public static CGLibTeacher create(CGLibAop aop){  
    3.         Enhancer en = new Enhancer();     
    4.         //进行代理    
    5.         en.setSuperclass(CGLibTeacher.class);     
    6.         en.setCallback(aop);     
    7.         //生成代理实例    
    8.         return (CGLibTeacher)en.create();     
    9.     }  
    10.       
    11.     public static void main(String[] args) {  
    12.         CGLibTeacher t = CGLibProxy.create(new CGLibAop());  
    13.         t.sign("names");  
    14.     }  
    15. }  
    从CGLib的代理对象创建中可以看到,代理对象需要设置代理目标以及AOP拦截实现,和Spring AOP的实现非常类似。
  • 相关阅读:
    再也不想更新的沙雕错误合集
    「HNSDFZ暑期集训 测试1」「LuoguT36488」 连连看
    「HNSDFZ暑期集训 测试1」「LuoguT36485」 括号(贪心
    「UVA524」 Prime Ring Problem 质数环
    「LuoguP2434」 [SDOI2005]区间(贪心
    「LuoguP4047」 [JSOI2010]部落划分
    「LuoguP1281」 书的复制(贪心
    「LuoguP1238」 走迷宫
    「LuoguP4753」濑 River Jumping(贪心
    BZOJ4400 TJOI2012桥(最短路+线段树)
  • 原文地址:https://www.cnblogs.com/Raymond-YYC/p/4054328.html
Copyright © 2020-2023  润新知