Spring 的IOC和AOP(静态代理,jdk的动态代理,CGLIB代理)

Spring 的IOC和AOP

Spring框架的精髓就是他的IOC和AOP了,面试中也会经常问道,所以在这里整理一下:

IOC(Inversion of Control 控制反转)

首先我们要明确是将什么控制去反转,如何反转的:

当我们不使用Spring的时候,我们通常创建对象都是通过自己手动new一个,并调用该对象的类的构造方法进行初始化。这样无形之中增强了各个层之间的耦合性,举个例子:
比方说我们使用MySQL作为我们的DAO层,先抛开工厂不说,我们是不是在业务层写一个有参的构造方法,传递我们的DAO层对象,然后再new出来;
这就意味着我们的业务层和持久层具有很强的耦合性,一旦你的持久层改为Oracle,或者SQL Sever 是不是你的持久层也必须去修改大量代码;
那么我们使用IOC的时候,就可以不用自己去new一个对象,也就是把我们控制对象的创建的这个权力,反转到Spring的Bean工厂给我们创建,我们直接使用就可以了。

 
		
	
	

DI(Dependecy Injection 依赖注入)

其实依赖注入和控制反转是相辅相成的,正因为我们使用IOC来创建各个层的一些对象,所以我们要通过配置的方式,将这些对象的普通属性和关联属性以注入的方式传给A该对象。
也就是说,当我们调用者去调用被调用者的某些方法时,他们之间产生了某种依赖关系,但是被调用者的对象,又是通过Spring的IOC创建,那么就必须把他注入到待用者中去。

AOP(面向切面编程)

一个晦涩难懂的词,举个例子:
当我们写好代码,准备交付甲方爸爸的时候,突然接到更改需求,需要再某某方法之前加个身份验证,调用完之后再添加日志功能,怎么办,头都烂了。 改源代码?不可能的,为了某个方法,很可能会出现其他方法大面积的出错,那我们使用Spring中AOP面向切面编程,便可以完美解决了:
首先介绍一些名词:

  1. 切面 Advice : 也就是你需要新增的方法,本例中的身份验证,添加日志就是切面;
  2. 切面类 :就是包含切面的类
  3. 切入点 Pointcut: 就是你所添加切面的那个方法
  4. 织入 Weave: 就是你的切面加入到PointCut的那个过程就叫织入;如果你在PointCut之前织入Advice,这个Advice就叫BeforeAdvice,相反的就叫AfterAdvice

拿本例来说 ,身份验证就是BeforeAdvice ,添加日志就是AfterAdvice,某某方法就是PointCut,
也就是说我们可以利用AOP,在不修改代码的情况下,去给你的pointcut去织入Advice,配置如下:


		//ref就是你的切面类
		 
		//为所有权限下的指定包下的该类中以add开头的所有方法为PointCut
			
			//方法名为checkSecurity的方法为beforeAdvice
			
		
		

AOP的代理模式

首先说说什么是代理模式:
就是当你调用某个类的方法,去创建该类对象时,并不是创建真正的对象,而是这个类的代理对象,这是之所以能够实现AOP的原因,实际上很多框架都是使用的代理模式,这样提高效率,节省空间。

静态代理

静态代理其实很简单,
他需要你自己写一个类,去实现和目标类相同的接口;
在这个类中还需要定义目标对象,可以交由通过IOC来实现;
之后去实现接口中的方法,进行重写,比如先执行某方法,在执行目标类对象的该方法;
这样你就可以去创建静态代理类对象,调用静态代理类的方法,去实现"织入"。

public class UserManagerImplProxy implements UserManager {
	private UserManager userManager;
	public void addUser(String username, String password) {
		checkSecurity();
		this.userManager.addUser(username, password);
	}
	private void checkSecurity() {
	System.out.println("----------checkSecurity()---------------");
}
}
//////////
 UserManager userManager = (UserManager)factory.getBean("UserManagerImplProxy");
		 userManager.addUser("admin", "123");

jdk的动态代理

Spring真正的代理模式就是使用的jdk的动态代理。

我们需要写一个类:
但是你实现的不是目标类的接口,而是一个叫做InvocationHandler的这么个东西;
有一个指向目标类的Object类型的变量 ;
实现接口中的方法,直接上代码:


public class SecurityHandler implements InvocationHandler {

	private Object targetObject; //需要他指向目标类对象
	
	public Object newProxy(Object targetObject) {
		this.targetObject = targetObject;
		System.out.println("----------newProxy()---------------");	
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),  
									  targetObject.getClass().getInterfaces(), 
									  this); 
		//return后的方法,可以理解为得到目标类的代理类对象的方法,注意他的三个参数
		(目标类的类加载器,目标类实现的接口,以及调用该方法的this指针)	
		这个this指针就是你这个类的对象了;
	}
	

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		checkSecurity();   //这是你加在目标类对象之前的方法
		Object ret = null;
		try {
			ret = method.invoke(this.targetObject, args);  //通过方法反射去执行目标类的该方法
		
		}catch(Exception e) {
			e.printStackTrace();
			throw new java.lang.RuntimeException(e);
		}
		return ret;
	}

	private void checkSecurity() {
		System.out.println("----------checkSecurity()---------------");
	}
	
}
////////////////////测试类/////////////////
public class Client {

	public static void main(String[] args) {
	//创建实现InvocationHandler 接口类的对象
		SecurityHandler handler = new SecurityHandler();
		//执行该对象的newProxy方法,得到目标类的代理类
		UserManager userManager = (UserManager)handler
									.newProxy(new UserManagerImpl());
		//执行代理类的addUser();
		//注意,这里他是代理类,他会先调用this.invoke方法,也就是handler的invoke方法
		//这样就可以做到"织入"了
		userManager.addUser("admin", "123");
		userManager.deleteUser(1);
	 
	}
}

静态代理和动态代理的区别

当我们使用静态代理的时候,我们需要知道目标类的类名,方法名
但是很多情况下,我们无法得知,我们只能在程序运行中知道,这时候就得使用动态代理,更加灵活
但是我们发现一点,就是我们的目标类必须实现某个接口,如果某个类没有实现接口呢?

CGLIB代理

他的原理也很简单,他是你目标类的一个子类,并且会重写你目标类的所有方法,
有一个Object类型的变量 target
在你调用某个方法时,想调用子类的某个方法,在调用父类的该方法。
注意正因为他和目标类是继承关系,那么你的目标类就最好不要有final类型的方法,否则无法实现。

你可能感兴趣的:(Spring 的IOC和AOP(静态代理,jdk的动态代理,CGLIB代理))