Spring框架关于IOC/DI与AOP所使用的设计模式

IOC与AOP所使用的设计模式

  • 1.控制反转(IOC)和依赖注入(DI)
    • 1.1工厂设计模式
    • 2.1单例模式
  • 2.面向切面编程(AOP)
    • 2.1代理模式(proxy-pattern)

在使用Spring框架的过程中,IOC/DI和AOP就是Spring框架的灵魂和核心。
主要用到的设计模式有工厂模式和代理模式。
IOC就是典型的工厂模式,通过sessionfactory去注入实例。
AOP就是典型的代理模式的体现。

1.控制反转(IOC)和依赖注入(DI)

IOC(inversion of control, 控制反转):是一种设计思想,而不是一种技术。这里的控制指把控制权从应用程序中剥离出来。ioc它可以把创建对象和查找依赖对象的权限交给Ioc容器控制,而不是传统的由这些对象的使用方(消费者)进行创建初始化操作。IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。
DI(Dependency Injection, 依赖注入):是IOC思想的一种实现。 它负责实现IOC动态地向某个对象提供它所需要的其他对象.,比如一个人需要一根扫把, 不是由这个人自己去做扫把, 而是由Spring将做好的扫把给他, 而这个人全程需要做的,就是喊我要一根扫把. 什么叫做依赖, 人需要扫把, 人就对扫把有了依赖. 而DI就是根据这个依赖,将扫把分配给这个人的.

1.1工厂设计模式

工厂模式是将将创建对象的责任转移到工厂类。
场景:中午吃午餐,提供的食物有:fastfood,noodles,hamburger
对象:人,FastFood,Noodles,Hamburger,提供服务service()
实现一:传统方法实现
1.食物类对象提供服务

public class FastFood {
	public void service() {
		System.out.println("麻婆豆腐盖浇饭...");
	}
}
public class Noodles{
	public void service() {
		System.out.println("大盘鸡拌面...");
	}
}
public class Hamburger{
	public void service() {
		System.out.println("牛肉汉堡...");
	}
}

2.人对象选择食物

public class Person {
	public void eat() {
		Noddles service = new Noddles();
		service.service();
	}
}

实现一的问题:
1.Noddles service = new Noddles(); 确定了对象的类型,替换类型,就要替换实例对象。
2.针对具体的开发并不稳定
实现二:实现一的改进,面向接口的开发
1.创建接口,统一规范

public interface IService {
	void service();
}

2.使用统一接口,规范服务

public class FastFood implements IService{
	@Override
	public void service() {
		System.out.println("麻婆豆腐盖浇饭...");
	}
}
public class Noodles implements IService{
	@Override
	public void service() {
		System.out.println("大盘鸡拌面...");
	}
}
public class Hamburgerimplements IService{
	@Override
	public void service() {
		System.out.println("牛肉汉堡...");
	}
}

3.人对象选择食物

public class Person {
	public void eat() {
		IService service = new Noddles();
		service.service();
	}

}

实现二的问题:
Person需要service服务时Noodles出现,Person对象与Noodles()具有依赖性
实现三:职责分离,解除依赖。使用工厂类来管理对象
1.工厂类

public class FoodFactory {
	
	public static IService create(int index) {
		switch (index) {
		case 1:
			return new FastFood();
		case 2:
			return new Hamburger();
		default:
			break;
		}
		return new Noddles();
	}

}

2.人选择食物

@Test
	public void test() {
		Person 小王= new Person();
		小王.eat(2);
	}

改进工厂:利用反射机制创建顾客想要的食物
1.创建一个菜单,顾客将想要选择的食物写到菜单上(FastFood,Noodles,Hamburger),顾客想修改食物,直接在菜单修改即可。
在这里插入图片描述
2.工厂类读取菜单并返回食物对象

public class FoodFactory {
	
	public static IService create() {
		IService is= null;
		try {
			BufferedReader in = new BufferedReader(new FileReader("src/menu.txt"));
			String line = in.readLine();\
			is = (IService) Class.forName("com.yang.entity."+line).newInstance();
			} catch (Exception e) {
			e.printStackTrace();
		}
		return is;
	}
}

