SpringAOP切面编程原理解释

     之前找工作复习Java时, 又重新看来一篇Spring的六大模块, 包括之前不太明白的AOP模块, 现在来总结一下原理, 请注意这是我的理解,可以参考.

     内容包括

主要内容包括:

 一 代理

   1) 代理模式

                -- 静态代理

               -- 动态代理(也叫JDK代理)

               -- Cglib代理

  二 AOP  

   1)手动实现AOP编程 

   2)AOP编程

         * 注解方式实现

   3) Spring事务里面的AOP

     一 :代理模式

      代理模式是Java23中设计模式之一, 如果想更加详细的了解设计模式,推荐查阅<head first 设计模式> 书写风趣

         代理(Proxy提供了对目标对象另外的访问方式;即通过代理访问目标对象。 这样好处: 可以在目标对象实现的基础上,增强额外的功能操作。(扩展目标对象的功能)

   举个例子: 生活中,秘书职位也是代理,你找到秘书,你的感觉是什么? 找到了老板.秘书把一些繁琐的工作做好,接着老板一看,准了,秘书再联系客户; 在这里秘书就是代理对象,老板就是目标对象! 

   1.1 静态代理, 

         该代理要求: 代理对象和目标对象必须实现相同的接口,

  静态代理优缺点:

       优点: 可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。

         缺点:--》  因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。

            --》  一旦接口增加方法,目标对象与代理对象都要维护。

    注:其实不认为这是什么缺点. 

   实现如下:

  //接口 

package cn.itcast.scm.test.proxy;

public interface IuserService {
    public void save();
}

    //目标对象

package cn.itcast.scm.test.proxy;

public class UserService implements IuserService{

	@Override
	public void save() {
     System.out.println("----数据保存成功----");		
	}

}
 //代理对象
public class UserServiceProxy implements IuserService{
	//目标对象
  private IuserService target;
  public UserServiceProxy(IuserService target) {
	  this.target=target;
}
	@Override
	public void save() {
		System.out.println("开启事务");
		target.save();
		System.out.println("提交事务");
	}

}

   //测试一下

    
package cn.itcast.scm.test.proxy;
/**
 * 测试代理模式
 * */
public class TestProxy {

	public static void main(String[] args) {
      //静态代理
		IuserService target=new UserService();
		 UserServiceProxy proxy=new UserServiceProxy(target);
		 proxy.save();
	}

}

  1.2 动态代理

            也叫JDK代理,  该代理对目标对象的要求是: 必须实现接口。为什么? 可以思考!

  

package cn.itcast.scm.test.JDKproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 *  代理工厂
 * */
public class ProxyFactory {
    //维护一个目标对象
	private Object target;
	public ProxyFactory( Object userserive) {
		this.target=userserive;
	}
	//给目标对象,生成一个代理对象
	public Object getProxyInstance(){
		return Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable { 
//可以判断方法;method
						System.out.println("开启事务");
						Object value=method.invoke(target, args);
						System.out.println("提交事务");
						return value;
					}
				});
	}
}

 1.3 cglib代理

      

 分析:静态代理不足之处是:代理对象和目标对象必须实现一样的接口,动态代理则是目标对象必须实现接口,此时可使用cglib代理模式

package it.cw.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibFactory implements MethodInterceptor {
 //维护一个目标对象
	private Object  target;
	public CglibFactory(Object  target) {
		this.target=target;
	}
	//生成代理对象
	public Object getCglibInstant(){
		//1) 代理工具类
		Enhancer eh=new Enhancer();
		//2)设置父类
		eh.setSuperclass(target.getClass());
		//3) 设置回调函数
		eh.setCallback(this);
		//4)返回代理对象
		return eh.create();
	}
	@Override
	public Object intercept(Object obj, Method method, Object[] arg2,
			MethodProxy proxy) throws Throwable {
		System.out.println("开启事务");
		Object object=method.invoke(target, arg2);
		System.out.println("提交事务");
		return object;
	}

}
  

Cglib子类代理:

1) 需要引入cglib – jar文件, 但是spring的核心包中已经包括了cglib功能,所以直接引入spring-core-3.2.5.jar即可。

2)引入功能包后,就可以在内存中动态构建子类

3)代理的类(目标对象)不能为final, 否则报错。 因为代理对象要继承它,

4)目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。

 在Spring的AOP编程中,

如果加入容器的目标对象有实现接口,用JDK代理(动态代理);

如果目标对象没有实现接口,用Cglib代理;

***************************以上就是三种代理模式**********************************

 了解了代理模式之后,接着就来理解AOP

   二 AOP编程

    2.1 好,我们来手动实现AOP,这是全手动实现,与springAOP无关! 

     首先在spring配置文件加上:用IOC容器创建代理对象;   

 <!--开启注解扫描-->
   <context:component-scan base-package="it.cw.AOP.Design"></context:component-scan>
  <!-- 创建一个代理对象 -->
  <bean id="proxy" class="it.cw.AOP.Design.ProxyFactory" factory-method="getsProInstance">
       <constructor-arg index="0" ref="userDao"></constructor-arg>
       <constructor-arg index="1" ref="aop"> </constructor-arg>
  </bean
  

    Aop 切面类: 这是什么? 重复的代码叫关注点代码,组成的一个类就做切面类  