3.人进行吃饭服务

public class TestFactory {
@Test
	public void test() {
		Person 小王= new Person();
		小王.eat();
	}

}

工厂设计模式的优点:
1.面向接口编程,体现了面向对象的思想;
2.将创建对象的工作转移到了工厂类;
工厂模式应用范围:

  • 创建对象—》new

  • 创建对象比较复杂的业务

    • SqlSessionFactory–mybatis

    • BeanFactory —spring

    • ApplicationContext—>spring

工厂模式升级:

  1. 由框架创建工厂
  2. 对象由工厂(容器)进行管理
  3. 业务需要什么,工厂根据业务依赖自动注入–好莱坞原则

2.1单例模式

单例模式

2.面向切面编程(AOP)

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

一.AOP的基本概念

  • Aspect(切面):通常是一个类,里面可以定义切入点和通知
  • JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
  • Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
  • Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
  • AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

通知方法:

  • 前置通知:在我们执行目标方法之前运行(@Before)
  • 后置通知:在我们目标方法运行结束之后 ,不管有没有异常(@After)
  • 返回通知:在我们的目标方法正常返回值后运行(@AfterReturning)
  • 异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
  • 环绕通知:动态代理, 需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知(@Around)

二.Spring AOP
Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。

2.1代理模式(proxy-pattern)

代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
场景描述:房东准备出租自己的房子,没有时间找中介帮助出租房子
对象:房东(Real Object),中介(Proxy Object)
静态代理
核心:接口
1.声明租房接口

public interface IRent(){
         void rent();
}

2.房东实现租房接口

public class HouseKeeper implements IRent{
   
    @Override
    public void rent(){
      system.out.println("我有一套三居室出租,租金1500元");
}
}

3.中介基于房东的需求实现租房借口

public class HouseAgent implements IRent{
    private IRent rent;
    public HouseAgent(IRent rent){
        this,rent=rent;
    }
    @Override
    public void rent(){
    before();
    this,rent.rent();
    after();
   }
   private void before(){
  system.out.println("每月物业费收取100元");
   }
   private void after(){
  system.out.println("转租额外收一个月房租");
   }
}

4.租客看到的租房信息

   @Test
   public void test(){
    IRent house = new HouseAgent(new HouseKeeper());
    house.rent();
}

缺点:代理对象随着接口的变化而变化
动态代理
实现InvocationHandler(产生代理对象的类)


public class HouseProxy implements InvocationHandler {
	
	private Object real;
	
	public HouseProxy(Object real) {
		this.real = real;
	}

	/**
	 *  产生代理对象
	 * @return
	 */
	public Object createProxy() {
		return Proxy.newProxyInstance(this.real.getClass().getClassLoader(),
				this.real.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		before();
		Object retVal = method.invoke(this.real, args);//real对象调用method方法,传入参数args,retVal方法的返回值
		after();
		return retVal;
	}
	private void before() {
		System.out.println("我来了,准备工作...");
	}
	
	private void after() {
		System.out.println("工作完成,走人....");
	}

}

Proxy.newProxyInstance()—产生代理对象

IRent rent  =(IRent) new HouseProxy(new HouseKeeper()).createProxy();	
  		rent.xuequ();
  • 优点: 可以随着接口的变化而变化。
  • 缺点: 依赖于接口

CGLIB代理
不使用接口实现代理, 面向的继承原则

public class CGLIBProxy implements MethodInterceptor {

	private Object real;

	public CGLIBProxy(Object real) {

		this.real = real;
	}

	public Object createProxy() {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(this.real.getClass());
		enhancer.setCallback(this);
		return enhancer.create();

	}

	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

		System.out.println("我来了....");
		Object retVal = proxy.invokeSuper(obj, args);
		System.out.println("我走了...");
		return retVal;
	}

}
		HouseKeeper hs = (HouseKeeper) new CGLIBProxy(new HouseKeeper()).createProxy();
		
		hs.rent();

CGLIB代理总结: CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

你可能感兴趣的:(java,设计模式,spring,aop,代理模式,工厂方法模式)