/** 存放关注点代码, 即重复代码
	 * 
	 * */
	@Component
	public class Aop {
	   public void begin(){
		   System.out.println("AOP begin");
	   }  
	   public void end(){
		   System.out.println("end");
	   }
	}
 

 代理工厂 ProxyFactory 

     生成代理对象时: 二个参数, 目标对象和AOP对象

package it.cw.AOP.Design;

Import  java.lang.reflect.InvocationHandler;
Import  java.lang.reflect.Method;
Import  java.lang.reflect.Proxy;
public class ProxyFactory {
	private static Object target;
	private static Aop aop;
   //生成代理对象
	public static Object getsProInstance(Object target_, Aop aop_){
		target=target_;
		aop=aop_;
	return Proxy.newProxyInstance(
			target.getClass().getClassLoader(), 
			target.getClass().getInterfaces(), 
			new InvocationHandler() {
				@Override
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					aop.begin();
					Object  obj=method.invoke(target, args);
					aop.end();
					return obj;
				}
			});
	}
	
}

   测试:

public class TestDao {
	ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
	//测试手动AOP实现
   @Test
   public void test(){
	 IUserDao daoproxy= (IUserDao) ac.getBean("proxy");
	 daoproxy.save();
   }
}

  2.2 注解方式实现AOP

        

 描述: 注解方式实现AOP编程, 便于理解Spring内置的AOP实现, ”事务”处理机制, 如果需要我们应该自己去实现Aop给自己的业务使用;   了解几个概念;

 Aop  aspect object programming  面向切面编程

功能: 让关注点代码与业务代码分离!

关注点,

重复代码就叫做关注点;

切面,

 关注点形成的类,就叫切面()

 面向切面编程,就是指 对很多功能都有的重复的代码抽取,再在运行的时候网业务方法上动态植入“切面类代码”。

切入点,

执行目标对象方法,动态植入切面代码。

可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。

          这不是全手动实现(我们实现AOP,SpringAOP自动生成代理对象), 这就需要得到spring的AOP支持,所以需要加上如下配置:

//名称空间和AOP注解支持
http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 开启aop注解方式:自动生成代理对象
	 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    AOP切面类: 里面包括切入点,
/** 切面类, 重复代码构成
 * */
@Component
@Aspect
public class AspectObject {
	//切入点, 拦截那些方法
	@Pointcut("execution(* cn.cw.AOP.announce.ServiceImpl.*(..))")
	public void pointCut_(){
	}
	
	// 前置通知 : 在执行目标方法之前执行
		@Before("pointCut_()")
		public void begin(){
			System.out.println("开始事务/异常");
		}
		
		// 后置/最终通知:在执行目标方法之后执行  【无论是否出现异常最终都会执行】
		@After("pointCut_()")
		public void after(){
			System.out.println("提交事务/关闭");
		}
		
		// 返回后通知: 在调用目标方法结束后执行 【出现异常不执行】
		@AfterReturning("pointCut_()")
		public void afterReturning() {
			System.out.println("afterReturning()");
		}
		
		// 异常通知: 当目标方法执行异常时候执行此关注点代码
		@AfterThrowing("pointCut_()")
		public void afterThrowing(){
			System.out.println("afterThrowing()");
		}
		
		// 环绕通知:环绕目标方式执行, 刚刚学习AOP,可以不看他
		@Around("pointCut_()")
		public void around(ProceedingJoinPoint pjp) throws Throwable{
			System.out.println("环绕前....");
			pjp.proceed();  // 执行目标方法
			System.out.println("环绕后....");
		}
}
public class TestAop {
	@Test
	public  void test() {
		ApplicationContext ac=new  ClassPathXmlApplicationContext("bean.xml");
		Service se=(ServiceImpl) ac.getBean("service");
		se.save();
	}
 
}

       测试:

         在xml配置文件中创建service这个Bean时, 注意会自动创建代理对象, 传入哪二个参数? 我们写好的AOP, 和service目标对象

public class TestAop {
	@Test
	public  void test() {
		ApplicationContext ac=new  ClassPathXmlApplicationContext("bean.xml");
		Service se=(ServiceImpl) ac.getBean("service");
		se.save();
	}
 
}

        下面图解一下:

      更明白了吧! 先想想SpringAOP对事物的处理是怎么样的! 



  

 2.3  Spring事务里面的AOP

       和上面注解实现是一样的, 我们在事物中配置切入点时, 创建对象的类时,会先将切入点传入AOP(该类应该是内置存在的), 比如before要开启事务,after要关闭事物,一切都写明白了,在传入切入点参数,AOP就存在了, 在创建切入点指定的Service对象时, 将Service,AOP 作为参数生成代理对象, 我们就拿到了Service(看起来像老板,其实是秘书)的对象!  

      --- 到这里基本上就OK了, 我的理解是这样子的。




猜你喜欢

转载自blog.csdn.net/huangchongwen/article/details/79900431